Files
cloudron-box/migrations/20250724102340-backupSites-create-table.js
Girish Ramakrishnan 36aa641cb9 migrate to "export default"
also, set no-use-before-define in linter
2026-02-14 15:43:24 +01:00

113 lines
5.0 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').default;
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),' +
'enableForUpdates 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', enableForUpdates = 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 = 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 }); // this gets chown'ed by start.sh
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, enableForUpdates) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ id, name, provider, JSON.stringify(config), JSON.stringify(limits), JSON.stringify(integrityKeyPair), JSON.stringify(retention), schedule, encryption ? JSON.stringify(encryption) : null, format, enableForUpdates ]);
await deleteOldSettings(db);
await db.runSql('UPDATE tasks SET type=? WHERE type=?', [ `backup_${id}`, 'backup' ]); // migrate the tasks
await db.runSql('COMMIT');
};
exports.down = async function (db) {
await db.runSql('DROP TABLE backupSites');
};