diff --git a/packages/core/strapi/ee/index.js b/packages/core/strapi/ee/index.js index 16be6f249d..7883fe7ef1 100644 --- a/packages/core/strapi/ee/index.js +++ b/packages/core/strapi/ee/index.js @@ -4,7 +4,7 @@ const { pick } = require('lodash/fp'); const { readLicense, verifyLicense, fetchLicense, LicenseCheckError } = require('./license'); const { eeStoreModel } = require('./ee-store'); -const { getRecurringCronExpression } = require('../lib/utils/cron'); +const { shiftCronExpression } = require('../lib/utils/cron'); const ONE_MINUTE = 1000 * 60; const DEFAULT_FEATURES = { @@ -142,7 +142,7 @@ const checkLicense = async ({ strapi }) => { if (!shouldStayOffline) { await onlineUpdate({ strapi }); - strapi.cron.add({ [getRecurringCronExpression()]: onlineUpdate }); + strapi.cron.add({ [shiftCronExpression('0 0 */12 * * *')]: onlineUpdate }); } else { if (!ee.licenseInfo.expireAt) { return disable('Your license does not have offline support.'); diff --git a/packages/core/strapi/lib/utils/cron.js b/packages/core/strapi/lib/utils/cron.js index 98b787df50..75fa389c8d 100644 --- a/packages/core/strapi/lib/utils/cron.js +++ b/packages/core/strapi/lib/utils/cron.js @@ -1,17 +1,50 @@ 'use strict'; -const shiftHours = (date, step) => { - const frequency = 24 / step; - const list = Array.from({ length: frequency }, (_, index) => index * step); - const hour = date.getHours(); - return list.map((value) => (value + hour) % 24).sort((a, b) => a - b); +const { isEmpty, negate } = require('lodash/fp'); + +const integerRegex = /^\d+$/; +const stepRegex = /^\*\/\d+$/; +const components = [ + { limit: 60, zeroBasedIndices: true, functionName: 'getSeconds' }, + { limit: 60, zeroBasedIndices: true, functionName: 'getMinutes' }, + { limit: 24, zeroBasedIndices: true, functionName: 'getHours' }, + { limit: 31, zeroBasedIndices: false, functionName: 'getDate' }, + { limit: 12, zeroBasedIndices: false, functionName: 'getMonth' }, + { limit: 7, zeroBasedIndices: true, functionName: 'getDay' }, +]; + +const shift = (component, index, date) => { + if (component === '*') { + return '*'; + } + + const { limit, zeroBasedIndices, functionName } = components[index]; + const offset = +!zeroBasedIndices; + const currentValue = date[functionName](); + + if (integerRegex.test(component)) { + return ((Number.parseInt(component, 10) + currentValue) % limit) + offset; + } + + if (stepRegex.test(component)) { + const [, step] = component.split('/'); + const frequency = Math.floor(limit / step); + const list = Array.from({ length: frequency }, (_, index) => index * step); + return list.map((value) => ((value + currentValue) % limit) + offset).sort((a, b) => a - b); + } + + // Unsupported syntax, fallback to '*' + return '*'; }; -// TODO: This should be transformed into a cron expression shifter that could be reused in other places -// For now it's tailored to the license check cron, scheduled every 12h -const getRecurringCronExpression = (date = new Date()) => - `${date.getMinutes()} ${shiftHours(date, 12)} * * *`; +const shiftCronExpression = (rule, date = new Date()) => { + const components = rule.trim().split(' ').filter(negate(isEmpty)); + const secondsIncluded = components.length === 6; + return components + .map((component, index) => shift(component, secondsIncluded ? index : index + 1, date)) + .join(' '); +}; module.exports = { - getRecurringCronExpression, + shiftCronExpression, };