Files
cloudron-box/migrations/20250724141339-backups-add-targetId.js
T
Girish Ramakrishnan 12e073e8cf use node: prefix for requires
mostly because code is being autogenerated by all the AI stuff using
this prefix. it's also used in the stack trace.
2025-08-14 12:55:35 +05:30

56 lines
3.2 KiB
JavaScript

'use strict';
const crypto = require('node:crypto'),
path = require('node:path'),
paths = require('../src/paths.js');
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;
}
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 backupTargets = await db.runSql('SELECT * FROM backupTargets');
const currentBackupTarget = backupTargets[0];
let cloneBackupTarget = null;
if (totalCount && currentBackupTarget.format !== theOneFormat) {
const cloneId = `bc-${crypto.randomUUID()}`;
cloneBackupTarget = Object.assign({}, backupTargets[0], { id: cloneId });
cloneBackupTarget.format = currentBackupTarget.format === 'rsync' ? 'tgz' : 'rsync';
cloneBackupTarget.priority = false;
cloneBackupTarget.name = 'Copy of Default';
cloneBackupTarget.schedule = 'never';
cloneBackupTarget._managedMountPath = path.join(paths.MANAGED_BACKUP_MOUNT_DIR, cloneId); // this won't work until the user remounts
console.log(`Existing format is ${currentBackupTarget.format} . Adding clone backup target for ${cloneBackupTarget.format}`);
await db.runSql('INSERT INTO backupTargets (id, name, configJson, limitsJson, retentionJson, schedule, encryptionJson, format, priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ cloneBackupTarget.id, cloneBackupTarget.name, cloneBackupTarget.configJson, cloneBackupTarget.limitsJson, cloneBackupTarget.retentionJson, cloneBackupTarget.schedule,
cloneBackupTarget.encryptionJson, cloneBackupTarget.format, cloneBackupTarget.priority ]);
}
await db.runSql('ALTER TABLE backups ADD targetId VARCHAR(128)');
if (totalCount) {
if (currentBackupTarget.format === 'tgz') {
const ext = currentBackupTarget.encryptionJson ? '.tar.gz.enc' : '.tar.gz';
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 targetId=? WHERE format=?', [ currentBackupTarget.id, currentBackupTarget.format ]);
if (cloneBackupTarget) await db.runSql('UPDATE backups SET targetId=? WHERE format=?', [ cloneBackupTarget.id, cloneBackupTarget.format ]);
}
await db.runSql('ALTER TABLE backups MODIFY targetId VARCHAR(128) NOT NULL');
await db.runSql('ALTER TABLE backups ADD FOREIGN KEY(targetId) REFERENCES backupTargets(id)');
await db.runSql('ALTER TABLE backups DROP COLUMN format');
};
exports.down = async function(/*db*/) {
};