66 lines
1.7 KiB
JavaScript
66 lines
1.7 KiB
JavaScript
import { createServer } from "node:http";
|
|
|
|
export function jsonResponse(res, statusCode, body) {
|
|
const payload = JSON.stringify(body, null, 2);
|
|
res.writeHead(statusCode, {
|
|
"content-type": "application/json; charset=utf-8",
|
|
"content-length": Buffer.byteLength(payload)
|
|
});
|
|
res.end(payload);
|
|
}
|
|
|
|
export async function readJson(req) {
|
|
const chunks = [];
|
|
for await (const chunk of req) {
|
|
chunks.push(chunk);
|
|
}
|
|
|
|
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
if (!raw) {
|
|
return {};
|
|
}
|
|
return JSON.parse(raw);
|
|
}
|
|
|
|
export function createJsonServer({ routes }) {
|
|
return createServer(async (req, res) => {
|
|
try {
|
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
const key = `${req.method ?? "GET"} ${url.pathname}`;
|
|
const handler = routes.get(key);
|
|
|
|
if (!handler) {
|
|
jsonResponse(res, 404, {
|
|
error: "not_found",
|
|
path: url.pathname
|
|
});
|
|
return;
|
|
}
|
|
|
|
const body = await handler({ req, url });
|
|
jsonResponse(res, 200, body);
|
|
} catch (error) {
|
|
jsonResponse(res, 500, {
|
|
error: "internal_error",
|
|
message: error instanceof Error ? error.message : String(error)
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
export function parsePort(value, fallback) {
|
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
if (Number.isInteger(parsed) && parsed > 0 && parsed < 65536) {
|
|
return parsed;
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
export function listen(server, port, host = "0.0.0.0") {
|
|
server.listen(port, host, () => {
|
|
const address = server.address();
|
|
const bound = typeof address === "object" && address ? `${address.address}:${address.port}` : String(address);
|
|
console.log(`listening on ${bound}`);
|
|
});
|
|
}
|