import { createServerFn } from "@tanstack/react-start"; import { z } from "zod"; import { requireViewer, viewerMiddleware } from "./middleware"; import { adminOverview } from "~/server/admin-overview"; import { requireSiteAdmin } from "~/server/metering/access"; import { limitWrite } from "~/server/limits"; import { globalCeilingHit } from "~/server/sites/caps"; import { listHostingSuspensions, resumeHosting, suspendHosting } from "~/server/sites/suspend"; import { listDisabledAccounts } from "~/server/account-disable"; import { adminDisableAccount, enableAccount } from "~/server/account-disable-actions"; import { listAdminAudit, recordAdminAction } from "~/server/admin-audit"; import { resolveAbuseTarget } from "~/server/abuse-targets"; import { setModerationHidden } from "~/server/moderation"; import { badRequest } from "~/server/errors"; import { approveWaitlistEntry, listWaitlist, MAX_SIGNUP_CAP, setSignupCap, waitlistSummary } from "~/server/waitlist"; import { listSiteAdmins, setSiteAdmin } from "~/server/site-admins"; export const getAdminOverview = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .handler(async ({ context }) => { requireSiteAdmin(requireViewer(context.viewer)); const [overview, suspensions, ceilingHit, disabledAccounts, waitlist] = await Promise.all([ adminOverview(), listHostingSuspensions(), globalCeilingHit(), listDisabledAccounts(), waitlistSummary(), ]); const siteAdmins = await listSiteAdmins(); return { ...overview, hosting: { suspensions, ceilingHit }, disabledAccounts, waitlist, siteAdmins }; }); /** Caps user accounts (null lifts the cap); any newly free seats go to the waitlist in order. */ export const adminSetSignupCap = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ cap: z.number().int().min(1).max(MAX_SIGNUP_CAP).nullable() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); await setSignupCap(data.cap); await recordAdminAction({ actorId: viewer.id, action: "signups.cap", targetKind: "instance", targetId: data.cap === null ? "none" : String(data.cap), }); return { ok: true }; }); /** Operator kill switch for one account's Sites. */ export const adminSuspendHosting = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ name: z.string().min(1).max(100) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); const accountId = await suspendHosting(data.name); await recordAdminAction({ actorId: viewer.id, action: "hosting.suspend", targetKind: "account", targetId: accountId, }); return { ok: true }; }); export const adminResumeHosting = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ accountId: z.string().min(1).max(100) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); await resumeHosting(data.accountId); await recordAdminAction({ actorId: viewer.id, action: "hosting.resume", targetKind: "account", targetId: data.accountId, }); return { ok: true }; }); /** Disables an account: sign-in and every credential stop working, billing and hosting stop, repos go read-only. */ export const adminDisable = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ name: z.string().min(1).max(100), note: z.string().max(500).nullable() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); const accountId = await adminDisableAccount(data.name, data.note?.trim() || null, viewer.id); await recordAdminAction({ actorId: viewer.id, action: "account.disable", targetKind: "account", targetId: accountId, reason: data.note, }); return { ok: true }; }); /** Grants or removes site admin; demotion revokes deploy tokens held only through site-admin standing. */ export const adminSetSiteAdmin = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ name: z.string().min(1).max(100), admin: z.boolean() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); const userId = await setSiteAdmin(data.name, data.admin); await recordAdminAction({ actorId: viewer.id, action: data.admin ? "admin.grant" : "admin.revoke", targetKind: "account", targetId: userId, }); return { ok: true }; }); /** Re-enables an account and resumes hosting; cancelled subscriptions are not re-created. */ export const adminEnable = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ accountId: z.string().min(1).max(100) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); await enableAccount(data.accountId, `admin:${viewer.id}`); await recordAdminAction({ actorId: viewer.id, action: "account.enable", targetKind: "account", targetId: data.accountId, }); return { ok: true }; }); /** Hides (or restores) the repository, issue or comment behind a forge URL. */ export const adminModerate = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator( z.object({ url: z.url({ protocol: /^https?$/ }).max(2000), hidden: z.boolean(), reason: z.string().max(1000).nullable(), }), ) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); const target = await resolveAbuseTarget(data.url); if (!target) throw badRequest("That URL is not a repository, issue or comment"); await setModerationHidden(target.kind, target.id, data.hidden, viewer.id, data.reason?.trim() || null); return { ok: true, target }; }); export const getAdminAudit = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .validator( z.object({ after: z.object({ createdAt: z.number().int().nonnegative(), id: z.string().min(1).max(64) }).optional(), }), ) .handler(async ({ data, context }) => { requireSiteAdmin(requireViewer(context.viewer)); return { entries: await listAdminAudit({ after: data.after }) }; }); /** Lets one waitlisted person sign up regardless of the cap. */ export const adminApproveWaitlist = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(z.object({ id: z.number().int().positive() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); requireSiteAdmin(viewer); await limitWrite(viewer); const email = await approveWaitlistEntry(data.id); await recordAdminAction({ actorId: viewer.id, action: "signups.approve", targetKind: "waitlist", targetId: email }); return { ok: true }; }); export const getAdminWaitlist = createServerFn({ method: "GET" }) .middleware([viewerMiddleware]) .validator( z.object({ query: z.string().max(200).default(""), status: z.enum(["all", "waiting", "invited", "approved"]).default("all"), page: z.number().int().min(1).max(100_000).default(1), }), ) .handler(async ({ data, context }) => { requireSiteAdmin(requireViewer(context.viewer)); return listWaitlist(data); });