igneum/relay/api/wake.mjs
igneum-josh 5ee7909d5c relay: /wake long-poll for the apps' remote jobs (public GET held 45 s, authenticated POST of the stamp)
GET /wake?since=<stamp> is public (the apps hold no token) and rate limited (30 a minute per IP). It holds up to
45 s, re-reading the stamp every 2 s, and answers {stamp, at, added, changed, held_ms} the moment the stored stamp
differs from since, else the unchanged stamp at the deadline. POST /r/<token>/wake {stamp, added} (the relay's
auth, also x-relay-token or x-igneum-key on /wake) records a stamp; one row per stamp in relay_wake, created by the
first POST. maxDuration 60 s for api/wake.mjs in vercel.json. api/relay.mjs is untouched.

The handler lives in lib/wake.mjs with its dependencies injected; relay/test/wake.test.mjs drives it with a fake
database, a fake clock and a fake sleep (the hold, the change, the deadline, the rate limit, the hold cap, auth, a
database error). CI's site job runs it with the other relay tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 09:22:02 +01:00

18 lines
1.2 KiB
JavaScript

// Igneum wake: the apps' long-poll for a new jobs file (app/igneum-app/src/jobrun.rs, 0.3.6). Reached at /wake
// (public: the apps hold no token) and at /r/<token>/wake (the same function; the token matters only to POST).
// The contract and the whole handler live in ../lib/wake.mjs so the test can drive it without a database.
// GET wake?since=<stamp>[&hold=45] up to 45 s; {stamp, at, added, changed, held_ms}; 30 a minute per IP
// POST wake {stamp, added?} the relay's auth (token in the path or x-relay-token, or x-igneum-key)
// maxDuration 60 s for this file is set in vercel.json.
import { neon, authed, readJson } from '../lib/relay.mjs';
import { makeHandler, RateLimit } from '../lib/wake.mjs';
const json = (res, status, obj) => { res.status(status).setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify(obj)); };
const limiter = new RateLimit();
const holds = { n: 0 };
export default async function handler(req, res) {
let sql;
try { sql = neon(); } catch (e) { res.setHeader('Cache-Control', 'no-store'); return json(res, 500, { ok: false, error: e.message }); }
return makeHandler({ sql, authed, readJson, json, limiter, holds })(req, res);
}