diff --git a/frontend/package.json b/frontend/package.json index e7b3c04..0fb86cd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "dev": "NEXT_TELEMETRY_DISABLED=1 next dev -H 0.0.0.0", - "build": "NEXT_TELEMETRY_DISABLED=1 next build", - "start": "NEXT_TELEMETRY_DISABLED=1 next start -H 0.0.0.0", + "build": "NEXT_TELEMETRY_DISABLED=1 next build --webpack", + "start": "NEXT_TELEMETRY_DISABLED=1 NODE_ENV=production node server.mjs", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/frontend/server.mjs b/frontend/server.mjs new file mode 100644 index 0000000..f14aebc --- /dev/null +++ b/frontend/server.mjs @@ -0,0 +1,57 @@ +import { existsSync, statSync } from "node:fs"; +import { createServer } from "node:http"; +import path from "node:path"; +import { fileURLToPath, parse } from "node:url"; + +import next from "next"; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const port = Number.parseInt(process.env.PORT ?? "3910", 10); +const hostname = process.env.HOSTNAME ?? "0.0.0.0"; +const dev = process.env.NODE_ENV !== "production"; + +const app = next({ dev, hostname, port }); +const handle = app.getRequestHandler(); +const nextStaticRoot = path.resolve(dirname, ".next/static"); + +function isMissingNextStaticAsset(pathname) { + if (!pathname.startsWith("/_next/static/")) { + return false; + } + + let relativePath; + try { + relativePath = decodeURIComponent(pathname.replace("/_next/static/", "")); + } catch { + return true; + } + + const assetPath = path.resolve(nextStaticRoot, relativePath); + if (!assetPath.startsWith(`${nextStaticRoot}${path.sep}`)) { + return true; + } + + try { + return !existsSync(assetPath) || !statSync(assetPath).isFile(); + } catch { + return true; + } +} + +await app.prepare(); + +createServer((req, res) => { + const parsedUrl = parse(req.url ?? "/", true); + const pathname = parsedUrl.pathname ?? "/"; + + if (isMissingNextStaticAsset(pathname)) { + res.statusCode = 404; + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end("Not found"); + return; + } + + handle(req, res, parsedUrl); +}).listen(port, hostname, () => { + console.log(`TicketTracker frontend listening on http://${hostname}:${port}`); +});