Files
cloudron-box/migrations/20250724141339-backups-add-siteId.js
2025-11-17 09:08:46 +01:00

59 lines
3.5 KiB
JavaScript

'use strict';
const crypto = require('node:crypto'),
fs = require('node:fs'),
path = require('node:path'),
paths = require('../src/paths.js'),
safe = require('safetydance');
exports.up = async function(db) {
const backups = await db.runSql('SELECT format, COUNT(*) AS count FROM backups GROUP BY format WITH ROLLUP', []); // https://dev.mysql.com/doc/refman/8.4/en/group-by-modifiers.html
let tgzCount = 0, rsyncCount = 0, totalCount = 0;
for (const r of backups) {
if (r.format === 'tgz') tgzCount = r.count;
else if (r.format === 'rsync') rsyncCount = r.count;
else if (r.format === null) totalCount = r.count; // ROLLUP gives count by group and also an accumulated "null"
}
let theOneFormat = null;
if (tgzCount === totalCount) theOneFormat = 'tgz';
else if (rsyncCount === totalCount) theOneFormat = 'rsync';
console.log(`Backup counts. rsync: ${rsyncCount} tgz: ${tgzCount} total: ${totalCount} . theOneFormat: ${theOneFormat}`);
const backupSites = await db.runSql('SELECT * FROM backupSites');
const currentBackupSite = backupSites.length ? backupSites[0] : null;
let cloneBackupSite = null;
if (totalCount && currentBackupSite.format !== theOneFormat) {
const cloneId = crypto.randomUUID();
cloneBackupSite = Object.assign({}, backupSites[0], { id: cloneId });
cloneBackupSite.format = currentBackupSite.format === 'rsync' ? 'tgz' : 'rsync';
cloneBackupSite.name = 'Copy of Default';
cloneBackupSite.schedule = 'never';
cloneBackupSite._managedMountPath = path.join(paths.MANAGED_BACKUP_MOUNT_DIR, cloneId); // this won't work until the user remounts
cloneBackupSite.enableForUpdates = false;
console.log(`Existing format is ${currentBackupSite.format} . Adding clone backup site for ${cloneBackupSite.format}`);
await db.runSql('INSERT INTO backupSites (id, name, provider, configJson, limitsJson, integrityKeyPairJson, retentionJson, schedule, encryptionJson, format, enableForUpdates) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ cloneBackupSite.id, cloneBackupSite.name, cloneBackupSite.provider, cloneBackupSite.configJson, cloneBackupSite.limitsJson, cloneBackupSite.integrityKeyPairJson, cloneBackupSite.retentionJson, cloneBackupSite.schedule,
cloneBackupSite.encryptionJson, cloneBackupSite.format, cloneBackupSite.enableForUpdates ]);
const cloneInfoDir = path.join(paths.BACKUP_INFO_DIR, cloneId);
fs.mkdirSync(cloneInfoDir, { recursive: true }); // this gets chown'ed by start.sh
}
await db.runSql('ALTER TABLE backups ADD siteId VARCHAR(128)');
if (totalCount) {
const ext = safe.JSON.parse(currentBackupSite.encryptionJson) ? '.tar.gz.enc' : '.tar.gz'; // assume user had encryption in the past too
console.log(`Adjusting remotePath of existing tgz backups with ${ext}`);
await db.runSql('UPDATE backups SET remotePath=CONCAT(remotePath, ?) WHERE format=?', [ ext, 'tgz' ]);
await db.runSql('UPDATE backups SET siteId=? WHERE format=?', [ currentBackupSite.id, currentBackupSite.format ]);
if (cloneBackupSite) await db.runSql('UPDATE backups SET siteId=? WHERE format=?', [ cloneBackupSite.id, cloneBackupSite.format ]);
}
await db.runSql('ALTER TABLE backups MODIFY siteId VARCHAR(128) NOT NULL');
await db.runSql('ALTER TABLE backups ADD FOREIGN KEY(siteId) REFERENCES backupSites(id)');
await db.runSql('ALTER TABLE backups DROP COLUMN format');
};
exports.down = async function(/*db*/) {
};