Files
tiny-todo/server.js
T
2026-03-30 20:14:14 -07:00

133 lines
3.5 KiB
JavaScript

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();