mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
chore: relocate backend into apps/api ahead of monorepo merge
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name : cronRegistry.util.js
|
||||
* Type : Utility
|
||||
* Description : Shared machinery for settings-backed cron jobs (the 4 jobs
|
||||
* that emit notifications and are configurable from
|
||||
* /admin/notifications/settings). Not every cron job in the
|
||||
* app goes through this — jobs with no notification tied to
|
||||
* them (e.g. lift_expired_bans) keep using node-cron directly.
|
||||
*
|
||||
* startSettingsBackedJobs() reads each job's schedule from
|
||||
* cron_notification_settings (falling back to — and seeding —
|
||||
* the job's own hardcoded default on first boot), then keeps
|
||||
* a live reference to the scheduled task so it can be swapped
|
||||
* out later via rescheduleJob() without a server restart.
|
||||
*
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Jul. 2, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
'use strict';
|
||||
|
||||
const cron = require('node-cron');
|
||||
const CronNotificationSetting = require('../models/notifications/cron_notification_setting.mdl');
|
||||
|
||||
// job_name -> { task: ScheduledTask, run: fn }
|
||||
const runningTasks = new Map();
|
||||
|
||||
// CockroachDB can't run Sequelize's findOrCreate() — it wraps the insert in a
|
||||
// pg_temp PL/pgSQL function to atomically catch unique_violation, which
|
||||
// CockroachDB rejects ("cannot create user-defined functions under a temporary
|
||||
// schema"). Plain findOne-then-create sidesteps it; the race window (two boots
|
||||
// racing to seed the same job_name) is a non-issue here — jobs are seeded once.
|
||||
async function getOrCreateSetting(jobName, defaultSchedule) {
|
||||
let row = await CronNotificationSetting.findOne({ where: { job_name: jobName } });
|
||||
if (row) return row;
|
||||
|
||||
try {
|
||||
row = await CronNotificationSetting.create({ job_name: jobName, enabled: true, schedule: defaultSchedule });
|
||||
} catch (err) {
|
||||
row = await CronNotificationSetting.findOne({ where: { job_name: jobName } });
|
||||
if (!row) throw err;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
async function startSettingsBackedJobs(jobs, scopeLabel) {
|
||||
const registered = [];
|
||||
|
||||
for (const { name, schedule: defaultSchedule, run } of jobs) {
|
||||
let schedule = defaultSchedule;
|
||||
try {
|
||||
const settings = await getOrCreateSetting(name, defaultSchedule);
|
||||
schedule = settings.schedule || defaultSchedule;
|
||||
} catch (err) {
|
||||
console.error(`[CRON][${scopeLabel}] Failed to load settings for "${name}", using hardcoded default:`, err);
|
||||
}
|
||||
|
||||
if (!cron.validate(schedule)) {
|
||||
console.error(`[CRON][${scopeLabel}] Invalid schedule for "${name}": "${schedule}" — skipped.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const task = cron.schedule(schedule, run);
|
||||
runningTasks.set(name, { task, run });
|
||||
registered.push({ name, scope: scopeLabel, schedule });
|
||||
}
|
||||
|
||||
return registered;
|
||||
}
|
||||
|
||||
// Live-swap a running job's schedule — used by notificationSettings.controller.js
|
||||
// after an admin picks a new preset. No server restart required.
|
||||
function rescheduleJob(jobName, newSchedule) {
|
||||
const entry = runningTasks.get(jobName);
|
||||
if (!entry) throw new Error(`No running cron task found for "${jobName}".`);
|
||||
if (!cron.validate(newSchedule)) throw new Error(`Invalid cron schedule: "${newSchedule}".`);
|
||||
|
||||
entry.task.stop();
|
||||
const task = cron.schedule(newSchedule, entry.run);
|
||||
runningTasks.set(jobName, { task, run: entry.run });
|
||||
}
|
||||
|
||||
module.exports = { startSettingsBackedJobs, rescheduleJob, getOrCreateSetting };
|
||||
Reference in New Issue
Block a user