import express from "express"; import { createServer as createViteServer } from "vite"; import { createProxyMiddleware } from "http-proxy-middleware"; import Database from "better-sqlite3"; import path from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const db = new Database("tasks.db"); // Initiera databasen db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, color TEXT NOT NULL, avatar TEXT DEFAULT 'UserIcon' ); CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT, status TEXT NOT NULL CHECK (status IN ('pending', 'in_progress', 'completed')), points INTEGER DEFAULT 0, user_id INTEGER, is_recurring BOOLEAN DEFAULT 0, recurrence_period TEXT, due_date DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); `); // Migreringar (för säkerhets skull) try { db.prepare("ALTER TABLE tasks ADD COLUMN recurrence_period TEXT").run(); } catch (e) {} try { db.prepare("ALTER TABLE users ADD COLUMN avatar TEXT DEFAULT 'UserIcon'").run(); } catch (e) {} async function startServer() { const app = express(); const BACKEND_URL = process.env.VITE_API_BASE_URL; if (BACKEND_URL) { console.log(`Proxying /api requests to ${BACKEND_URL}`); app.use(createProxyMiddleware({ target: BACKEND_URL, changeOrigin: true, pathFilter: "/api", })); } app.use(express.json()); // API-rutter app.get("/api/users", (req, res) => { const users = db.prepare("SELECT * FROM users").all(); res.json(users); }); app.post("/api/users", (req, res) => { const { name, color, avatar } = req.body; try { const info = db.prepare("INSERT INTO users (name, color, avatar) VALUES (?, ?, ?)").run(name, color, avatar || 'UserIcon'); res.json({ id: info.lastInsertRowid, name, color, avatar: avatar || 'UserIcon' }); } catch (e) { res.status(400).json({ error: "Användaren finns redan" }); } }); app.get("/api/tasks", (req, res) => { const tasks = db.prepare(` SELECT tasks.*, users.name as user_name, users.color as user_color, users.avatar as user_avatar FROM tasks LEFT JOIN users ON tasks.user_id = users.id `).all(); res.json(tasks); }); app.post("/api/tasks", (req, res) => { const { title, description, status, points, user_id, is_recurring, recurrence_period, due_date } = req.body; const info = db.prepare(` INSERT INTO tasks (title, description, status, points, user_id, is_recurring, recurrence_period, due_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `).run(title, description, status || 'pending', points || 0, user_id || null, is_recurring ? 1 : 0, recurrence_period || null, due_date || null); res.json({ id: info.lastInsertRowid, ...req.body }); }); app.patch("/api/tasks/:id", (req, res) => { const { id } = req.params; const { status, user_id } = req.body; const existingTask = db.prepare("SELECT * FROM tasks WHERE id = ?").get(id) as any; if (!existingTask) return res.status(404).json({ error: "Hittade inte uppgiften" }); const updates = Object.keys(req.body).map(key => `${key} = ?`).join(", "); const values = Object.values(req.body); db.prepare(`UPDATE tasks SET ${updates} WHERE id = ?`).run(...values, id); if (status === 'completed' && existingTask.is_recurring && existingTask.recurrence_period && existingTask.due_date) { const nextDueDate = new Date(existingTask.due_date); if (existingTask.recurrence_period === 'daily') nextDueDate.setDate(nextDueDate.getDate() + 1); else if (existingTask.recurrence_period === 'weekly') nextDueDate.setDate(nextDueDate.getDate() + 7); else if (existingTask.recurrence_period === 'biweekly') nextDueDate.setDate(nextDueDate.getDate() + 14); else if (existingTask.recurrence_period === 'monthly') nextDueDate.setMonth(nextDueDate.getMonth() + 1); db.prepare(` INSERT INTO tasks (title, description, status, points, user_id, is_recurring, recurrence_period, due_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `).run(existingTask.title, existingTask.description, 'pending', existingTask.points, null, 1, existingTask.recurrence_period, nextDueDate.toISOString()); } res.json({ success: true }); }); app.delete("/api/tasks/:id", (req, res) => { db.prepare("DELETE FROM tasks WHERE id = ?").run(req.params.id); res.json({ success: true }); }); // Vite integration if (process.env.NODE_ENV !== "production") { const vite = await createViteServer({ server: { middlewareMode: true }, appType: "spa", }); app.use(vite.middlewares); } else { app.use(express.static(path.join(__dirname, "dist"))); app.get("/{*path}", (req: any, res: any) => res.sendFile(path.join(__dirname, "dist", "index.html"))); } const PORT = 3000; app.listen(PORT, "0.0.0.0", () => { console.log(`Server körs på http://localhost:${PORT}`); }); } startServer();