···99import { verify } from '@octokit/webhooks-methods'
1010import { sql } from 'drizzle-orm'
1111import { installation, webhookEvent } from '#server/db/schema'
1212+import { kickWorker } from '#server/utils/kick-worker'
1213import { enqueue } from '#server/utils/queue'
1314import { revokeKeysForInstallation } from '#server/utils/tangled-pubkey'
1415···110111 // the raw webhook body. See PLAN.md "Deferred / follow-ups".
111112 if (RECOGNISED_EVENTS.has(eventName)) {
112113 await enqueueForEvent(event, eventName, deliveryId)
114114+ // Nudge the worker to drain the just-enqueued job now rather than on the
115115+ // next cron tick. Fire-and-forget via waitUntil; cron is the safety net.
116116+ kickWorker(event)
113117 }
114118115119 return { ok: true, deliveryId }
+28
server/utils/kick-worker.ts
···11+import type { H3Event } from 'h3'
22+33+/**
44+ * Nudge the job worker to run now, instead of waiting for the next cron tick.
55+ *
66+ * Fire-and-forget: registered via `event.waitUntil` so the serverless function
77+ * stays alive to complete the kick after the response flushes, but the webhook
88+ * never blocks on it. Any failure is swallowed; the per-minute cron is the
99+ * safety net, so a missed kick only costs latency, not correctness. The worker
1010+ * itself is safe to run concurrently with cron (claims are `FOR UPDATE SKIP
1111+ * LOCKED` with a lease), so a kick racing a tick can't double-process a job.
1212+ *
1313+ * Auth uses the same `CRON_SECRET` bearer the worker route already requires.
1414+ */
1515+export function kickWorker(event: H3Event): void {
1616+ const cronSecret = process.env.CRON_SECRET
1717+ const base = useRuntimeConfig().public.url?.replace(/\/$/, '')
1818+ if (!cronSecret || !base) return
1919+2020+ const run = globalThis.fetch(`${base}/api/jobs/run`, {
2121+ method: 'GET',
2222+ headers: { authorization: `Bearer ${cronSecret}` },
2323+ })
2424+ .then(() => undefined)
2525+ .catch(() => undefined)
2626+2727+ event.waitUntil(run)
2828+}
+79
test/unit/kick-worker.spec.ts
···11+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
22+import type { H3Event } from 'h3'
33+import { kickWorker } from '../../server/utils/kick-worker'
44+55+const ORIGINAL_SECRET = process.env.CRON_SECRET
66+const ORIGINAL_URL = process.env.NUXT_PUBLIC_URL
77+88+function fakeEvent() {
99+ const waitUntil = vi.fn<(p: Promise<unknown>) => void>()
1010+ // eslint-disable-next-line ts/no-unsafe-type-assertion -- only waitUntil is exercised
1111+ return { event: { waitUntil } as unknown as H3Event, waitUntil }
1212+}
1313+1414+describe('kickWorker', () => {
1515+ beforeEach(() => {
1616+ process.env.CRON_SECRET = 'test-secret'
1717+ process.env.NUXT_PUBLIC_URL = 'https://synchub.to'
1818+ // kick-worker reads `useRuntimeConfig().public.url`; the Nuxt auto-import
1919+ // isn't available in plain unit tests, so stub it to read from env.
2020+ vi.stubGlobal('useRuntimeConfig', () => ({ public: { url: process.env.NUXT_PUBLIC_URL ?? '' } }))
2121+ })
2222+2323+ afterEach(() => {
2424+ if (ORIGINAL_SECRET === undefined) delete process.env.CRON_SECRET
2525+ else process.env.CRON_SECRET = ORIGINAL_SECRET
2626+ if (ORIGINAL_URL === undefined) delete process.env.NUXT_PUBLIC_URL
2727+ else process.env.NUXT_PUBLIC_URL = ORIGINAL_URL
2828+ vi.unstubAllGlobals()
2929+ })
3030+3131+ it('fires a GET to the worker with the cron bearer and registers it via waitUntil', async () => {
3232+ const fetchMock = vi.fn<(url: string, init?: RequestInit) => Promise<Response>>(async () => new Response('{}'))
3333+ vi.stubGlobal('fetch', fetchMock)
3434+ const { event, waitUntil } = fakeEvent()
3535+3636+ kickWorker(event)
3737+3838+ expect(waitUntil).toHaveBeenCalledTimes(1)
3939+ expect(fetchMock).toHaveBeenCalledTimes(1)
4040+ const [url, init] = fetchMock.mock.calls[0]!
4141+ expect(url).toBe('https://synchub.to/api/jobs/run')
4242+ expect(init).toMatchObject({ method: 'GET', headers: { authorization: 'Bearer test-secret' } })
4343+ })
4444+4545+ it('strips a trailing slash from the public URL', () => {
4646+ process.env.NUXT_PUBLIC_URL = 'https://synchub.to/'
4747+ const fetchMock = vi.fn<(url: string, init?: RequestInit) => Promise<Response>>(async () => new Response('{}'))
4848+ vi.stubGlobal('fetch', fetchMock)
4949+ const { event } = fakeEvent()
5050+5151+ kickWorker(event)
5252+5353+ expect(fetchMock.mock.calls[0]![0]).toBe('https://synchub.to/api/jobs/run')
5454+ })
5555+5656+ it('no-ops when CRON_SECRET is unset', () => {
5757+ delete process.env.CRON_SECRET
5858+ const fetchMock = vi.fn<(url: string, init?: RequestInit) => Promise<Response>>()
5959+ vi.stubGlobal('fetch', fetchMock)
6060+ const { event, waitUntil } = fakeEvent()
6161+6262+ kickWorker(event)
6363+6464+ expect(fetchMock).not.toHaveBeenCalled()
6565+ expect(waitUntil).not.toHaveBeenCalled()
6666+ })
6767+6868+ it('swallows a fetch rejection (cron is the safety net)', async () => {
6969+ const rejecting = vi.fn<(url: string, init?: RequestInit) => Promise<Response>>(async () => { throw new Error('network down') })
7070+ vi.stubGlobal('fetch', rejecting)
7171+ const captured: Promise<unknown>[] = []
7272+ // eslint-disable-next-line ts/no-unsafe-type-assertion -- minimal event stub
7373+ const event = { waitUntil: (p: Promise<unknown>) => { captured.push(p) } } as unknown as H3Event
7474+7575+ kickWorker(event)
7676+7777+ await expect(captured[0]).resolves.toBeUndefined()
7878+ })
7979+})