From 0664ff7f5fb6707faa48b67284a8cddca5a1a7b3 Mon Sep 17 00:00:00 2001 From: belminpre Date: Thu, 11 Jun 2026 10:33:16 +0200 Subject: [PATCH] Add /qa-302, /qa-303, /qa-307, /qa-308, /qa-401, /qa-404, /qa-429 endpoints Adds seven QA-only HTTP status endpoints alongside the existing /qa-status (200/500), /qa-301, and /qa-403. Implemented as a single parameterized Netlify Function netlify/functions/qa-code.ts which inspects the request's public path (req.url in Functions v2 reflects the original /qa-NNN, not the internal /.netlify/functions/qa-code path it was rewritten to) and emits the matching status: /qa-302, /qa-303, /qa-307, /qa-308 -> real 3xx + Location header (https://weirdanimals.life/) /qa-401, /qa-404, /qa-429 -> real 4xx + text/html body with and <h1> Parameterized over 7 separate files because the only thing that varies per code is the status number and (for 3xx) the Location header. The existing per-code files keep their dedicated handlers (qa-status has a QA_FORCE_500 env toggle that doesn't fit a generic dispatcher). Defensive: unknown paths return 404 + plain text so misconfiguration fails loud rather than silently emitting a default status. - netlify/functions/qa-code.ts: new parameterized function. - netlify.toml: add 7 new [[redirects]] blocks (one per code), all targeting /.netlify/functions/qa-code with status = 200 (rewrite TYPE, preserves upstream status + headers). Placed between the existing /qa-403 block and the SPA /* catch-all so they are not swallowed. The existing /qa-status, /qa-301, /qa-403 functions and redirects are untouched. No app routes, pages, or dependencies changed. Co-authored-by: Cursor <cursoragent@cursor.com> --- netlify.toml | 44 ++++++++++++++++++ netlify/functions/qa-code.ts | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 netlify/functions/qa-code.ts diff --git a/netlify.toml b/netlify.toml index 29bc444..6665c11 100644 --- a/netlify.toml +++ b/netlify.toml @@ -40,6 +40,50 @@ to = "/.netlify/functions/qa-403" status = 200 +# QA-only: additional fixed-status endpoints handled by a single +# parameterized function (netlify/functions/qa-code.ts). The function +# inspects the public path it was hit at (req.url in v2 reflects the +# original /qa-NNN, not the internal /.netlify/functions/qa-code) and +# emits the matching status. Same rewrite-proxy mechanism as the QA +# endpoints above — status = 200 is the rewrite TYPE, not a forced +# response code, so Netlify preserves the function's real status code +# and headers (Location for 3xx, Content-Type for 4xx HTML). +# All MUST remain BEFORE the SPA catch-all below. +[[redirects]] + from = "/qa-302" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-303" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-307" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-308" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-401" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-404" + to = "/.netlify/functions/qa-code" + status = 200 + +[[redirects]] + from = "/qa-429" + to = "/.netlify/functions/qa-code" + status = 200 + # Redirect all requests to /index.html for SPA routing [[redirects]] from = "/*" diff --git a/netlify/functions/qa-code.ts b/netlify/functions/qa-code.ts new file mode 100644 index 0000000..d7f2792 --- /dev/null +++ b/netlify/functions/qa-code.ts @@ -0,0 +1,90 @@ +// QA-only Netlify Function used to emit a fixed HTTP status code based on +// the public path the request came in on. One function, many rewrites in +// netlify.toml (one per code: /qa-302, /qa-303, /qa-307, /qa-308, /qa-401, +// /qa-404, /qa-429). Used to test how Prerender handles each status on a +// clean, page-looking URL. +// +// How status preservation works: +// - The status code AND headers (Location / Content-Type) are set by THIS +// function and passed through unchanged by Netlify's status=200 rewrite +// proxy in netlify.toml. The client sees a real 302/303/307/308/401/ +// 404/429 — not a 200 with error text, and Netlify does not auto-follow +// the 3xx redirects server-side. +// +// How the path is read: +// - In Netlify Functions v2, req.url reflects the URL the client actually +// requested (e.g. https://weirdanimals.life/qa-302), even when the +// function was reached via a rewrite-proxy. We parse the pathname and +// match against an explicit allow-list of codes. Unknown paths return +// a 404 so misconfiguration fails loud instead of silently emitting a +// wrong status. + +type RedirectConfig = { + kind: "redirect"; + status: number; +}; + +type HtmlConfig = { + kind: "html"; + status: number; + title: string; + heading: string; +}; + +type CodeConfig = RedirectConfig | HtmlConfig; + +const REDIRECT_LOCATION = "https://weirdanimals.life/"; + +const CODES: Record<string, CodeConfig> = { + "302": { kind: "redirect", status: 302 }, + "303": { kind: "redirect", status: 303 }, + "307": { kind: "redirect", status: 307 }, + "308": { kind: "redirect", status: 308 }, + "401": { kind: "html", status: 401, title: "401 - QA", heading: "Unauthorized" }, + "404": { kind: "html", status: 404, title: "404 - QA", heading: "Not Found" }, + "429": { kind: "html", status: 429, title: "429 - QA", heading: "Too Many Requests" }, +}; + +function renderHtml(title: string, heading: string): string { + return `<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <title>${title} + + +

${heading}

+ + +`; +} + +export default async (req: Request): Promise => { + const { pathname } = new URL(req.url); + const match = pathname.match(/^\/qa-(\d{3})\/?$/); + const code = match?.[1]; + const cfg = code ? CODES[code] : undefined; + + if (!cfg) { + return new Response( + `QA: no handler for path ${pathname}. Expected /qa- where is one of: ${Object.keys(CODES).join(", ")}.`, + { + status: 404, + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }, + ); + } + + if (cfg.kind === "redirect") { + return new Response(null, { + status: cfg.status, + headers: { Location: REDIRECT_LOCATION }, + }); + } + + return new Response(renderHtml(cfg.title, cfg.heading), { + status: cfg.status, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); +};