toes/apps/cron/20260201-000000/lib/discovery.ts
2026-02-01 23:00:49 -08:00

66 lines
1.7 KiB
TypeScript

import { readdir } from 'fs/promises'
import { existsSync } from 'fs'
import { join } from 'path'
import { isValidSchedule, toCronExpr, type CronJob, type Schedule } from './schedules'
const APPS_DIR = process.env.APPS_DIR!
export async function getApps(): Promise<string[]> {
const entries = await readdir(APPS_DIR, { withFileTypes: true })
const apps: string[] = []
for (const entry of entries) {
if (!entry.isDirectory()) continue
// Check if it has a current symlink (valid app)
if (existsSync(join(APPS_DIR, entry.name, 'current'))) {
apps.push(entry.name)
}
}
return apps.sort()
}
export async function discoverCronJobs(): Promise<CronJob[]> {
const jobs: CronJob[] = []
const apps = await readdir(APPS_DIR, { withFileTypes: true })
for (const app of apps) {
if (!app.isDirectory()) continue
const cronDir = join(APPS_DIR, app.name, 'current', 'cron')
if (!existsSync(cronDir)) continue
const files = await readdir(cronDir)
for (const file of files) {
if (!file.endsWith('.ts')) continue
const filePath = join(cronDir, file)
const name = file.replace(/\.ts$/, '')
try {
const mod = await import(filePath)
const schedule = mod.schedule as Schedule
if (!isValidSchedule(schedule)) {
console.error(`Invalid schedule in ${filePath}: ${schedule}`)
continue
}
jobs.push({
id: `${app.name}:${name}`,
app: app.name,
name,
file: filePath,
schedule,
cronExpr: toCronExpr(schedule),
state: 'idle',
})
} catch (e) {
console.error(`Failed to load cron file ${filePath}:`, e)
}
}
}
return jobs
}