From a3eeeab259612139993d94316ac54910dfc13c1b Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:57:21 +0100 Subject: [PATCH] feat: Handle deleting vercel domain when org is deleted (#9661) --- .../routers/viewer/teams/delete.handler.ts | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/trpc/server/routers/viewer/teams/delete.handler.ts b/packages/trpc/server/routers/viewer/teams/delete.handler.ts index 5bde167011..29d7d07c17 100644 --- a/packages/trpc/server/routers/viewer/teams/delete.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/delete.handler.ts @@ -1,8 +1,10 @@ +import { subdomainSuffix } from "@calcom/ee/organizations/lib/orgDomains"; import { cancelTeamSubscriptionFromStripe } from "@calcom/features/ee/teams/lib/payments"; -import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; +import { IS_PRODUCTION, IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; import { isTeamOwner } from "@calcom/lib/server/queries/teams"; import { closeComDeleteTeam } from "@calcom/lib/sync/SyncServiceManager"; import { prisma } from "@calcom/prisma"; +import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -16,6 +18,41 @@ type DeleteOptions = { input: TDeleteInputSchema; }; +const deleteVercelDomain = async ({ + slug, + isOrganization, +}: { + slug?: string | null; + isOrganization?: boolean | null; +}) => { + if (!isOrganization || !slug) { + return false; + } + + const fullDomain = `${slug}.${subdomainSuffix()}`; + const response = await fetch( + `https://api.vercel.com/v9/projects/${process.env.PROJECT_ID_VERCEL}/domains/${fullDomain}?teamId=${process.env.TEAM_ID_VERCEL}`, + { + headers: { + Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN_VERCEL}`, + }, + method: "DELETE", + } + ); + + const data = await response.json(); + + // Domain is already owned by another team but you can request delegation to access it + if (data.error?.code === "forbidden") + throw new TRPCError({ code: "CONFLICT", message: "domain_taken_team" }); + + // Domain is already being used by a different project + if (data.error?.code === "domain_taken") + throw new TRPCError({ code: "CONFLICT", message: "domain_taken_project" }); + + return true; +}; + export const deleteHandler = async ({ ctx, input }: DeleteOptions) => { if (!(await isTeamOwner(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -34,6 +71,14 @@ export const deleteHandler = async ({ ctx, input }: DeleteOptions) => { }, }); + const deletedTeamMetadata = teamMetadataSchema.parse(deletedTeam.metadata); + + if (IS_PRODUCTION) + deleteVercelDomain({ + slug: deletedTeam.slug, + isOrganization: deletedTeamMetadata?.isOrganization, + }); + // Sync Services: Close.cm closeComDeleteTeam(deletedTeam); };