5157789774
previously, we had a singleton 'main' flag to indicate a site can be used for updates. with this new approach, we can get rid of the 'primary' concept. each site can be used for updates or not.
112 lines
4.8 KiB
JavaScript
112 lines
4.8 KiB
JavaScript
'use strict';
|
|
|
|
const child_process = require('node:child_process'),
|
|
crypto = require('node:crypto'),
|
|
fs = require('node:fs'),
|
|
path = require('node:path'),
|
|
paths = require('../src/paths.js');
|
|
|
|
async function deleteOldSettings(db) {
|
|
await db.runSql('DELETE FROM settings WHERE name=? OR name=? OR name=?', [ 'backup_storage', 'backup_limits', 'backup_policy' ]);
|
|
}
|
|
|
|
exports.up = async function (db) {
|
|
const cmd = 'CREATE TABLE IF NOT EXISTS backupSites(' +
|
|
'id VARCHAR(128) NOT NULL UNIQUE,' +
|
|
'name VARCHAR(128) NOT NULL,' +
|
|
'provider VARCHAR(32) NOT NULL,' +
|
|
'configJson TEXT,' +
|
|
'limitsJson TEXT,' +
|
|
'retentionJson TEXT,' +
|
|
'encryptionJson TEXT,' +
|
|
'integrityKeyPairJson TEXT,' +
|
|
'format VARCHAR(16) NOT NULL,' +
|
|
'schedule VARCHAR(128),' +
|
|
'enabledForUpdates BOOLEAN DEFAULT false,' +
|
|
'contentsJson TEXT,' +
|
|
'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 domainCountResults = await db.runSql('SELECT COUNT(*) AS total FROM domains');
|
|
if (domainCountResults[0].total === 0) {
|
|
console.log('This cloudron is not activated. Deleting the default backup config from 20171205124434-settings-default-backupConfig.js'); // will be added at provision time
|
|
await deleteOldSettings(db);
|
|
return;
|
|
}
|
|
|
|
const name = 'Default', enabledForUpdates = true;
|
|
let config = null, limits = null, encryption = null, format = null, provider = null;
|
|
let retention = { keepWithinSecs: 2 * 24 * 60 * 60 };
|
|
let schedule = '00 00 23 * * *';
|
|
const integrityKeyPair = crypto.generateKeyPairSync('ed25519', {
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
});
|
|
|
|
const id = `bc-${crypto.randomUUID()}`;
|
|
|
|
// convert existing configuration into a backup target
|
|
for (const r of results) {
|
|
if (r.name === 'backup_storage') {
|
|
const tmp = JSON.parse(r.value);
|
|
// provider is top level
|
|
provider = tmp.provider;
|
|
|
|
if (provider === 'noop') return await deleteOldSettings(db); // noop is migrated as 0 sites. user has to manually update with skipBackup
|
|
|
|
// the s3 and filesystem backend use the _provider internal property
|
|
if (provider !== 'gcs') tmp._provider = tmp.provider;
|
|
delete tmp.provider;
|
|
|
|
// backupFolder is now backupDir
|
|
if ('backupFolder' in tmp) {
|
|
tmp.backupDir = tmp.backupFolder;
|
|
delete tmp.backupFolder;
|
|
}
|
|
|
|
// encryption is not part of config anymore, it is top level
|
|
encryption = tmp.encryption || null;
|
|
delete tmp.encryption;
|
|
|
|
// format is not part of config anymore, it is top level
|
|
format = tmp.format;
|
|
delete tmp.format;
|
|
|
|
// previous releases only had a single "managed" mount at /mnt/cloudronbackup .
|
|
// new release has it under /mnt/managedbackups .
|
|
if (tmp.mountOptions) tmp._managedMountPath = '/mnt/cloudronbackup';
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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}/ {} +`);
|
|
|
|
console.log(`Delete any existing rsync cache files since old one has no integrity information`);
|
|
child_process.execSync(`rm -f ${targetInfoDir}/*.cache`);
|
|
|
|
await db.runSql('START TRANSACTION');
|
|
await db.runSql('INSERT INTO backupSites (id, name, provider, configJson, limitsJson, integrityKeyPairJson, retentionJson, schedule, encryptionJson, format, enabledForUpdates) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
[ id, name, provider, JSON.stringify(config), JSON.stringify(limits), JSON.stringify(integrityKeyPair), JSON.stringify(retention), schedule, JSON.stringify(encryption), format, enabledForUpdates ]);
|
|
await deleteOldSettings(db);
|
|
await db.runSql('COMMIT');
|
|
};
|
|
|
|
exports.down = async function (db) {
|
|
await db.runSql('DROP TABLE backupSites');
|
|
};
|