mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
refactor(core): Centralize CronJob management (#10033)
This commit is contained in:
committed by
GitHub
parent
36b314d031
commit
09f2cf9eaf
@@ -1,93 +1,128 @@
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
import moment from 'moment-timezone';
|
||||
import type { IRecurencyRule } from './SchedulerInterface';
|
||||
import { type CronExpression, randomInt } from 'n8n-workflow';
|
||||
import type { IRecurrenceRule, ScheduleInterval } from './SchedulerInterface';
|
||||
|
||||
export function recurencyCheck(
|
||||
recurrency: IRecurencyRule,
|
||||
recurrencyRules: number[],
|
||||
export function recurrenceCheck(
|
||||
recurrence: IRecurrenceRule,
|
||||
recurrenceRules: number[],
|
||||
timezone: string,
|
||||
): boolean {
|
||||
const recurrencyRuleIndex = recurrency.index;
|
||||
const intervalSize = recurrency.intervalSize;
|
||||
const typeInterval = recurrency.typeInterval;
|
||||
if (!recurrence.activated) return true;
|
||||
|
||||
const lastExecution =
|
||||
recurrencyRuleIndex !== undefined ? recurrencyRules[recurrencyRuleIndex] : undefined;
|
||||
const intervalSize = recurrence.intervalSize;
|
||||
if (!intervalSize) return false;
|
||||
|
||||
if (
|
||||
intervalSize &&
|
||||
recurrencyRuleIndex !== undefined &&
|
||||
(typeInterval === 'weeks' || typeInterval === 'undefined')
|
||||
) {
|
||||
const index = recurrence.index;
|
||||
const typeInterval = recurrence.typeInterval;
|
||||
const lastExecution = recurrenceRules[index];
|
||||
|
||||
const momentTz = moment.tz(timezone);
|
||||
if (typeInterval === 'hours') {
|
||||
const hour = momentTz.hour();
|
||||
if (lastExecution === undefined || hour === (intervalSize + lastExecution) % 24) {
|
||||
recurrenceRules[index] = hour;
|
||||
return true;
|
||||
}
|
||||
} else if (typeInterval === 'days') {
|
||||
const dayOfYear = momentTz.dayOfYear();
|
||||
if (lastExecution === undefined || dayOfYear === (intervalSize + lastExecution) % 365) {
|
||||
recurrenceRules[index] = dayOfYear;
|
||||
return true;
|
||||
}
|
||||
} else if (typeInterval === 'weeks') {
|
||||
const week = momentTz.week();
|
||||
if (
|
||||
lastExecution === undefined || // First time executing this rule
|
||||
moment.tz(timezone).week() === (intervalSize + lastExecution) % 52 || // not first time, but minimum interval has passed
|
||||
moment.tz(timezone).week() === lastExecution // Trigger on multiple days in the same week
|
||||
week === (intervalSize + lastExecution) % 52 || // not first time, but minimum interval has passed
|
||||
week === lastExecution // Trigger on multiple days in the same week
|
||||
) {
|
||||
recurrencyRules[recurrencyRuleIndex] = moment.tz(timezone).week();
|
||||
recurrenceRules[index] = week;
|
||||
return true;
|
||||
}
|
||||
} else if (intervalSize && recurrencyRuleIndex !== undefined && typeInterval === 'days') {
|
||||
if (
|
||||
lastExecution === undefined ||
|
||||
moment.tz(timezone).dayOfYear() === (intervalSize + lastExecution) % 365
|
||||
) {
|
||||
recurrencyRules[recurrencyRuleIndex] = moment.tz(timezone).dayOfYear();
|
||||
} else if (typeInterval === 'months') {
|
||||
const month = momentTz.month();
|
||||
if (lastExecution === undefined || month === (intervalSize + lastExecution) % 12) {
|
||||
recurrenceRules[index] = month;
|
||||
return true;
|
||||
}
|
||||
} else if (intervalSize && recurrencyRuleIndex !== undefined && typeInterval === 'hours') {
|
||||
if (
|
||||
lastExecution === undefined ||
|
||||
moment.tz(timezone).hour() === (intervalSize + lastExecution) % 24
|
||||
) {
|
||||
recurrencyRules[recurrencyRuleIndex] = moment.tz(timezone).hour();
|
||||
return true;
|
||||
}
|
||||
} else if (intervalSize && recurrencyRuleIndex !== undefined && typeInterval === 'months') {
|
||||
if (
|
||||
lastExecution === undefined ||
|
||||
moment.tz(timezone).month() === (intervalSize + lastExecution) % 12
|
||||
) {
|
||||
recurrencyRules[recurrencyRuleIndex] = moment.tz(timezone).month();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function convertMonthToUnix(expression: string): string {
|
||||
if (!isNaN(parseInt(expression)) || expression.includes('-') || expression.includes(',')) {
|
||||
let matches = expression.match(/([0-9])+/g) as string[];
|
||||
if (matches) {
|
||||
matches = matches.map((match) =>
|
||||
parseInt(match) !== 0 ? String(parseInt(match) - 1) : match,
|
||||
);
|
||||
}
|
||||
expression = matches?.join(expression.includes('-') ? '-' : ',') || '';
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
export const toCronExpression = (interval: ScheduleInterval): CronExpression => {
|
||||
if (interval.field === 'cronExpression') return interval.expression;
|
||||
if (interval.field === 'seconds') return `*/${interval.secondsInterval} * * * * *`;
|
||||
|
||||
export function convertToUnixFormat(interval: IDataObject) {
|
||||
const expression = (interval.expression as string).split(' ');
|
||||
if (expression.length === 5) {
|
||||
expression[3] = convertMonthToUnix(expression[3]);
|
||||
expression[4] = expression[4].replace('7', '0');
|
||||
} else if (expression.length === 6) {
|
||||
expression[4] = convertMonthToUnix(expression[4]);
|
||||
expression[5] = expression[5].replace('7', '0');
|
||||
}
|
||||
interval.expression = expression.join(' ');
|
||||
}
|
||||
const randomSecond = randomInt(0, 60);
|
||||
if (interval.field === 'minutes') return `${randomSecond} */${interval.minutesInterval} * * * *`;
|
||||
|
||||
export const addFallbackValue = <T>(enabled: boolean, fallback: T) => {
|
||||
if (enabled) {
|
||||
return (value: T) => {
|
||||
if (!value) return fallback;
|
||||
return value;
|
||||
};
|
||||
const minute = interval.triggerAtMinute ?? randomInt(0, 60);
|
||||
if (interval.field === 'hours')
|
||||
return `${randomSecond} ${minute} */${interval.hoursInterval} * * *`;
|
||||
|
||||
// Since Cron does not support `*/` for days or weeks, all following expressions trigger more often, but are then filtered by `recurrenceCheck`
|
||||
const hour = interval.triggerAtHour ?? randomInt(0, 24);
|
||||
if (interval.field === 'days') return `${randomSecond} ${minute} ${hour} * * *`;
|
||||
if (interval.field === 'weeks') {
|
||||
const days = interval.triggerAtDay;
|
||||
const daysOfWeek = days.length === 0 ? '*' : days.join(',');
|
||||
return `${randomSecond} ${minute} ${hour} * * ${daysOfWeek}` as CronExpression;
|
||||
}
|
||||
return (value: T) => value;
|
||||
|
||||
const dayOfMonth = interval.triggerAtDayOfMonth ?? randomInt(0, 31);
|
||||
return `${randomSecond} ${minute} ${hour} ${dayOfMonth} */${interval.monthsInterval} *`;
|
||||
};
|
||||
|
||||
export function intervalToRecurrence(interval: ScheduleInterval, index: number) {
|
||||
let recurrence: IRecurrenceRule = { activated: false };
|
||||
|
||||
if (interval.field === 'hours') {
|
||||
const { hoursInterval } = interval;
|
||||
if (hoursInterval !== 1) {
|
||||
recurrence = {
|
||||
activated: true,
|
||||
index,
|
||||
intervalSize: hoursInterval,
|
||||
typeInterval: 'hours',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (interval.field === 'days') {
|
||||
const { daysInterval } = interval;
|
||||
if (daysInterval !== 1) {
|
||||
recurrence = {
|
||||
activated: true,
|
||||
index,
|
||||
intervalSize: daysInterval,
|
||||
typeInterval: 'days',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (interval.field === 'weeks') {
|
||||
const { weeksInterval } = interval;
|
||||
if (weeksInterval !== 1) {
|
||||
recurrence = {
|
||||
activated: true,
|
||||
index,
|
||||
intervalSize: weeksInterval,
|
||||
typeInterval: 'weeks',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (interval.field === 'months') {
|
||||
const { monthsInterval } = interval;
|
||||
if (monthsInterval !== 1) {
|
||||
recurrence = {
|
||||
activated: true,
|
||||
index,
|
||||
intervalSize: monthsInterval,
|
||||
typeInterval: 'months',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return recurrence;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user