From 3abadc3b517abdea6b26c8905a37f8e49013f558 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Fri, 25 Jul 2025 14:31:11 -0700 Subject: [PATCH] Clean up --- packages/http/README.md | 33 ++++++++++++++++++++++++++++++ packages/http/package.json | 8 ++++---- packages/http/src/cron.ts | 3 ++- packages/http/src/routes/index.tsx | 20 +++++++++++++++--- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/packages/http/README.md b/packages/http/README.md index c94b823..2f6beda 100644 --- a/packages/http/README.md +++ b/packages/http/README.md @@ -8,3 +8,36 @@ A proxy server that will start all subdomain servers and a proxy server to route 2. Add a `subdomain:start` script to the `package.json` file of the new package. 3. Optionally add a `subdomain:dev` script for development (so you can add things like hot reloading). 4. It uses the directory name of the package to serve the subdomain. + +## How to setup a cron job + +In your package's `package.json`, add a script that starts with `cron:` followed by the cron schedule. + +``` +Every minute: `"cron:constantly": "your-script-goes-here"` +Every hour: `"cron:hourly": "your-script-goes-here"` +Every day: `"cron:daily": "your-script-goes-here"` // Runs at midnight +Every week: `"cron:weekly": "your-script-goes-here"` // Runs on Sunday +Every month: `"cron:monthly": "your-script-goes-here"` // Runs on the 1st +``` + +If you want to run a script at a specific time, you can use the following format: + +**ALL TIMES ARE IN UTC!!!** +**ALL TIMES USE THE 24 HOUR FORMAT!!!** + +``` +cron:hourly@0:25": "your-script-goes-here" // The hour is ignored +cron:daily@1:20": "your-script-goes-here" // This is at 1:20 AM UTC for 1:20 PM use 13:20 +cron:weekly@12:00": "your-script-goes-here" +cron:monthly@13:00": "your-script-goes-here" +``` + +What happens if you have two scripts with the same cron schedule? + +``` +cron:daily": "your-script-goes-here" +cron:daily #comment": "your-script-goes-here" // Everything after # is ignored so you can use this to make cron jobs unique or more readable +``` + +You can view the cron job status at http://theworkshop.cc diff --git a/packages/http/package.json b/packages/http/package.json index 212a0cb..913113d 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -7,10 +7,10 @@ "dev": "bun run --hot src/main.ts", "start": "bun run src/main.ts", "cron:constantly": "echo '💰 constantly'", - "cron:hourly@0:25": "echo '⌛ hourly@:030'", - "cron:daily@21:20": "echo '🏬 daily at midnight'", - "cron:monthly@13:00": "echo '🏬 daily at noon'", - "cron:daily@21:22 #fail": "echo '🍖 constantly with failure' && ls /nonexistant" + "cron:hourly@0:35": "echo '⌛ hourly@0:25'", + "cron:daily@12:12": "echo '🏬 daily at 12:12 UTC'", + "cron:monthly@13:00": "echo '🏬 monthly at 13:00 UTC'", + "cron:hourly@0:30 #fail": "echo '🍖 fails at 30 mins after the hour' && ls /nonexistant" }, "prettier": { "printWidth": 110, diff --git a/packages/http/src/cron.ts b/packages/http/src/cron.ts index 85a38ae..d37dc78 100644 --- a/packages/http/src/cron.ts +++ b/packages/http/src/cron.ts @@ -53,6 +53,7 @@ const getCrons = async () => { } } } + return cronScripts } @@ -180,7 +181,7 @@ export class CronJob { } finally { await kv.update("cronJobs", {}, (jobs) => { let history = jobs[this.id] || [] - history = history.slice(0, 9) // Keep only the last 10 runs + history = history.slice(0, 4) // Keep only the last 5 runs history.unshift({ runAt: now.toISO()!, error: runError ? getErrorMessage(runError) : undefined, diff --git a/packages/http/src/routes/index.tsx b/packages/http/src/routes/index.tsx index 8fd3e66..04ae2aa 100644 --- a/packages/http/src/routes/index.tsx +++ b/packages/http/src/routes/index.tsx @@ -1,5 +1,5 @@ import { subdomainInfo } from "../orchestrator" -import type { LoaderProps } from "@workshop/nano-remix" +import { Form, useAction, type LoaderProps } from "@workshop/nano-remix" import kv, { type CronJobHistory } from "@workshop/shared/kv" import { zone } from "@workshop/shared/utils" import { useMemo } from "hono/jsx" @@ -11,7 +11,13 @@ export const loader = async (_req: Request) => { return { subdomains: info.map((i) => i.name), cronJobs } } +export const action = async () => { + await kv.remove("cronJobs") + return { success: true } +} + export default function Index({ subdomains, cronJobs }: LoaderProps) { + const actionResult = useAction() const url = new URL(import.meta.url) const sortedEntries = useMemo(() => { @@ -37,7 +43,15 @@ export default function Index({ subdomains, cronJobs }: LoaderProps ))} -

CronJobs

+

CronJobs (Only showing the last 5 runs)

+
+ + {actionResult.data?.success &&

Cron jobs successfully erased!

} + {actionResult.error &&

Error clearing cron jobs: {actionResult.error}

} +
+
    {sortedEntries.map(([id, history]) => (
  • @@ -53,7 +67,7 @@ export default function Index({ subdomains, cronJobs }: LoaderProps { return (
      - {history.slice(0, 2).map((run, index) => ( + {history.map((run, index) => (
    • {DateTime.fromISO(run.runAt).setZone(zone).toRFC2822()} {run.error &&
      Error: {run.error}
      }