about summary refs log tree commit diff stats
path: root/src/index.ts
diff options
context:
space:
mode:
authorAlan Pearce2023-09-17 17:31:18 +0200
committerAlan Pearce2023-09-17 17:31:18 +0200
commit602f249c2cfac0e7b6613fb63f5fb519aa1ca952 (patch)
treebe522208e3172e62b4777a66af4bd931677f7fcb /src/index.ts
parent1a7abb3723d6b9db0d199c26d2a207e03636738a (diff)
downloadwebsite-602f249c2cfac0e7b6613fb63f5fb519aa1ca952.tar.lz
website-602f249c2cfac0e7b6613fb63f5fb519aa1ca952.tar.zst
website-602f249c2cfac0e7b6613fb63f5fb519aa1ca952.zip
Move servers into app.ts and export for testing
Diffstat (limited to 'src/index.ts')
-rw-r--r--src/index.ts234
1 files changed, 5 insertions, 229 deletions
diff --git a/src/index.ts b/src/index.ts
index 450fe8f..83ad03d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,231 +1,7 @@
-import path from "node:path";
-import fs, { Stats } from "node:fs";
-import type { BunFile, Serve } from "bun";
-import * as Sentry from "@sentry/node";
-import prom from "bun-prometheus-client";
+import { server, metricsServer } from "./app";
 
-import readConfig from "./config";
+const metricsServed = Bun.serve(metricsServer);
+console.info(`Metrics server started on port ${metricsServed.port}`);
 
-Sentry.init({
-  release: `homestead@${Bun.env.FLY_MACHINE_VERSION}`,
-  tracesSampleRate: 1.0,
-});
-
-const base = ".";
-const publicDir = path.resolve(base, "public") + path.sep;
-
-const config = readConfig(base);
-const defaultHeaders = {
-  ...config.extra.headers,
-  vary: "Accept-Encoding",
-};
-
-type File = {
-  filename: string;
-  handle: BunFile;
-  relPath: string;
-  headers?: Record<string, string>;
-  type: string;
-  size: number;
-  mtime: Date;
-};
-
-const metrics = {
-  requests: new prom.Counter({
-    name: "homestead_requests",
-    help: "Number of requests by path, status code, and method",
-    labelNames: ["path", "status_code", "method"],
-  }),
-  requestDuration: new prom.Histogram({
-    name: "homestead_request_duration_seconds",
-    help: "Request duration in seconds",
-    labelNames: ["path"],
-  }),
-};
-
-let files = new Map<string, File>();
-
-function registerFile(
-  path: string,
-  pathname: string,
-  filename: string,
-  stat: Stats,
-): void {
-  pathname = "/" + (pathname === "." || pathname === "./" ? "" : pathname);
-
-  if (files.get(pathname) !== undefined) {
-    console.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,
-    headers:
-      pathname === "/404.html"
-        ? Object.assign({}, defaultHeaders, { "cache-control": "no-cache" })
-        : undefined,
-    size: stat.size,
-    mtime: stat.mtime,
-  });
-}
-
-function walkDirectory(root: string, dir: string) {
-  const absDir = path.join(root, dir);
-  for (let pathname of fs.readdirSync(absDir)) {
-    const relPath = path.join(dir, pathname);
-    const absPath = path.join(absDir, pathname);
-    const stat = fs.statSync(absPath);
-    if (stat.isDirectory()) {
-      walkDirectory(root, relPath + path.sep);
-    } else if (stat.isFile()) {
-      if (pathname.startsWith("index.html")) {
-        const dir = relPath.replace("index.html", "");
-        registerFile(relPath, dir, absPath, stat);
-        if (dir !== "") {
-          registerFile(relPath, dir + path.sep, absPath, stat);
-        }
-      }
-      registerFile(relPath, relPath, absPath, stat);
-    }
-  }
-}
-
-walkDirectory(publicDir, "");
-
-async function serveFile(
-  file: File | undefined,
-  statusCode: number = 200,
-  extraHeaders: Record<string, string> = {},
-): Promise<Response> {
-  return new Response(await file.handle.arrayBuffer(), {
-    headers: {
-      "last-modified": file.mtime.toUTCString(),
-      ...extraHeaders,
-      ...(file.headers || defaultHeaders),
-    },
-    status: statusCode,
-  });
-}
-
-function parseIfModifiedSinceHeader(header: string | null): number {
-  return header ? new Date(header).getTime() + 999 : 0;
-}
-
-const metricsServer = Bun.serve({
-  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 });
-    }
-  },
-});
-
-console.info(
-  `Serving metrics on http://${metricsServer.hostname}:${metricsServer.port}/metrics`,
-);
-
-const server = Bun.serve({
-  fetch: async function (request) {
-    const pathname = new URL(request.url).pathname.replace(/\/\/+/g, "/");
-    const endTimer = metrics.requestDuration.startTimer({ path: pathname });
-    const transaction = Sentry.startTransaction({
-      name: pathname,
-      op: "http.server",
-      description: `${request.method} ${pathname}`,
-      tags: {
-        url: request.url,
-        "http.method": request.method,
-        "http.user_agent": request.headers.get("user-agent"),
-      },
-    });
-    try {
-      const file = files.get(pathname);
-      metrics.requests.inc({ path: pathname });
-      if (file && (await file.handle.exists())) {
-        if (
-          parseIfModifiedSinceHeader(
-            request.headers.get("if-modified-since"),
-          ) >= file?.mtime.getTime()
-        ) {
-          metrics.requests.inc({
-            method: request.method,
-            path: pathname,
-            status_code: 304,
-          });
-          transaction.setHttpStatus(304);
-          return new Response("", { status: 304, headers: defaultHeaders });
-        }
-        metrics.requests.inc({
-          method: request.method,
-          path: pathname,
-          status_code: 200,
-        });
-        const encodings = (request.headers.get("accept-encoding") || "")
-          .split(",")
-          .map((x) => x.trim().toLowerCase());
-        if (encodings.includes("br") && files.has(file.relPath + ".br")) {
-          transaction.setHttpStatus(200);
-          transaction.setTag("http.content-encoding", "br");
-          return serveFile(files.get(file.relPath + ".br"), 200, {
-            "content-encoding": "br",
-            "content-type": file.type,
-          });
-        } else if (
-          encodings.includes("zstd") &&
-          files.has(file.relPath + ".zst")
-        ) {
-          transaction.setHttpStatus(200);
-          transaction.setTag("http.content-encoding", "zstd");
-          return serveFile(files.get(file.relPath + ".zst"), 200, {
-            "content-encoding": "zstd",
-            "content-type": file.type,
-          });
-        } else if (
-          encodings.includes("gzip") &&
-          files.has(file.relPath + ".gz")
-        ) {
-          transaction.setHttpStatus(200);
-          transaction.setTag("http.content-encoding", "gzip");
-          return serveFile(files.get(file.relPath + ".gz"), 200, {
-            "content-encoding": "gzip",
-            "content-type": file.type,
-          });
-        }
-        transaction.setHttpStatus(200);
-        transaction.setTag("http.content-encoding", "identity");
-        return serveFile(file);
-      } else {
-        metrics.requests.inc({
-          method: request.method,
-          path: pathname,
-          status_code: 404,
-        });
-        transaction.setHttpStatus(404);
-        transaction.setTag("http.content-encoding", "identity");
-        return serveFile(files.get("/404.html"), 404);
-      }
-    } catch (error) {
-      transaction.setTag("http.content-encoding", "identity");
-      transaction.setHttpStatus(503);
-      metrics.requests.inc({
-        method: request.method,
-        path: pathname,
-        status_code: 503,
-      });
-      Sentry.captureException(error);
-      return new Response("Something went wrong", { status: 503 });
-    } finally {
-      const seconds = endTimer();
-      metrics.requestDuration.observe(seconds);
-      transaction.finish();
-    }
-  },
-});
-
-console.info(`Serving website on http://${server.hostname}:${server.port}/`);
+const served = Bun.serve(server);
+console.info(`Serving website on http://${served.hostname}:${served.port}/`);