feat: add admin org deletion (#2795)

This commit is contained in:
David Nguyen
2026-05-13 15:28:27 +10:00
committed by GitHub
parent 9a45b3564f
commit cfaad6efc9
17 changed files with 1505 additions and 5 deletions
@@ -0,0 +1,54 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma';
import { adminProcedure } from '../trpc';
import { ZDeleteOrganisationRequestSchema, ZDeleteOrganisationResponseSchema } from './delete-organisation.types';
export const deleteOrganisationRoute = adminProcedure
.input(ZDeleteOrganisationRequestSchema)
.output(ZDeleteOrganisationResponseSchema)
.mutation(async ({ input, ctx }) => {
const { organisationId, organisationName, sendEmailToOwner } = input;
const { user } = ctx;
ctx.logger.info({
input: {
organisationId,
sendEmailToOwner,
},
});
const organisation = await prisma.organisation.findUnique({
where: {
id: organisationId,
},
select: {
id: true,
name: true,
},
});
if (!organisation) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Organisation not found',
});
}
if (organisation.name !== organisationName) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Organisation name does not match',
});
}
// The deletion itself is offloaded to a background job because orphaning
// potentially-large numbers of envelopes can take a while.
await jobs.triggerJob({
name: 'internal.admin-delete-organisation',
payload: {
organisationId: organisation.id,
sendEmailToOwner,
requestedByUserId: user.id,
},
});
});
@@ -0,0 +1,20 @@
import { z } from 'zod';
export const ZDeleteOrganisationRequestSchema = z.object({
organisationId: z.string().min(1),
/**
* The organisation name as typed by the admin in the confirmation dialog.
* Must exactly match the persisted organisation's name for the deletion
* to proceed.
*/
organisationName: z.string().min(1),
/**
* Whether to email the organisation owner notifying them of the deletion.
*/
sendEmailToOwner: z.boolean(),
});
export const ZDeleteOrganisationResponseSchema = z.void();
export type TDeleteOrganisationRequest = z.infer<typeof ZDeleteOrganisationRequestSchema>;
export type TDeleteOrganisationResponse = z.infer<typeof ZDeleteOrganisationResponseSchema>;
@@ -3,6 +3,7 @@ import { createAdminOrganisationRoute } from './create-admin-organisation';
import { createStripeCustomerRoute } from './create-stripe-customer';
import { createSubscriptionClaimRoute } from './create-subscription-claim';
import { deleteDocumentRoute } from './delete-document';
import { deleteOrganisationRoute } from './delete-organisation';
import { deleteAdminOrganisationMemberRoute } from './delete-organisation-member';
import { deleteSubscriptionClaimRoute } from './delete-subscription-claim';
import { deleteAdminTeamMemberRoute } from './delete-team-member';
@@ -41,6 +42,7 @@ export const adminRouter = router({
get: getAdminOrganisationRoute,
create: createAdminOrganisationRoute,
update: updateAdminOrganisationRoute,
delete: deleteOrganisationRoute,
swapSubscription: swapOrganisationSubscriptionRoute,
},
organisationMember: {