// RAPPORT-HOST — Betreiber-Bereich (/api/admin). Separates Admin-Login // (ADMIN_PASSWORD), getrennt von Kundenkonten. Übersicht aller Kunden, Abos, // Instanzen + Sperren/Reaktivieren. import { Router } from "express"; import { one, query } from "../db.js"; import { requireAdmin, signAdminToken } from "../auth.js"; import { getPlan } from "../plans.js"; import { env } from "../env.js"; export const adminRouter = Router(); // — Separates Admin-Login (Passwort aus ADMIN_PASSWORD) — kein requireAdmin davor. adminRouter.post("/login", (req, res) => { if (!env.adminPassword) return res.status(503).json({ error: "Admin-Bereich nicht konfiguriert." }); if ((req.body?.password || "") !== env.adminPassword) { return res.status(401).json({ error: "Passwort falsch." }); } res.json({ token: signAdminToken() }); }); // Alle folgenden Routen verlangen einen gültigen Operator-Token. adminRouter.use(requireAdmin); // — Kennzahlen fürs Dashboard — adminRouter.get("/stats", async (_req, res) => { const accounts = await one("select count(*)::int n from accounts"); const activeSubs = await one("select count(*)::int n from subscriptions where status = 'active'"); const instances = await one("select count(*)::int n from instances"); const activeInst = await one("select count(*)::int n from instances where status = 'active'"); // MRR (geschätzt): Summe der Plan-Preise aller aktiven Abos. const { rows: subs } = await query("select plan from subscriptions where status = 'active'"); const mrr = subs.reduce((sum, s) => sum + (getPlan(s.plan)?.priceChf || 0), 0); res.json({ accounts: accounts.n, activeSubscriptions: activeSubs.n, instances: instances.n, activeInstances: activeInst.n, mrrChf: mrr, }); }); // — Alle Kunden mit Abo + Instanzen — adminRouter.get("/accounts", async (_req, res) => { const { rows } = await query(` select a.id, a.email, a.company, a.created_at, s.plan, s.status as sub_status, s.current_period_end, coalesce(i.cnt, 0)::int as instance_count from accounts a left join lateral ( select plan, status, current_period_end from subscriptions where account_id = a.id order by created_at desc limit 1 ) s on true left join ( select account_id, count(*) cnt from instances group by account_id ) i on i.account_id = a.id order by a.created_at desc `); res.json({ accounts: rows }); }); // — Instanzen eines Kontos — adminRouter.get("/accounts/:id/instances", async (req, res) => { const { rows } = await query( "select id, studio_slug, label, instance_url, status, created_at from instances where account_id = $1 order by created_at", [req.params.id] ); res.json({ instances: rows }); }); // — Instanz sperren / reaktivieren — adminRouter.post("/instances/:id/:action", async (req, res) => { const { id, action } = req.params; const map = { suspend: "suspended", reactivate: "active" }; const status = map[action]; if (!status) return res.status(400).json({ error: "Unbekannte Aktion." }); const row = await one("update instances set status = $1 where id = $2 returning id, status", [status, id]); if (!row) return res.status(404).json({ error: "Instanz nicht gefunden." }); res.json({ instance: row }); });