120 lines
3.0 KiB
TypeScript
120 lines
3.0 KiB
TypeScript
export type Schedule =
|
|
| "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday"
|
|
| "week" | "day" | "midnight" | "noon" | "hour"
|
|
| "30 minutes" | "15 minutes" | "5 minutes" | "1 minute"
|
|
| "30minutes" | "15minutes" | "5minutes" | "1minute"
|
|
| 30 | 15 | 5 | 1
|
|
| (string & {}) // time strings like "7am", "7:30pm", "14:00"
|
|
|
|
export type CronJob = {
|
|
id: string // "appname:filename"
|
|
app: string
|
|
name: string // filename without .ts
|
|
file: string // full path
|
|
schedule: Schedule
|
|
cronExpr: string
|
|
state: 'idle' | 'running' | 'disabled'
|
|
lastRun?: number
|
|
lastDuration?: number
|
|
lastExitCode?: number
|
|
lastError?: string
|
|
lastOutput?: string
|
|
nextRun?: number
|
|
}
|
|
|
|
export type InvalidJob = {
|
|
id: string
|
|
app: string
|
|
name: string
|
|
file: string
|
|
error: string
|
|
}
|
|
|
|
export const SCHEDULES = [
|
|
'1 minute',
|
|
'5 minutes',
|
|
'15 minutes',
|
|
'30 minutes',
|
|
'hour',
|
|
'noon',
|
|
'midnight',
|
|
'day',
|
|
'week',
|
|
'sunday',
|
|
'monday',
|
|
'tuesday',
|
|
'wednesday',
|
|
'thursday',
|
|
'friday',
|
|
'saturday',
|
|
] as const
|
|
|
|
const SCHEDULE_MAP: Record<string, string> = {
|
|
sunday: '0 0 * * 0',
|
|
monday: '0 0 * * 1',
|
|
tuesday: '0 0 * * 2',
|
|
wednesday: '0 0 * * 3',
|
|
thursday: '0 0 * * 4',
|
|
friday: '0 0 * * 5',
|
|
saturday: '0 0 * * 6',
|
|
week: '0 0 * * 0',
|
|
day: '0 0 * * *',
|
|
midnight: '0 0 * * *',
|
|
noon: '0 12 * * *',
|
|
hour: '0 * * * *',
|
|
'30 minutes': '0,30 * * * *',
|
|
'15 minutes': '0,15,30,45 * * * *',
|
|
'5 minutes': '*/5 * * * *',
|
|
'1 minute': '* * * * *',
|
|
'30minutes': '0,30 * * * *',
|
|
'15minutes': '0,15,30,45 * * * *',
|
|
'5minutes': '*/5 * * * *',
|
|
'1minute': '* * * * *',
|
|
}
|
|
|
|
export function isValidSchedule(value: unknown): value is Schedule {
|
|
if (typeof value === 'number') {
|
|
return [1, 5, 15, 30].includes(value)
|
|
}
|
|
if (typeof value === 'string') {
|
|
return value in SCHEDULE_MAP || parseTime(value) !== null
|
|
}
|
|
return false
|
|
}
|
|
|
|
function parseTime(s: string): { hour: number, minute: number } | null {
|
|
// 12h: "7am", "7pm", "7:30am", "7:30pm", "12am", "12:00pm"
|
|
const m12 = s.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/i)
|
|
if (m12) {
|
|
let hour = parseInt(m12[1]!)
|
|
const minute = m12[2] ? parseInt(m12[2]) : 0
|
|
const period = m12[3]!.toLowerCase()
|
|
if (hour < 1 || hour > 12 || minute > 59) return null
|
|
if (period === 'am' && hour === 12) hour = 0
|
|
else if (period === 'pm' && hour !== 12) hour += 12
|
|
return { hour, minute }
|
|
}
|
|
|
|
// 24h: "14:00", "0:00", "23:59"
|
|
const m24 = s.match(/^(\d{1,2}):(\d{2})$/)
|
|
if (m24) {
|
|
const hour = parseInt(m24[1]!)
|
|
const minute = parseInt(m24[2]!)
|
|
if (hour > 23 || minute > 59) return null
|
|
return { hour, minute }
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function toCronExpr(schedule: Schedule): string {
|
|
if (typeof schedule === 'number') {
|
|
return SCHEDULE_MAP[`${schedule}minutes`]!
|
|
}
|
|
if (schedule in SCHEDULE_MAP) {
|
|
return SCHEDULE_MAP[schedule]!
|
|
}
|
|
const time = parseTime(schedule)!
|
|
return `${time.minute} ${time.hour} * * *`
|
|
}
|