const express = require("express"); const cors = require("cors"); const path = require("path"); const fs = require("fs").promises; const app = express(); const PORT = 3000; // Middleware app.use(cors()); app.use(express.json()); // File path for todo persistence const TODO_FILE = "todos.json"; // Load todos from file async function loadTodos() { try { const data = await fs.readFile(TODO_FILE, "utf8"); const parsed = JSON.parse(data); if (Array.isArray(parsed)) { return parsed; } return []; } catch (error) { // If file doesn't exist or is invalid, return empty array return []; } } // Save todos to file async function saveTodos(todos) { try { await fs.writeFile(TODO_FILE, JSON.stringify(todos, null, 2)); } catch (error) { console.error("Failed to save todos:", error); } } // Load todos on startup let todos = []; let nextId = 1; async function initializeTodos() { todos = await loadTodos(); if (todos.length > 0) { // Find the highest ID and set nextId accordingly nextId = Math.max(...todos.map(todo => todo.id)) + 1; } } // Initialize todos on startup initializeTodos(); // GET /todos - get all todos app.get("/todos", (req, res) => { res.json(todos); }); // POST /todos - create a new todo app.post("/todos", (req, res) => { const { text } = req.body; if (!text) { return res.status(400).json({ error: "Text is required" }); } const newTodo = { id: nextId++, text }; todos.push(newTodo); saveTodos(todos); // Save to file after adding res.status(201).json(newTodo); }); // PUT /todos/:id - update a todo app.put("/todos/:id", (req, res) => { const id = parseInt(req.params.id); const todo = todos.find((t) => t.id === id); if (!todo) return res.status(404).json({ error: "Todo not found" }); const { text, completed } = req.body; if (text !== undefined) todo.text = text; if (completed !== undefined) todo.completed = completed; saveTodos(todos); // Save to file after updating res.json(todo); }); // DELETE /todos/:id - delete a todo app.delete("/todos/:id", (req, res) => { const id = parseInt(req.params.id); const index = todos.findIndex((t) => t.id === id); if (index === -1) return res.status(404).json({ error: "Todo not found" }); const deleted = todos.splice(index, 1)[0]; saveTodos(todos); // Save to file after deleting res.json(deleted); }); // Reorder todos (client can send new order of ids) // Expected body: { order: [1,2,3] } where each number is a todo id app.post("/reorder", (req, res) => { const { order } = req.body; if (!Array.isArray(order)) { return res.status(400).json({ error: "Order must be an array of ids" }); } // Build a new array in the requested order const orderedTodos = []; for (const id of order) { const item = todos.find((t) => t.id === id); if (item) orderedTodos.push(item); } // Replace the current todos array with the reordered version todos.length = 0; // empty the existing array todos.push(...orderedTodos); saveTodos(todos); // Save to file after reordering res.json(orderedTodos); }); // Serve static files (index.html, etc.) from the same directory app.use(express.static(path.join(__dirname))); // Start server app.listen(PORT, () => { console.log(`Todo API server running at http://localhost:${PORT}`); }); // Create todos.json file if it doesn't exist async function ensureTodoFileExists() { try { await fs.access(TODO_FILE); } catch (error) { // File doesn't exist, create it with empty array await saveTodos([]); } } ensureTodoFileExists();