UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 7.32 kB
{"version":3,"file":"setup-cron-jobs.mjs","names":["constants","getCronJobs"],"sources":["../../../src/libs/runtime/setup-cron-jobs.ts"],"sourcesContent":["import constants from \"../../constants/constants.js\";\nimport serviceWrapper from \"../../utils/services/service-wrapper.js\";\nimport type { ServiceContext } from \"../../utils/services/types.js\";\nimport { copy } from \"../i18n/index.js\";\nimport logger from \"../logger/index.js\";\nimport passthroughQueueAdapter from \"../queue/adapters/passthrough.js\";\nimport {\n\tdestroyQueueAdapter,\n\tgetInitializedQueueAdapter,\n} from \"../queue/lifecycle.js\";\nimport type { QueueAdapterInstance } from \"../queue/types.js\";\nimport getCronJobs, { type CronJobDefinition } from \"./cron-jobs.js\";\nimport type { AdapterRuntimeContext, EnvironmentVariables } from \"./types.js\";\n\nconst MAX_RETRIES = 3;\n\n/**\n * Executes a single cron job with retry support.\n * Uses serviceWrapper so errors are returned as values rather than thrown.\n */\nconst executeCronJob = async (\n\tkey: string,\n\tjob: CronJobDefinition,\n\tcontext: ServiceContext,\n): Promise<{ key: string; success: boolean; error?: string }> => {\n\tfor (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n\t\tconst result = await serviceWrapper(job.fn, {\n\t\t\ttransaction: job.transaction,\n\t\t\tlogError: true,\n\t\t\tdefaultError: {\n\t\t\t\ttype: \"cron\",\n\t\t\t\tname: copy(\"server:core.cron.job.error.name\"),\n\t\t\t\tmessage: job.error,\n\t\t\t},\n\t\t})(context);\n\n\t\tif (!result.error) {\n\t\t\treturn { key, success: true };\n\t\t}\n\n\t\tif (attempt < MAX_RETRIES) {\n\t\t\tlogger.warn({\n\t\t\t\tmessage: `Cron job \"${job.label}\" failed (attempt ${attempt}/${MAX_RETRIES}), retrying...`,\n\t\t\t\tscope: constants.logScopes.cron,\n\t\t\t\tdata: { error: result.error.message },\n\t\t\t});\n\t\t} else {\n\t\t\tlogger.error({\n\t\t\t\tmessage: `Cron job \"${job.label}\" failed after ${MAX_RETRIES} attempts`,\n\t\t\t\tscope: constants.logScopes.cron,\n\t\t\t\tdata: { error: result.error.message },\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tkey,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: context.translate.english(result.error.message),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { key, success: false };\n};\n\n/**\n * Creates the environment for running cron jobs\n * - creates a passthrough queue adapter so runtime adapters can build the ServiceContext. This allows crons to insert jobs into the queue.\n */\nconst setupCronJobs = async (config: {\n\tcreateQueue: boolean;\n\truntimeContext?: AdapterRuntimeContext;\n\tenv?: EnvironmentVariables;\n}) => {\n\tlet queueInstance: QueueAdapterInstance | undefined;\n\n\t//* depending on the runtime adapter, they may already have access to the host's queue adapter.\n\tif (config.createQueue) {\n\t\t//* we dont pass additionalJobHandlers as at least currently, we dont expose a way for devs to register their own CRON jobs,\n\t\t//* meaning we dont need crons to be able to access job handlers that are not core.\n\t\tqueueInstance = passthroughQueueAdapter({\n\t\t\t//* we bypass immediate execution as we only want to use the queue adapter so CRON job services can push jobs into the queue.\n\t\t\tbypassImmediateExecution: true,\n\t\t});\n\t}\n\n\tconst schedules = Object.values(constants.cronSchedules);\n\n\treturn {\n\t\tschedules,\n\t\tregister: async (\n\t\t\tcontext: ServiceContext,\n\t\t\toptions?: {\n\t\t\t\tschedule?: string;\n\t\t\t},\n\t\t) => {\n\t\t\tlet cronQueue = context.queue ?? queueInstance;\n\t\t\tlet createdQueue: QueueAdapterInstance | undefined;\n\t\t\ttry {\n\t\t\t\tif (config.createQueue && config.runtimeContext) {\n\t\t\t\t\tcreatedQueue = await getInitializedQueueAdapter(context.config, {\n\t\t\t\t\t\truntimeContext: config.runtimeContext,\n\t\t\t\t\t\tenv: config.env ?? context.env ?? undefined,\n\t\t\t\t\t});\n\t\t\t\t\tcronQueue = createdQueue;\n\t\t\t\t}\n\n\t\t\t\tconst locale = context.config.i18n.defaultLocale;\n\t\t\t\tconst cronContext: ServiceContext = {\n\t\t\t\t\t...context,\n\t\t\t\t\tqueue: cronQueue,\n\t\t\t\t\ttranslate: context.translate.forLocale(locale),\n\t\t\t\t\trequest: {\n\t\t\t\t\t\t...context.request,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tlogger.info({\n\t\t\t\t\tmessage: \"Running cron jobs\",\n\t\t\t\t\tscope: constants.logScopes.cron,\n\t\t\t\t});\n\n\t\t\t\tconst jobs = Object.entries(getCronJobs()).filter(([, job]) => {\n\t\t\t\t\tif (!options?.schedule) return true;\n\t\t\t\t\treturn constants.cronSchedules[job.schedule] === options.schedule;\n\t\t\t\t});\n\n\t\t\t\tconst results = await Promise.allSettled(\n\t\t\t\t\tjobs.map(([key, job]) => executeCronJob(key, job, cronContext)),\n\t\t\t\t);\n\n\t\t\t\tconst failures = results.filter(\n\t\t\t\t\t(r) =>\n\t\t\t\t\t\tr.status === \"rejected\" ||\n\t\t\t\t\t\t(r.status === \"fulfilled\" && !r.value.success),\n\t\t\t\t);\n\n\t\t\t\tif (failures.length > 0) {\n\t\t\t\t\tlogger.error({\n\t\t\t\t\t\tmessage: `${failures.length} of ${results.length} cron jobs failed`,\n\t\t\t\t\t\tscope: constants.logScopes.cron,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (_) {\n\t\t\t\tlogger.error({\n\t\t\t\t\tmessage: \"Error running or registering cron jobs\",\n\t\t\t\t\tscope: constants.logScopes.cron,\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tawait destroyQueueAdapter(createdQueue, {\n\t\t\t\t\tconfig: context.config,\n\t\t\t\t\truntimeContext: config.runtimeContext,\n\t\t\t\t\tenv: config.env ?? context.env ?? undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tqueue: queueInstance,\n\t};\n};\n\nexport default setupCronJobs;\n"],"mappings":"6VAcA,MAMM,EAAiB,MACtB,EACA,EACA,IACgE,CAChE,IAAK,IAAI,EAAU,EAAG,GAAW,EAAa,IAAW,CACxD,IAAM,EAAS,MAAM,EAAe,EAAI,GAAI,CAC3C,YAAa,EAAI,YACjB,SAAU,GACV,aAAc,CACb,KAAM,OACN,KAAM,EAAK,iCAAiC,EAC5C,QAAS,EAAI,KACd,CACD,CAAC,CAAC,CAAC,CAAO,EAEV,GAAI,CAAC,EAAO,MACX,MAAO,CAAE,MAAK,QAAS,EAAK,EAG7B,GAAI,EAAU,EACb,EAAO,KAAK,CACX,QAAS,aAAa,EAAI,MAAM,oBAAoB,EAAQ,kBAC5D,MAAOA,EAAU,UAAU,KAC3B,KAAM,CAAE,MAAO,EAAO,MAAM,OAAQ,CACrC,CAAC,OAOD,OALA,EAAO,MAAM,CACZ,QAAS,aAAa,EAAI,MAAM,2BAChC,MAAOA,EAAU,UAAU,KAC3B,KAAM,CAAE,MAAO,EAAO,MAAM,OAAQ,CACrC,CAAC,EACM,CACN,MACA,QAAS,GACT,MAAO,EAAQ,UAAU,QAAQ,EAAO,MAAM,OAAO,CACtD,CAEF,CAEA,MAAO,CAAE,MAAK,QAAS,EAAM,CAC9B,EAMM,EAAgB,KAAO,IAIvB,CACL,IAAI,EAcJ,OAXI,EAAO,cAGV,EAAgB,EAAwB,CAEvC,yBAA0B,EAC3B,CAAC,GAKK,CACN,UAHiB,OAAO,OAAOA,EAAU,aAGjC,EACR,SAAU,MACT,EACA,IAGI,CACJ,IAAI,EAAY,EAAQ,OAAS,EAC7B,EACJ,GAAI,CACC,EAAO,aAAe,EAAO,iBAChC,EAAe,MAAM,EAA2B,EAAQ,OAAQ,CAC/D,eAAgB,EAAO,eACvB,IAAK,EAAO,KAAO,EAAQ,KAAO,IAAA,EACnC,CAAC,EACD,EAAY,GAGb,IAAM,EAAS,EAAQ,OAAO,KAAK,cAC7B,EAA8B,CACnC,GAAG,EACH,MAAO,EACP,UAAW,EAAQ,UAAU,UAAU,CAAM,EAC7C,QAAS,CACR,GAAG,EAAQ,QACX,QACD,CACD,EAEA,EAAO,KAAK,CACX,QAAS,oBACT,MAAOA,EAAU,UAAU,IAC5B,CAAC,EAED,IAAM,EAAO,OAAO,QAAQC,EAAY,CAAC,CAAC,CAAC,QAAQ,EAAG,KACrD,CAAK,GAAS,UACPD,EAAU,cAAc,EAAI,YAAc,EAAQ,QACzD,EAEK,EAAU,MAAM,QAAQ,WAC7B,EAAK,KAAK,CAAC,EAAK,KAAS,EAAe,EAAK,EAAK,CAAW,CAAC,CAC/D,EAEM,EAAW,EAAQ,OACvB,GACA,EAAE,SAAW,YACZ,EAAE,SAAW,aAAe,CAAC,EAAE,MAAM,OACxC,EAEI,EAAS,OAAS,GACrB,EAAO,MAAM,CACZ,QAAS,GAAG,EAAS,OAAO,MAAM,EAAQ,OAAO,mBACjD,MAAOA,EAAU,UAAU,IAC5B,CAAC,CAEH,MAAY,CACX,EAAO,MAAM,CACZ,QAAS,yCACT,MAAOA,EAAU,UAAU,IAC5B,CAAC,CACF,QAAU,CACT,MAAM,EAAoB,EAAc,CACvC,OAAQ,EAAQ,OAChB,eAAgB,EAAO,eACvB,IAAK,EAAO,KAAO,EAAQ,KAAO,IAAA,EACnC,CAAC,CACF,CACD,EACA,MAAO,CACR,CACD"}