- Updated isolated cron jobs to support new delivery modes: `announce` and `none`, improving output management. - Refactored job configuration to remove legacy fields and streamline delivery settings. - Enhanced the `CronJobEditor` UI to reflect changes in delivery options, including a new segmented control for delivery mode selection. - Updated documentation to clarify the new delivery configurations and their implications for job execution. - Improved tests to validate the new delivery behavior and ensure backward compatibility with legacy settings. This update provides users with greater flexibility in managing how isolated jobs deliver their outputs, enhancing overall usability and clarity in job configurations.
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import type { CronSchedule } from "./types.js";
|
|
import { parseAbsoluteTimeMs } from "./parse.js";
|
|
|
|
const ONE_MINUTE_MS = 60 * 1000;
|
|
const TEN_YEARS_MS = 10 * 365.25 * 24 * 60 * 60 * 1000;
|
|
|
|
export type TimestampValidationError = {
|
|
ok: false;
|
|
message: string;
|
|
};
|
|
|
|
export type TimestampValidationSuccess = {
|
|
ok: true;
|
|
};
|
|
|
|
export type TimestampValidationResult = TimestampValidationSuccess | TimestampValidationError;
|
|
|
|
/**
|
|
* Validates at timestamps in cron schedules.
|
|
* Rejects timestamps that are:
|
|
* - More than 1 minute in the past
|
|
* - More than 10 years in the future
|
|
*/
|
|
export function validateScheduleTimestamp(
|
|
schedule: CronSchedule,
|
|
nowMs: number = Date.now(),
|
|
): TimestampValidationResult {
|
|
if (schedule.kind !== "at") {
|
|
return { ok: true };
|
|
}
|
|
|
|
const atRaw = typeof schedule.at === "string" ? schedule.at.trim() : "";
|
|
const atMs = atRaw ? parseAbsoluteTimeMs(atRaw) : null;
|
|
|
|
if (atMs === null || !Number.isFinite(atMs)) {
|
|
return {
|
|
ok: false,
|
|
message: `Invalid schedule.at: expected ISO-8601 timestamp (got ${String(schedule.at)})`,
|
|
};
|
|
}
|
|
|
|
const diffMs = atMs - nowMs;
|
|
|
|
// Check if timestamp is in the past (allow 1 minute grace period)
|
|
if (diffMs < -ONE_MINUTE_MS) {
|
|
const nowDate = new Date(nowMs).toISOString();
|
|
const atDate = new Date(atMs).toISOString();
|
|
const minutesAgo = Math.floor(-diffMs / ONE_MINUTE_MS);
|
|
return {
|
|
ok: false,
|
|
message: `schedule.at is in the past: ${atDate} (${minutesAgo} minutes ago). Current time: ${nowDate}`,
|
|
};
|
|
}
|
|
|
|
// Check if timestamp is too far in the future
|
|
if (diffMs > TEN_YEARS_MS) {
|
|
const atDate = new Date(atMs).toISOString();
|
|
const yearsAhead = Math.floor(diffMs / (365.25 * 24 * 60 * 60 * 1000));
|
|
return {
|
|
ok: false,
|
|
message: `schedule.at is too far in the future: ${atDate} (${yearsAhead} years ahead). Maximum allowed: 10 years`,
|
|
};
|
|
}
|
|
|
|
return { ok: true };
|
|
}
|