import { usePrisma } from "../../utils/prisma"; import { runDowngradeReconciliation } from "../../utils/downgrade-reconciliation"; import type { Plan } from "../../utils/plan-features"; const VALID_PLANS = ["free", "pro", "legend"] as const; type AppPlan = (typeof VALID_PLANS)[number]; /** * POST /api/dev/set-plan * * DEV/STAGING-ONLY: Setzt den eigenen Plan ohne Admin-Rechte. * Blocked in Production (appUrl enthält "rebreak.org" aber NICHT "staging"). * * Body: { plan: "free" | "pro" | "legend", foundingMember?: boolean } * Response: { success: true, plan: AppPlan, foundingMember: boolean, reconciled: boolean } */ export default defineEventHandler(async (event) => { const user = await requireUser(event); // Prod-Guard: analog cooldown/request.post.ts const config = useRuntimeConfig(event); const appUrl = (config.public?.appUrl as string) ?? ""; const isProductionUrl = appUrl.includes("rebreak.org") && !appUrl.includes("staging"); if (isProductionUrl) { throw createError({ statusCode: 403, message: "dev-only" }); } const body = await readBody(event).catch(() => ({})); const plan = body?.plan as string | undefined; const setFoundingMember = body?.foundingMember as boolean | undefined; if (!plan || !(VALID_PLANS as readonly string[]).includes(plan)) { throw createError({ statusCode: 400, data: { error: "INVALID_PLAN", message: `plan must be one of: ${VALID_PLANS.join(", ")}`, }, }); } const db = usePrisma(); // Aktuellen Plan lesen für Reconciliation const current = await db.profile.findUnique({ where: { id: user.id }, select: { plan: true, foundingMember: true }, }); const fromPlan = (current?.plan ?? "free") as Plan; const toPlan = plan as AppPlan; // Plan + optional foundingMember setzen const updateData: Record = { plan: toPlan }; if (typeof setFoundingMember === "boolean") { updateData.foundingMember = setFoundingMember; } await db.profile.update({ where: { id: user.id }, data: updateData, }); // Downgrade-Reconciliation (überspringt automatisch wenn foundingMember=true) let reconciled = false; try { await runDowngradeReconciliation(user.id, fromPlan, toPlan); reconciled = true; } catch (err) { // Reconciliation-Fehler darf Plan-Wechsel nicht blockieren console.error("[set-plan] reconciliation error:", err); } const updated = await db.profile.findUnique({ where: { id: user.id }, select: { foundingMember: true }, }); return { success: true, plan: toPlan, foundingMember: updated?.foundingMember ?? false, reconciled, }; });