This commit is contained in:
David Nguyen
2025-02-03 14:10:28 +11:00
parent 28fb35327d
commit b2af10173a
141 changed files with 7340 additions and 394 deletions

View File

@ -7,7 +7,7 @@ import {
} from '@documenso/lib/constants/feature-flags';
export function useAnalytics() {
const featureFlags = useFeatureFlags();
// const featureFlags = useFeatureFlags();
const isPostHogEnabled = extractPostHogConfig();
/**
@ -30,27 +30,29 @@ export function useAnalytics() {
* @param eventFlag The event to check against feature flags to determine whether tracking is enabled.
*/
const startSessionRecording = (eventFlag?: string) => {
const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
const isSessionRecordingEnabledForEvent = Boolean(eventFlag && featureFlags.getFlag(eventFlag));
return;
// const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
// const isSessionRecordingEnabledForEvent = Boolean(eventFlag && featureFlags.getFlag(eventFlag));
if (!isPostHogEnabled || !isSessionRecordingEnabled || !isSessionRecordingEnabledForEvent) {
return;
}
// if (!isPostHogEnabled || !isSessionRecordingEnabled || !isSessionRecordingEnabledForEvent) {
// return;
// }
posthog.startSessionRecording();
// posthog.startSessionRecording();
};
/**
* Stop the current session recording.
*/
const stopSessionRecording = () => {
const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
return;
// const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
if (!isPostHogEnabled || !isSessionRecordingEnabled) {
return;
}
// if (!isPostHogEnabled || !isSessionRecordingEnabled) {
// return;
// }
posthog.stopSessionRecording();
// posthog.stopSessionRecording();
};
return {

View File

@ -5,14 +5,15 @@ import type { Session, User } from '@prisma/client';
interface AuthProviderProps {
children: React.ReactNode;
session: Session;
user: User;
session: DocumensoSession | null;
}
const SessionContext = createContext<{
export type DocumensoSession = {
user: User; // Todo: Exclude password
session: Session;
} | null>(null);
};
const SessionContext = createContext<DocumensoSession | null>(null);
export const useSession = () => {
const context = useContext(SessionContext);
@ -24,6 +25,15 @@ export const useSession = () => {
return context;
};
export const SessionProvider = ({ children, session, user }: AuthProviderProps) => {
return <SessionContext.Provider value={{ session, user }}>{children}</SessionContext.Provider>;
export const useOptionalSession = () => {
return (
useContext(SessionContext) || {
user: null,
session: null,
}
);
};
export const SessionProvider = ({ children, session }: AuthProviderProps) => {
return <SessionContext.Provider value={session}>{children}</SessionContext.Provider>;
};

View File

@ -3,21 +3,10 @@ import { env } from '@documenso/lib/utils/env';
export const APP_DOCUMENT_UPLOAD_SIZE_LIMIT =
Number(env('NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT')) || 50;
// Todo: env('NEXT_PUBLIC_WEBAPP_URL')
export const NEXT_PUBLIC_WEBAPP_URL = () => 'http://localhost:3000';
export const NEXT_PUBLIC_WEBAPP_URL = () =>
env('NEXT_PUBLIC_WEBAPP_URL') ?? 'http://localhost:3000';
export const NEXT_PUBLIC_MARKETING_URL = () => env('NEXT_PUBLIC_MARKETING_URL');
export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL =
env('NEXT_PRIVATE_INTERNAL_WEBAPP_URL') ?? NEXT_PUBLIC_WEBAPP_URL();
export const IS_APP_MARKETING = env('NEXT_PUBLIC_PROJECT') === 'marketing';
export const IS_APP_WEB = env('NEXT_PUBLIC_PROJECT') === 'web';
export const IS_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED') === 'true';
export const IS_APP_WEB_I18N_ENABLED = true;
export const APP_FOLDER = () => (IS_APP_MARKETING ? 'marketing' : 'web');
export const APP_BASE_URL = () =>
IS_APP_WEB ? NEXT_PUBLIC_WEBAPP_URL() : NEXT_PUBLIC_MARKETING_URL();
export const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000';
export const MARKETING_BASE_URL = NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001';

View File

@ -1,6 +1,6 @@
import { env } from '@documenso/lib/utils/env';
import { APP_BASE_URL, WEBAPP_BASE_URL } from './app';
import { NEXT_PUBLIC_WEBAPP_URL } from './app';
const NEXT_PUBLIC_FEATURE_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED');
const NEXT_PUBLIC_POSTHOG_KEY = () => env('NEXT_PUBLIC_POSTHOG_KEY');
@ -24,7 +24,7 @@ export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
app_allow_encrypted_documents: false,
app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true',
app_document_page_view_history_sheet: false,
app_passkey: WEBAPP_BASE_URL === 'http://localhost:3000', // Temp feature flag.
app_passkey: true,
app_public_profile: true,
marketing_header_single_player_mode: false,
marketing_profiles_announcement_bar: true,
@ -35,7 +35,7 @@ export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
*/
export function extractPostHogConfig(): { key: string; host: string } | null {
const postHogKey = NEXT_PUBLIC_POSTHOG_KEY();
const postHogHost = `${APP_BASE_URL()}/ingest`;
const postHogHost = `${NEXT_PUBLIC_WEBAPP_URL()}/ingest`;
if (!postHogKey || !postHogHost) {
return null;

View File

@ -1,4 +1,4 @@
import { APP_BASE_URL } from './app';
import { NEXT_PUBLIC_WEBAPP_URL } from './app';
export const DEFAULT_STANDARD_FONT_SIZE = 12;
export const DEFAULT_HANDWRITING_FONT_SIZE = 50;
@ -6,4 +6,4 @@ export const DEFAULT_HANDWRITING_FONT_SIZE = 50;
export const MIN_STANDARD_FONT_SIZE = 8;
export const MIN_HANDWRITING_FONT_SIZE = 20;
export const CAVEAT_FONT_PATH = () => `${APP_BASE_URL()}/fonts/caveat.ttf`;
export const CAVEAT_FONT_PATH = () => `${NEXT_PUBLIC_WEBAPP_URL()}/fonts/caveat.ttf`;

View File

@ -8,7 +8,7 @@ import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
@ -60,8 +60,8 @@ export const run = async ({
`send-team-member-joined-email--${invitedMember.id}_${member.id}`,
async () => {
const emailContent = createElement(TeamJoinEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
memberName: invitedMember.user.name || '',
memberEmail: invitedMember.user.email,
teamName: team.name,

View File

@ -8,7 +8,7 @@ import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
@ -50,8 +50,8 @@ export const run = async ({
for (const member of team.members) {
await io.runTask(`send-team-member-left-email--${oldMember.id}_${member.id}`, async () => {
const emailContent = createElement(TeamJoinEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
memberName: oldMember.name || '',
memberEmail: oldMember.email,
teamName: team.name,

View File

@ -1,6 +1,6 @@
import { getCheckoutSession } from '@documenso/ee/server-only/stripe/get-checkout-session';
import { getTeamPrices } from '@documenso/ee/server-only/stripe/get-team-prices';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
@ -32,7 +32,7 @@ export const createTeamPendingCheckoutSession = async ({
const stripeCheckoutSession = await getCheckoutSession({
customerId: teamPendingCreation.customerId,
priceId,
returnUrl: `${WEBAPP_BASE_URL}/settings/teams`,
returnUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/settings/teams`,
subscriptionMetadata: {
pendingTeamId: pendingTeamId.toString(),
},

View File

@ -7,7 +7,7 @@ import { z } from 'zod';
import { mailer } from '@documenso/email/mailer';
import { ConfirmTeamEmailTemplate } from '@documenso/email/templates/confirm-team-email';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@ -127,7 +127,7 @@ export const sendTeamEmailVerificationEmail = async (
const template = createElement(ConfirmTeamEmailTemplate, {
assetBaseUrl,
baseUrl: WEBAPP_BASE_URL,
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
teamName: team.name,
teamUrl: team.url,
token,

View File

@ -7,7 +7,7 @@ import { nanoid } from 'nanoid';
import { mailer } from '@documenso/email/mailer';
import { TeamInviteEmailTemplate } from '@documenso/email/templates/team-invite';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@ -154,8 +154,8 @@ export const sendTeamMemberInviteEmail = async ({
team,
}: SendTeamMemberInviteEmailOptions) => {
const template = createElement(TeamInviteEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
senderName,
token,
teamName: team.name,

View File

@ -4,7 +4,7 @@ import { msg } from '@lingui/macro';
import { mailer } from '@documenso/email/mailer';
import { TeamEmailRemovedTemplate } from '@documenso/email/templates/team-email-removed';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { prisma } from '@documenso/prisma';
@ -74,7 +74,7 @@ export const deleteTeamEmail = async ({ userId, userEmail, teamId }: DeleteTeamE
const template = createElement(TeamEmailRemovedTemplate, {
assetBaseUrl,
baseUrl: WEBAPP_BASE_URL,
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
teamEmail: team.teamEmail?.email ?? '',
teamName: team.name,
teamUrl: team.url,

View File

@ -5,7 +5,7 @@ import type { Team, TeamGlobalSettings } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import { TeamDeleteEmailTemplate } from '@documenso/email/templates/team-delete';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { AppError } from '@documenso/lib/errors/app-error';
import { stripe } from '@documenso/lib/server-only/stripe';
@ -96,8 +96,8 @@ type SendTeamDeleteEmailOptions = {
export const sendTeamDeleteEmail = async ({ email, isOwner, team }: SendTeamDeleteEmailOptions) => {
const template = createElement(TeamDeleteEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
teamUrl: team.url,
isOwner,
});

View File

@ -4,7 +4,7 @@ import { msg } from '@lingui/macro';
import { mailer } from '@documenso/email/mailer';
import { TeamTransferRequestTemplate } from '@documenso/email/templates/team-transfer-request';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { createTokenVerification } from '@documenso/lib/utils/token-verification';
import { prisma } from '@documenso/prisma';
@ -89,8 +89,8 @@ export const requestTeamOwnershipTransfer = async ({
});
const template = createElement(TeamTransferRequestTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
senderName: userName,
teamName: team.name,
teamUrl: team.url,

View File

@ -2,7 +2,7 @@ import { z } from 'zod';
import type { TFeatureFlagValue } from '@documenso/lib/client-only/providers/feature-flag.types';
import { ZFeatureFlagValueSchema } from '@documenso/lib/client-only/providers/feature-flag.types';
import { APP_BASE_URL } from '@documenso/lib/constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { LOCAL_FEATURE_FLAGS, isFeatureFlagEnabled } from '@documenso/lib/constants/feature-flags';
/**
@ -23,7 +23,7 @@ export const getFlag = async (
return LOCAL_FEATURE_FLAGS[flag] ?? true;
}
const url = new URL(`${APP_BASE_URL()}/api/feature-flag/get`);
const url = new URL(`${NEXT_PUBLIC_WEBAPP_URL()}/api/feature-flag/get`);
url.searchParams.set('flag', flag);
return await fetch(url, {
@ -58,7 +58,7 @@ export const getAllFlags = async (
return LOCAL_FEATURE_FLAGS;
}
const url = new URL(`${APP_BASE_URL()}/api/feature-flag/all`);
const url = new URL(`${NEXT_PUBLIC_WEBAPP_URL()}/api/feature-flag/all`);
return fetch(url, {
headers: {
@ -86,7 +86,7 @@ export const getAllAnonymousFlags = async (): Promise<Record<string, TFeatureFla
return LOCAL_FEATURE_FLAGS;
}
const url = new URL(`${APP_BASE_URL()}/api/feature-flag/all`);
const url = new URL(`${NEXT_PUBLIC_WEBAPP_URL()}/api/feature-flag/all`);
return fetch(url, {
next: {

View File

@ -10,7 +10,7 @@ import path from 'node:path';
import { env } from '@documenso/lib/utils/env';
import { APP_BASE_URL } from '../../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { ONE_HOUR, ONE_SECOND } from '../../constants/time';
import { alphaid } from '../id';
@ -22,7 +22,7 @@ export const getPresignPostUrl = async (fileName: string, contentType: string) =
const token: JWT | null = null;
try {
const baseUrl = APP_BASE_URL() ?? 'http://localhost:3000';
const baseUrl = NEXT_PUBLIC_WEBAPP_URL();
// Todo
// token = await getToken({

View File

@ -1,17 +1,17 @@
import { WEBAPP_BASE_URL } from '../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
import { PASSKEY_TIMEOUT } from '../constants/auth';
/**
* Extracts common fields to identify the RP (relying party)
*/
export const getAuthenticatorOptions = () => {
const webAppBaseUrl = new URL(WEBAPP_BASE_URL);
const webAppBaseUrl = new URL(NEXT_PUBLIC_WEBAPP_URL());
const rpId = webAppBaseUrl.hostname;
return {
rpName: 'Documenso',
rpId,
origin: WEBAPP_BASE_URL,
origin: NEXT_PUBLIC_WEBAPP_URL(),
timeout: PASSKEY_TIMEOUT,
};
};

View File

@ -2,7 +2,7 @@
type EnvironmentVariable = keyof NodeJS.ProcessEnv;
export const env = (variable: EnvironmentVariable | (string & {})) => {
export const env = (variable: EnvironmentVariable | (string & {})): string | undefined => {
// console.log({
// ['typeof window']: typeof window,
// ['process.env']: process.env,

View File

@ -2,7 +2,6 @@ import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension
import type { I18n, MessageDescriptor } from '@lingui/core';
import { IS_APP_WEB, IS_APP_WEB_I18N_ENABLED } from '../constants/app';
import type { I18nLocaleData, SupportedLanguageCodes } from '../constants/i18n';
import { APP_I18N_OPTIONS } from '../constants/i18n';
import { env } from './env';
@ -88,11 +87,6 @@ export const extractLocaleData = ({
lang = langHeader.lang;
}
// Override web app to be English.
if (!IS_APP_WEB_I18N_ENABLED && IS_APP_WEB) {
lang = 'en';
}
// Filter out locales that are not valid.
const locales = (langHeader?.locales ?? []).filter((locale) => {
try {

View File

@ -1,15 +1,15 @@
import { WEBAPP_BASE_URL } from '../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
export const formatUserProfilePath = (
profileUrl: string,
options: { excludeBaseUrl?: boolean } = {},
) => {
return `${!options?.excludeBaseUrl ? WEBAPP_BASE_URL : ''}/p/${profileUrl}`;
return `${!options?.excludeBaseUrl ? NEXT_PUBLIC_WEBAPP_URL() : ''}/p/${profileUrl}`;
};
export const formatTeamProfilePath = (
profileUrl: string,
options: { excludeBaseUrl?: boolean } = {},
) => {
return `${!options?.excludeBaseUrl ? WEBAPP_BASE_URL : ''}/p/${profileUrl}`;
return `${!options?.excludeBaseUrl ? NEXT_PUBLIC_WEBAPP_URL() : ''}/p/${profileUrl}`;
};

View File

@ -1,9 +1,9 @@
import { WEBAPP_BASE_URL } from '../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
import type { TEAM_MEMBER_ROLE_MAP } from '../constants/teams';
import { TEAM_MEMBER_ROLE_HIERARCHY, TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../constants/teams';
export const formatTeamUrl = (teamUrl: string, baseUrl?: string) => {
const formattedBaseUrl = (baseUrl ?? WEBAPP_BASE_URL).replace(/https?:\/\//, '');
const formattedBaseUrl = (baseUrl ?? NEXT_PUBLIC_WEBAPP_URL()).replace(/https?:\/\//, '');
return `${formattedBaseUrl}/t/${teamUrl}`;
};

View File

@ -1,9 +1,9 @@
import type { Recipient } from '@prisma/client';
import { WEBAPP_BASE_URL } from '../constants/app';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
export const formatDirectTemplatePath = (token: string) => {
return `${WEBAPP_BASE_URL}/d/${token}`;
return `${NEXT_PUBLIC_WEBAPP_URL()}/d/${token}`;
};
/**