Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
2.2 kB ยท 55 lines
TypeScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556import { PreconditionFailedError } from "@openstatus/services";import { deleteAccount } from "@openstatus/services/user";import { listOwnedWorkspaces } from "@openstatus/services/workspace";
import { removeDomainFromVercelIfUnused } from "../lib/vercel";import { toServiceCtx, toTRPCError } from "../service-adapter";import { createTRPCRouter, protectedProcedure } from "../trpc";import { cancelOwnedTrials } from "./stripe/trial";
export const userRouter = createTRPCRouter({ // The authed middleware already loaded this row; re-selecting it would be // the same query. `undefined` for a soft-deleted user matches `getUser`. get: protectedProcedure.query(({ ctx }) => ctx.user.deletedAt ? undefined : ctx.user, ),
deleteAccount: protectedProcedure.mutation(async ({ ctx }) => { try { // A trial is not a paid plan the user has to cancel first โ but a paid // workspace elsewhere aborts the delete, so check before touching any // trial or the user loses it for nothing. The trial itself must go // before `deleteAccount`, which refuses any non-free plan; it is not // rolled back if the delete fails, the user simply retries. const owned = await listOwnedWorkspaces({ input: { userId: ctx.user.id }, db: ctx.db, }); // A signup workspace has no plan column yet โ that is free. if ( owned.some((ws) => ws.plan && ws.plan !== "free" && !ws.trialEndsAt) ) { throw new PreconditionFailedError( "You must cancel your subscription before deleting your account.", ); } const customDomains = await cancelOwnedTrials({ userId: ctx.user.id, db: ctx.db, }); // `userId` is derived from `ctx.actor` inside the service โ no // input needed. await deleteAccount({ ctx: toServiceCtx(ctx) }); for (const domain of customDomains) { await removeDomainFromVercelIfUnused(ctx.db, domain).catch((error) => console.error("Failed to release domain from Vercel:", { domain, error, }), ); } } catch (err) { toTRPCError(err); } }),});