import { createServerFn } from "@tanstack/react-start"; import { z } from "zod"; import { requireViewer, viewerMiddleware } from "./middleware"; import { forbidden } from "~/server/errors"; import { limitWrite } from "~/server/limits"; import { billingAccountFor, requireSiteAdmin, topUsage } from "~/server/metering/access"; import { setPlan, setSpendingLimit } from "~/server/metering/quota"; import { periodOf } from "~/server/metering/record"; import { usageSummary } from "~/server/metering/summary"; const period = z .string() .regex(/^\d{4}-(0[1-9]|1[0-2])$/) .optional(); const org = z.string().min(1).max(100).nullable(); export const getBilling = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .validator(z.object({ org, period })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); const account = await billingAccountFor(viewer, data.org); const summary = await usageSummary(account.accountId, data.period ?? periodOf()); return { account, summary }; }); export const updateSpendingLimit = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ org, cents: z.number().int().min(0).max(100_000_000).nullable() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const account = await billingAccountFor(viewer, data.org); if (!account.canEdit) throw forbidden("Only organization owners can change the spending limit"); await setSpendingLimit(account.accountId, data.cents); return { ok: true }; }); export const getAdminBilling = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .validator(z.object({ period })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); const current = data.period ?? periodOf(); return { period: current, rows: await topUsage(current) }; }); export const adminSetPlan = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ accountId: z.string().min(1).max(100), plan: z.enum(["free", "pro"]) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); await setPlan(data.accountId, data.plan); return { ok: true }; });