POST /api/dev/set-plan { plan: 'free'|'pro'|'legend' } — requireUser, sets the
caller's own profile.plan via Prisma. Refuses on production URL (same guard as
the cooldown testMode: appUrl includes rebreak.org && !includes staging). Lets
the __DEV__ tier-toggle work without admin rights. Does NOT weaken updateProfile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import { usePrisma } from "../../utils/prisma";
|
|
|
|
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" }
|
|
* Response: { success: true, plan: AppPlan }
|
|
*/
|
|
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;
|
|
|
|
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();
|
|
await db.profile.update({
|
|
where: { id: user.id },
|
|
data: { plan: plan as AppPlan },
|
|
});
|
|
|
|
return { success: true, plan: plan as AppPlan };
|
|
});
|