From 102f78b5238c3f788d7bd987d4a6dcf78fe1511a Mon Sep 17 00:00:00 2001 From: nate Date: Wed, 8 Apr 2026 15:58:27 +0400 Subject: [PATCH] fix --- apps/status/src/index.ts | 125 +++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 58 deletions(-) diff --git a/apps/status/src/index.ts b/apps/status/src/index.ts index 19a52ec..1d5370b 100644 --- a/apps/status/src/index.ts +++ b/apps/status/src/index.ts @@ -40,69 +40,78 @@ function isAuthorised(page: { id: string; password_hash: string | null }, req: R return verifyAuthCookie(req.headers.get("cookie"), page.id); } +// Memoirist (Elysia's router) treats `:slug.json` as a single parameter named +// "slug.json" and refuses to coexist with `:slug`. Instead, use one `:slug` +// route and dispatch on the suffix in the handler. +function splitSlugAndFormat(raw: string): { slug: string; format: "html" | "json" | "rss" } { + if (raw.endsWith(".json")) return { slug: raw.slice(0, -5), format: "json" }; + if (raw.endsWith(".rss")) return { slug: raw.slice(0, -4), format: "rss" }; + return { slug: raw, format: "html" }; +} + +async function renderHtml(slug: string, request: Request): Promise { + const page = await cached(`page:${slug}`, 60, () => loadStatusPage(slug)); + if (!page) return notFound(); + if (!isAuthorised(page, request)) { + return new Response(eta.render("password", { title: page.title, slug: page.slug, error: null }), { + status: 401, + headers: { "content-type": "text/html; charset=utf-8" }, + }); + } + const payload = await cached(`payload:${slug}`, 60, () => loadPagePayload(slug)); + if (!payload) return notFound(); + const html = eta.render("page", payload); + const headers: Record = { + "content-type": "text/html; charset=utf-8", + "cache-control": "public, max-age=30, s-maxage=60", + "x-frame-options":"SAMEORIGIN", + "x-content-type-options": "nosniff", + "referrer-policy":"strict-origin-when-cross-origin", + }; + if (!page.index_search) headers["x-robots-tag"] = "noindex, nofollow"; + return new Response(html, { headers }); +} + +async function renderJson(slug: string, request: Request, win?: Window): Promise { + const page = await cached(`page:${slug}`, 60, () => loadStatusPage(slug)); + if (!page) return new Response(JSON.stringify({ error: "not found" }), { status: 404, headers: { "content-type": "application/json" } }); + if (!isAuthorised(page, request)) return new Response(JSON.stringify({ error: "password required" }), { status: 401, headers: { "content-type": "application/json" } }); + const cacheKey = `payload:${slug}:${win ?? page.default_window}`; + const payload = await cached(cacheKey, 60, () => loadPagePayload(slug, win)); + if (!payload) return new Response(JSON.stringify({ error: "not found" }), { status: 404, headers: { "content-type": "application/json" } }); + return new Response(JSON.stringify(payload), { + headers: { + "content-type": "application/json", + "cache-control": "public, max-age=30, s-maxage=60", + ...(page.index_search ? {} : { "x-robots-tag": "noindex, nofollow" }), + }, + }); +} + +async function renderRssResp(slug: string): Promise { + const page = await loadStatusPage(slug); + if (!page) return notFound(); + const xml = await cached(`rss:${slug}`, 300, () => renderRss(page, PUBLIC_BASE)); + return new Response(xml, { + headers: { + "content-type": "application/rss+xml; charset=utf-8", + "cache-control": "public, max-age=300, s-maxage=300", + }, + }); +} + const app = new Elysia() .get("/", () => new Response("PingQL status service", { headers: { "content-type": "text/plain" }, })) - // Public HTML page - .get("/:slug", async ({ params, request, set }) => { - if (!allow(params.slug, clientIp(request))) return rateLimited(); - const page = await cached(`page:${params.slug}`, 60, () => loadStatusPage(params.slug)); - if (!page) return notFound(); - if (!isAuthorised(page, request)) { - return new Response(eta.render("password", { title: page.title, slug: page.slug, error: null }), { - status: 401, - headers: { "content-type": "text/html; charset=utf-8" }, - }); - } - const payload = await cached(`payload:${params.slug}`, 60, () => loadPagePayload(params.slug)); - if (!payload) return notFound(); - - const html = eta.render("page", payload); - const headers: Record = { - "content-type": "text/html; charset=utf-8", - "cache-control": "public, max-age=30, s-maxage=60", - "x-frame-options":"SAMEORIGIN", - "x-content-type-options": "nosniff", - "referrer-policy":"strict-origin-when-cross-origin", - }; - if (!page.index_search) headers["x-robots-tag"] = "noindex, nofollow"; - return new Response(html, { headers }); - }) - - // Public JSON - .get("/:slug.json", async ({ params, request, set, query }) => { - if (!allow(params.slug, clientIp(request))) return rateLimited(); - const page = await cached(`page:${params.slug}`, 60, () => loadStatusPage(params.slug)); - if (!page) { set.status = 404; return { error: "not found" }; } - if (!isAuthorised(page, request)) { set.status = 401; return { error: "password required" }; } - - const win = (query as any)?.window as Window | undefined; - const cacheKey = `payload:${params.slug}:${win ?? page.default_window}`; - const payload = await cached(cacheKey, 60, () => loadPagePayload(params.slug, win)); - if (!payload) { set.status = 404; return { error: "not found" }; } - return new Response(JSON.stringify(payload), { - headers: { - "content-type": "application/json", - "cache-control": "public, max-age=30, s-maxage=60", - ...(page.index_search ? {} : { "x-robots-tag": "noindex, nofollow" }), - }, - }); - }) - - // Public RSS - .get("/:slug.rss", async ({ params, request }) => { - if (!allow(params.slug, clientIp(request))) return rateLimited(); - const page = await loadStatusPage(params.slug); - if (!page) return notFound(); - const xml = await cached(`rss:${params.slug}`, 300, () => renderRss(page, PUBLIC_BASE)); - return new Response(xml, { - headers: { - "content-type": "application/rss+xml; charset=utf-8", - "cache-control": "public, max-age=300, s-maxage=300", - }, - }); + // Single public route — dispatches HTML / JSON / RSS by extension on the slug. + .get("/:slug", async ({ params, request, query }) => { + const { slug, format } = splitSlugAndFormat(params.slug); + if (!allow(slug, clientIp(request))) return rateLimited(); + if (format === "json") return renderJson(slug, request, (query as any)?.window as Window | undefined); + if (format === "rss") return renderRssResp(slug); + return renderHtml(slug, request); }) // Public SVG badge