initial commit

This commit is contained in:
Urban Modig
2026-03-22 16:36:05 +01:00
parent 67a2cd4290
commit 2c93f6777b
54 changed files with 9822 additions and 0 deletions

139
frontend/server.ts Normal file
View File

@ -0,0 +1,139 @@
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();