This commit is contained in:
Corey Johnson 2025-07-25 14:31:11 -07:00
parent e7b334a72b
commit 3abadc3b51
4 changed files with 56 additions and 8 deletions

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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<typeof loader>) {
const actionResult = useAction<typeof action>()
const url = new URL(import.meta.url)
const sortedEntries = useMemo(() => {
@ -37,7 +43,15 @@ export default function Index({ subdomains, cronJobs }: LoaderProps<typeof loade
</li>
))}
</ul>
<h2>CronJobs</h2>
<h2>CronJobs (Only showing the last 5 runs)</h2>
<Form name="clearCronJobs">
<button type="submit" disabled={actionResult.loading}>
Erase Cron Jobs Logs
</button>
{actionResult.data?.success && <p style={{ color: "green" }}>Cron jobs successfully erased!</p>}
{actionResult.error && <p style={{ color: "red" }}>Error clearing cron jobs: {actionResult.error}</p>}
</Form>
<ul>
{sortedEntries.map(([id, history]) => (
<li key={id}>
@ -53,7 +67,7 @@ export default function Index({ subdomains, cronJobs }: LoaderProps<typeof loade
const JobHistory = ({ history }: { history: CronJobHistory[] }) => {
return (
<ul>
{history.slice(0, 2).map((run, index) => (
{history.map((run, index) => (
<li key={index}>
<strong>{DateTime.fromISO(run.runAt).setZone(zone).toRFC2822()}</strong>
{run.error && <div style={{ color: "red" }}>Error: {run.error}</div>}