import { redisTtl } from "@config/environment"; import { fetch, redis } from "bun"; import { marked } from "marked"; const routeDef: RouteDef = { method: "GET", accepts: "*/*", returns: "*/*", log: false, }; async function addLazyLoading(html: string): Promise { return new HTMLRewriter() .on("img", { element(el) { el.setAttribute("loading", "lazy"); }, }) .transform(html); } async function sanitizeHtml(html: string): Promise { return new HTMLRewriter() .on( "script, iframe, object, embed, link[rel=import], svg, math, base, meta[http-equiv='refresh']", { element(el) { el.remove(); }, }, ) .on("*", { element(el) { for (const [name, value] of el.attributes) { const lowerName = name.toLowerCase(); const lowerValue = value.toLowerCase(); if (lowerName.startsWith("on")) { el.removeAttribute(name); } if ( (lowerName === "href" || lowerName === "src" || lowerName === "action") && (lowerValue.startsWith("javascript:") || lowerValue.startsWith("data:")) ) { el.removeAttribute(name); } } }, }) .on("img", { element(el) { el.setAttribute("loading", "lazy"); }, }) .transform(html); } async function fetchAndCacheReadme(url: string): Promise { const cacheKey = `readme:${url}`; const cached = await redis.get(cacheKey); if (cached) return cached; const res = await fetch(url, { headers: { Accept: "text/markdown", }, }); if (!res.ok) return null; if (res.headers.has("content-length")) { const size = Number.parseInt(res.headers.get("content-length") || "0", 10); if (size > 1024 * 100) return null; } const text = await res.text(); if (!text || text.length < 10) return null; const html = /\.(html?|htm)$/i.test(url) ? text : await marked.parse(text); const safe = await sanitizeHtml(html); await redis.set(cacheKey, safe); await redis.expire(cacheKey, redisTtl); return safe; } async function handler(request: ExtendedRequest): Promise { const { url } = request.query; if ( !url || !url.startsWith("http") || !/\.(md|markdown|txt|html?)$/i.test(url) ) { return Response.json( { success: false, error: { code: "INVALID_URL", message: "Invalid URL provided", }, }, { status: 400 }, ); } const safe = await fetchAndCacheReadme(url); if (!safe) { return Response.json( { success: false, error: { code: "FETCH_FAILED", message: "Failed to fetch or process file", }, }, { status: 400 }, ); } return new Response(safe, { headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate", Pragma: "no-cache", Expires: "0", }, status: 200, }); } export { handler, routeDef };