import type { ScheduledTask } from "node-cron"; import { objectEntries, objectKeys } from "@homarr/common"; import { db } from "@homarr/db"; import type { JobCallback } from "./creator"; import type { Logger } from "./logger"; import { jobRegistry } from "./registry"; type Jobs = { [name in TAllowedNames]: ReturnType>; }; export interface CreateCronJobGroupCreatorOptions { logger: Logger; } export const createJobGroupCreator = ( options: CreateCronJobGroupCreatorOptions, ) => { return >(jobs: TJobs) => { options.logger.logDebug(`Creating job group with ${Object.keys(jobs).length} jobs.`); for (const [key, job] of objectEntries(jobs)) { if (typeof key !== "string" || typeof job.name !== "string") continue; options.logger.logDebug(`Added job ${job.name} to the job registry.`); jobRegistry.set(key, { ...job, name: job.name, }); } const tasks = new Map(); return { initializeAsync: async () => { const configurations = await db.query.cronJobConfigurations.findMany(); for (const job of jobRegistry.values()) { const configuration = configurations.find(({ name }) => name === job.name); if (configuration?.isEnabled === false) { continue; } if (tasks.has(job.name)) { continue; } const scheduledTask = await job.createTaskAsync(); if (!scheduledTask) continue; tasks.set(job.name, scheduledTask); } }, startAsync: async (name: keyof TJobs) => { const job = jobRegistry.get(name as string); if (!job) return; if (!tasks.has(job.name)) return; options.logger.logInfo(`Starting schedule cron job ${job.name}.`); await job.onStartAsync(); await tasks.get(name as string)?.start(); }, startAllAsync: async () => { for (const job of jobRegistry.values()) { if (!tasks.has(job.name)) { continue; } options.logger.logInfo(`Starting schedule of cron job ${job.name}.`); await job.onStartAsync(); await tasks.get(job.name)?.start(); } }, runManuallyAsync: async (name: keyof TJobs) => { const job = jobRegistry.get(name as string); if (!job) return; if (job.preventManualExecution) { throw new Error(`The job "${job.name}" can not be executed manually.`); } options.logger.logInfo(`Running schedule cron job ${job.name} manually.`); await tasks.get(name as string)?.execute(); }, stopAsync: async (name: keyof TJobs) => { const job = jobRegistry.get(name as string); if (!job) return; options.logger.logInfo(`Stopping schedule cron job ${job.name}.`); await tasks.get(name as string)?.stop(); }, stopAllAsync: async () => { for (const job of jobRegistry.values()) { options.logger.logInfo(`Stopping schedule cron job ${job.name}.`); await tasks.get(job.name)?.stop(); } }, getJobRegistry() { return jobRegistry as Map>>; }, getTask(name: keyof TJobs) { return tasks.get(name as string) ?? null; }, setTask(name: keyof TJobs, task: ScheduledTask) { tasks.set(name as string, task); }, getKeys() { return objectKeys(jobs); }, }; }; };