mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
feat: add organisations (#1820)
This commit is contained in:
175
packages/lib/server-only/organisation/create-organisation.ts
Normal file
175
packages/lib/server-only/organisation/create-organisation.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import { OrganisationType } from '@prisma/client';
|
||||
import { OrganisationMemberRole } from '@prisma/client';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { ORGANISATION_INTERNAL_GROUPS } from '../../constants/organisations';
|
||||
import { AppErrorCode } from '../../errors/app-error';
|
||||
import { AppError } from '../../errors/app-error';
|
||||
import type { InternalClaim } from '../../types/subscription';
|
||||
import { INTERNAL_CLAIM_ID, internalClaims } from '../../types/subscription';
|
||||
import { generateDatabaseId, prefixedId } from '../../universal/id';
|
||||
import { generateDefaultOrganisationSettings } from '../../utils/organisations';
|
||||
import { createTeam } from '../team/create-team';
|
||||
|
||||
type CreateOrganisationOptions = {
|
||||
userId: number;
|
||||
name: string;
|
||||
type: OrganisationType;
|
||||
url?: string;
|
||||
customerId?: string;
|
||||
claim: InternalClaim;
|
||||
};
|
||||
|
||||
export const createOrganisation = async ({
|
||||
name,
|
||||
url,
|
||||
type,
|
||||
userId,
|
||||
customerId,
|
||||
claim,
|
||||
}: CreateOrganisationOptions) => {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const organisationSetting = await tx.organisationGlobalSettings.create({
|
||||
data: {
|
||||
...generateDefaultOrganisationSettings(),
|
||||
id: generateDatabaseId('org_setting'),
|
||||
},
|
||||
});
|
||||
|
||||
const organisationClaim = await tx.organisationClaim.create({
|
||||
data: {
|
||||
id: generateDatabaseId('org_claim'),
|
||||
originalSubscriptionClaimId: claim.id,
|
||||
...createOrganisationClaimUpsertData(claim),
|
||||
},
|
||||
});
|
||||
|
||||
const orgIdAndUrl = prefixedId('org');
|
||||
|
||||
const organisation = await tx.organisation
|
||||
.create({
|
||||
data: {
|
||||
id: orgIdAndUrl,
|
||||
name,
|
||||
type,
|
||||
url: url || orgIdAndUrl,
|
||||
ownerUserId: userId,
|
||||
organisationGlobalSettingsId: organisationSetting.id,
|
||||
organisationClaimId: organisationClaim.id,
|
||||
groups: {
|
||||
create: ORGANISATION_INTERNAL_GROUPS.map((group) => ({
|
||||
...group,
|
||||
id: generateDatabaseId('org_group'),
|
||||
})),
|
||||
},
|
||||
customerId,
|
||||
},
|
||||
include: {
|
||||
groups: true,
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.code === 'P2002') {
|
||||
throw new AppError(AppErrorCode.ALREADY_EXISTS, {
|
||||
message: 'Organisation URL already exists',
|
||||
});
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
|
||||
const adminGroup = organisation.groups.find(
|
||||
(group) => group.organisationRole === OrganisationMemberRole.ADMIN,
|
||||
);
|
||||
|
||||
if (!adminGroup) {
|
||||
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
|
||||
message: 'Admin group not found',
|
||||
});
|
||||
}
|
||||
|
||||
await tx.organisationMember.create({
|
||||
data: {
|
||||
id: generateDatabaseId('member'),
|
||||
userId,
|
||||
organisationId: organisation.id,
|
||||
organisationGroupMembers: {
|
||||
create: {
|
||||
id: generateDatabaseId('group_member'),
|
||||
groupId: adminGroup.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return organisation;
|
||||
});
|
||||
};
|
||||
|
||||
type CreatePersonalOrganisationOptions = {
|
||||
userId: number;
|
||||
orgUrl?: string;
|
||||
throwErrorOnOrganisationCreationFailure?: boolean;
|
||||
inheritMembers?: boolean;
|
||||
type?: OrganisationType;
|
||||
};
|
||||
|
||||
export const createPersonalOrganisation = async ({
|
||||
userId,
|
||||
orgUrl,
|
||||
throwErrorOnOrganisationCreationFailure = false,
|
||||
inheritMembers = true,
|
||||
type = OrganisationType.PERSONAL,
|
||||
}: CreatePersonalOrganisationOptions) => {
|
||||
const organisation = await createOrganisation({
|
||||
name: 'Personal Organisation',
|
||||
userId,
|
||||
url: orgUrl,
|
||||
type,
|
||||
claim: internalClaims[INTERNAL_CLAIM_ID.FREE],
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
|
||||
if (throwErrorOnOrganisationCreationFailure) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Todo: (LOGS)
|
||||
});
|
||||
|
||||
if (organisation) {
|
||||
await createTeam({
|
||||
userId,
|
||||
teamName: 'Personal Team',
|
||||
teamUrl: prefixedId('personal'),
|
||||
organisationId: organisation.id,
|
||||
inheritMembers,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
|
||||
// Todo: (LOGS)
|
||||
});
|
||||
}
|
||||
|
||||
return organisation;
|
||||
};
|
||||
|
||||
export const createOrganisationClaimUpsertData = (subscriptionClaim: InternalClaim) => {
|
||||
// Done like this to ensure type errors are thrown if items are added.
|
||||
const data: Omit<
|
||||
Prisma.SubscriptionClaimCreateInput,
|
||||
'id' | 'createdAt' | 'updatedAt' | 'locked' | 'name'
|
||||
> = {
|
||||
flags: {
|
||||
...subscriptionClaim.flags,
|
||||
},
|
||||
teamCount: subscriptionClaim.teamCount,
|
||||
memberCount: subscriptionClaim.memberCount,
|
||||
};
|
||||
|
||||
return {
|
||||
...data,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user