140 lines
5.1 KiB
TypeScript
140 lines
5.1 KiB
TypeScript
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();
|