diff --git a/src/middleware/subscription.ts b/src/middleware/subscription.ts index 92d1b35..557dc62 100644 --- a/src/middleware/subscription.ts +++ b/src/middleware/subscription.ts @@ -38,6 +38,11 @@ export async function subscriptionMiddleware(ctx: Context, next: Next): Promise< let message = ""; switch (org.subscription_status) { + case "free": + // Exempt from fees - always active + isActive = true; + break; + case "active": // Check if subscription has ended if (org.subscription_ends_at && new Date(org.subscription_ends_at) < now) { diff --git a/src/routes/admin.ts b/src/routes/admin.ts index 06b8117..7e1712b 100644 --- a/src/routes/admin.ts +++ b/src/routes/admin.ts @@ -369,6 +369,49 @@ adminRouter.post("/organizations/:id/extend-trial", requireSuperAdmin, async (ct ctx.response.body = { message: `Trial um ${extendDays} Tage verlängert` }; }); +// Set organization as free (exempt from fees) +adminRouter.post("/organizations/:id/set-free", requireSuperAdmin, async (ctx) => { + const orgId = ctx.params.id; + const body = await ctx.request.body.json(); + const { reason } = body; + + await execute( + `UPDATE organizations SET + subscription_status = 'free', + subscription_plan = 'enterprise', + subscription_ends_at = NULL, + trial_ends_at = NULL, + subscription_paused_at = NULL, + subscription_pause_reason = NULL, + settings = settings || $1::jsonb + WHERE id = $2`, + [JSON.stringify({ free_reason: reason || "Freigestellt durch Admin", free_since: new Date().toISOString() }), orgId] + ); + + ctx.response.body = { message: "Organisation freigestellt (kostenlos)" }; +}); + +// Remove free status (back to trial or active) +adminRouter.post("/organizations/:id/remove-free", requireSuperAdmin, async (ctx) => { + const orgId = ctx.params.id; + const body = await ctx.request.body.json(); + const { set_trial_days } = body; + + const trialDays = set_trial_days || 14; + + await execute( + `UPDATE organizations SET + subscription_status = 'trial', + subscription_plan = 'starter', + trial_ends_at = NOW() + INTERVAL '${trialDays} days', + settings = settings - 'free_reason' - 'free_since' + WHERE id = $1`, + [orgId] + ); + + ctx.response.body = { message: `Freistellung aufgehoben, ${trialDays} Tage Trial gesetzt` }; +}); + // List all organizations with subscription info adminRouter.get("/subscriptions", requireSuperAdmin, async (ctx) => { const filter = ctx.request.url.searchParams.get("filter"); // all, trial, active, paused, expired