feat: add organisations (#1820)

This commit is contained in:
David Nguyen
2025-06-10 11:49:52 +10:00
committed by GitHub
parent 0b37f19641
commit e6dc237ad2
631 changed files with 37616 additions and 25695 deletions

View File

@ -0,0 +1,35 @@
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../../errors/app-error';
import type { JobRunIO } from '../../client/_internal/job';
import type { TBackportSubscriptionClaimJobDefinition } from './backport-subscription-claims';
export const run = async ({
payload,
io,
}: {
payload: TBackportSubscriptionClaimJobDefinition;
io: JobRunIO;
}) => {
const { subscriptionClaimId, flags } = payload;
const subscriptionClaim = await prisma.subscriptionClaim.findFirst({
where: {
id: subscriptionClaimId,
},
});
if (!subscriptionClaim) {
throw new AppError(AppErrorCode.NOT_FOUND, { message: 'Subscription claim not found' });
}
await io.runTask('backport-claims', async () => {
const newFlagsJson = JSON.stringify(flags);
await prisma.$executeRaw`
UPDATE "OrganisationClaim"
SET "flags" = "flags" || ${newFlagsJson}::jsonb
WHERE "originalSubscriptionClaimId" = ${subscriptionClaimId}
`;
});
};

View File

@ -0,0 +1,44 @@
import { z } from 'zod';
import { type JobDefinition } from '../../client/_internal/job';
const BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_ID = 'internal.backport-subscription-claims';
const BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_SCHEMA = z.object({
subscriptionClaimId: z.string(),
// I would prefer to fetch the subscription within the runner, but
// it seems the local job runs it asynchronously, so we can't get
// the updated values in the job.
flags: z.object({
unlimitedDocuments: z.literal(true).optional(),
allowCustomBranding: z.literal(true).optional(),
hidePoweredBy: z.literal(true).optional(),
embedAuthoring: z.literal(true).optional(),
embedAuthoringWhiteLabel: z.literal(true).optional(),
embedSigning: z.literal(true).optional(),
embedSigningWhiteLabel: z.literal(true).optional(),
cfr21: z.literal(true).optional(),
}),
});
export type TBackportSubscriptionClaimJobDefinition = z.infer<
typeof BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_SCHEMA
>;
export const BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION = {
id: BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_ID,
name: 'Backport Subscription Claims',
version: '1.0.0',
trigger: {
name: BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_ID,
schema: BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload, io }) => {
const handler = await import('./backport-subscription-claims.handler');
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION_ID,
TBackportSubscriptionClaimJobDefinition
>;

View File

@ -1,7 +1,6 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { parse } from 'csv-parse/sync';
import { z } from 'zod';
@ -16,8 +15,8 @@ import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { AppError } from '../../../errors/app-error';
import { getEmailContext } from '../../../server-only/email/get-email-context';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import type { JobRunIO } from '../../client/_internal/job';
import type { TBulkSendTemplateJobDefinition } from './bulk-send-template';
@ -163,29 +162,24 @@ export const run = async ({
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
});
let teamGlobalSettings: TeamGlobalSettings | undefined | null;
const { branding, settings } = await getEmailContext({
source: {
type: 'team',
teamId,
},
});
if (template.teamId) {
teamGlobalSettings = await prisma.teamGlobalSettings.findUnique({
where: {
teamId: template.teamId,
},
});
}
const lang = template.templateMeta?.language ?? settings.documentLanguage;
const branding = teamGlobalSettings
? teamGlobalSettingsToBranding(teamGlobalSettings)
: undefined;
const i18n = await getI18nInstance(teamGlobalSettings?.documentLanguage);
const i18n = await getI18nInstance(lang);
const [html, text] = await Promise.all([
renderEmailWithI18N(completionTemplate, {
lang: teamGlobalSettings?.documentLanguage,
lang,
branding,
}),
renderEmailWithI18N(completionTemplate, {
lang: teamGlobalSettings?.documentLanguage,
lang,
branding,
plainText: true,
}),

View File

@ -7,7 +7,7 @@ const BULK_SEND_TEMPLATE_JOB_DEFINITION_ID = 'internal.bulk-send-template';
const BULK_SEND_TEMPLATE_JOB_DEFINITION_SCHEMA = z.object({
userId: z.number(),
teamId: z.number().optional(),
teamId: z.number(),
templateId: z.number(),
csvContent: z.string(),
sendImmediately: z.boolean(),

View File

@ -16,6 +16,7 @@ import { flattenForm } from '../../../server-only/pdf/flatten-form';
import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
import { legacy_insertFieldInPDF } from '../../../server-only/pdf/legacy-insert-field-in-pdf';
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
import { getTeamSettings } from '../../../server-only/team/get-team-settings';
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
import {
@ -47,18 +48,14 @@ export const run = async ({
include: {
documentMeta: true,
recipients: true,
team: {
select: {
teamGlobalSettings: {
select: {
includeSigningCertificate: true,
},
},
},
},
},
});
const settings = await getTeamSettings({
userId: document.userId,
teamId: document.teamId,
});
const isComplete =
document.recipients.some((recipient) => recipient.signingStatus === SigningStatus.REJECTED) ||
document.recipients.every((recipient) => recipient.signingStatus === SigningStatus.SIGNED);
@ -144,13 +141,12 @@ export const run = async ({
const pdfData = await getFileServerSide(documentData);
const certificateData =
(document.team?.teamGlobalSettings?.includeSigningCertificate ?? true)
? await getCertificatePdf({
documentId,
language: document.documentMeta?.language,
}).catch(() => null)
: null;
const certificateData = settings.includeSigningCertificate
? await getCertificatePdf({
documentId,
language: document.documentMeta?.language,
}).catch(() => null)
: null;
const newDataId = await io.runTask('decorate-and-sign-pdf', async () => {
const pdfDoc = await PDFDocument.load(pdfData);