igneum/site/api/acceptance.mjs

45 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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').replace(/\bPC [12]\b/g, 'the project’s own rig').replace(/Devnet 3\b/g, 'the earlier devnet')); // 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();