1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 |
// Zero-dependency static server for the built app (dist/), designed to run
// under PM2 behind an Nginx/Caddy reverse proxy at https://tlg.pablomurad.com.
//
// Kept deliberately tiny — no express/serve — so the production footprint is
// just Node core: HTTP + zlib. It streams from an in-memory cache, negotiates
// Brotli/Gzip, sets immutable caching for hashed assets, and falls back to
// index.html for client routes.
import { createServer } from "node:http";
import { readFile, stat } from "node:fs/promises";
import { join, normalize, extname } from "node:path";
import { fileURLToPath } from "node:url";
import { brotliCompressSync, gzipSync, constants as zlib } from "node:zlib";
const ROOT = fileURLToPath(new URL("./dist/", import.meta.url));
const PORT = Number(process.env.PORT) || 3200;
const HOST = process.env.HOST || "127.0.0.1";
const MIME = {
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".json": "application/json; charset=utf-8",
".webmanifest": "application/manifest+json; charset=utf-8",
".svg": "image/svg+xml",
".xml": "application/xml; charset=utf-8",
".txt": "text/plain; charset=utf-8",
".ico": "image/x-icon",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".webp": "image/webp",
".woff2": "font/woff2",
};
const COMPRESSIBLE = new Set([
".html",
".js",
".css",
".json",
".webmanifest",
".svg",
".xml",
".txt",
]);
// path -> { body, gzip, br, type, cacheControl, lastModified }
const cache = new Map();
async function load(filePath, urlPath) {
const cached = cache.get(filePath);
if (cached) return cached;
const info = await stat(filePath);
if (!info.isFile()) return null;
const body = await readFile(filePath);
const ext = extname(filePath);
const type = MIME[ext] ?? "application/octet-stream";
const compressible = COMPRESSIBLE.has(ext) && body.length > 512;
const entry = {
body,
type,
lastModified: info.mtime.toUTCString(),
cacheControl: urlPath.startsWith("/assets/")
? "public, max-age=31536000, immutable"
: ext === ".html"
? "no-cache"
: "public, max-age=86400, must-revalidate",
gzip: compressible ? gzipSync(body, { level: 9 }) : null,
br: compressible
? brotliCompressSync(body, {
params: { [zlib.BROTLI_PARAM_QUALITY]: 11, [zlib.BROTLI_PARAM_SIZE_HINT]: body.length },
})
: null,
};
cache.set(filePath, entry);
return entry;
}
function resolve(urlPath) {
let pathname = decodeURIComponent(urlPath.split("?")[0]);
if (pathname.endsWith("/")) pathname += "index.html";
const filePath = normalize(join(ROOT, pathname));
// Contain within ROOT — reject traversal attempts.
if (!filePath.startsWith(ROOT)) return null;
return { filePath, pathname };
}
const server = createServer(async (req, res) => {
try {
if (req.method !== "GET" && req.method !== "HEAD") {
res.writeHead(405, { Allow: "GET, HEAD" }).end("Method Not Allowed");
return;
}
const resolved = resolve(req.url || "/");
if (!resolved) {
res.writeHead(403).end("Forbidden");
return;
}
let { filePath, pathname } = resolved;
let entry = await load(filePath, pathname).catch(() => null);
// SPA fallback: unknown, extension-less routes serve index.html.
if (!entry && !extname(pathname)) {
pathname = "/index.html";
filePath = join(ROOT, "index.html");
entry = await load(filePath, pathname).catch(() => null);
}
if (!entry) {
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }).end("Not Found");
return;
}
// Conditional GET.
if (req.headers["if-modified-since"] === entry.lastModified) {
res.writeHead(304).end();
return;
}
const accept = req.headers["accept-encoding"] || "";
let body = entry.body;
const headers = {
"Content-Type": entry.type,
"Cache-Control": entry.cacheControl,
"Last-Modified": entry.lastModified,
"X-Content-Type-Options": "nosniff",
Vary: "Accept-Encoding",
};
if (entry.br && /\bbr\b/.test(accept)) {
body = entry.br;
headers["Content-Encoding"] = "br";
} else if (entry.gzip && /\bgzip\b/.test(accept)) {
body = entry.gzip;
headers["Content-Encoding"] = "gzip";
}
headers["Content-Length"] = body.length;
res.writeHead(200, headers);
res.end(req.method === "HEAD" ? undefined : body);
} catch {
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" }).end("Internal Server Error");
}
});
server.listen(PORT, HOST, () => {
console.log(`The Last Glyph — serving dist/ at http://${HOST}:${PORT}`);
});
// Graceful shutdown so PM2 reloads/restarts cleanly.
for (const signal of ["SIGINT", "SIGTERM"]) {
process.on(signal, () => server.close(() => process.exit(0)));
}
|