feat: initial sbsports deployment setup

Add Coolify/Woodpecker CI config, .gitignore, and deployment scripts.
This commit is contained in:
alexandrump
2026-04-22 01:01:42 +02:00
commit 78c5ed52ac
32 changed files with 6088 additions and 0 deletions

150
server/index.js Normal file
View File

@@ -0,0 +1,150 @@
import express from "express";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import db from "./db.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
// Serve Vue build in production
const distPath = join(__dirname, "../dist");
app.use(express.static(distPath));
// ── Students ──────────────────────────────────────────────────────────────
app.get("/api/students", (_req, res) => {
res.json(
db.prepare("SELECT * FROM students ORDER BY lastName, firstName").all(),
);
});
app.post("/api/students", (req, res) => {
const { id, firstName, lastName, age, sex, weight, height, imc } = req.body;
db.prepare(
`
INSERT INTO students (id, firstName, lastName, age, sex, weight, height, imc)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`,
).run(id, firstName, lastName, age, sex, weight, height, imc);
res.status(201).json({ id });
});
app.put("/api/students/:id", (req, res) => {
const { firstName, lastName, age, sex, weight, height, imc } = req.body;
const info = db
.prepare(
`
UPDATE students SET firstName=?, lastName=?, age=?, sex=?, weight=?, height=?, imc=?
WHERE id=?
`,
)
.run(firstName, lastName, age, sex, weight, height, imc, req.params.id);
if (info.changes === 0) return res.status(404).json({ error: "Not found" });
res.json({ ok: true });
});
app.delete("/api/students/:id", (req, res) => {
// CASCADE deletes linked activities automatically (FK ON DELETE CASCADE)
db.prepare("DELETE FROM students WHERE id=?").run(req.params.id);
res.json({ ok: true });
});
// ── Activities ────────────────────────────────────────────────────────────
app.get("/api/activities", (_req, res) => {
res.json(
db.prepare("SELECT * FROM activities ORDER BY createdAt DESC").all(),
);
});
app.post("/api/activities", (req, res) => {
const {
id,
studentId,
type,
durationInput,
durationUnit,
duration,
displayDuration,
intensity,
lifestyle,
anxiety,
grade,
date,
} = req.body;
db.prepare(
`
INSERT INTO activities
(id, studentId, type, durationInput, durationUnit, duration, displayDuration, intensity, lifestyle, anxiety, grade, date)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
).run(
id,
studentId,
type,
durationInput,
durationUnit,
duration,
displayDuration,
intensity,
lifestyle,
anxiety ?? 0,
grade,
date,
);
res.status(201).json({ id });
});
app.put("/api/activities/:id", (req, res) => {
const {
type,
durationInput,
durationUnit,
duration,
displayDuration,
intensity,
lifestyle,
anxiety,
grade,
date,
} = req.body;
const info = db
.prepare(
`
UPDATE activities
SET type=?, durationInput=?, durationUnit=?, duration=?, displayDuration=?,
intensity=?, lifestyle=?, anxiety=?, grade=?, date=?
WHERE id=?
`,
)
.run(
type,
durationInput,
durationUnit,
duration,
displayDuration,
intensity,
lifestyle,
anxiety ?? 0,
grade,
date,
req.params.id,
);
if (info.changes === 0) return res.status(404).json({ error: "Not found" });
res.json({ ok: true });
});
app.delete("/api/activities/:id", (req, res) => {
db.prepare("DELETE FROM activities WHERE id=?").run(req.params.id);
res.json({ ok: true });
});
// ── SPA fallback ──────────────────────────────────────────────────────────
app.get("*", (_req, res) => {
res.sendFile(join(distPath, "index.html"));
});
app.listen(PORT, () => console.log(`SB Sports API running on :${PORT}`));