diff --git a/migrations/20250724141339-backups-add-targetId.js b/migrations/20250724141339-backups-add-targetId.js index 916934b81..e0c4a9945 100644 --- a/migrations/20250724141339-backups-add-targetId.js +++ b/migrations/20250724141339-backups-add-targetId.js @@ -22,6 +22,7 @@ exports.up = async function(db) { clone = Object.assign({}, results[0], { id: `bc-${uuid.v4()}` }); clone.format = current.format === 'rsync' ? 'tgz' : 'rsync'; clone.priority = false; + clone.schedule = 'never'; console.log(`Existing format is ${current.format} . Adding clone backup target for ${clone.format}`); await db.runSql('INSERT INTO backupTargets (id, label, configJson, limitsJson, retentionJson, schedule, encryptionJson, format, priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', diff --git a/src/backuptargets.js b/src/backuptargets.js index a85d4154e..86a3e0bad 100644 --- a/src/backuptargets.js +++ b/src/backuptargets.js @@ -230,6 +230,9 @@ async function del(target, auditSource) { if (result[1].affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Target not found'); // await eventlog.add(eventlog.ACTION_ARCHIVES_DEL, auditSource, { id: archive.id, backupId: archive.backupId }); + target.schedule = constants.CRON_PATTERN_NEVER; + await cron.handleBackupScheduleChanged(target); + debug('del: clearing backup cache'); cleanupCacheFilesSync(); } diff --git a/src/cron.js b/src/cron.js index b11bd070e..b6157f447 100644 --- a/src/cron.js +++ b/src/cron.js @@ -43,11 +43,12 @@ const appHealthMonitor = require('./apphealthmonitor.js'), safe = require('safetydance'), scheduler = require('./scheduler.js'), system = require('./system.js'), - updater = require('./updater.js'); + updater = require('./updater.js'), + util = require('util'); const gJobs = { autoUpdater: null, - backup: null, + backups: new Map(), updateChecker: null, systemChecks: null, mailStatusCheck: null, @@ -178,7 +179,6 @@ async function startJobs() { start: true }); - gJobs.checkDomainConfigs = CronJob.from({ cronTime: `00 ${minute} 5 * * *`, // once a day onTick: async () => await safe(domains.checkConfigs(AuditSource.CRON), { debug }), @@ -203,7 +203,9 @@ async function startJobs() { start: true }); - await handleBackupScheduleChanged(await backupTargets._getDefault()); + for (const backupTarget of await backupTargets.list(1, 100)) { + await handleBackupScheduleChanged(backupTarget); + } await handleAutoupdatePatternChanged(await updater.getAutoupdatePattern()); await handleDynamicDnsChanged(await network.getDynamicDns()); await handleExternalLdapChanged(await externalLdap.getConfig()); @@ -214,17 +216,24 @@ async function handleBackupScheduleChanged(target) { const tz = await cloudron.getTimeZone(); - debug(`backupPolicyChanged: schedule ${target.schedule} (${tz})`); + debug(`handleBackupScheduleChanged: schedule ${target.schedule} (${tz})`); - if (gJobs.backup) gJobs.backup.stop(); - gJobs.backup = null; + if (gJobs.backups.has(target.id)) gJobs.backups.get(target.id).stop(); + gJobs.backups.delete(target.id); - gJobs.backup = CronJob.from({ + if (target.schedule === constants.CRON_PATTERN_NEVER) return; + + const job = CronJob.from({ cronTime: target.schedule, - onTick: async () => await safe(backupTargets.startBackupTask(AuditSource.CRON), { debug }), + onTick: async () => { + const target = await backupTargets.get(target.id); + if (!target) return; + await safe(backupTargets.startBackupTask(target, AuditSource.CRON), { debug }); + }, start: true, timeZone: tz }); + gJobs.backups.set(target.id, job); } async function handleTimeZoneChanged(tz) { @@ -290,9 +299,14 @@ async function handleExternalLdapChanged(config) { } async function stopJobs() { - for (const job in gJobs) { - if (!gJobs[job]) continue; - gJobs[job].stop(); - gJobs[job] = null; + for (const jobName in gJobs) { + if (!gJobs[jobName]) continue; + if (util.types.isMap(gJobs[jobName])) { + gJobs[jobName].forEach((job) => job.stop()); + gJobs[jobName] = new Map(); + } else { + gJobs[jobName].stop(); + gJobs[jobName] = null; + } } }