Skip to content

Commit

Permalink
Dowload Backup #129
Browse files Browse the repository at this point in the history
  • Loading branch information
haimkastner committed Apr 29, 2020
1 parent 462531a commit 9153038
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 0 deletions.
5 changes: 5 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
},
"license": "ISC",
"dependencies": {
"adm-zip": "^0.4.14",
"async-mqtt": "^2.3.0",
"await-semaphore": "^0.1.3",
"bcryptjs": "^2.4.3",
Expand Down
1 change: 1 addition & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { logger } from './utilities/logger';

// controllers need to be referenced in order to get crawled by the TSOA generator
import './controllers/authController';
import './controllers/backupController';
import './controllers/devicesController';
import './controllers/feedController';
import './controllers/iftttController';
Expand Down
48 changes: 48 additions & 0 deletions backend/src/controllers/backupController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as AdmZip from 'adm-zip';
import * as express from 'express';
import * as fse from 'fs-extra';
import * as path from 'path';
import { Controller, Get, Request, Response, Route, Security } from 'tsoa';
import { MinionsBlSingleton } from '../business-layer/minionsBl';
import { OperationsBlSingleton } from '../business-layer/operationsBl';
import { TimelineBlSingleton } from '../business-layer/timelineBl';
import { TimingsBlSingleton } from '../business-layer/timingsBl';
import { ErrorResponse, UpdateResults, VersionInfo, VersionUpdateStatus } from '../models/sharedInterfaces';
import { logger } from '../utilities/logger';

@Route('backup')
export class BackupController extends Controller {

/**
* Get the current server data as a ZIP file
*/
@Security('adminAuth')
@Response<ErrorResponse>(501, 'Server error')
@Get()
public async getSettingsBackup(@Request() request: express.Request) {
const zip = new AdmZip();

const minions = await MinionsBlSingleton.getMinions();
const minionsData = JSON.stringify(minions, null, 2);
zip.addFile("minions.json", Buffer.alloc(minionsData.length, minionsData), "minions");

const operations = await OperationsBlSingleton.getOperations();
const operationsData = JSON.stringify(operations, null, 2);
zip.addFile("operations.json", Buffer.alloc(operationsData.length, operationsData), "operations");

const timings = await TimingsBlSingleton.getTimings();
const timingsData = JSON.stringify(timings, null, 2);
zip.addFile("timings.json", Buffer.alloc(timingsData.length, timingsData), "timings");

const timeline = await TimelineBlSingleton.getTimeline();
const timelineData = JSON.stringify(timeline, null, 2);
zip.addFile("timeline.json", Buffer.alloc(timelineData.length, timelineData), "timeline");

const res = request.res as express.Response;
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename=casanet_backup_${new Date().toLocaleDateString()}.zip`);

res.end(zip.toBuffer());
logger.info(`[backup ctrl] The backup sent successfully to the admin ${request.user}`);
}
}
25 changes: 25 additions & 0 deletions backend/src/routers/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { IftttController } from './../controllers/iftttController';
import { VersionsController } from './../controllers/versionsController';
import { RfController } from './../controllers/radioFrequencyController';
import { LogsController } from './../controllers/logsController';
import { BackupController } from './../controllers/backupController';
import { expressAuthentication } from './../security/authentication';
import * as express from 'express';
import { ErrorResponse, User } from '../../../backend/src/models/sharedInterfaces';
Expand Down Expand Up @@ -1883,6 +1884,30 @@ export function RegisterRoutes(app: express.Express) {
const promise = controller.getLastLogs.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, next);
});
app.get('/API/backup',
authenticateMiddleware([{ "adminAuth": [] }]),
function(request: any, response: any, next: any) {
const args = {
request: { "in": "request", "name": "request", "required": true, "dataType": "object" },
};

let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request);
} catch (err) {
response.status(422).send({
responseCode: 1422,
message: JSON.stringify(err.fields),
} as ErrorResponse);
return;
}

const controller = new BackupController();


const promise = controller.getSettingsBackup.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, next);
});

function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return (request: any, _response: any, next: any) => {
Expand Down
17 changes: 17 additions & 0 deletions backend/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,23 @@ paths:
-
adminAuth: []
parameters: []
/backup:
get:
operationId: GetSettingsBackup
produces:
- application/json
responses:
'204':
description: 'No content'
'501':
description: 'Server error'
schema:
$ref: '#/definitions/ErrorResponse'
description: 'Get the last logs of the local-server (download as text file)'
security:
-
adminAuth: []
parameters: []
produces:
- application/json
swagger: '2.0'
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/app/core/sidebar/sidebar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@
</div>
</div>

<div *ngIf="userProfile.scope === 'adminAuth'" class="settings-viewer-wrapper">
<div class="settings-viewer-title">
<div class="settings-viewer-title-spacer"> {{ 'DOWNLOAD_BACKUP' | translate }}</div>
<button mat-icon-button matTooltipPosition="below" (click)="downloadBackup()"
matTooltip="{{'DOWNLOAD_BACKUP' | translate}}">
<mat-icon>settings_backup_restore</mat-icon>
</button>
</div>
</div>

<div class="settings-viewer-wrapper">
<div class="settings-viewer-title">
<div class="settings-viewer-title-spacer"> {{ 'PWA' | translate }}</div>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/app/core/sidebar/sidebar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,8 @@ export class SidebarComponent implements OnInit, OnDestroy {
public async downloadLogs() {
await this.settingsService.downloadLogs();
}

public async downloadBackup() {
await this.settingsService.downloadBackup();
}
}
4 changes: 4 additions & 0 deletions frontend/src/app/services/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ export class SettingsService {
window.open(`${environment.baseUrl}/logs`);
}

public async downloadBackup() {
window.open(`${environment.baseUrl}/backup`);
}

private sleep(delayMs: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
"OVERRIDE_LOCKS" : "Override Locks",
"DOWNLOAD_LOGS" : "Download Logs",
"MUSIC" : "Music",
"DOWNLOAD_BACKUP" : "Download backup copy",
"notConfigured": "Remote server not configured",
"cantReachRemoteServer": "Remote server offline",
"authorizationFail": "Local server fail to authenticate",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/i18n/he.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
"DOWNLOAD_LOGS" : "הורדת לוגים",
"OVERRIDE_LOCKS" : "דרוס נעילות",
"MUSIC" : "מוזיקה",
"DOWNLOAD_BACKUP" : "הורד עותק גיבוי",
"notConfigured": "לא הוגדר שרת מרוחק",
"cantReachRemoteServer": "שרת מרוחק לא נגיש",
"authorizationFail": "אימות מול שרת מרוחק נכשל",
Expand Down

0 comments on commit 9153038

Please sign in to comment.