Files
cloudron-box/migrations/20250724102340-backupTargets-create-table.js
T
Girish Ramakrishnan ae3a34287a backup target: create snapshot and cache files per target
snapshot file tracks the snapshot directory. when app gets deleted,
the cleaner will remove the upstream snapshot directory when it runs.

cache files are used in rsync logic to track what was uploading into
snapshot in the previous run without needing to rescan upstream.
2025-07-30 11:44:42 +02:00

80 lines
3.1 KiB
JavaScript

'use strict';
const child_process = require('child_process'),
crypto = require('crypto'),
fs = require('fs'),
path = require('path'),
paths = require('../src/paths.js');
exports.up = async function (db) {
const cmd = 'CREATE TABLE IF NOT EXISTS backupTargets(' +
'id VARCHAR(128) NOT NULL UNIQUE,' +
'label VARCHAR(128),' +
'provider VARCHAR(32) NOT NULL,' +
'configJson TEXT,' +
'limitsJson TEXT,' +
'retentionJson TEXT,' +
'encryptionJson TEXT,' +
'format VARCHAR(16) NOT NULL,' +
'schedule VARCHAR(128),' +
'main BOOLEAN DEFAULT false,' +
'creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,' +
'ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,' +
'PRIMARY KEY (id)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin';
await db.runSql(cmd);
const results = await db.runSql('SELECT name, value FROM settings WHERE name=? OR name=? OR name=?', [ 'backup_storage', 'backup_limits', 'backup_policy' ]);
const label = 'Default', main = true;
let config = null, limits = null, encryption = null, format = null, provider = null;
let retention = { keepWithinSecs: 2 * 24 * 60 * 60 };
let schedule = '00 00 23 * * *';
if (results.length === 0) {
provider = 'filesystem';
config = { backupFolder: paths.DEFAULT_BACKUP_DIR };
format = 'tgz';
} else {
for (const r of results) {
if (r.name === 'backup_storage') {
const tmp = JSON.parse(r.value);
encryption = tmp.encryption || null;
delete tmp.encryption;
format = tmp.format;
delete tmp.format;
provider = tmp.provider;
delete tmp.provider;
config = tmp;
} else if (r.name === 'backup_limits') {
limits = JSON.parse(r.value);
} else if (r.name === 'backup_policy') {
const tmp = JSON.parse(r.value);
retention = tmp.retention;
schedule = tmp.schedule;
}
}
}
await db.runSql('START TRANSACTION');
const id = `bc-${crypto.randomUUID()}`;
const targetInfoDir = path.join(paths.BACKUP_INFO_DIR, id);
console.log(`Moving existing cache and snapshot file into ${targetInfoDir}`);
fs.mkdirSync(targetInfoDir, { recursive: true });
child_process.execSync(`find ${paths.BACKUP_INFO_DIR}/ -maxdepth 1 -type f -exec mv -t ${targetInfoDir}/ {} +`);
await db.runSql('INSERT INTO backupTargets (id, label, provider, configJson, limitsJson, retentionJson, schedule, encryptionJson, format, main) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ id, label, provider, JSON.stringify(config), JSON.stringify(limits), JSON.stringify(retention), schedule, JSON.stringify(encryption), format, main ]);
await db.runSql('DELETE FROM settings WHERE name=? OR name=? OR name=?', [ 'backup_storage', 'backup_limits', 'backup_policy' ]);
await db.runSql('COMMIT');
};
exports.down = async function (db) {
await db.runSql('DROP TABLE backupTargets');
};