CMU Coding Bootcamp
at main 3.9 kB view raw
1import express from "express"; 2import { readFile, writeFile, exists } from "fs/promises"; 3 4const router = express.Router(); 5 6interface Task { 7 id: string; 8 title: string; 9 completed: boolean; 10} 11 12const getTasks: () => Promise<Task[]> = async () => { 13 if (!(await exists("tasks.json"))) { 14 await writeFile("tasks.json", JSON.stringify([])); 15 } 16 const file = await readFile("tasks.json", "utf-8"); 17 if (file.length === 0) { 18 return []; 19 } 20 return JSON.parse(file); 21}; 22 23const updateTask = async (task: Task): Promise<void> => { 24 const tasks = await getTasks(); 25 const index = tasks.findIndex((t) => t.id === task.id); 26 if (index !== -1) { 27 tasks[index] = task; 28 } else { 29 tasks.push(task); 30 } 31 await writeFile("tasks.json", JSON.stringify(tasks)); 32}; 33 34const removeTask = async (id: string): Promise<void> => { 35 const tasks = await getTasks(); 36 const index = tasks.findIndex((t) => t.id === id); 37 if (index !== -1) { 38 tasks.splice(index, 1); 39 await writeFile("tasks.json", JSON.stringify(tasks)); 40 } 41}; 42 43router.use(express.json()); 44 45router.get("/", async (_req, res) => { 46 res.json(await getTasks()); 47}); 48 49router.post("/", async (req, res) => { 50 if (!(typeof req.body.title === "string")) { 51 res.status(400).send("Invalid title"); 52 return; 53 } 54 const newTask = { 55 id: Math.random().toString(16).substring(2, 8), 56 title: req.body.title, 57 completed: false, 58 }; 59 await updateTask(newTask); 60 res.json(newTask).status(201); 61}); 62 63router.get("/:id", async (req, res) => { 64 const task = (await getTasks()).find((t) => t.id === req.params.id); 65 if (!task) { 66 res.status(404).send("Task not found"); 67 } else { 68 res.json(task); 69 } 70}); 71 72router.put("/:id", async (req, res) => { 73 const task = (await getTasks()).find((t) => t.id === req.params.id); 74 if (!task) { 75 res.status(404).send("Task not found"); 76 } else { 77 const missing = []; 78 if (req.body.title === undefined) missing.push("title"); 79 if (req.body.completed === undefined) missing.push("completed"); 80 if (missing.length > 0) { 81 res 82 .status(400) 83 .send( 84 `Missing field${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`, 85 ); 86 } 87 const badTypes = []; 88 if (!(typeof req.body.title === "string")) badTypes.push("title"); 89 if (!(typeof req.body.completed === "boolean")) badTypes.push("completed"); 90 if (badTypes.length > 0) { 91 res 92 .status(400) 93 .send( 94 `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, 95 ); 96 return; 97 } 98 task.title = req.body.title ?? task.title; 99 task.completed = req.body.completed ?? task.completed; 100 await updateTask(task); 101 res.json(task); 102 } 103}); 104 105router.patch("/:id", async (req, res) => { 106 const task = (await getTasks()).find((t) => t.id === req.params.id); 107 if (!task) { 108 res.status(404).send("Task not found"); 109 } else { 110 const badTypes = []; 111 if ( 112 Object.keys(req.body).includes("title") && 113 !(typeof req.body.title === "string") 114 ) 115 badTypes.push("title"); 116 if ( 117 Object.keys(req.body).includes("completed") && 118 !(typeof req.body.completed === "boolean") 119 ) 120 badTypes.push("completed"); 121 if (badTypes.length > 0) { 122 res 123 .status(400) 124 .send( 125 `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, 126 ); 127 return; 128 } 129 task.title = req.body.title ?? task.title; 130 task.completed = req.body.completed ?? task.completed; 131 await updateTask(task); 132 res.json(task); 133 } 134}); 135 136router.delete("/:id", async (req, res) => { 137 const task = (await getTasks()).find((t) => t.id === req.params.id); 138 if (!task) { 139 res.status(404).send("Task not found"); 140 } else { 141 await removeTask(task.id); 142 res.status(204).send(); 143 } 144}); 145 146export default router;