From 2fbaf56c06dfb53308925c9151480ff2c2a8991b Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 25 Feb 2025 07:54:28 +1100 Subject: [PATCH] fix: early adopters can use platform features --- .../app/embed/direct/[[...url]]/client.tsx | 6 +- .../src/app/embed/direct/[[...url]]/page.tsx | 9 ++- .../src/app/embed/sign/[[...url]]/client.tsx | 6 +- .../src/app/embed/sign/[[...url]]/page.tsx | 9 ++- .../ee/server-only/util/is-community-plan.ts | 56 +++++++++++++++++++ 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 packages/ee/server-only/util/is-community-plan.ts diff --git a/apps/web/src/app/embed/direct/[[...url]]/client.tsx b/apps/web/src/app/embed/direct/[[...url]]/client.tsx index dbb74a36a..5ff905e39 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/client.tsx @@ -49,7 +49,7 @@ export type EmbedDirectTemplateClientPageProps = { fields: Field[]; metadata?: DocumentMeta | TemplateMeta | null; hidePoweredBy?: boolean; - isPlatformOrEnterprise?: boolean; + allowWhiteLabelling?: boolean; }; export const EmbedDirectTemplateClientPage = ({ @@ -60,7 +60,7 @@ export const EmbedDirectTemplateClientPage = ({ fields, metadata, hidePoweredBy = false, - isPlatformOrEnterprise = false, + allowWhiteLabelling = false, }: EmbedDirectTemplateClientPageProps) => { const { _ } = useLingui(); const { toast } = useToast(); @@ -288,7 +288,7 @@ export const EmbedDirectTemplateClientPage = ({ document.documentElement.classList.add('dark-mode-disabled'); } - if (isPlatformOrEnterprise) { + if (allowWhiteLabelling) { injectCss({ css: data.css, cssVars: data.cssVars, diff --git a/apps/web/src/app/embed/direct/[[...url]]/page.tsx b/apps/web/src/app/embed/direct/[[...url]]/page.tsx index f4ef467d2..85ef40c78 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import { match } from 'ts-pattern'; +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'; @@ -55,12 +56,16 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem documentAuth: template.authOptions, }); - const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([ + 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) @@ -106,7 +111,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem fields={fields} metadata={template.templateMeta} hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} - isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument} + allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} /> diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index f7635ddab..ea024bde0 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -51,7 +51,7 @@ export type EmbedSignDocumentClientPageProps = { metadata?: DocumentMeta | TemplateMeta | null; isCompleted?: boolean; hidePoweredBy?: boolean; - isPlatformOrEnterprise?: boolean; + allowWhitelabelling?: boolean; allRecipients?: RecipientWithFields[]; }; @@ -64,7 +64,7 @@ export const EmbedSignDocumentClientPage = ({ metadata, isCompleted, hidePoweredBy = false, - isPlatformOrEnterprise = false, + allowWhitelabelling = false, allRecipients = [], }: EmbedSignDocumentClientPageProps) => { const { _ } = useLingui(); @@ -212,7 +212,7 @@ export const EmbedSignDocumentClientPage = ({ document.documentElement.classList.add('dark-mode-disabled'); } - if (isPlatformOrEnterprise) { + if (allowWhitelabelling) { injectCss({ css: data.css, cssVars: data.cssVars, diff --git a/apps/web/src/app/embed/sign/[[...url]]/page.tsx b/apps/web/src/app/embed/sign/[[...url]]/page.tsx index 0e9ac7a60..8b7223cb1 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import { match } from 'ts-pattern'; +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'; @@ -62,12 +63,16 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen return ; } - const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([ + 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({ @@ -127,7 +132,7 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen metadata={document.documentMeta} isCompleted={document.status === DocumentStatus.COMPLETED} hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} - isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument} + allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} allRecipients={allRecipients} /> diff --git a/packages/ee/server-only/util/is-community-plan.ts b/packages/ee/server-only/util/is-community-plan.ts new file mode 100644 index 000000000..b32769d41 --- /dev/null +++ b/packages/ee/server-only/util/is-community-plan.ts @@ -0,0 +1,56 @@ +import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; +import { prisma } from '@documenso/prisma'; +import type { Subscription } from '@documenso/prisma/client'; + +import { getCommunityPlanPriceIds } from '../stripe/get-community-plan-prices'; + +export type IsCommunityPlanOptions = { + userId: number; + teamId?: number; +}; + +/** + * Whether the user or team is on the community plan. + */ +export const isCommunityPlan = async ({ + userId, + teamId, +}: IsCommunityPlanOptions): Promise => { + let subscriptions: Subscription[] = []; + + if (teamId) { + subscriptions = await prisma.team + .findFirstOrThrow({ + where: { + id: teamId, + }, + select: { + owner: { + include: { + subscriptions: true, + }, + }, + }, + }) + .then((team) => team.owner.subscriptions); + } else { + subscriptions = await prisma.user + .findFirstOrThrow({ + where: { + id: userId, + }, + select: { + subscriptions: true, + }, + }) + .then((user) => user.subscriptions); + } + + if (subscriptions.length === 0) { + return false; + } + + const communityPlanPriceIds = await getCommunityPlanPriceIds(); + + return subscriptionsContainsActivePlan(subscriptions, communityPlanPriceIds); +};