Skip to content

Commit 4912079

Browse files
authored
feat: register cron jobs at runtime instead of a generic cron job that runs every minute (#216)
1 parent cf97086 commit 4912079

2 files changed

Lines changed: 153 additions & 23 deletions

File tree

server/config/cron-tasks.js

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,58 @@
11
import getPluginService from '../utils/getPluginService';
2+
import logMessage from '../utils/logMessage';
23

3-
const registerCronTasks = ({ strapi }) => {
4-
const settings = getPluginService('settingsService').get();
5-
// create cron check
6-
strapi.cron.add({
7-
publisherCronTask: {
8-
options: settings.actions.syncFrequency,
9-
task: async () => {
10-
// fetch all actions that have passed
11-
const records = await getPluginService('action').find({
12-
filters: {
13-
executeAt: {
14-
$lte: new Date(Date.now()),
15-
},
16-
},
17-
});
18-
19-
// process action records
20-
for (const record of records.results) {
21-
getPluginService('publicationService').toggle(record, record.mode);
4+
const registerCronTasks = ({ strapi }) => {
5+
// On startup, schedule cron jobs for all existing actions that are in the future
6+
// and execute actions that should have already been executed
7+
const scheduleExistingActions = async () => {
8+
try {
9+
// Find all pending actions
10+
const allActions = await getPluginService('action').find({
11+
pagination: {
12+
pageSize: 1000, // Get all actions
13+
},
14+
});
15+
16+
const now = new Date();
17+
const futureActions = [];
18+
const pastActions = [];
19+
20+
// Separate actions into past and future
21+
for (const record of allActions.results) {
22+
const executeAt = new Date(record.executeAt);
23+
if (executeAt > now) {
24+
futureActions.push(record);
25+
} else {
26+
pastActions.push(record);
2227
}
23-
},
24-
},
28+
}
29+
30+
strapi.log.info(logMessage(`Found ${pastActions.length} past actions to execute and ${futureActions.length} future actions to schedule on startup.`));
31+
32+
// Execute past actions immediately
33+
for (const record of pastActions) {
34+
try {
35+
await getPluginService('publicationService').toggle(record, record.mode);
36+
strapi.log.info(logMessage(`Executed overdue action ${record.documentId}`));
37+
} catch (error) {
38+
strapi.log.error(logMessage(`Error executing overdue action ${record.documentId}: ${error.message}`));
39+
}
40+
}
41+
42+
// Schedule cron jobs for all future actions
43+
for (const record of futureActions) {
44+
getPluginService('action').scheduleCronJob(record);
45+
}
46+
} catch (error) {
47+
strapi.log.error(logMessage(`Error scheduling existing actions on startup: ${error.message}`));
48+
}
49+
};
50+
51+
// Schedule existing actions after Strapi is fully started
52+
// Using setImmediate to ensure all services are initialized
53+
setImmediate(() => {
54+
scheduleExistingActions();
2555
});
26-
}
56+
};
2757

2858
export default registerCronTasks;

server/services/action-service.js

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,105 @@
33
*/
44

55
import { factories } from '@strapi/strapi';
6+
import getPluginService from '../utils/getPluginService';
7+
import logMessage from '../utils/logMessage';
68

7-
export default factories.createCoreService('plugin::publisher.action');
9+
export default factories.createCoreService('plugin::publisher.action', ({ strapi }) => ({
10+
/**
11+
* Schedule a cron job for a specific action
12+
*/
13+
scheduleCronJob(action) {
14+
const taskName = `publisherAction_${action.documentId}`;
15+
const executeAt = new Date(action.executeAt);
16+
17+
// Check if the execution time is in the future
18+
if (executeAt <= new Date()) {
19+
strapi.log.warn(logMessage(`Action ${action.documentId} has an executeAt time in the past, skipping cron job creation.`));
20+
return;
21+
}
22+
23+
strapi.log.info(logMessage(`Scheduling cron job for action ${action.documentId} at ${executeAt.toISOString()}`));
24+
25+
strapi.cron.add({
26+
[taskName]: {
27+
async task() {
28+
try {
29+
// Fetch the action again to ensure it still exists
30+
const currentAction = await strapi.documents('plugin::publisher.action').findOne({
31+
documentId: action.documentId,
32+
});
33+
34+
if (!currentAction) {
35+
strapi.log.warn(logMessage(`Action ${action.documentId} no longer exists, skipping execution.`));
36+
return;
37+
}
38+
39+
// Execute the publication action
40+
await getPluginService('publicationService').toggle(currentAction, currentAction.mode);
41+
42+
strapi.log.info(logMessage(`Successfully executed action ${action.documentId}`));
43+
} catch (error) {
44+
strapi.log.error(logMessage(`Error executing action ${action.documentId}: ${error.message}`));
45+
} finally {
46+
// Remove the cron job after execution
47+
strapi.cron.remove(taskName);
48+
}
49+
},
50+
options: executeAt,
51+
},
52+
});
53+
},
54+
55+
/**
56+
* Remove a scheduled cron job
57+
*/
58+
removeCronJob(actionDocumentId) {
59+
const taskName = `publisherAction_${actionDocumentId}`;
60+
61+
try {
62+
strapi.cron.remove(taskName);
63+
strapi.log.info(logMessage(`Removed cron job for action ${actionDocumentId}`));
64+
} catch (error) {
65+
strapi.log.warn(logMessage(`Could not remove cron job ${taskName}: ${error.message}`));
66+
}
67+
},
68+
69+
/**
70+
* Override create to schedule cron job
71+
*/
72+
async create(...args) {
73+
const result = await super.create(...args);
74+
75+
// Schedule the cron job for this action
76+
this.scheduleCronJob(result);
77+
78+
return result;
79+
},
80+
81+
/**
82+
* Override update to reschedule cron job
83+
*/
84+
async update(documentId, ...args) {
85+
// Remove the old cron job
86+
this.removeCronJob(documentId);
87+
88+
const result = await super.update(documentId, ...args);
89+
90+
// Schedule a new cron job with updated data
91+
this.scheduleCronJob(result);
92+
93+
return result;
94+
},
95+
96+
/**
97+
* Override delete to remove cron job
98+
*/
99+
async delete(documentId, ...args) {
100+
// Remove the cron job before deleting
101+
this.removeCronJob(documentId);
102+
103+
const result = await super.delete(documentId, ...args);
104+
105+
return result;
106+
},
107+
}));

0 commit comments

Comments
 (0)