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

@ -2,11 +2,8 @@ import { data } from 'react-router';
import { match } from 'ts-pattern';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { getOrganisationClaimByTeamId } from '@documenso/lib/server-only/organisation/get-organisation-claims';
import { getTemplateByDirectLinkToken } from '@documenso/lib/server-only/template/get-template-by-direct-link-token';
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
@ -36,10 +33,15 @@ export async function loader({ params, request }: Route.LoaderArgs) {
throw new Response('Not found', { status: 404 });
}
const organisationClaim = await getOrganisationClaimByTeamId({ teamId: template.teamId });
const allowEmbedSigningWhitelabel = organisationClaim.flags.embedSigningWhiteLabel;
const hidePoweredBy = organisationClaim.flags.hidePoweredBy;
// TODO: Make this more robust, we need to ensure the owner is either
// TODO: the member of a team that has an active subscription, is an early
// TODO: adopter or is an enterprise user.
if (IS_BILLING_ENABLED() && !template.teamId) {
if (IS_BILLING_ENABLED() && !organisationClaim.flags.embedSigning) {
throw data(
{
type: 'embed-paywall',
@ -56,18 +58,6 @@ export async function loader({ params, request }: Route.LoaderArgs) {
documentAuth: template.authOptions,
});
const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([
isDocumentPlatform(template),
isUserEnterprise({
userId: template.userId,
teamId: template.teamId ?? undefined,
}),
isUserCommunityPlan({
userId: template.userId,
teamId: template.teamId ?? undefined,
}),
]);
const isAccessAuthValid = match(derivedRecipientAccessAuth.at(0))
.with(DocumentAccessAuth.ACCOUNT, () => !!user)
.with(undefined, () => true)
@ -98,12 +88,6 @@ export async function loader({ params, request }: Route.LoaderArgs) {
const fields = template.fields.filter((field) => field.recipientId === directTemplateRecipientId);
const team = template.teamId
? await getTeamById({ teamId: template.teamId, userId: template.userId }).catch(() => null)
: null;
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
return superLoaderJson({
token,
user,
@ -111,24 +95,13 @@ export async function loader({ params, request }: Route.LoaderArgs) {
recipient,
fields,
hidePoweredBy,
isPlatformDocument,
isEnterpriseDocument,
isCommunityPlan,
allowEmbedSigningWhitelabel,
});
}
export default function EmbedDirectTemplatePage() {
const {
token,
user,
template,
recipient,
fields,
hidePoweredBy,
isPlatformDocument,
isEnterpriseDocument,
isCommunityPlan,
} = useSuperLoaderData<typeof loader>();
const { token, user, template, recipient, fields, hidePoweredBy, allowEmbedSigningWhitelabel } =
useSuperLoaderData<typeof loader>();
return (
<DocumentSigningProvider
@ -152,10 +125,8 @@ export default function EmbedDirectTemplatePage() {
recipient={recipient}
fields={fields}
metadata={template.templateMeta}
hidePoweredBy={
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
}
allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
hidePoweredBy={hidePoweredBy}
allowWhiteLabelling={allowEmbedSigningWhitelabel}
/>
</DocumentSigningRecipientProvider>
</DocumentSigningAuthProvider>

View File

@ -3,17 +3,14 @@ import { data } from 'react-router';
import { match } from 'ts-pattern';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getCompletedFieldsForToken } from '@documenso/lib/server-only/field/get-completed-fields-for-token';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getOrganisationClaimByTeamId } from '@documenso/lib/server-only/organisation/get-organisation-claims';
import { getIsRecipientsTurnToSign } from '@documenso/lib/server-only/recipient/get-is-recipient-turn';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
@ -51,10 +48,15 @@ export async function loader({ params, request }: Route.LoaderArgs) {
throw new Response('Not found', { status: 404 });
}
const organisationClaim = await getOrganisationClaimByTeamId({ teamId: document.teamId });
const allowEmbedSigningWhitelabel = organisationClaim.flags.embedSigningWhiteLabel;
const hidePoweredBy = organisationClaim.flags.hidePoweredBy;
// TODO: Make this more robust, we need to ensure the owner is either
// TODO: the member of a team that has an active subscription, is an early
// TODO: adopter or is an enterprise user.
if (IS_BILLING_ENABLED() && !document.teamId) {
if (IS_BILLING_ENABLED() && !organisationClaim.flags.embedSigning) {
throw data(
{
type: 'embed-paywall',
@ -65,18 +67,6 @@ export async function loader({ params, request }: Route.LoaderArgs) {
);
}
const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([
isDocumentPlatform(document),
isUserEnterprise({
userId: document.userId,
teamId: document.teamId ?? undefined,
}),
isUserCommunityPlan({
userId: document.userId,
teamId: document.teamId ?? undefined,
}),
]);
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
documentAuth: document.authOptions,
});
@ -119,12 +109,6 @@ export async function loader({ params, request }: Route.LoaderArgs) {
})
: [];
const team = document.teamId
? await getTeamById({ teamId: document.teamId, userId: document.userId }).catch(() => null)
: null;
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
return superLoaderJson({
token,
user,
@ -134,9 +118,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
fields,
completedFields,
hidePoweredBy,
isPlatformDocument,
isEnterpriseDocument,
isCommunityPlan,
allowEmbedSigningWhitelabel,
});
}
@ -150,9 +132,7 @@ export default function EmbedSignDocumentPage() {
fields,
completedFields,
hidePoweredBy,
isPlatformDocument,
isEnterpriseDocument,
isCommunityPlan,
allowEmbedSigningWhitelabel,
} = useSuperLoaderData<typeof loader>();
return (
@ -178,10 +158,8 @@ export default function EmbedSignDocumentPage() {
completedFields={completedFields}
metadata={document.documentMeta}
isCompleted={isDocumentCompleted(document.status)}
hidePoweredBy={
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
}
allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
hidePoweredBy={hidePoweredBy}
allowWhitelabelling={allowEmbedSigningWhitelabel}
allRecipients={allRecipients}
/>
</DocumentSigningAuthProvider>

View File

@ -2,10 +2,8 @@ import { useLayoutEffect } from 'react';
import { Outlet, useLoaderData } from 'react-router';
import { isCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { getOrganisationClaimByTeamId } from '@documenso/lib/server-only/organisation/get-organisation-claims';
import { TrpcProvider } from '@documenso/trpc/react';
import { ZBaseEmbedAuthoringSchema } from '~/types/embed-authoring-base-schema';
@ -27,39 +25,25 @@ export const loader = async ({ request }: Route.LoaderArgs) => {
const result = await verifyEmbeddingPresignToken({ token }).catch(() => null);
let hasPlatformPlan = false;
let hasEnterprisePlan = false;
let hasCommunityPlan = false;
let allowEmbedAuthoringWhiteLabel = false;
if (result) {
[hasCommunityPlan, hasPlatformPlan, hasEnterprisePlan] = await Promise.all([
isCommunityPlan({
userId: result.userId,
teamId: result.teamId ?? undefined,
}),
isDocumentPlatform({
userId: result.userId,
teamId: result.teamId,
}),
isUserEnterprise({
userId: result.userId,
teamId: result.teamId ?? undefined,
}),
]);
const organisationClaim = await getOrganisationClaimByTeamId({
teamId: result.teamId,
});
allowEmbedAuthoringWhiteLabel = organisationClaim.flags.embedAuthoringWhiteLabel ?? false;
}
return {
hasValidToken: !!result,
token,
hasCommunityPlan,
hasPlatformPlan,
hasEnterprisePlan,
allowEmbedAuthoringWhiteLabel,
};
};
export default function AuthoringLayout() {
const { hasValidToken, token, hasCommunityPlan, hasPlatformPlan, hasEnterprisePlan } =
useLoaderData<typeof loader>();
const { hasValidToken, token, allowEmbedAuthoringWhiteLabel } = useLoaderData<typeof loader>();
useLayoutEffect(() => {
try {
@ -79,7 +63,7 @@ export default function AuthoringLayout() {
document.documentElement.classList.add('dark-mode-disabled');
}
if (hasCommunityPlan || hasPlatformPlan || hasEnterprisePlan) {
if (allowEmbedAuthoringWhiteLabel) {
injectCss({
css,
cssVars,

View File

@ -4,12 +4,9 @@ import { SigningStatus } from '@prisma/client';
import { useRevalidator } from 'react-router';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getOrganisationClaimByTeamId } from '@documenso/lib/server-only/organisation/get-organisation-claims';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { BrandingLogo } from '~/components/general/branding-logo';
import { DocumentSigningAuthProvider } from '~/components/general/document-signing/document-signing-auth-provider';
@ -54,26 +51,10 @@ export async function loader({ request }: Route.LoaderArgs) {
});
}
const team = firstDocument.teamId
? await getTeamById({ teamId: firstDocument.teamId, userId: firstDocument.userId }).catch(
() => null,
)
: null;
const organisationClaim = await getOrganisationClaimByTeamId({ teamId: firstDocument.teamId });
const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([
isDocumentPlatform(firstDocument),
isUserEnterprise({
userId: firstDocument.userId,
teamId: firstDocument.teamId ?? undefined,
}),
isUserCommunityPlan({
userId: firstDocument.userId,
teamId: firstDocument.teamId ?? undefined,
}),
]);
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
const allowWhitelabelling = isCommunityPlan || isPlatformDocument || isEnterpriseDocument;
const allowWhitelabelling = organisationClaim.flags.embedSigningWhiteLabel;
const hidePoweredBy = organisationClaim.flags.hidePoweredBy;
return superLoaderJson({
envelopes,