Skip to content

Commit

Permalink
Merge branch 'main' of github.com:koush/scrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Jan 21, 2024
2 parents 72f79ea + 4198869 commit c446ddc
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 54 deletions.
2 changes: 1 addition & 1 deletion install/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Home Assistant Addon Configuration
name: Scrypted
version: "18-jammy-full.s6-v0.85.0"
version: "18-jammy-full.s6-v0.88.0"
slug: scrypted
description: Scrypted is a high performance home video integration and automation platform
url: "https://github.com/koush/scrypted"
Expand Down
4 changes: 4 additions & 0 deletions server/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { getNpmPackageInfo, PluginComponent } from './services/plugin';
import { ServiceControl } from './services/service-control';
import { UsersService } from './services/users';
import { getState, ScryptedStateManager, setState } from './state';
import { Backup } from './services/backup';

interface DeviceProxyPair {
handler: PluginDeviceProxyHandler;
Expand Down Expand Up @@ -102,6 +103,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
addressSettings = new AddressSettings(this);
usersService = new UsersService(this);
info = new Info();
backup = new Backup(this);
pluginHosts = new Map<string, RuntimeHost>();

constructor(public mainFilename: string, public datastore: Level, insecure: http.Server, secure: https.Server, app: express.Application) {
Expand Down Expand Up @@ -443,6 +445,8 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
return this.addressSettings;
case "users":
return this.usersService;
case 'backup':
return this.backup;
}
}

Expand Down
55 changes: 2 additions & 53 deletions server/src/scrypted-server-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,73 +339,22 @@ async function start(mainFilename: string, options?: {

app.post('/web/component/restore', async (req, res) => {
const buffers: Buffer[] = [];
let zip: AdmZip;
req.on('data', b => buffers.push(b));
try {
await once(req, 'end');
zip = new AdmZip(Buffer.concat(buffers));
if (!zip.test())
throw new Error('backup zip test failed.');
await scrypted.backup.restore(Buffer.concat(buffers))
}
catch (e) {
res.send({
error: "Error during restore.",
});
return;
}
try {
scrypted.kill();
await sleep(5000);
await db.close();

await fs.promises.rm(volumeDir, {
recursive: true,
force: true,
});

await fs.promises.mkdir(volumeDir, {
recursive: true
});

zip.extractAllTo(dbPath, true);

res.send({
success: true,
});
}
catch (e) {
res.send({
error: "Error during restore.",
});
}

scrypted.serviceControl.restart();
});

app.get('/web/component/backup', async (req, res) => {
try {
const backupDbPath = path.join(volumeDir, 'backup.db');
await fs.promises.rm(backupDbPath, {
recursive: true,
force: true,
});

const backupDb = new Level(backupDbPath);
await backupDb.open();
for await (const [key, value] of db.iterator()) {
await backupDb.put(key, value);
}
await backupDb.close();

const backupZip = path.join(volumeDir, 'backup.zip');
await fs.promises.rm(backupZip, {
recursive: true,
force: true,
});

const zip = new AdmZip();
await zip.addLocalFolderPromise(backupDbPath, {});
const zipBuffer = await zip.toBufferPromise();
const zipBuffer = await scrypted.backup.createBackup();
// the file is a normal zip file, but an extension is added to prevent safari, etc, from unzipping it automatically.
res.header('Content-Disposition', 'attachment; filename="scrypted.zip.backup"')
res.send(zipBuffer);
Expand Down
63 changes: 63 additions & 0 deletions server/src/services/backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import fs from 'fs';
import path from 'path';
import Level from '../level';
import { sleep } from '../sleep';
import { getScryptedVolume } from '../plugin/plugin-volume';
import AdmZip from 'adm-zip';
import { ScryptedRuntime } from '../runtime';

export class Backup {
constructor(public runtime: ScryptedRuntime) {}

async createBackup(): Promise<Buffer> {
const volumeDir = getScryptedVolume();

const backupDbPath = path.join(volumeDir, 'backup.db');
await fs.promises.rm(backupDbPath, {
recursive: true,
force: true,
});

const backupDb = new Level(backupDbPath);
await backupDb.open();
for await (const [key, value] of this.runtime.datastore.iterator()) {
await backupDb.put(key, value);
}
await backupDb.close();

const backupZip = path.join(volumeDir, 'backup.zip');
await fs.promises.rm(backupZip, {
recursive: true,
force: true,
});

const zip = new AdmZip();
await zip.addLocalFolderPromise(backupDbPath, {});
return zip.toBufferPromise();
}

async restore(b: Buffer): Promise<void> {
const volumeDir = getScryptedVolume();
const dbPath = path.join(volumeDir, 'scrypted.db');

const zip = new AdmZip(b);
if (!zip.test())
throw new Error('backup zip test failed.');

this.runtime.kill();
await sleep(5000);
await this.runtime.datastore.close();

await fs.promises.rm(volumeDir, {
recursive: true,
force: true,
});

await fs.promises.mkdir(volumeDir, {
recursive: true
});

zip.extractAllTo(dbPath, true);
this.runtime.serviceControl.restart();
}
}

0 comments on commit c446ddc

Please sign in to comment.