133 lines
3.5 KiB
JavaScript
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();
|