import { DateFormatter } from "@internationalized/date"; import type { PrismaClient } from "@trigger.dev/database"; import { prisma } from "~/db.server"; import { featuresForRequest } from "~/features.server"; import { DeleteProjectService } from "./deleteProject.server"; import { getCurrentPlan } from "./platform.v3.server"; import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server"; export class DeleteOrganizationService { #prismaClient: PrismaClient; constructor(prismaClient: PrismaClient = prisma) { this.#prismaClient = prismaClient; } public async call({ organizationSlug, userId, request, }: { organizationSlug: string; userId: string; request: Request; }) { const organization = await this.#prismaClient.organization.findFirst({ include: { projects: true, members: true, }, where: { slug: organizationSlug, members: { some: { userId: userId } }, }, }); if (!organization) { throw new Error("Organization not found"); } if (organization.deletedAt) { throw new Error("Organization already deleted"); } //Check if they have an active subscription const { isManagedCloud } = featuresForRequest(request); const currentPlan = isManagedCloud ? await getCurrentPlan(organization.id) : undefined; if (currentPlan && currentPlan.v3Subscription && currentPlan.v3Subscription.isPaying) { //they've cancelled and that date hasn't passed yet if ( currentPlan.v3Subscription.canceledAt && new Date(currentPlan.v3Subscription.canceledAt) > new Date() ) { //a dateformatter that produces results like "Jan 1 2024" const dateFormatter = new DateFormatter("en-us", { year: "numeric", month: "short", day: "numeric", }); throw new Error( `This Organization has a canceled subscription. You can delete it when the cancelation date (${dateFormatter.format( new Date(currentPlan.v3Subscription.canceledAt) )}) is in the past.` ); } throw new Error("You can't delete an Organization that has an active subscription"); } // loop through the projects and delete them const projectDeleteService = new DeleteProjectService(); for (const project of organization.projects) { await projectDeleteService.call({ projectId: project.id, userId }); } //mark the organization as deleted await this.#prismaClient.organization.update({ where: { id: organization.id, }, data: { runsEnabled: false, deletedAt: new Date(), }, }); // runsEnabled + the org's projects (project.deletedAt) changed; drop all cached env rows. controlPlaneResolver.invalidateOrganization(organization.id); } }