import express from "express"; import { readFile, writeFile, exists } from "fs/promises"; const router = express.Router(); interface Task { id: string; title: string; completed: boolean; } const getTasks: () => Promise = async () => { if (!(await exists("tasks.json"))) { await writeFile("tasks.json", JSON.stringify([])); } const file = await readFile("tasks.json", "utf-8"); if (file.length === 0) { return []; } return JSON.parse(file); }; const updateTask = async (task: Task): Promise => { const tasks = await getTasks(); const index = tasks.findIndex((t) => t.id === task.id); if (index !== -1) { tasks[index] = task; } else { tasks.push(task); } await writeFile("tasks.json", JSON.stringify(tasks)); }; const removeTask = async (id: string): Promise => { const tasks = await getTasks(); const index = tasks.findIndex((t) => t.id === id); if (index !== -1) { tasks.splice(index, 1); await writeFile("tasks.json", JSON.stringify(tasks)); } }; router.use(express.json()); router.get("/", async (_req, res) => { res.json(await getTasks()); }); router.post("/", async (req, res) => { if (!(typeof req.body.title === "string")) { res.status(400).send("Invalid title"); return; } const newTask = { id: Math.random().toString(16).substring(2, 8), title: req.body.title, completed: false, }; await updateTask(newTask); res.json(newTask).status(201); }); router.get("/:id", async (req, res) => { const task = (await getTasks()).find((t) => t.id === req.params.id); if (!task) { res.status(404).send("Task not found"); } else { res.json(task); } }); router.put("/:id", async (req, res) => { const task = (await getTasks()).find((t) => t.id === req.params.id); if (!task) { res.status(404).send("Task not found"); } else { const missing = []; if (req.body.title === undefined) missing.push("title"); if (req.body.completed === undefined) missing.push("completed"); if (missing.length > 0) { res .status(400) .send( `Missing field${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`, ); } const badTypes = []; if (!(typeof req.body.title === "string")) badTypes.push("title"); if (!(typeof req.body.completed === "boolean")) badTypes.push("completed"); if (badTypes.length > 0) { res .status(400) .send( `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, ); return; } task.title = req.body.title ?? task.title; task.completed = req.body.completed ?? task.completed; await updateTask(task); res.json(task); } }); router.patch("/:id", async (req, res) => { const task = (await getTasks()).find((t) => t.id === req.params.id); if (!task) { res.status(404).send("Task not found"); } else { const badTypes = []; if ( Object.keys(req.body).includes("title") && !(typeof req.body.title === "string") ) badTypes.push("title"); if ( Object.keys(req.body).includes("completed") && !(typeof req.body.completed === "boolean") ) badTypes.push("completed"); if (badTypes.length > 0) { res .status(400) .send( `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, ); return; } task.title = req.body.title ?? task.title; task.completed = req.body.completed ?? task.completed; await updateTask(task); res.json(task); } }); router.delete("/:id", async (req, res) => { const task = (await getTasks()).find((t) => t.id === req.params.id); if (!task) { res.status(404).send("Task not found"); } else { await removeTask(task.id); res.status(204).send(); } }); export default router;