Compare commits
No commits in common. "75af5f3d316e0d93050e9daf784482adcf49c8ee" and "512d9fe96b96f344fe7bb35b419e90b3b5cb4aa1" have entirely different histories.
75af5f3d31
...
512d9fe96b
|
|
@ -4,7 +4,6 @@ export type Schedule =
|
||||||
| "30 minutes" | "15 minutes" | "5 minutes" | "1 minute"
|
| "30 minutes" | "15 minutes" | "5 minutes" | "1 minute"
|
||||||
| "30minutes" | "15minutes" | "5minutes" | "1minute"
|
| "30minutes" | "15minutes" | "5minutes" | "1minute"
|
||||||
| 30 | 15 | 5 | 1
|
| 30 | 15 | 5 | 1
|
||||||
| (string & {}) // time strings like "7am", "7:30pm", "14:00"
|
|
||||||
|
|
||||||
export type CronJob = {
|
export type CronJob = {
|
||||||
id: string // "appname:filename"
|
id: string // "appname:filename"
|
||||||
|
|
@ -72,48 +71,19 @@ const SCHEDULE_MAP: Record<string, string> = {
|
||||||
'1minute': '* * * * *',
|
'1minute': '* * * * *',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toCronExpr(schedule: Schedule): string {
|
||||||
|
if (typeof schedule === 'number') {
|
||||||
|
return SCHEDULE_MAP[`${schedule}minutes`]!
|
||||||
|
}
|
||||||
|
return SCHEDULE_MAP[schedule]!
|
||||||
|
}
|
||||||
|
|
||||||
export function isValidSchedule(value: unknown): value is Schedule {
|
export function isValidSchedule(value: unknown): value is Schedule {
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
return [1, 5, 15, 30].includes(value)
|
return [1, 5, 15, 30].includes(value)
|
||||||
}
|
}
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value in SCHEDULE_MAP || parseTime(value) !== null
|
return value in SCHEDULE_MAP
|
||||||
}
|
}
|
||||||
return false
|
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} * * *`
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,6 @@ export const STATE_ICONS: Record<string, string> = {
|
||||||
invalid: color.red('◌'),
|
invalid: color.red('◌'),
|
||||||
}
|
}
|
||||||
|
|
||||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
||||||
|
|
||||||
async function waitForState(name: string, target: string, timeout: number): Promise<string | undefined> {
|
|
||||||
const start = Date.now()
|
|
||||||
while (Date.now() - start < timeout) {
|
|
||||||
await sleep(500)
|
|
||||||
const app: App | undefined = await get(`/api/apps/${name}`)
|
|
||||||
if (!app) return undefined
|
|
||||||
if (app.state === target) return target
|
|
||||||
// Terminal failure states — stop polling
|
|
||||||
if (target === 'running' && (app.state === 'stopped' || app.state === 'invalid')) return app.state
|
|
||||||
if (target === 'stopped' && app.state === 'invalid') return app.state
|
|
||||||
}
|
|
||||||
// Timed out — return last known state
|
|
||||||
const app: App | undefined = await get(`/api/apps/${name}`)
|
|
||||||
return app?.state
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function configShow() {
|
export async function configShow() {
|
||||||
console.log(`Host: ${color.bold(HOST)}`)
|
console.log(`Host: ${color.bold(HOST)}`)
|
||||||
|
|
||||||
|
|
@ -250,15 +232,7 @@ export async function renameApp(arg: string | undefined, newName: string) {
|
||||||
export async function restartApp(arg?: string) {
|
export async function restartApp(arg?: string) {
|
||||||
const name = resolveAppName(arg)
|
const name = resolveAppName(arg)
|
||||||
if (!name) return
|
if (!name) return
|
||||||
const result = await post(`/api/apps/${name}/restart`)
|
await post(`/api/apps/${name}/restart`)
|
||||||
if (!result) return
|
|
||||||
process.stdout.write(`${color.yellow('↻')} Restarting ${color.bold(name)}...`)
|
|
||||||
const state = await waitForState(name, 'running', 15000)
|
|
||||||
if (state === 'running') {
|
|
||||||
console.log(` ${color.green('running')}`)
|
|
||||||
} else {
|
|
||||||
console.log(` ${color.red(state ?? 'unknown')}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rmApp(arg?: string) {
|
export async function rmApp(arg?: string) {
|
||||||
|
|
@ -290,28 +264,11 @@ export async function rmApp(arg?: string) {
|
||||||
export async function startApp(arg?: string) {
|
export async function startApp(arg?: string) {
|
||||||
const name = resolveAppName(arg)
|
const name = resolveAppName(arg)
|
||||||
if (!name) return
|
if (!name) return
|
||||||
const result = await post(`/api/apps/${name}/start`)
|
await post(`/api/apps/${name}/start`)
|
||||||
if (!result) return
|
|
||||||
process.stdout.write(`${color.green('▶')} Starting ${color.bold(name)}...`)
|
|
||||||
const state = await waitForState(name, 'running', 15000)
|
|
||||||
if (state === 'running') {
|
|
||||||
console.log(` ${color.green('running')}`)
|
|
||||||
} else {
|
|
||||||
console.log(` ${color.red(state ?? 'unknown')}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopApp(arg?: string) {
|
export async function stopApp(arg?: string) {
|
||||||
const name = resolveAppName(arg)
|
const name = resolveAppName(arg)
|
||||||
if (!name) return
|
if (!name) return
|
||||||
const result = await post(`/api/apps/${name}/stop`)
|
await post(`/api/apps/${name}/stop`)
|
||||||
if (!result) return
|
|
||||||
process.stdout.write(`${color.red('■')} Stopping ${color.bold(name)}...`)
|
|
||||||
const state = await waitForState(name, 'stopped', 10000)
|
|
||||||
if (state === 'stopped') {
|
|
||||||
console.log(` ${color.gray('stopped')}`)
|
|
||||||
} else {
|
|
||||||
console.log(` ${color.yellow(state ?? 'unknown')}`)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user