45 lines
2.8 KiB
JavaScript
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();
|