diff options
author | Alan Pearce | 2024-04-17 20:28:04 +0200 |
---|---|---|
committer | Alan Pearce | 2024-04-17 20:53:45 +0200 |
commit | 30fed9da228ef9bab5734e000e598ff380cb55f5 (patch) | |
tree | cfc89a40a12dcc572c0a457b62bdde129690e482 /src/app.ts | |
parent | 1e05c18ed8e44078e3e102ff5bb8af6e25717fc9 (diff) | |
download | website-30fed9da228ef9bab5734e000e598ff380cb55f5.tar.lz website-30fed9da228ef9bab5734e000e598ff380cb55f5.tar.zst website-30fed9da228ef9bab5734e000e598ff380cb55f5.zip |
bye bye bun
Diffstat (limited to 'src/app.ts')
-rw-r--r-- | src/app.ts | 272 |
1 files changed, 0 insertions, 272 deletions
diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index a51e235..0000000 --- a/src/app.ts +++ /dev/null @@ -1,272 +0,0 @@ -import path from "node:path"; -import fs from "node:fs/promises"; -import type { Stats } from "node:fs"; -import type { BunFile, Serve } from "bun"; -import * as Sentry from "@sentry/node"; -import prom from "bun-prometheus-client"; -import log from "loglevel"; - -import config from "./config"; - -log.setLevel((import.meta.env["LOG_LEVEL"] || "info") as log.LogLevelDesc); - -Sentry.init({ - release: `homestead@${import.meta.env["FLY_MACHINE_VERSION"]}`, - tracesSampleRate: 1.0, -}); - -const expectedHostURL = new URL( - import.meta.env.NODE_ENV === "production" - ? config.base_url - : "http://localhost:3000", -); -const defaultHeaders = { - ...config.extra.headers, - vary: "Accept-Encoding", -}; - -type File = { - filename: string; - handle: BunFile; - relPath: string; - type: string; - size: number; - mtime: Date; - etag: string; -}; - -const metrics = { - requests: new prom.Counter({ - name: "homestead_requests", - help: "Number of requests by path, status code, and method", - labelNames: ["status_code", "content_encoding", "cache_basis"] as const, - }), - requestDuration: new prom.Histogram({ - name: "homestead_request_duration_seconds", - help: "Request duration in seconds", - labelNames: ["path"] as const, - }), -}; - -let files = new Map<string, File>(); - -async function hashFile(file: BunFile): Promise<string> { - return new Bun.CryptoHasher("sha256") - .update(await file.arrayBuffer()) - .digest("base64"); -} - -async function registerFile( - path: string, - pathname: string, - filename: string, - stat: Stats, -): Promise<void> { - pathname = "/" + (pathname === "." || pathname === "./" ? "" : pathname); - - if (files.get(pathname) !== undefined) { - log.warn("File already registered:", pathname); - } - const handle = Bun.file(filename); - - files.set(pathname, { - filename, - relPath: "/" + path, - handle: handle, - type: pathname.startsWith("/feed-styles.xsl") ? "text/xsl" : handle.type, - size: stat.size, - mtime: stat.mtime, - etag: `W/"${await hashFile(handle)}"`, - }); -} - -async function walkDirectory(root: string) { - for (let relPath of await fs.readdir(root, { recursive: true })) { - const absPath = path.join(root, relPath); - const stat = await fs.stat(absPath); - if (stat.isFile()) { - if (relPath.includes("index.html")) { - const dir = relPath.replace("index.html", ""); - await registerFile(relPath, dir, absPath, stat); - } else { - await registerFile(relPath, relPath, absPath, stat); - } - } - } -} - -await walkDirectory("public/"); - -async function serveFile( - file: File, - statusCode: number = 200, - extraHeaders: Record<string, string> = {}, -): Promise<Response> { - return new Response(await file.handle.arrayBuffer(), { - headers: { - "last-modified": file.mtime.toUTCString(), - ...extraHeaders, - ...defaultHeaders, - }, - status: statusCode, - }); -} - -function parseIfModifiedSinceHeader(header: string | null): number { - return header ? new Date(header).getTime() + 999 : 0; -} - -export const metricsServer = { - port: 9091, - fetch: async function (request) { - const pathname = new URL(request.url).pathname; - switch (pathname) { - case "/metrics": - return new Response(await prom.register.metrics()); - default: - return new Response("", { status: 404 }); - } - }, -} satisfies Serve; - -export const server = { - fetch: async function (request) { - const url = new URL(request.url); - const pathname = url.pathname.replace(/\/\/+/g, "/"); - const hostname = request.headers.get("host")?.toLowerCase() || "unknown"; - const endTimer = metrics.requestDuration.startTimer({ path: pathname }); - let status; - let newpath; - try { - if (pathname === "/health") { - return new Response("OK", { status: (status = 200) }); - } else if ( - config.redirect_other_hostnames && - hostname !== expectedHostURL.host - ) { - metrics.requests.inc({ - content_encoding: "identity", - status_code: (status = 301), - }); - return new Response("", { - status, - headers: { - location: new URL(pathname, expectedHostURL).toString(), - }, - }); - } - const { base, ext } = path.parse(pathname); - const file = files.get(pathname); - let contentEncoding = "identity"; - let suffix = ""; - if ( - ![".br", ".zst", ".gz"].includes(ext || base) && - !pathname.startsWith("/404.html") && - file && - (await file.handle.exists()) - ) { - let etagMatch = request.headers.get("if-none-match") === file.etag; - let mtimeMatch = - parseIfModifiedSinceHeader( - request.headers.get("if-modified-since"), - ) >= file?.mtime.getTime(); - if (etagMatch || mtimeMatch) { - metrics.requests.inc({ - content_encoding: contentEncoding, - status_code: (status = 304), - cache_basis: etagMatch ? "etag" : "mtime", - }); - return new Response("", { status: status, headers: defaultHeaders }); - } - const encodings = (request.headers.get("accept-encoding") || "") - .split(",") - .map((x) => x.trim().toLowerCase()); - if (encodings.includes("br") && files.has(pathname + ".br")) { - contentEncoding = "br"; - suffix = ".br"; - } else if (encodings.includes("zstd") && files.has(pathname + ".zst")) { - contentEncoding = "zstd"; - suffix = ".zst"; - } else if (encodings.includes("gzip") && files.has(pathname + ".gz")) { - contentEncoding = "gzip"; - suffix = ".gz"; - } - - status = 200; - metrics.requests.inc({ - status_code: status, - content_encoding: contentEncoding, - }); - const endFile = files.get(pathname + suffix); - if (!endFile) { - throw new Error(`File ${pathname} not found`); - } - return serveFile(endFile, status, { - "content-encoding": contentEncoding, - "content-type": file.type, - // weak etags can be used for multiple equivalent representations - etag: file.etag, - }); - } else { - if (files.has(pathname + "/")) { - newpath = pathname + "/"; - metrics.requests.inc({ - content_encoding: contentEncoding, - status_code: (status = 302), - }); - return new Response("", { - status: status, - headers: { location: newpath }, - }); - } else if ( - pathname.endsWith("index.html") && - files.has(pathname.replace(/index.html$/, "")) - ) { - newpath = pathname.replace(/index.html$/, ""); - metrics.requests.inc({ - content_encoding: contentEncoding, - status_code: (status = 302), - }); - return new Response("", { - status: status, - headers: { location: newpath }, - }); - } - status = 404; - const notfound = files.get("/404.html"); - if (!request.headers.get("accept")?.split(",").includes("text/html")) { - return new Response("404 Not Found", { - status, - headers: defaultHeaders, - }); - } - if (notfound) { - return serveFile(notfound, status, { - "content-type": "text/html; charset=utf-8", - }); - } else { - log.warn("404.html not found"); - return new Response("404 Not Found", { - status: status, - headers: { "content-type": "text/plain", ...defaultHeaders }, - }); - } - } - } catch (error) { - metrics.requests.inc({ - status_code: status, - content_encoding: "identity", - }); - Sentry.captureException(error); - log.error("Error", error); - return new Response("Something went wrong", { status: status }); - } finally { - if (status === 200) { - const seconds = endTimer(); - metrics.requestDuration.observe(seconds); - } - } - }, -} satisfies Serve; - -export default server; |