diff --git a/apps/planner/server.ts b/apps/planner/server.ts index 097ddad..d876aef 100644 --- a/apps/planner/server.ts +++ b/apps/planner/server.ts @@ -1,14 +1,56 @@ import { createRequestListener } from "@react-router/node"; -import { createServer } from "node:http"; +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; +import { createReadStream, statSync } from "node:fs"; +import { join, extname, resolve } from "node:path"; import { setupYjsWebSocket } from "./app/lib/yjs-server.ts"; const port = Number(process.env.PORT ?? 3001); +const CLIENT_DIR = resolve(import.meta.dirname, "build", "client"); + +const MIME: Record = { + ".js": "application/javascript", + ".css": "text/css", + ".html": "text/html", + ".json": "application/json", + ".png": "image/png", + ".jpg": "image/jpeg", + ".svg": "image/svg+xml", + ".ico": "image/x-icon", + ".woff": "font/woff", + ".woff2": "font/woff2", +}; + +function serveStatic(req: IncomingMessage, res: ServerResponse): boolean { + if (req.method !== "GET" && req.method !== "HEAD") return false; + + const url = new URL(req.url ?? "/", `http://${req.headers.host}`); + const filePath = resolve(join(CLIENT_DIR, url.pathname)); + + if (!filePath.startsWith(CLIENT_DIR)) return false; + + try { + if (!statSync(filePath).isFile()) return false; + } catch { + return false; + } + + res.setHeader("Content-Type", MIME[extname(filePath)] ?? "application/octet-stream"); + if (url.pathname.startsWith("/assets/")) { + res.setHeader("Cache-Control", "public, immutable, max-age=31536000"); + } + createReadStream(filePath).pipe(res); + return true; +} const listener = createRequestListener({ build: () => import("./build/server/index.js") as never, }); -const server = createServer(listener); +const server = createServer((req, res) => { + if (!serveStatic(req, res)) { + listener(req, res); + } +}); setupYjsWebSocket(server);