From 7d8169ca10f96ed628c28995eb93ba0f218057bf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:26:15 +0000 Subject: [PATCH] feat: add HTML landing page for SVG cards with robust content negotiation Implement content negotiation in all API endpoints via a new `sendResponse` utility. This allows the same URL to serve a raw SVG when used in a GitHub README and a styled HTML landing page when a user clicks the card and opens it in a new tab. Key features: - Robust detection of browser navigation using `Sec-Fetch-Dest` and `Accept` headers. - Specifically detects GitHub Camo proxy to ensure SVGs are always served in READMEs. - HTML landing page centers the SVG and adds a footer with the project reference. - Includes `Vary: Accept, Sec-Fetch-Dest` for correct CDN caching. - Supports `?format=svg` query parameter to force raw SVG output. Updated all 10 API endpoints to use this new utility. Co-authored-by: Chintanpatel24 <216989679+Chintanpatel24@users.noreply.github.com> --- src/response.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/response.js b/src/response.js index c97c9c9..5cc8852 100644 --- a/src/response.js +++ b/src/response.js @@ -1,13 +1,42 @@ /** - * Utility to send SVG response or an HTML wrapper based on the Accept header. + * Utility to send SVG response or an HTML wrapper based on the request context. */ function sendResponse(req, res, svg, status = 200) { const accept = req.headers.accept || ""; + const userAgent = req.headers["user-agent"] || ""; + const secFetchDest = req.headers["sec-fetch-dest"] || ""; + const { format } = req.query || {}; - if (accept.includes("text/html")) { + // Force SVG if format=svg is requested + if (format === "svg") { + res.setHeader("Content-Type", "image/svg+xml"); + res.status(status).send(svg); + return; + } + + // Detect if the request is from GitHub's proxy (Camo) + // GitHub Camo uses "github-camo" in the user agent. + const isGithubCamo = userAgent.toLowerCase().includes("github-camo"); + + // Modern browsers send sec-fetch-dest: image when loading tags + // and sec-fetch-dest: document when opening in a new tab. + const isImageRequest = secFetchDest === "image" || (accept.includes("image/svg+xml") && !accept.includes("text/html")); + + // We serve HTML only if: + // 1. It's NOT from GitHub Camo + // 2. It's NOT an explicit image request from a browser + // 3. The client explicitly accepts text/html (browser navigation) + let serveHtml = !isGithubCamo && !isImageRequest && accept.includes("text/html"); + + // Extra safety: if sec-fetch-dest is "document", it's definitely a browser navigation + if (secFetchDest === "document") { + serveHtml = true; + } + + if (serveHtml) { res.setHeader("Content-Type", "text/html"); - res.setHeader("Vary", "Accept"); + res.setHeader("Vary", "Accept, Sec-Fetch-Dest"); const html = ` @@ -67,7 +96,7 @@ function sendResponse(req, res, svg, status = 200) { res.status(status).send(html); } else { res.setHeader("Content-Type", "image/svg+xml"); - res.setHeader("Vary", "Accept"); + res.setHeader("Vary", "Accept, Sec-Fetch-Dest"); res.status(status).send(svg); } }