Files
documenso/packages/lib/server-only/branding/load-recipient-branding.ts
T
Lucas Smith 0b86ece1d5 feat: add custom branding for signing pages (#2785)
Platform-plan organisations and teams can now customise non-embed
signing pages with six brand colour tokens, a border-radius, and
a free-text custom CSS block (up to 256 KB).

- Stored on OrganisationGlobalSettings / TeamGlobalSettings;
  teams inherit from the org via brandingEnabled === null.
- CSS is sanitised on save (PostCSS) so we can inline it at SSR
  with no per-render parsing.
- Rendered via a nonce'd <style> scoped under .documenso-branded,
  using native CSS nesting so user selectors don't need scoping.
- Gated on the existing embedSigningWhiteLabel claim (or
  self-hosted) — reuses the embed white-label decision.
2026-05-11 13:03:02 +10:00

56 lines
1.9 KiB
TypeScript

import { IS_BILLING_ENABLED } from '../../constants/app';
import type { TCssVarsSchema } from '../../types/css-vars';
import { ZCssVarsSchema } from '../../types/css-vars';
import { getOrganisationClaimByTeamId } from '../organisation/get-organisation-claims';
import { getTeamSettings } from '../team/get-team-settings';
export type RecipientBrandingPayload = {
allowCustomBranding: boolean;
hidePoweredBy: boolean;
colors: TCssVarsSchema | null;
css: string | null;
};
/**
* Resolve the branding payload for a recipient-facing route, given the team
* the envelope/document belongs to. Reads inherited team-or-org branding settings,
* checks the org's claim flags, and returns a payload safe to send to the client.
*
* Returns a minimal disabled payload if the team is not on a plan that allows
* custom branding.
*/
export const loadRecipientBrandingByTeamId = async ({
teamId,
}: {
teamId: number;
}): Promise<RecipientBrandingPayload> => {
const billingEnabled = IS_BILLING_ENABLED();
const [settings, claim] = await Promise.all([
getTeamSettings({ teamId }),
billingEnabled ? getOrganisationClaimByTeamId({ teamId }).catch(() => null) : Promise.resolve(null),
]);
const allowCustomBranding = !billingEnabled || claim?.flags?.embedSigningWhiteLabel === true;
const hidePoweredBy = !billingEnabled || claim?.flags?.hidePoweredBy === true;
if (!allowCustomBranding) {
return {
allowCustomBranding: false,
hidePoweredBy,
colors: null,
css: null,
};
}
// brandingColors is stored as JSON; parse defensively. Drop unknown keys via Zod.
const parsedColors = settings.brandingColors ? ZCssVarsSchema.safeParse(settings.brandingColors) : null;
return {
allowCustomBranding: true,
hidePoweredBy,
colors: parsedColors?.success ? parsedColors.data : null,
css: settings.brandingCss && settings.brandingCss.length > 0 ? settings.brandingCss : null,
};
};