// RAPPORT-HOST Backend-Entry. import express from "express"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { env } from "./env.js"; import { authRouter } from "./routes/auth.js"; import { billingRouter } from "./routes/billing.js"; import { accountRouter } from "./routes/account.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const app = express(); // WICHTIG: Der Stripe-Webhook braucht den ROHEN Body für die Signaturprüfung. // express.raw greift nur für diese Route und setzt req._body, sodass das danach // registrierte express.json() den Webhook-Body NICHT erneut parst. app.use("/api/billing/webhook", express.raw({ type: "application/json" })); app.use(express.json()); app.get("/api/health", (_req, res) => res.json({ ok: true, service: "rapport-host" })); app.use("/api/auth", authRouter); app.use("/api/billing", billingRouter); app.use("/api/account", accountRouter); // ── Statische Auslieferung der RAPPORT-WEBSITE (Hugo-Build) ────────────────── // Die komplette Frontend-Oberfläche (Marketing + Hosting + Login/Konto als // Vanilla-JS-Seiten) kommt aus dem AGPL-Repo RAPPORT-WEBSITE. Dieses Backend // liefert dessen gebautes public/ aus und stellt darunter /api bereit. // Saubere Trennung: die Website enthält keinen Backend-Code, nur fetch('/api'). // Pfad per WEBSITE_PUBLIC_DIR überschreibbar (Default: Schwester-Repo). const websitePublic = env.websitePublicDir; app.use(express.static(websitePublic)); app.get("*", (req, res, next) => { if (req.path.startsWith("/api/")) return next(); // Hugo erzeugt pro Seite eine eigene index.html → Clean-URLs funktionieren // direkt über express.static. Unbekannte Pfade → 404-Seite. res.status(404).sendFile(path.join(websitePublic, "404.html"), (err) => err && next()); }); // Express-Fehlerhandler: fängt synchron geworfene Fehler aus Routen → 500 // statt stiller Empty-Reply. app.use((err, _req, res, _next) => { console.error("Unbehandelter Routen-Fehler:", err); if (!res.headersSent) res.status(500).json({ error: "Interner Fehler." }); }); // Letzte Verteidigungslinie: ein einzelner async-Fehler darf den Prozess nicht // killen (im Dev-Test sonst "connection refused" für alle Folge-Requests). process.on("unhandledRejection", (reason) => console.error("unhandledRejection:", reason)); app.listen(env.port, () => { console.log(`RAPPORT-HOST API läuft auf :${env.port} (Base: ${env.publicBaseUrl})`); });