diff --git a/.env.example b/.env.example index 4bebf30..375d28e 100644 --- a/.env.example +++ b/.env.example @@ -75,11 +75,12 @@ NUXT_GITHUB_APP_INSTALL_URL=https://github.com/apps/synchub-to/installations/new # --------------------------------------------------------------------------- # Cron secret — protects the worker tick endpoint (`/api/jobs/run`) from -# unauthenticated callers. In prod, Vercel Cron sends this automatically; -# locally, `pnpm jobs:tick` reads it from this env var. +# unauthenticated callers. Vercel auto-injects this as the `Authorization: +# Bearer` header on cron invocations, so the name must be exactly CRON_SECRET +# (not NUXT_-prefixed). Locally, `pnpm jobs:tick` reads the same var. # Generate with: pnpm gen:cron-secret # --------------------------------------------------------------------------- -NUXT_CRON_SECRET= +CRON_SECRET= # Optional: per-invocation worker time budget in milliseconds. # Default 25_000. Set lower in dev so `pnpm jobs:tick` returns sooner when diff --git a/.gitignore b/.gitignore index b3ddbb6..c167977 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .nitro .cache dist +.vercel # Node dependencies node_modules diff --git a/README.md b/README.md index c22cb2c..8c0044b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ helpers: ```bash pnpm gen:jwk # NUXT_ATPROTO_PRIVATE_JWK pnpm gen:encryption-key # NUXT_ENCRYPTION_KEY and NUXT_SESSION_PASSWORD -pnpm gen:cron-secret # NUXT_CRON_SECRET +pnpm gen:cron-secret # CRON_SECRET ``` The rest (`NUXT_DATABASE_URL`, the `NUXT_GITHUB_APP_*` values) come from your @@ -72,7 +72,7 @@ synchub.to runs on Vercel with a Neon Postgres database. Mark the secrets (`NUXT_DATABASE_URL`, `NUXT_GITHUB_APP_PRIVATE_KEY`, `NUXT_GITHUB_APP_CLIENT_SECRET`, `NUXT_ATPROTO_PRIVATE_JWK`, `NUXT_ENCRYPTION_KEY`, `NUXT_SESSION_PASSWORD`, - `NUXT_GITHUB_WEBHOOK_SECRET`, `NUXT_CRON_SECRET`) as **Sensitive**. + `NUXT_GITHUB_WEBHOOK_SECRET`, `CRON_SECRET`) as **Sensitive**. 3. Set `NUXT_PUBLIC_URL` to your real origin, point the GitHub App webhook at `https:///api/github/webhook`, and set the App's Setup + Callback URLs to `https:///connect` and diff --git a/nuxt.config.ts b/nuxt.config.ts index cca84d9..bf74fd2 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -26,7 +26,6 @@ export default defineNuxtConfig({ githubAppClientId: '', githubAppClientSecret: '', githubWebhookSecret: '', - cronSecret: '', workerBudgetMs: '', maxPackBytes: '', encryptionKey: '', diff --git a/scripts/jobs-tick.ts b/scripts/jobs-tick.ts index 8360d76..02780da 100644 --- a/scripts/jobs-tick.ts +++ b/scripts/jobs-tick.ts @@ -1,26 +1,26 @@ /** * Trigger the worker tick endpoint locally. * - * Reads `NUXT_CRON_SECRET` from `.env` (loaded by Node's --env-file when run - * via `pnpm jobs:tick`) and POSTs to /api/jobs/run with the matching - * `Authorization: Bearer …` header. + * Reads `CRON_SECRET` from `.env` (loaded by Node's --env-file when run via + * `pnpm jobs:tick`) and GETs /api/jobs/run with the matching + * `Authorization: Bearer …` header — the same shape Vercel Cron sends. * - * In production, Vercel Cron does this automatically every minute; this - * script is the local equivalent. + * In production, Vercel Cron does this automatically; this script is the + * local equivalent. */ import process from 'node:process' const url = process.env.JOBS_TICK_URL ?? 'http://127.0.0.1:3000/api/jobs/run' -const secret = process.env.NUXT_CRON_SECRET +const secret = process.env.CRON_SECRET if (!secret) { - console.error('NUXT_CRON_SECRET not set; copy from .env or run `pnpm gen:cron-secret`') + console.error('CRON_SECRET not set; copy from .env or run `pnpm gen:cron-secret`') process.exit(1) } const response = await fetch(url, { - method: 'POST', + method: 'GET', headers: { authorization: `Bearer ${secret}` }, }) diff --git a/server/api/jobs/run.post.ts b/server/api/jobs/run.get.ts similarity index 92% rename from server/api/jobs/run.post.ts rename to server/api/jobs/run.get.ts index ea60ba4..6cebedb 100644 --- a/server/api/jobs/run.post.ts +++ b/server/api/jobs/run.get.ts @@ -6,8 +6,7 @@ const LEASE_MS = 5 * 60_000 // 5 min — generous for a sync job const DEFAULT_BUDGET_MS = 25_000 // leave headroom under Vercel's 10s default; pro tiers can override export default defineEventHandler(async event => { - const config = useRuntimeConfig() - const cronSecret = config.cronSecret + const cronSecret = process.env.CRON_SECRET if (!cronSecret) { throw createError({ statusCode: 500, statusMessage: 'cron secret not configured' }) } @@ -18,7 +17,7 @@ export default defineEventHandler(async event => { } const workerId = `${process.env.VERCEL_DEPLOYMENT_ID ?? 'local'}:${crypto.randomUUID()}` - const budgetMs = Number(config.workerBudgetMs) || DEFAULT_BUDGET_MS + const budgetMs = Number(useRuntimeConfig().workerBudgetMs) || DEFAULT_BUDGET_MS const deadline = Date.now() + budgetMs let processed = 0