Files
NodeBB/src/cron.js
Barış Soner Uşaklı 2187f160a9 add active flag to jobs
dont delete everything on startup
2026-03-10 22:32:36 -04:00

100 lines
2.6 KiB
JavaScript

'use strict';
const nconf = require('nconf');
const { CronJob } = require('cron');
const cronstrue = require('cronstrue');
const winston = require('winston');
const db = require('./database');
const utils = require('./utils');
const jobs = Object.create(null);
exports.markJobsInactive = async function () {
const jobs = await db.getSortedSetRange('cronJobs', 0, -1);
await db.setObject(jobs.map(name => `cronJob:${name}`), { active: 0 });
};
exports.addJob = async function (options) {
const {
name,
cronTime,
onTick,
onComplete = null,
start = true,
runOnInit = false,
runOnAllNodes = false,
} = options;
const isJobEnabled = nconf.get('runJobs');
if (!isJobEnabled && !runOnAllNodes) {
return;
}
if (!name || !cronTime || typeof onTick !== 'function') {
throw new Error('[cron] Invalid options');
}
if (Object.hasOwn(jobs, name)) {
throw new Error('[cron] Job with that name already exists');
}
const job = new CronJob(cronTime, async function () {
const start = Date.now();
try {
await db.setObjectField(`cronJob:${name}`, 'running', 1);
await onTick();
await db.deleteObjectField(`cronJob:${name}`, 'lastError');
} catch (err) {
winston.error(`[cron] ${err.stack}`);
await db.setObjectField(`cronJob:${name}`, 'lastError', err.stack);
} finally {
await db.setObject(`cronJob:${name}`, {
running: 0,
duration: Date.now() - start,
nextRun: job.nextDate().toMillis(),
});
}
}, onComplete, start, null, null, runOnInit);
jobs[name] = job;
await db.sortedSetAdd('cronJobs', Date.now(), name);
await db.setObject(`cronJob:${name}`, {
name,
cronTime,
cronTimeHuman: cronstrue.toString(cronTime),
nextRun: job.nextDate().toMillis(),
running: runOnInit ? 1 : 0,
active: 1,
});
winston.verbose(`[cron/jobs] Registered job: ${name} (${cronTime})`);
return job;
};
exports.getJobs = async function () {
const jobNames = await db.getSortedSetRange('cronJobs', 0, -1);
const jobs = await db.getObjects(jobNames.map(name => `cronJob:${name}`));
jobs.forEach((job) => {
if (job) {
job.active = parseInt(job.active, 10) === 1;
job.running = parseInt(job.running, 10) === 1;
job.duration = job.duration || 0;
job.durationReadable = formatDuration(job.duration);
job.nextRunISO = utils.toISOString(job.nextRun);
}
});
jobs.sort((a, b) => b.cronTimeHuman.localeCompare(a.cronTimeHuman));
return jobs;
};
function formatDuration(ms) {
const totalSeconds = Math.floor(ms / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
if (minutes > 0) {
return `${minutes}m${String(seconds).padStart(2, '0')}s`;
}
return `${seconds}s`;
}