sokko-g/server.mjs
Shinuwa 1dee8b528f
Some checks failed
Deploy Sokko G / deploy (push) Failing after 4s
Migrate to React/Vite & Sass
2026-07-21 10:09:25 +02:00

99 lines
2.9 KiB
JavaScript

import { createReadStream, existsSync, readFileSync, statSync } from "node:fs";
import { createServer } from "node:http";
import { extname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const projectRoot = resolve(fileURLToPath(new URL(".", import.meta.url)));
const websiteRoot = join(projectRoot, "website");
const distRoot = join(websiteRoot, "dist");
const root = existsSync(distRoot) ? distRoot : websiteRoot;
const publicRoot = existsSync(distRoot) ? distRoot : join(websiteRoot, "public");
function loadEnvFile(file = ".env") {
if (!existsSync(file)) return;
const lines = readFileSync(file, "utf8").split(/\r?\n/);
lines.forEach((line) => {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) return;
const separatorIndex = trimmed.indexOf("=");
if (separatorIndex < 1) return;
const key = trimmed.slice(0, separatorIndex).trim();
const value = trimmed.slice(separatorIndex + 1).trim().replace(/^["']|["']$/g, "");
if (!process.env[key]) process.env[key] = value;
});
}
loadEnvFile();
const port = Number(process.env.PORT || 5173);
const types = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".svg": "image/svg+xml",
".ico": "image/x-icon",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".webp": "image/webp"
};
function isInsideRoot(parent, file) {
const relativePath = relative(parent, file);
return Boolean(relativePath) && !relativePath.startsWith("..") && !relativePath.startsWith("/");
}
function fileIfReadable(file) {
return isInsideRoot(root, file) && existsSync(file) && statSync(file).isFile() ? file : null;
}
function resolvePath(url) {
let requested;
try {
requested = decodeURIComponent(new URL(url, "http://localhost").pathname);
} catch {
return null;
}
const candidate = fileIfReadable(resolve(root, `.${requested}`));
if (candidate) return candidate;
const publicCandidate = fileIfReadable(resolve(publicRoot, `.${requested}`));
if (publicCandidate && isInsideRoot(publicRoot, publicCandidate)) return publicCandidate;
return join(root, "index.html");
}
const server = createServer((req, res) => {
const file = resolvePath(req.url || "/");
if (!file || !existsSync(file)) {
res.writeHead(404);
res.end("Not found");
return;
}
res.writeHead(200, {
"Content-Type": types[extname(file)] || "application/octet-stream",
"Cache-Control": "no-store"
});
createReadStream(file).pipe(res);
});
server.on("error", (error) => {
if (error.code === "EADDRINUSE") {
console.error(`Port ${port} is already in use. Set PORT to another value.`);
process.exit(1);
}
console.error(error);
process.exit(1);
});
server.listen(port, () => {
console.log(`Sokko G dev server running at http://localhost:${port}`);
});