igneum/site/api/acceptance.mjs

45 lines
2.8 KiB
JavaScript

// The acceptance registry, live. GET /api/acceptance: docs/plans/igneum-2.0-test-registry.json as it stands on master on the
// public git host (git.igneum.network), so /acceptance shows each case as it passes without a redeploy. The page renders its
// build-time copy first and polls this every 60 seconds; it re-renders only when a case state or the registry's source_sha256
// moves. Cached 60 s in this function instance and at the edge. On any failure the reply is { ok: false, reason } with
// status 200, and the page keeps the copy it has.
export const SOURCE_URL = 'https://git.igneum.network/api/v1/repos/igneum-network/igneum/contents/docs/plans/igneum-2.0-test-registry.json?ref=master';
export const SOURCE = 'git.igneum.network master';
export const TTL_MS = 60_000;
// the git host's contents reply: JSON with a base64 `content` field; the registry must carry its suites
export function decode(reply) {
if (!reply || typeof reply.content !== 'string') throw new Error('the git host reply has no content field');
const b64 = reply.encoding === 'base64' || !reply.encoding;
const text = b64 ? Buffer.from(reply.content.replace(/\s+/g, ''), 'base64').toString('utf8') : reply.content;
const registry = JSON.parse(text.replace(/\bBST\b/g, 'UK')); // the zone name is a forbidden string on a served page: the times read UK
if (!registry || !Array.isArray(registry.suites)) throw new Error('the registry has no suites');
return registry;
}
export function createHandler({ fetchImpl = globalThis.fetch, now = () => Date.now() } = {}) {
let cache = null; // { at, body }: one per function instance
return async function handler(req, res) {
res.setHeader('Cache-Control', 'public, max-age=30, s-maxage=60, stale-while-revalidate=60');
res.setHeader('Access-Control-Allow-Origin', '*');
if (req.method !== 'GET') { res.setHeader('Allow', 'GET'); return res.status(405).json({ ok: false, reason: 'method not allowed' }); }
const t = now();
if (cache && t - cache.at < TTL_MS) return res.status(200).json(cache.body);
try {
const ctl = typeof AbortController !== 'undefined' ? new AbortController() : null;
const timer = ctl ? setTimeout(() => ctl.abort(), 8000) : null;
let r;
try { r = await fetchImpl(SOURCE_URL, { headers: { accept: 'application/json' }, signal: ctl ? ctl.signal : undefined }); }
finally { if (timer) clearTimeout(timer); }
if (!r.ok) throw new Error(`the git host answered ${r.status}`);
const registry = decode(await r.json());
const body = { ok: true, fetched_at: new Date(t).toISOString(), source: SOURCE, registry };
cache = { at: t, body };
return res.status(200).json(body);
} catch (e) {
res.setHeader('Cache-Control', 'no-store');
return res.status(200).json({ ok: false, reason: String(e && e.message || e) });
}
};
}
export default createHandler();