Repository navigation
Expand file tree
/
Copy pathserver.js
More file actions
98 lines (84 loc) · 2.89 KB
/
Copy pathserver.js
File metadata and controls
98 lines (84 loc) · 2.89 KB
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
import { createServer } from "node:http";
import { readFile } from "node:fs/promises";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const todos = [
{ id: 1, text: "Read the render-factory README", done: false },
{ id: 2, text: "Apply factory:full-run to an issue", done: false },
];
let nextId = 3;
function jsonResponse(res, status, body) {
res.writeHead(status, { "Content-Type": "application/json" });
res.end(JSON.stringify(body));
}
function readBody(req) {
return new Promise((resolve, reject) => {
const chunks = [];
req.on("data", (c) => chunks.push(c));
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
req.on("error", reject);
});
}
async function handler(req, res) {
const url = new URL(req.url, `http://${req.headers.host}`);
if (url.pathname === "/" && req.method === "GET") {
const html = await readFile(join(__dirname, "public", "index.html"), "utf8");
res.writeHead(200, { "Content-Type": "text/html" });
res.end(html);
return;
}
if (url.pathname === "/api/todos" && req.method === "GET") {
jsonResponse(res, 200, todos);
return;
}
if (url.pathname === "/api/todos" && req.method === "POST") {
const raw = await readBody(req);
const { text } = JSON.parse(raw);
if (!text || typeof text !== "string") {
jsonResponse(res, 400, { error: "text is required" });
return;
}
const todo = { id: nextId++, text, done: false };
todos.push(todo);
jsonResponse(res, 201, todo);
return;
}
if (url.pathname.startsWith("/api/todos/") && req.method === "PATCH") {
const id = Number(url.pathname.split("/").pop());
const todo = todos.find((t) => t.id === id);
if (!todo) {
jsonResponse(res, 404, { error: "not found" });
return;
}
const raw = await readBody(req);
const updates = JSON.parse(raw);
if (typeof updates.done === "boolean") todo.done = updates.done;
if (typeof updates.text === "string") todo.text = updates.text;
jsonResponse(res, 200, todo);
return;
}
if (url.pathname.startsWith("/api/todos/") && req.method === "DELETE") {
const id = Number(url.pathname.split("/").pop());
const idx = todos.findIndex((t) => t.id === id);
if (idx === -1) {
jsonResponse(res, 404, { error: "not found" });
return;
}
todos.splice(idx, 1);
jsonResponse(res, 204, null);
return;
}
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Not found");
}
function portFromArgs(args) {
const index = args.findIndex((arg) => arg === "--port" || arg.startsWith("--port="));
if (index === -1) return undefined;
return args[index].includes("=") ? args[index].split("=")[1] : args[index + 1];
}
const port = Number(portFromArgs(process.argv.slice(2)) ?? process.env.PORT ?? 3000);
const server = createServer(handler);
server.listen(port, "0.0.0.0", () => {
console.log(`todo server listening on http://0.0.0.0:${port}`);
});