mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-19 01:25:06 +00:00
Add cron expression support for M3U and EPG refreshes with interactive builder modal and quick reference examples. Refactor backup scheduling to use shared ScheduleInput component for consistency. - New CronBuilder modal with preset buttons and custom field editors - Info popover with common cron expression examples - Shared ScheduleInput component for interval/cron toggle
63 lines
1.7 KiB
JavaScript
63 lines
1.7 KiB
JavaScript
/**
|
|
* Cron expression validation utility.
|
|
*
|
|
* Shared across CronModal, BackupManager, and any other component
|
|
* that needs to validate 5-part cron expressions.
|
|
*/
|
|
|
|
export function validateCronExpression(expression) {
|
|
if (!expression || expression.trim() === '') {
|
|
return { valid: false, error: 'Cron expression is required' };
|
|
}
|
|
|
|
const parts = expression.trim().split(/\s+/);
|
|
if (parts.length !== 5) {
|
|
return {
|
|
valid: false,
|
|
error:
|
|
'Cron expression must have exactly 5 parts: minute hour day month weekday',
|
|
};
|
|
}
|
|
|
|
const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
|
|
|
|
const cronPartRegex =
|
|
/^(\*\/\d+|\*|\d+(-\d+)?(\/\d+)?(,\d+(-\d+)?(\/\d+)?)*)$/;
|
|
|
|
const fields = [
|
|
{ value: minute, label: 'minute', min: 0, max: 59 },
|
|
{ value: hour, label: 'hour', min: 0, max: 23 },
|
|
{ value: dayOfMonth, label: 'day', min: 1, max: 31 },
|
|
{ value: month, label: 'month', min: 1, max: 12 },
|
|
{ value: dayOfWeek, label: 'weekday', min: 0, max: 6 },
|
|
];
|
|
|
|
for (const { value, label, min, max } of fields) {
|
|
if (!cronPartRegex.test(value)) {
|
|
return {
|
|
valid: false,
|
|
error: `Invalid ${label} field (${min}-${max}, *, or cron syntax)`,
|
|
};
|
|
}
|
|
|
|
// Extra numeric-range check for plain numbers
|
|
if (
|
|
!(
|
|
value === '*' ||
|
|
value.includes('/') ||
|
|
value.includes('-') ||
|
|
value.includes(',')
|
|
)
|
|
) {
|
|
const num = parseInt(value, 10);
|
|
if (isNaN(num) || num < min || num > max) {
|
|
return {
|
|
valid: false,
|
|
error: `${label.charAt(0).toUpperCase() + label.slice(1)} must be between ${min} and ${max}`,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return { valid: true, error: null };
|
|
}
|