- Donut: naadloze segmenten (donkere spleten weg), hover-tooltip per categorie met naam, bedrag en aandeel, middenwaarde schaalt mee - Update-check via de eigen backend (/api/update-check) i.p.v. direct naar de Gitea API, die geen CORS-headers stuurt - Semver-vergelijking, herhaalde check en release-notes in de melding - release.ps1 maakt de Gitea-release aan (stap 4) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
64 lines
3.4 KiB
JavaScript
64 lines
3.4 KiB
JavaScript
import express from "express";
|
|
import cors from "cors";
|
|
import helmet from "helmet";
|
|
import rateLimit from "express-rate-limit";
|
|
import cookieParser from "cookie-parser";
|
|
import pino from "pino";
|
|
import pinoHttp from "pino-http";
|
|
import { config } from "./config/index.js";
|
|
import authRouter from "./routes/auth.js";
|
|
import dataRouter from "./routes/data.js";
|
|
import usersRouter from "./routes/users.js";
|
|
import healthRouter from "./routes/health.js";
|
|
import updateRouter from "./routes/update.js";
|
|
import { errorHandler } from "./middleware/errorHandler.js";
|
|
|
|
export const logger = pino({
|
|
level: config.NODE_ENV === "production" ? "info" : "debug",
|
|
...(config.NODE_ENV !== "production" && {
|
|
transport: { target: "pino-pretty", options: { colorize: true } },
|
|
}),
|
|
});
|
|
|
|
const app = express();
|
|
|
|
// ── Security ──────────────────────────────────────────────────────────────────
|
|
app.use(helmet());
|
|
app.use(cors({ origin: config.CORS_ORIGIN, credentials: true }));
|
|
app.set("trust proxy", 1);
|
|
|
|
// ── Rate limiting ─────────────────────────────────────────────────────────────
|
|
app.use("/api/auth", rateLimit({ windowMs: 15 * 60 * 1000, max: 20, standardHeaders: true, legacyHeaders: false }));
|
|
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 200, standardHeaders: true, legacyHeaders: false }));
|
|
|
|
// ── Parsing ───────────────────────────────────────────────────────────────────
|
|
app.use(express.json({ limit: "2mb" }));
|
|
app.use(cookieParser());
|
|
|
|
// ── Logging ───────────────────────────────────────────────────────────────────
|
|
if (config.NODE_ENV !== "test") {
|
|
app.use(pinoHttp({ logger }));
|
|
}
|
|
|
|
// ── Routes ────────────────────────────────────────────────────────────────────
|
|
app.use("/api/health", healthRouter);
|
|
app.use("/api/update-check", updateRouter);
|
|
app.use("/api/auth", authRouter);
|
|
app.use("/api/data", dataRouter);
|
|
app.use("/api/users", usersRouter);
|
|
|
|
// ── 404 voor overige /api/* ───────────────────────────────────────────────────
|
|
app.use("/api/*", (_req, res) => res.status(404).json({ error: "Endpoint niet gevonden." }));
|
|
|
|
// ── Error handler ─────────────────────────────────────────────────────────────
|
|
app.use(errorHandler);
|
|
|
|
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
if (config.NODE_ENV !== "test") {
|
|
app.listen(config.PORT, () => {
|
|
logger.info(`VibeFinance API gestart op http://localhost:${config.PORT} [${config.NODE_ENV}]`);
|
|
});
|
|
}
|
|
|
|
export default app;
|