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 readConfig from "./config";
Sentry.init({});
const base = "./website/";
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;
size: number;
mtime: Date;
};
const collectDefaultMetrics = prom.collectDefaultMetrics;
collectDefaultMetrics({
labels: {
FLY_APP_NAME: Bun.env.FLY_APP_NAME,
FLY_ALLOC_ID: Bun.env.FLY_ALLOC_ID,
FLY_REGION: Bun.env.FLY_REGION,
},
});
const counters = {
requestsByStatus: new prom.Counter({
name: "requests_by_status",
help: "Number of requests by status code",
labelNames: ["status_code"],
}),
requestsByPath: new prom.Counter({
name: "requests_by_path",
help: "Number of requests by path",
labelNames: ["path"],
}),
};
let files = new Map();
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);
}
files.set(pathname, {
filename,
relPath: "/" + path,
handle: Bun.file(filename),
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 = {},
): Promise {
if (file && (await file.handle.exists())) {
counters.requestsByStatus.inc({ status_code: statusCode });
return new Response(file.handle, {
headers: {
"last-modified": file.mtime.toUTCString(),
...extraHeaders,
...(file.headers || defaultHeaders),
},
status: statusCode,
});
} else {
counters.requestsByStatus.inc({ status_code: 404 });
// TODO return encoded
return serveFile(files.get("/404.html"), 404);
}
}
async function serveEncodedFile(
file: File | undefined,
statusCode: number = 200,
extraHeaders: Record = {},
): Promise {
const res = await serveFile(file, statusCode, extraHeaders);
res.headers.delete("content-disposition");
return res;
}
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) {
try {
const pathname = new URL(request.url).pathname;
const file = files.get(pathname);
counters.requestsByPath.inc({ path: pathname });
if (file) {
if (
parseIfModifiedSinceHeader(
request.headers.get("if-modified-since"),
) >= file?.mtime.getTime()
) {
counters.requestsByStatus.inc({ status_code: 304 });
return new Response("", { status: 304, headers: defaultHeaders });
}
const encodings = (request.headers.get("accept-encoding") || "")
.split(",")
.map((x) => x.trim().toLowerCase());
if (encodings.includes("br") && files.has(file.relPath + ".br")) {
return serveEncodedFile(files.get(file.relPath + ".br"), 200, {
"content-encoding": "br",
"content-type": file.handle.type,
});
} else if (
encodings.includes("zstd") &&
files.has(file.relPath + ".zst")
) {
return serveEncodedFile(files.get(file.relPath + ".zst"), 200, {
"content-encoding": "zstd",
"content-type": file.handle.type,
});
} else if (
encodings.includes("gzip") &&
files.has(file.relPath + ".gz")
) {
return serveEncodedFile(files.get(file.relPath + ".gz"), 200, {
"content-encoding": "gzip",
"content-type": file.handle.type,
});
}
}
return serveFile(file);
} catch (error) {
counters.requestsByStatus.inc({ status_code: 503 });
Sentry.captureException(error);
return new Response("Something went wrong", { status: 503 });
}
},
});
console.info(`Serving website on http://${server.hostname}:${server.port}/`);
src/index.ts (view raw)