From d451a7accecc19bfdf1be559e4c5af79c3e14002 Mon Sep 17 00:00:00 2001 From: Catalin Pit <25515812+catalinpit@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:48:20 +0200 Subject: [PATCH 1/5] feat: add next-runtime-env --- .../(dashboard)/documents/[id]/edit-document.tsx | 2 +- .../billing/create-billing-portal.action.ts | 6 +++++- .../settings/billing/create-checkout.action.ts | 8 ++++++-- .../app/(share)/share/[slug]/opengraph/route.tsx | 5 ++++- apps/web/src/app/(share)/share/[slug]/page.tsx | 8 ++++++-- .../src/app/(unauthenticated)/signin/page.tsx | 6 +++++- .../src/app/(unauthenticated)/signup/page.tsx | 6 +++++- apps/web/src/app/layout.tsx | 9 +++++++-- .../(dashboard)/avatar/avatar-with-recipient.tsx | 6 +++++- apps/web/src/helpers/get-asset-buffer.ts | 6 +++++- package-lock.json | 16 ++++++++++++++++ package.json | 4 +++- packages/ee/server-only/limits/client.ts | 5 +++-- .../template-document-self-signed.tsx | 6 +++++- .../template-reset-password.tsx | 6 +++++- packages/lib/constants/app.ts | 14 +++++++++----- packages/lib/constants/feature-flags.ts | 9 +++++++-- packages/lib/next-auth/auth-options.ts | 5 ++++- .../server-only/auth/send-confirmation-email.ts | 12 +++++++++--- .../lib/server-only/auth/send-forgot-password.ts | 8 ++++++-- .../lib/server-only/auth/send-reset-password.ts | 6 +++++- .../lib/server-only/document/delete-document.ts | 6 +++++- .../lib/server-only/document/resend-document.tsx | 8 ++++++-- .../server-only/document/send-completed-email.ts | 8 ++++++-- .../lib/server-only/document/send-document.tsx | 8 ++++++-- .../server-only/document/send-pending-email.ts | 6 +++++- packages/lib/server-only/feature-flags/all.ts | 8 ++++++-- packages/lib/server-only/feature-flags/get.ts | 11 ++++++++--- packages/lib/universal/get-base-url.ts | 8 ++++++-- packages/lib/universal/upload/put-file.ts | 5 ++++- packages/lib/universal/upload/server-actions.ts | 5 ++++- packages/trpc/server/auth-router/router.ts | 5 ++++- .../trpc/server/singleplayer-router/router.ts | 5 ++++- .../document/document-share-button.tsx | 12 +++++++----- 34 files changed, 192 insertions(+), 56 deletions(-) diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx index 2159b87f2..832e255fd 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx @@ -61,7 +61,7 @@ export const EditDocumentForm = ({ const { mutateAsync: setPasswordForDocument } = trpc.document.setPasswordForDocument.useMutation(); - const documentFlow: Record = { + const documentFlow: Record = { title: { title: 'Add Title', description: 'Add the title to the document.', diff --git a/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts b/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts index 885414515..7d952d599 100644 --- a/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts +++ b/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts @@ -1,16 +1,20 @@ 'use server'; +import { env } from 'next-runtime-env'; + import { getStripeCustomerByUser } from '@documenso/ee/server-only/stripe/get-customer'; import { getPortalSession } from '@documenso/ee/server-only/stripe/get-portal-session'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; export const createBillingPortal = async () => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const { user } = await getRequiredServerComponentSession(); const { stripeCustomer } = await getStripeCustomerByUser(user); return getPortalSession({ customerId: stripeCustomer.id, - returnUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, }); }; diff --git a/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts b/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts index f8f20030c..ef3fb0f30 100644 --- a/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts +++ b/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts @@ -1,5 +1,7 @@ 'use server'; +import { env } from 'next-runtime-env'; + import { getCheckoutSession } from '@documenso/ee/server-only/stripe/get-checkout-session'; import { getStripeCustomerByUser } from '@documenso/ee/server-only/stripe/get-customer'; import { getPortalSession } from '@documenso/ee/server-only/stripe/get-portal-session'; @@ -11,6 +13,8 @@ export type CreateCheckoutOptions = { }; export const createCheckout = async ({ priceId }: CreateCheckoutOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const session = await getRequiredServerComponentSession(); const { user, stripeCustomer } = await getStripeCustomerByUser(session.user); @@ -27,13 +31,13 @@ export const createCheckout = async ({ priceId }: CreateCheckoutOptions) => { if (foundSubscription) { return getPortalSession({ customerId: stripeCustomer.id, - returnUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, }); } return getCheckoutSession({ customerId: stripeCustomer.id, priceId, - returnUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, }); }; diff --git a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx index d8f0ecac8..e9977b8f3 100644 --- a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx +++ b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx @@ -1,6 +1,7 @@ import { ImageResponse } from 'next/og'; import { NextResponse } from 'next/server'; +import { env } from 'next-runtime-env'; import { P, match } from 'ts-pattern'; import type { ShareHandlerAPIResponse } from '~/pages/api/share'; @@ -22,6 +23,8 @@ type SharePageOpenGraphImageProps = { }; export async function GET(_request: Request, { params: { slug } }: SharePageOpenGraphImageProps) { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const [interSemiBold, interRegular, caveatRegular, shareFrameImage] = await Promise.all([ fetch(new URL('@documenso/assets/fonts/inter-semibold.ttf', import.meta.url)).then( async (res) => res.arrayBuffer(), @@ -37,7 +40,7 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen ), ]); - const baseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const baseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const recipientOrSender: ShareHandlerAPIResponse = await fetch( new URL(`/api/share?slug=${slug}`, baseUrl), diff --git a/apps/web/src/app/(share)/share/[slug]/page.tsx b/apps/web/src/app/(share)/share/[slug]/page.tsx index 8e8fd7769..ab530ba0b 100644 --- a/apps/web/src/app/(share)/share/[slug]/page.tsx +++ b/apps/web/src/app/(share)/share/[slug]/page.tsx @@ -1,7 +1,9 @@ -import { Metadata } from 'next'; +import type { Metadata } from 'next'; import { headers } from 'next/headers'; import { redirect } from 'next/navigation'; +import { env } from 'next-runtime-env'; + import { APP_BASE_URL } from '@documenso/lib/constants/app'; type SharePageProps = { @@ -28,6 +30,8 @@ export function generateMetadata({ params: { slug } }: SharePageProps) { } export default function SharePage() { + const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); + const userAgent = headers().get('User-Agent') ?? ''; // https://stackoverflow.com/questions/47026171/how-to-detect-bots-for-open-graph-with-user-agent @@ -35,5 +39,5 @@ export default function SharePage() { return null; } - redirect(process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001'); + redirect(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001'); } diff --git a/apps/web/src/app/(unauthenticated)/signin/page.tsx b/apps/web/src/app/(unauthenticated)/signin/page.tsx index 5fda07e70..bbea70ecb 100644 --- a/apps/web/src/app/(unauthenticated)/signin/page.tsx +++ b/apps/web/src/app/(unauthenticated)/signin/page.tsx @@ -1,10 +1,14 @@ import Link from 'next/link'; +import { env } from 'next-runtime-env'; + import { IS_GOOGLE_SSO_ENABLED } from '@documenso/lib/constants/auth'; import { SignInForm } from '~/components/forms/signin'; export default function SignInPage() { + const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); + return (

Sign in to your account

@@ -15,7 +19,7 @@ export default function SignInPage() { - {process.env.NEXT_PUBLIC_DISABLE_SIGNUP !== 'true' && ( + {NEXT_PUBLIC_DISABLE_SIGNUP !== 'true' && (

Don't have an account?{' '} diff --git a/apps/web/src/app/(unauthenticated)/signup/page.tsx b/apps/web/src/app/(unauthenticated)/signup/page.tsx index 353716d9b..996797d55 100644 --- a/apps/web/src/app/(unauthenticated)/signup/page.tsx +++ b/apps/web/src/app/(unauthenticated)/signup/page.tsx @@ -1,10 +1,14 @@ import Link from 'next/link'; import { redirect } from 'next/navigation'; +import { env } from 'next-runtime-env'; + import { SignUpForm } from '~/components/forms/signup'; export default function SignUpPage() { - if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { + const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); + + if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { redirect('/signin'); } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index ac88469b0..f36d59353 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,6 +2,8 @@ import { Suspense } from 'react'; import { Caveat, Inter } from 'next/font/google'; +import { PublicEnvScript, env } from 'next-runtime-env'; + import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag'; import { LocaleProvider } from '@documenso/lib/client-only/providers/locale'; import { getServerComponentAllFlags } from '@documenso/lib/server-only/feature-flags/get-server-component-feature-flag'; @@ -19,6 +21,8 @@ import './globals.css'; const fontInter = Inter({ subsets: ['latin'], variable: '--font-sans' }); const fontCaveat = Caveat({ subsets: ['latin'], variable: '--font-signature' }); +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + export const metadata = { title: 'Documenso - The Open Source DocuSign Alternative', description: @@ -32,12 +36,12 @@ export const metadata = { description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', type: 'website', - images: [`${process.env.NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], + images: [`${NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${process.env.NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], + images: [`${NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', }, @@ -59,6 +63,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo + diff --git a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx index d04b3a998..727627ddd 100644 --- a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx +++ b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx @@ -2,6 +2,8 @@ import React from 'react'; +import { env } from 'next-runtime-env'; + import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard'; import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; @@ -16,6 +18,8 @@ export type AvatarWithRecipientProps = { }; export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const [, copy] = useCopyToClipboard(); const { toast } = useToast(); @@ -24,7 +28,7 @@ export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { return; } - void copy(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`).then(() => { + void copy(`${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`).then(() => { toast({ title: 'Copied to clipboard', description: 'The signing link has been copied to your clipboard.', diff --git a/apps/web/src/helpers/get-asset-buffer.ts b/apps/web/src/helpers/get-asset-buffer.ts index 85887071e..12c27d1a5 100644 --- a/apps/web/src/helpers/get-asset-buffer.ts +++ b/apps/web/src/helpers/get-asset-buffer.ts @@ -1,3 +1,5 @@ +import { env } from 'next-runtime-env'; + /** * getAssetBuffer is used to retrieve array buffers for various assets * that are hosted in the `public` folder. @@ -8,7 +10,9 @@ * @param path The path to the asset, relative to the `public` folder. */ export const getAssetBuffer = async (path: string) => { - const baseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + + const baseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; return fetch(new URL(path, baseUrl)).then(async (res) => res.arrayBuffer()); }; diff --git a/package-lock.json b/package-lock.json index 69825e8d8..d92461436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "apps/*", "packages/*" ], + "dependencies": { + "next-runtime-env": "^3.2.0" + }, "devDependencies": { "@commitlint/cli": "^17.7.1", "@commitlint/config-conventional": "^17.7.0", @@ -14503,6 +14506,19 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/next-runtime-env": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/next-runtime-env/-/next-runtime-env-3.2.0.tgz", + "integrity": "sha512-rwe3flUgSRm51hzRN4Vt5MMSYMS4aDMEPJa0r+CMONA3UyUZl8Y5O8zjHSIlaNb3yquTCttZ0ahObPyPprBj9g==", + "dependencies": { + "next": "^14", + "react": "^18" + }, + "peerDependencies": { + "next": "^14", + "react": "^18" + } + }, "node_modules/next-themes": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", diff --git a/package.json b/package.json index 30076100f..853de1c6b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,9 @@ "apps/*", "packages/*" ], - "dependencies": {}, + "dependencies": { + "next-runtime-env": "^3.2.0" + }, "overrides": { "next-auth": { "next": "14.0.3" diff --git a/packages/ee/server-only/limits/client.ts b/packages/ee/server-only/limits/client.ts index 7f48e6856..9bb2bd0ac 100644 --- a/packages/ee/server-only/limits/client.ts +++ b/packages/ee/server-only/limits/client.ts @@ -1,7 +1,8 @@ import { APP_BASE_URL } from '@documenso/lib/constants/app'; import { FREE_PLAN_LIMITS } from './constants'; -import { TLimitsResponseSchema, ZLimitsResponseSchema } from './schema'; +import type { TLimitsResponseSchema } from './schema'; +import { ZLimitsResponseSchema } from './schema'; export type GetLimitsOptions = { headers?: Record; @@ -10,7 +11,7 @@ export type GetLimitsOptions = { export const getLimits = async ({ headers }: GetLimitsOptions = {}) => { const requestHeaders = headers ?? {}; - const url = new URL(`${APP_BASE_URL}/api/limits`); + const url = new URL('/api/limits', APP_BASE_URL ?? 'http://localhost:3000'); return fetch(url, { headers: { diff --git a/packages/email/template-components/template-document-self-signed.tsx b/packages/email/template-components/template-document-self-signed.tsx index 90a1d3951..db16fb000 100644 --- a/packages/email/template-components/template-document-self-signed.tsx +++ b/packages/email/template-components/template-document-self-signed.tsx @@ -1,3 +1,5 @@ +import { env } from 'next-runtime-env'; + import { Button, Column, Img, Link, Section, Text } from '../components'; import { TemplateDocumentImage } from './template-document-image'; @@ -10,7 +12,9 @@ export const TemplateDocumentSelfSigned = ({ documentName, assetBaseUrl, }: TemplateDocumentSelfSignedProps) => { - const signUpUrl = `${process.env.NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000'}/signup`; + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + + const signUpUrl = `${NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000'}/signup`; const getAssetUrl = (path: string) => { return new URL(path, assetBaseUrl).toString(); diff --git a/packages/email/template-components/template-reset-password.tsx b/packages/email/template-components/template-reset-password.tsx index 8788d60b8..d05393c83 100644 --- a/packages/email/template-components/template-reset-password.tsx +++ b/packages/email/template-components/template-reset-password.tsx @@ -1,3 +1,5 @@ +import { env } from 'next-runtime-env'; + import { Button, Section, Text } from '../components'; import { TemplateDocumentImage } from './template-document-image'; @@ -8,6 +10,8 @@ export interface TemplateResetPasswordProps { } export const TemplateResetPassword = ({ assetBaseUrl }: TemplateResetPasswordProps) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + return ( <> @@ -24,7 +28,7 @@ export const TemplateResetPassword = ({ assetBaseUrl }: TemplateResetPasswordPro

diff --git a/packages/lib/constants/app.ts b/packages/lib/constants/app.ts index 827fcef0a..cee5a7586 100644 --- a/packages/lib/constants/app.ts +++ b/packages/lib/constants/app.ts @@ -1,8 +1,12 @@ -export const IS_APP_MARKETING = process.env.NEXT_PUBLIC_PROJECT === 'marketing'; -export const IS_APP_WEB = process.env.NEXT_PUBLIC_PROJECT === 'web'; +import { env } from 'next-runtime-env'; + +const NEXT_PUBLIC_PROJECT = process.env.NEXT_PUBLIC_PROJECT; +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); +const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); + +export const IS_APP_MARKETING = NEXT_PUBLIC_PROJECT === 'marketing'; +export const IS_APP_WEB = NEXT_PUBLIC_PROJECT === 'web'; export const APP_FOLDER = IS_APP_MARKETING ? 'marketing' : 'web'; -export const APP_BASE_URL = IS_APP_WEB - ? process.env.NEXT_PUBLIC_WEBAPP_URL - : process.env.NEXT_PUBLIC_MARKETING_URL; +export const APP_BASE_URL = IS_APP_WEB ? NEXT_PUBLIC_WEBAPP_URL : NEXT_PUBLIC_MARKETING_URL; diff --git a/packages/lib/constants/feature-flags.ts b/packages/lib/constants/feature-flags.ts index e972b47c2..a0e958e3a 100644 --- a/packages/lib/constants/feature-flags.ts +++ b/packages/lib/constants/feature-flags.ts @@ -1,5 +1,10 @@ +import { env } from 'next-runtime-env'; + import { APP_BASE_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'); + /** * The flag name for global session recording feature flag. */ @@ -16,7 +21,7 @@ export const FEATURE_FLAG_POLL_INTERVAL = 30000; * Does not take any person or group properties into account. */ export const LOCAL_FEATURE_FLAGS: Record = { - app_billing: process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true', + app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true', marketing_header_single_player_mode: false, } as const; @@ -24,7 +29,7 @@ export const LOCAL_FEATURE_FLAGS: Record = { * Extract the PostHog configuration from the environment. */ export function extractPostHogConfig(): { key: string; host: string } | null { - const postHogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY; + const postHogKey = NEXT_PUBLIC_POSTHOG_KEY; const postHogHost = `${APP_BASE_URL}/ingest`; if (!postHogKey || !postHogHost) { diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index 3b9492807..dc5f1b6f4 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -7,6 +7,7 @@ import type { JWT } from 'next-auth/jwt'; import CredentialsProvider from 'next-auth/providers/credentials'; import type { GoogleProfile } from 'next-auth/providers/google'; import GoogleProvider from 'next-auth/providers/google'; +import { env } from 'next-runtime-env'; import { prisma } from '@documenso/prisma'; @@ -15,6 +16,8 @@ import { validateTwoFactorAuthentication } from '../server-only/2fa/validate-2fa import { getUserByEmail } from '../server-only/user/get-user-by-email'; import { ErrorCode } from './error-codes'; +const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); + export const NEXT_AUTH_OPTIONS: AuthOptions = { adapter: PrismaAdapter(prisma), secret: process.env.NEXTAUTH_SECRET ?? 'secret', @@ -166,7 +169,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { async signIn({ user }) { // We do this to stop OAuth providers from creating an account // when signups are disabled - if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { + if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { const userData = await getUserByEmail({ email: user.email! }); return !!userData; diff --git a/packages/lib/server-only/auth/send-confirmation-email.ts b/packages/lib/server-only/auth/send-confirmation-email.ts index 7defdb1bd..c808e8f3d 100644 --- a/packages/lib/server-only/auth/send-confirmation-email.ts +++ b/packages/lib/server-only/auth/send-confirmation-email.ts @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ConfirmEmailTemplate } from '@documenso/email/templates/confirm-email'; @@ -10,6 +12,10 @@ export interface SendConfirmationEmailProps { } export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailProps) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const NEXT_PRIVATE_SMTP_FROM_NAME = env('NEXT_PRIVATE_SMTP_FROM_NAME'); + const NEXT_PRIVATE_SMTP_FROM_ADDRESS = env('NEXT_PRIVATE_SMTP_FROM_ADDRESS'); + const user = await prisma.user.findFirstOrThrow({ where: { id: userId, @@ -30,10 +36,10 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro throw new Error('Verification token not found for the user'); } - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const confirmationLink = `${assetBaseUrl}/verify-email/${verificationToken.token}`; - const senderName = process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso'; - const senderAdress = process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com'; + const senderName = NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso'; + const senderAdress = NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com'; const confirmationTemplate = createElement(ConfirmEmailTemplate, { assetBaseUrl, diff --git a/packages/lib/server-only/auth/send-forgot-password.ts b/packages/lib/server-only/auth/send-forgot-password.ts index e62d5e176..125db9338 100644 --- a/packages/lib/server-only/auth/send-forgot-password.ts +++ b/packages/lib/server-only/auth/send-forgot-password.ts @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ForgotPasswordTemplate } from '@documenso/email/templates/forgot-password'; @@ -10,6 +12,8 @@ export interface SendForgotPasswordOptions { } export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const user = await prisma.user.findFirstOrThrow({ where: { id: userId, @@ -29,8 +33,8 @@ export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions) } const token = user.PasswordResetToken[0].token; - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const resetPasswordLink = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/reset-password/${token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const resetPasswordLink = `${NEXT_PUBLIC_WEBAPP_URL}/reset-password/${token}`; const template = createElement(ForgotPasswordTemplate, { assetBaseUrl, diff --git a/packages/lib/server-only/auth/send-reset-password.ts b/packages/lib/server-only/auth/send-reset-password.ts index 9479f1a45..3dcec113c 100644 --- a/packages/lib/server-only/auth/send-reset-password.ts +++ b/packages/lib/server-only/auth/send-reset-password.ts @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ResetPasswordTemplate } from '@documenso/email/templates/reset-password'; @@ -10,13 +12,15 @@ export interface SendResetPasswordOptions { } export const sendResetPassword = async ({ userId }: SendResetPasswordOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const user = await prisma.user.findFirstOrThrow({ where: { id: userId, }, }); - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(ResetPasswordTemplate, { assetBaseUrl, diff --git a/packages/lib/server-only/document/delete-document.ts b/packages/lib/server-only/document/delete-document.ts index 22365a727..58b1f48a9 100644 --- a/packages/lib/server-only/document/delete-document.ts +++ b/packages/lib/server-only/document/delete-document.ts @@ -2,6 +2,8 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import DocumentCancelTemplate from '@documenso/email/templates/document-cancel'; @@ -16,6 +18,8 @@ export type DeleteDocumentOptions = { status: DocumentStatus; }; +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + export const deleteDocument = async ({ id, userId, status }: DeleteDocumentOptions) => { // if the document is a draft, hard-delete if (status === DocumentStatus.DRAFT) { @@ -49,7 +53,7 @@ export const deleteDocument = async ({ id, userId, status }: DeleteDocumentOptio if (document.Recipient.length > 0) { await Promise.all( document.Recipient.map(async (recipient) => { - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(DocumentCancelTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/resend-document.tsx b/packages/lib/server-only/document/resend-document.tsx index da4ffcb58..a048cf600 100644 --- a/packages/lib/server-only/document/resend-document.tsx +++ b/packages/lib/server-only/document/resend-document.tsx @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite'; @@ -14,6 +16,8 @@ export type ResendDocumentOptions = { recipients: number[]; }; +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + export const resendDocument = async ({ documentId, userId, recipients }: ResendDocumentOptions) => { const user = await prisma.user.findFirstOrThrow({ where: { @@ -67,8 +71,8 @@ export const resendDocument = async ({ documentId, userId, recipients }: ResendD 'document.name': document.title, }; - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const signDocumentLink = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; const template = createElement(DocumentInviteEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index 226ff43ec..9b624a23c 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed'; @@ -12,6 +14,8 @@ export interface SendDocumentOptions { } export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const document = await prisma.document.findUnique({ where: { id: documentId, @@ -36,12 +40,12 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => document.Recipient.map(async (recipient) => { const { email, name, token } = recipient; - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(DocumentCompletedEmailTemplate, { documentName: document.title, assetBaseUrl, - downloadLink: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${token}/complete`, + downloadLink: `${NEXT_PUBLIC_WEBAPP_URL}/sign/${token}/complete`, }); await mailer.sendMail({ diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index 25dc132ba..f72c79792 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite'; @@ -14,6 +16,8 @@ export type SendDocumentOptions = { }; export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const user = await prisma.user.findFirstOrThrow({ where: { id: userId, @@ -59,8 +63,8 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) return; } - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const signDocumentLink = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; const template = createElement(DocumentInviteEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/send-pending-email.ts b/packages/lib/server-only/document/send-pending-email.ts index 75861be78..abbecd72a 100644 --- a/packages/lib/server-only/document/send-pending-email.ts +++ b/packages/lib/server-only/document/send-pending-email.ts @@ -1,5 +1,7 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; + import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentPendingEmailTemplate } from '@documenso/email/templates/document-pending'; @@ -11,6 +13,8 @@ export interface SendPendingEmailOptions { } export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingEmailOptions) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const document = await prisma.document.findFirst({ where: { id: documentId, @@ -41,7 +45,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE const { email, name } = recipient; - const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(DocumentPendingEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/feature-flags/all.ts b/packages/lib/server-only/feature-flags/all.ts index 40e759221..fff6cd855 100644 --- a/packages/lib/server-only/feature-flags/all.ts +++ b/packages/lib/server-only/feature-flags/all.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getToken } from 'next-auth/jwt'; +import { env } from 'next-runtime-env'; import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags'; import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; @@ -11,6 +12,9 @@ import { extractDistinctUserId, mapJwtToFlagProperties } from './get'; * Get all the evaluated feature flags based on the current user if possible. */ export default async function handlerFeatureFlagAll(req: Request) { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); + const requestHeaders = Object.fromEntries(req.headers.entries()); const nextReq = new NextRequest(req, { @@ -38,11 +42,11 @@ export default async function handlerFeatureFlagAll(req: Request) { const origin = req.headers.get('origin'); if (origin) { - if (origin.startsWith(process.env.NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { + if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { res.headers.set('Access-Control-Allow-Origin', origin); } - if (origin.startsWith(process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { + if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { res.headers.set('Access-Control-Allow-Origin', origin); } } diff --git a/packages/lib/server-only/feature-flags/get.ts b/packages/lib/server-only/feature-flags/get.ts index 36aafc7b7..6f8a2c011 100644 --- a/packages/lib/server-only/feature-flags/get.ts +++ b/packages/lib/server-only/feature-flags/get.ts @@ -1,7 +1,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { nanoid } from 'nanoid'; -import { JWT, getToken } from 'next-auth/jwt'; +import type { JWT } from 'next-auth/jwt'; +import { getToken } from 'next-auth/jwt'; +import { env } from 'next-runtime-env'; import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags'; import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; @@ -13,6 +15,9 @@ import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-po * @returns A Response with the feature flag value. */ export default async function handleFeatureFlagGet(req: Request) { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); + const { searchParams } = new URL(req.url ?? ''); const flag = searchParams.get('flag'); @@ -57,11 +62,11 @@ export default async function handleFeatureFlagGet(req: Request) { const origin = req.headers.get('Origin'); if (origin) { - if (origin.startsWith(process.env.NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { + if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { res.headers.set('Access-Control-Allow-Origin', origin); } - if (origin.startsWith(process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { + if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { res.headers.set('Access-Control-Allow-Origin', origin); } } diff --git a/packages/lib/universal/get-base-url.ts b/packages/lib/universal/get-base-url.ts index 2120c9f54..b7c9d4ea4 100644 --- a/packages/lib/universal/get-base-url.ts +++ b/packages/lib/universal/get-base-url.ts @@ -1,4 +1,8 @@ /* eslint-disable turbo/no-undeclared-env-vars */ +import { env } from 'next-runtime-env'; + +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + export const getBaseUrl = () => { if (typeof window !== 'undefined') { return ''; @@ -8,8 +12,8 @@ export const getBaseUrl = () => { return `https://${process.env.VERCEL_URL}`; } - if (process.env.NEXT_PUBLIC_WEBAPP_URL) { - return process.env.NEXT_PUBLIC_WEBAPP_URL; + if (NEXT_PUBLIC_WEBAPP_URL) { + return NEXT_PUBLIC_WEBAPP_URL; } return `http://localhost:${process.env.PORT ?? 3000}`; diff --git a/packages/lib/universal/upload/put-file.ts b/packages/lib/universal/upload/put-file.ts index c54ccef77..498bed3bd 100644 --- a/packages/lib/universal/upload/put-file.ts +++ b/packages/lib/universal/upload/put-file.ts @@ -1,4 +1,5 @@ import { base64 } from '@scure/base'; +import { env } from 'next-runtime-env'; import { match } from 'ts-pattern'; import { DocumentDataType } from '@documenso/prisma/client'; @@ -11,8 +12,10 @@ type File = { arrayBuffer: () => Promise; }; +const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); + export const putFile = async (file: File) => { - const { type, data } = await match(process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT) + const { type, data } = await match(NEXT_PUBLIC_UPLOAD_TRANSPORT) .with('s3', async () => putFileInS3(file)) .otherwise(async () => putFileInDatabase(file)); diff --git a/packages/lib/universal/upload/server-actions.ts b/packages/lib/universal/upload/server-actions.ts index 69274c30c..61429cd86 100644 --- a/packages/lib/universal/upload/server-actions.ts +++ b/packages/lib/universal/upload/server-actions.ts @@ -11,12 +11,15 @@ import { } from '@aws-sdk/client-s3'; import slugify from '@sindresorhus/slugify'; import { type JWT, getToken } from 'next-auth/jwt'; +import { env } from 'next-runtime-env'; import path from 'node:path'; import { APP_BASE_URL } from '../../constants/app'; import { ONE_HOUR, ONE_SECOND } from '../../constants/time'; import { alphaid } from '../id'; +const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); + export const getPresignPostUrl = async (fileName: string, contentType: string) => { const client = getS3Client(); @@ -117,7 +120,7 @@ export const deleteS3File = async (key: string) => { }; const getS3Client = () => { - if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') { + if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') { throw new Error('Invalid upload transport'); } diff --git a/packages/trpc/server/auth-router/router.ts b/packages/trpc/server/auth-router/router.ts index 24dd272ee..0debd7f8d 100644 --- a/packages/trpc/server/auth-router/router.ts +++ b/packages/trpc/server/auth-router/router.ts @@ -1,4 +1,5 @@ import { TRPCError } from '@trpc/server'; +import { env } from 'next-runtime-env'; import { ErrorCode } from '@documenso/lib/next-auth/error-codes'; import { compareSync } from '@documenso/lib/server-only/auth/hash'; @@ -8,10 +9,12 @@ import { sendConfirmationToken } from '@documenso/lib/server-only/user/send-conf import { authenticatedProcedure, procedure, router } from '../trpc'; import { ZSignUpMutationSchema, ZVerifyPasswordMutationSchema } from './schema'; +const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); + export const authRouter = router({ signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => { try { - if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { + if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { throw new TRPCError({ code: 'BAD_REQUEST', message: 'Signups are disabled.', diff --git a/packages/trpc/server/singleplayer-router/router.ts b/packages/trpc/server/singleplayer-router/router.ts index 8e2266fcc..cb92aa7a4 100644 --- a/packages/trpc/server/singleplayer-router/router.ts +++ b/packages/trpc/server/singleplayer-router/router.ts @@ -1,5 +1,6 @@ import { createElement } from 'react'; +import { env } from 'next-runtime-env'; import { PDFDocument } from 'pdf-lib'; import { mailer } from '@documenso/email/mailer'; @@ -24,6 +25,8 @@ import { procedure, router } from '../trpc'; import { mapField } from './helper'; import { ZCreateSinglePlayerDocumentMutationSchema } from './schema'; +const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); + export const singleplayerRouter = router({ createSinglePlayerDocument: procedure .input(ZCreateSinglePlayerDocumentMutationSchema) @@ -148,7 +151,7 @@ export const singleplayerRouter = router({ const template = createElement(DocumentSelfSignedEmailTemplate, { documentName: documentName, - assetBaseUrl: process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000', + assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000', }); const [html, text] = await Promise.all([ diff --git a/packages/ui/components/document/document-share-button.tsx b/packages/ui/components/document/document-share-button.tsx index b366123fb..f304d92c0 100644 --- a/packages/ui/components/document/document-share-button.tsx +++ b/packages/ui/components/document/document-share-button.tsx @@ -4,6 +4,7 @@ import type { HTMLAttributes } from 'react'; import React, { useState } from 'react'; import { Copy, Sparkles } from 'lucide-react'; +import { env } from 'next-runtime-env'; import { FaXTwitter } from 'react-icons/fa6'; import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link'; @@ -38,6 +39,7 @@ export const DocumentShareButton = ({ className, trigger, }: DocumentShareButtonProps) => { + const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); const { toast } = useToast(); const { copyShareLink, createAndCopyShareLink, isCopyingShareLink } = useCopyShareLink({ @@ -68,7 +70,7 @@ export const DocumentShareButton = ({ const onCopyClick = async () => { if (shareLink) { - await copyShareLink(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${shareLink.slug}`); + await copyShareLink(`${NEXT_PUBLIC_WEBAPP_URL}/share/${shareLink.slug}`); } else { await createAndCopyShareLink({ token, @@ -92,7 +94,7 @@ export const DocumentShareButton = ({ } // Ensuring we've prewarmed the opengraph image for the Twitter - await fetch(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}/opengraph`, { + await fetch(`${NEXT_PUBLIC_WEBAPP_URL}/share/${slug}/opengraph`, { // We don't care about the response, so we can use no-cors mode: 'no-cors', }); @@ -100,7 +102,7 @@ export const DocumentShareButton = ({ window.open( generateTwitterIntent( `I just ${token ? 'signed' : 'sent'} a document in style with @documenso. Check it out!`, - `${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`, + `${NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`, ), '_blank', ); @@ -148,7 +150,7 @@ export const DocumentShareButton = ({ 'animate-pulse': !shareLink?.slug, })} > - {process.env.NEXT_PUBLIC_WEBAPP_URL}/share/{shareLink?.slug || '...'} + {NEXT_PUBLIC_WEBAPP_URL}/share/{shareLink?.slug || '...'}
{shareLink?.slug && ( sharing link From 08f82b23dcc78f309f648785574cfe30eb0e2387 Mon Sep 17 00:00:00 2001 From: Mythie Date: Wed, 31 Jan 2024 22:32:42 +1100 Subject: [PATCH 2/5] fix: update env entries to evaluate at runtime --- .../src/app/(marketing)/claimed/page.tsx | 7 ++----- .../src/app/(marketing)/singleplayer/client.tsx | 3 ++- .../src/components/(marketing)/pricing-table.tsx | 9 +++------ .../single-player-mode-success.tsx | 3 ++- .../src/components/(marketing)/widget.tsx | 7 ++++++- apps/marketing/src/pages/api/claim-plan/index.ts | 14 ++++++++------ .../(dashboard)/documents/[id]/edit-document.tsx | 2 +- .../billing/create-billing-portal.action.ts | 7 ++----- .../settings/billing/create-checkout.action.ts | 9 +++------ .../app/(share)/share/[slug]/opengraph/route.tsx | 7 +++---- apps/web/src/app/(share)/share/[slug]/page.tsx | 12 ++++-------- apps/web/src/app/layout.tsx | 9 ++++----- .../(dashboard)/avatar/avatar-with-recipient.tsx | 7 ++----- apps/web/src/helpers/get-asset-buffer.ts | 6 ++---- packages/ee/server-only/limits/client.ts | 2 +- packages/lib/constants/app.ts | 15 ++++++++------- packages/lib/constants/crypto.ts | 16 ++++++++-------- packages/lib/constants/feature-flags.ts | 10 +++++----- packages/lib/constants/pdf.ts | 2 +- packages/lib/next-auth/auth-options.ts | 4 +--- .../server-only/auth/send-confirmation-email.ts | 11 +++++------ .../lib/server-only/auth/send-forgot-password.ts | 10 ++++------ .../lib/server-only/auth/send-reset-password.ts | 8 +++----- .../lib/server-only/document/delete-document.ts | 7 ++----- .../lib/server-only/document/resend-document.tsx | 10 ++++------ .../server-only/document/send-completed-email.ts | 9 +++------ .../lib/server-only/document/send-document.tsx | 10 ++++------ .../server-only/document/send-pending-email.ts | 8 +++----- packages/lib/server-only/feature-flags/all.ts | 9 +++------ packages/lib/server-only/feature-flags/get.ts | 10 ++++------ .../lib/server-only/pdf/insert-text-in-pdf.ts | 2 +- packages/lib/universal/get-base-url.ts | 10 +++++----- packages/lib/universal/get-feature-flag.ts | 6 +++--- packages/lib/universal/upload/put-file.ts | 4 ++-- packages/lib/universal/upload/server-actions.ts | 8 +++++--- packages/trpc/server/auth-router/router.ts | 4 ++-- .../trpc/server/singleplayer-router/router.ts | 6 ++---- .../document/document-share-button.tsx | 13 ++++++------- 38 files changed, 129 insertions(+), 167 deletions(-) diff --git a/apps/marketing/src/app/(marketing)/claimed/page.tsx b/apps/marketing/src/app/(marketing)/claimed/page.tsx index 931045bd2..7f85b5d2e 100644 --- a/apps/marketing/src/app/(marketing)/claimed/page.tsx +++ b/apps/marketing/src/app/(marketing)/claimed/page.tsx @@ -4,6 +4,7 @@ import { redirect } from 'next/navigation'; import { ArrowRight } from 'lucide-react'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { redis } from '@documenso/lib/server-only/redis'; import { stripe } from '@documenso/lib/server-only/stripe'; import { prisma } from '@documenso/prisma'; @@ -175,11 +176,7 @@ export default async function ClaimedPlanPage({ searchParams = {} }: ClaimedPlan This is a temporary password. Please change it as soon as possible.

- + @@ -117,7 +114,7 @@ export const PricingTable = ({ className, ...props }: PricingTableProps) => {

diff --git a/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx b/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx index 1af71c775..d8a8e2c53 100644 --- a/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx +++ b/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx @@ -6,6 +6,7 @@ import Link from 'next/link'; import signingCelebration from '@documenso/assets/images/signing-celebration.png'; import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import type { Signature } from '@documenso/prisma/client'; import { DocumentStatus } from '@documenso/prisma/client'; import type { DocumentWithRecipient } from '@documenso/prisma/types/document-with-recipient'; @@ -85,7 +86,7 @@ export const SinglePlayerModeSuccess = ({

Create a{' '} diff --git a/apps/marketing/src/components/(marketing)/widget.tsx b/apps/marketing/src/components/(marketing)/widget.tsx index 80c13b275..b0baeb5fc 100644 --- a/apps/marketing/src/components/(marketing)/widget.tsx +++ b/apps/marketing/src/components/(marketing)/widget.tsx @@ -7,6 +7,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { AnimatePresence, motion } from 'framer-motion'; import { Loader } from 'lucide-react'; import { usePlausible } from 'next-plausible'; +import { env } from 'next-runtime-env'; import { Controller, useForm } from 'react-hook-form'; import { z } from 'zod'; @@ -144,7 +145,11 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => { setTimeout(resolve, 1000); }); - const planId = process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID; + const planId = env('NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID'); + + if (!planId) { + throw new Error('No plan ID found.'); + } const claimPlanInput = signatureDataUrl ? { diff --git a/apps/marketing/src/pages/api/claim-plan/index.ts b/apps/marketing/src/pages/api/claim-plan/index.ts index 57597001e..c63a727c5 100644 --- a/apps/marketing/src/pages/api/claim-plan/index.ts +++ b/apps/marketing/src/pages/api/claim-plan/index.ts @@ -1,13 +1,15 @@ -import { NextApiRequest, NextApiResponse } from 'next'; +import type { NextApiRequest, NextApiResponse } from 'next'; import { randomUUID } from 'crypto'; -import { TEarlyAdopterCheckoutMetadataSchema } from '@documenso/ee/server-only/stripe/webhook/early-adopter-checkout-metadata'; +import type { TEarlyAdopterCheckoutMetadataSchema } from '@documenso/ee/server-only/stripe/webhook/early-adopter-checkout-metadata'; +import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { redis } from '@documenso/lib/server-only/redis'; import { stripe } from '@documenso/lib/server-only/stripe'; import { prisma } from '@documenso/prisma'; -import { TClaimPlanResponseSchema, ZClaimPlanRequestSchema } from '~/api/claim-plan/types'; +import type { TClaimPlanResponseSchema } from '~/api/claim-plan/types'; +import { ZClaimPlanRequestSchema } from '~/api/claim-plan/types'; export default async function handler( req: NextApiRequest, @@ -40,7 +42,7 @@ export default async function handler( if (user) { return res.status(200).json({ - redirectUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/signin`, + redirectUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/signin`, }); } @@ -77,8 +79,8 @@ export default async function handler( mode: 'subscription', metadata, allow_promotion_codes: true, - success_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, - cancel_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}`, + success_url: `${NEXT_PUBLIC_MARKETING_URL()}/claimed?sessionId={CHECKOUT_SESSION_ID}`, + cancel_url: `${NEXT_PUBLIC_MARKETING_URL()}`, }); if (!checkout.url) { diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx index 832e255fd..2159b87f2 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx @@ -61,7 +61,7 @@ export const EditDocumentForm = ({ const { mutateAsync: setPasswordForDocument } = trpc.document.setPasswordForDocument.useMutation(); - const documentFlow: Record = { + const documentFlow: Record = { title: { title: 'Add Title', description: 'Add the title to the document.', diff --git a/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts b/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts index 7d952d599..5435aefb1 100644 --- a/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts +++ b/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts @@ -1,20 +1,17 @@ 'use server'; -import { env } from 'next-runtime-env'; - import { getStripeCustomerByUser } from '@documenso/ee/server-only/stripe/get-customer'; import { getPortalSession } from '@documenso/ee/server-only/stripe/get-portal-session'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; export const createBillingPortal = async () => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const { user } = await getRequiredServerComponentSession(); const { stripeCustomer } = await getStripeCustomerByUser(user); return getPortalSession({ customerId: stripeCustomer.id, - returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/settings/billing`, }); }; diff --git a/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts b/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts index ef3fb0f30..90dd77e79 100644 --- a/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts +++ b/apps/web/src/app/(dashboard)/settings/billing/create-checkout.action.ts @@ -1,10 +1,9 @@ 'use server'; -import { env } from 'next-runtime-env'; - import { getCheckoutSession } from '@documenso/ee/server-only/stripe/get-checkout-session'; import { getStripeCustomerByUser } from '@documenso/ee/server-only/stripe/get-customer'; import { getPortalSession } from '@documenso/ee/server-only/stripe/get-portal-session'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; import { getSubscriptionsByUserId } from '@documenso/lib/server-only/subscription/get-subscriptions-by-user-id'; @@ -13,8 +12,6 @@ export type CreateCheckoutOptions = { }; export const createCheckout = async ({ priceId }: CreateCheckoutOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const session = await getRequiredServerComponentSession(); const { user, stripeCustomer } = await getStripeCustomerByUser(session.user); @@ -31,13 +28,13 @@ export const createCheckout = async ({ priceId }: CreateCheckoutOptions) => { if (foundSubscription) { return getPortalSession({ customerId: stripeCustomer.id, - returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/settings/billing`, }); } return getCheckoutSession({ customerId: stripeCustomer.id, priceId, - returnUrl: `${NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, + returnUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/settings/billing`, }); }; diff --git a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx index e9977b8f3..27f39a6b1 100644 --- a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx +++ b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx @@ -1,9 +1,10 @@ import { ImageResponse } from 'next/og'; import { NextResponse } from 'next/server'; -import { env } from 'next-runtime-env'; import { P, match } from 'ts-pattern'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; + import type { ShareHandlerAPIResponse } from '~/pages/api/share'; export const runtime = 'edge'; @@ -23,8 +24,6 @@ type SharePageOpenGraphImageProps = { }; export async function GET(_request: Request, { params: { slug } }: SharePageOpenGraphImageProps) { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const [interSemiBold, interRegular, caveatRegular, shareFrameImage] = await Promise.all([ fetch(new URL('@documenso/assets/fonts/inter-semibold.ttf', import.meta.url)).then( async (res) => res.arrayBuffer(), @@ -40,7 +39,7 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen ), ]); - const baseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const baseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const recipientOrSender: ShareHandlerAPIResponse = await fetch( new URL(`/api/share?slug=${slug}`, baseUrl), diff --git a/apps/web/src/app/(share)/share/[slug]/page.tsx b/apps/web/src/app/(share)/share/[slug]/page.tsx index ab530ba0b..80d991934 100644 --- a/apps/web/src/app/(share)/share/[slug]/page.tsx +++ b/apps/web/src/app/(share)/share/[slug]/page.tsx @@ -2,9 +2,7 @@ import type { Metadata } from 'next'; import { headers } from 'next/headers'; import { redirect } from 'next/navigation'; -import { env } from 'next-runtime-env'; - -import { APP_BASE_URL } from '@documenso/lib/constants/app'; +import { APP_BASE_URL, NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app'; type SharePageProps = { params: { slug: string }; @@ -18,20 +16,18 @@ export function generateMetadata({ params: { slug } }: SharePageProps) { title: 'Documenso - Join the open source signing revolution', description: 'I just signed with Documenso!', type: 'website', - images: [`${APP_BASE_URL}/share/${slug}/opengraph`], + images: [`${APP_BASE_URL()}/share/${slug}/opengraph`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${APP_BASE_URL}/share/${slug}/opengraph`], + images: [`${APP_BASE_URL()}/share/${slug}/opengraph`], description: 'I just signed with Documenso!', }, } satisfies Metadata; } export default function SharePage() { - const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); - const userAgent = headers().get('User-Agent') ?? ''; // https://stackoverflow.com/questions/47026171/how-to-detect-bots-for-open-graph-with-user-agent @@ -39,5 +35,5 @@ export default function SharePage() { return null; } - redirect(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001'); + redirect(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001'); } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 1e6619a46..606aa0f10 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,10 +2,11 @@ import { Suspense } from 'react'; import { Caveat, Inter } from 'next/font/google'; -import { PublicEnvScript, env } from 'next-runtime-env'; +import { PublicEnvScript } from 'next-runtime-env'; import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag'; import { LocaleProvider } from '@documenso/lib/client-only/providers/locale'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { getServerComponentAllFlags } from '@documenso/lib/server-only/feature-flags/get-server-component-feature-flag'; import { getLocale } from '@documenso/lib/server-only/headers/get-locale'; import { TrpcProvider } from '@documenso/trpc/react'; @@ -21,8 +22,6 @@ import './globals.css'; const fontInter = Inter({ subsets: ['latin'], variable: '--font-sans' }); const fontCaveat = Caveat({ subsets: ['latin'], variable: '--font-signature' }); -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - export const metadata = { title: { template: '%s - Documenso', @@ -39,12 +38,12 @@ export const metadata = { description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', type: 'website', - images: [`${NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], + images: [`${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], + images: [`${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`], description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', }, diff --git a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx index 727627ddd..a809ed23e 100644 --- a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx +++ b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx @@ -2,10 +2,9 @@ import React from 'react'; -import { env } from 'next-runtime-env'; - import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard'; import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; import type { Recipient } from '@documenso/prisma/client'; import { cn } from '@documenso/ui/lib/utils'; @@ -18,8 +17,6 @@ export type AvatarWithRecipientProps = { }; export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const [, copy] = useCopyToClipboard(); const { toast } = useToast(); @@ -28,7 +25,7 @@ export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { return; } - void copy(`${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`).then(() => { + void copy(`${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`).then(() => { toast({ title: 'Copied to clipboard', description: 'The signing link has been copied to your clipboard.', diff --git a/apps/web/src/helpers/get-asset-buffer.ts b/apps/web/src/helpers/get-asset-buffer.ts index 12c27d1a5..871e669b1 100644 --- a/apps/web/src/helpers/get-asset-buffer.ts +++ b/apps/web/src/helpers/get-asset-buffer.ts @@ -1,4 +1,4 @@ -import { env } from 'next-runtime-env'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; /** * getAssetBuffer is used to retrieve array buffers for various assets @@ -10,9 +10,7 @@ import { env } from 'next-runtime-env'; * @param path The path to the asset, relative to the `public` folder. */ export const getAssetBuffer = async (path: string) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - - const baseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const baseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; return fetch(new URL(path, baseUrl)).then(async (res) => res.arrayBuffer()); }; diff --git a/packages/ee/server-only/limits/client.ts b/packages/ee/server-only/limits/client.ts index 9bb2bd0ac..fa31e6e40 100644 --- a/packages/ee/server-only/limits/client.ts +++ b/packages/ee/server-only/limits/client.ts @@ -11,7 +11,7 @@ export type GetLimitsOptions = { export const getLimits = async ({ headers }: GetLimitsOptions = {}) => { const requestHeaders = headers ?? {}; - const url = new URL('/api/limits', APP_BASE_URL ?? 'http://localhost:3000'); + const url = new URL('/api/limits', APP_BASE_URL() ?? 'http://localhost:3000'); return fetch(url, { headers: { diff --git a/packages/lib/constants/app.ts b/packages/lib/constants/app.ts index cee5a7586..0675bb84b 100644 --- a/packages/lib/constants/app.ts +++ b/packages/lib/constants/app.ts @@ -1,12 +1,13 @@ import { env } from 'next-runtime-env'; -const NEXT_PUBLIC_PROJECT = process.env.NEXT_PUBLIC_PROJECT; -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); -const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); +export const NEXT_PUBLIC_PROJECT = () => env('NEXT_PUBLIC_PROJECT'); +export const NEXT_PUBLIC_WEBAPP_URL = () => env('NEXT_PUBLIC_WEBAPP_URL'); +export const NEXT_PUBLIC_MARKETING_URL = () => env('NEXT_PUBLIC_MARKETING_URL'); -export const IS_APP_MARKETING = NEXT_PUBLIC_PROJECT === 'marketing'; -export const IS_APP_WEB = NEXT_PUBLIC_PROJECT === 'web'; +export const IS_APP_MARKETING = () => NEXT_PUBLIC_PROJECT() === 'marketing'; +export const IS_APP_WEB = () => NEXT_PUBLIC_PROJECT() === 'web'; -export const APP_FOLDER = IS_APP_MARKETING ? 'marketing' : 'web'; +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 APP_BASE_URL = () => + IS_APP_WEB() ? NEXT_PUBLIC_WEBAPP_URL() : NEXT_PUBLIC_MARKETING_URL(); diff --git a/packages/lib/constants/crypto.ts b/packages/lib/constants/crypto.ts index 40d3ef113..74f306a53 100644 --- a/packages/lib/constants/crypto.ts +++ b/packages/lib/constants/crypto.ts @@ -2,15 +2,15 @@ export const DOCUMENSO_ENCRYPTION_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_KEY; export const DOCUMENSO_ENCRYPTION_SECONDARY_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY; -if (!DOCUMENSO_ENCRYPTION_KEY || !DOCUMENSO_ENCRYPTION_SECONDARY_KEY) { - throw new Error('Missing DOCUMENSO_ENCRYPTION_KEY or DOCUMENSO_ENCRYPTION_SECONDARY_KEY keys'); -} +// if (!DOCUMENSO_ENCRYPTION_KEY || !DOCUMENSO_ENCRYPTION_SECONDARY_KEY) { +// throw new Error('Missing DOCUMENSO_ENCRYPTION_KEY or DOCUMENSO_ENCRYPTION_SECONDARY_KEY keys'); +// } -if (DOCUMENSO_ENCRYPTION_KEY === DOCUMENSO_ENCRYPTION_SECONDARY_KEY) { - throw new Error( - 'DOCUMENSO_ENCRYPTION_KEY and DOCUMENSO_ENCRYPTION_SECONDARY_KEY cannot be equal', - ); -} +// if (DOCUMENSO_ENCRYPTION_KEY === DOCUMENSO_ENCRYPTION_SECONDARY_KEY) { +// throw new Error( +// 'DOCUMENSO_ENCRYPTION_KEY and DOCUMENSO_ENCRYPTION_SECONDARY_KEY cannot be equal', +// ); +// } if (DOCUMENSO_ENCRYPTION_KEY === 'CAFEBABE') { console.warn('*********************************************************************'); diff --git a/packages/lib/constants/feature-flags.ts b/packages/lib/constants/feature-flags.ts index a0e958e3a..d33db549e 100644 --- a/packages/lib/constants/feature-flags.ts +++ b/packages/lib/constants/feature-flags.ts @@ -2,8 +2,8 @@ import { env } from 'next-runtime-env'; import { APP_BASE_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'); +const NEXT_PUBLIC_FEATURE_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED'); +const NEXT_PUBLIC_POSTHOG_KEY = () => env('NEXT_PUBLIC_POSTHOG_KEY'); /** * The flag name for global session recording feature flag. @@ -21,7 +21,7 @@ export const FEATURE_FLAG_POLL_INTERVAL = 30000; * Does not take any person or group properties into account. */ export const LOCAL_FEATURE_FLAGS: Record = { - app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true', + app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true', marketing_header_single_player_mode: false, } as const; @@ -29,8 +29,8 @@ export const LOCAL_FEATURE_FLAGS: Record = { * Extract the PostHog configuration from the environment. */ export function extractPostHogConfig(): { key: string; host: string } | null { - const postHogKey = NEXT_PUBLIC_POSTHOG_KEY; - const postHogHost = `${APP_BASE_URL}/ingest`; + const postHogKey = NEXT_PUBLIC_POSTHOG_KEY(); + const postHogHost = `${APP_BASE_URL()}/ingest`; if (!postHogKey || !postHogHost) { return null; diff --git a/packages/lib/constants/pdf.ts b/packages/lib/constants/pdf.ts index eba72ab56..19663ef58 100644 --- a/packages/lib/constants/pdf.ts +++ b/packages/lib/constants/pdf.ts @@ -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 = () => `${APP_BASE_URL()}/fonts/caveat.ttf`; diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index 4823833cc..8f324d248 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -17,8 +17,6 @@ import { validateTwoFactorAuthentication } from '../server-only/2fa/validate-2fa import { getUserByEmail } from '../server-only/user/get-user-by-email'; import { ErrorCode } from './error-codes'; -const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); - export const NEXT_AUTH_OPTIONS: AuthOptions = { adapter: PrismaAdapter(prisma), secret: process.env.NEXTAUTH_SECRET ?? 'secret', @@ -186,7 +184,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { async signIn({ user }) { // We do this to stop OAuth providers from creating an account // when signups are disabled - if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { + if (env('NEXT_PUBLIC_DISABLE_SIGNUP') === 'true') { const userData = await getUserByEmail({ email: user.email! }); return !!userData; diff --git a/packages/lib/server-only/auth/send-confirmation-email.ts b/packages/lib/server-only/auth/send-confirmation-email.ts index c808e8f3d..3e1ac920d 100644 --- a/packages/lib/server-only/auth/send-confirmation-email.ts +++ b/packages/lib/server-only/auth/send-confirmation-email.ts @@ -1,20 +1,19 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ConfirmEmailTemplate } from '@documenso/email/templates/confirm-email'; import { prisma } from '@documenso/prisma'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export interface SendConfirmationEmailProps { userId: number; } export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailProps) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const NEXT_PRIVATE_SMTP_FROM_NAME = env('NEXT_PRIVATE_SMTP_FROM_NAME'); - const NEXT_PRIVATE_SMTP_FROM_ADDRESS = env('NEXT_PRIVATE_SMTP_FROM_ADDRESS'); + const NEXT_PRIVATE_SMTP_FROM_NAME = process.env.NEXT_PRIVATE_SMTP_FROM_NAME; + const NEXT_PRIVATE_SMTP_FROM_ADDRESS = process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS; const user = await prisma.user.findFirstOrThrow({ where: { @@ -36,7 +35,7 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro throw new Error('Verification token not found for the user'); } - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const confirmationLink = `${assetBaseUrl}/verify-email/${verificationToken.token}`; const senderName = NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso'; const senderAdress = NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com'; diff --git a/packages/lib/server-only/auth/send-forgot-password.ts b/packages/lib/server-only/auth/send-forgot-password.ts index 125db9338..6e3a582a8 100644 --- a/packages/lib/server-only/auth/send-forgot-password.ts +++ b/packages/lib/server-only/auth/send-forgot-password.ts @@ -1,19 +1,17 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ForgotPasswordTemplate } from '@documenso/email/templates/forgot-password'; import { prisma } from '@documenso/prisma'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export interface SendForgotPasswordOptions { userId: number; } export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const user = await prisma.user.findFirstOrThrow({ where: { id: userId, @@ -33,8 +31,8 @@ export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions) } const token = user.PasswordResetToken[0].token; - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const resetPasswordLink = `${NEXT_PUBLIC_WEBAPP_URL}/reset-password/${token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; + const resetPasswordLink = `${NEXT_PUBLIC_WEBAPP_URL()}/reset-password/${token}`; const template = createElement(ForgotPasswordTemplate, { assetBaseUrl, diff --git a/packages/lib/server-only/auth/send-reset-password.ts b/packages/lib/server-only/auth/send-reset-password.ts index 3dcec113c..6bcd5820d 100644 --- a/packages/lib/server-only/auth/send-reset-password.ts +++ b/packages/lib/server-only/auth/send-reset-password.ts @@ -1,26 +1,24 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { ResetPasswordTemplate } from '@documenso/email/templates/reset-password'; import { prisma } from '@documenso/prisma'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export interface SendResetPasswordOptions { userId: number; } export const sendResetPassword = async ({ userId }: SendResetPasswordOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const user = await prisma.user.findFirstOrThrow({ where: { id: userId, }, }); - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const template = createElement(ResetPasswordTemplate, { assetBaseUrl, diff --git a/packages/lib/server-only/document/delete-document.ts b/packages/lib/server-only/document/delete-document.ts index 58b1f48a9..a5cf96cdd 100644 --- a/packages/lib/server-only/document/delete-document.ts +++ b/packages/lib/server-only/document/delete-document.ts @@ -2,14 +2,13 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import DocumentCancelTemplate from '@documenso/email/templates/document-cancel'; import { prisma } from '@documenso/prisma'; import { DocumentStatus } from '@documenso/prisma/client'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { FROM_ADDRESS, FROM_NAME } from '../../constants/email'; export type DeleteDocumentOptions = { @@ -18,8 +17,6 @@ export type DeleteDocumentOptions = { status: DocumentStatus; }; -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - export const deleteDocument = async ({ id, userId, status }: DeleteDocumentOptions) => { // if the document is a draft, hard-delete if (status === DocumentStatus.DRAFT) { @@ -53,7 +50,7 @@ export const deleteDocument = async ({ id, userId, status }: DeleteDocumentOptio if (document.Recipient.length > 0) { await Promise.all( document.Recipient.map(async (recipient) => { - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const template = createElement(DocumentCancelTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/resend-document.tsx b/packages/lib/server-only/document/resend-document.tsx index a048cf600..85e148a57 100644 --- a/packages/lib/server-only/document/resend-document.tsx +++ b/packages/lib/server-only/document/resend-document.tsx @@ -1,7 +1,5 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite'; @@ -10,14 +8,14 @@ import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-em import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export type ResendDocumentOptions = { documentId: number; userId: number; recipients: number[]; }; -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - export const resendDocument = async ({ documentId, userId, recipients }: ResendDocumentOptions) => { const user = await prisma.user.findFirstOrThrow({ where: { @@ -71,8 +69,8 @@ export const resendDocument = async ({ documentId, userId, recipients }: ResendD 'document.name': document.title, }; - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; + const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`; const template = createElement(DocumentInviteEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index 9b624a23c..c7134d164 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -1,12 +1,11 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed'; import { prisma } from '@documenso/prisma'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { getFile } from '../../universal/upload/get-file'; export interface SendDocumentOptions { @@ -14,8 +13,6 @@ export interface SendDocumentOptions { } export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const document = await prisma.document.findUnique({ where: { id: documentId, @@ -40,12 +37,12 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => document.Recipient.map(async (recipient) => { const { email, name, token } = recipient; - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const template = createElement(DocumentCompletedEmailTemplate, { documentName: document.title, assetBaseUrl, - downloadLink: `${NEXT_PUBLIC_WEBAPP_URL}/sign/${token}/complete`, + downloadLink: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}/complete`, }); await mailer.sendMail({ diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index f72c79792..86f610cc9 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -1,7 +1,5 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite'; @@ -10,14 +8,14 @@ import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-em import { prisma } from '@documenso/prisma'; import { DocumentStatus, SendStatus } from '@documenso/prisma/client'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export type SendDocumentOptions = { documentId: number; userId: number; }; export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const user = await prisma.user.findFirstOrThrow({ where: { id: userId, @@ -63,8 +61,8 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) return; } - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; - const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; + const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`; const template = createElement(DocumentInviteEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/document/send-pending-email.ts b/packages/lib/server-only/document/send-pending-email.ts index abbecd72a..73e938a7d 100644 --- a/packages/lib/server-only/document/send-pending-email.ts +++ b/packages/lib/server-only/document/send-pending-email.ts @@ -1,20 +1,18 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; - import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentPendingEmailTemplate } from '@documenso/email/templates/document-pending'; import { prisma } from '@documenso/prisma'; +import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + export interface SendPendingEmailOptions { documentId: number; recipientId: number; } export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingEmailOptions) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const document = await prisma.document.findFirst({ where: { id: documentId, @@ -45,7 +43,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE const { email, name } = recipient; - const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000'; const template = createElement(DocumentPendingEmailTemplate, { documentName: document.title, diff --git a/packages/lib/server-only/feature-flags/all.ts b/packages/lib/server-only/feature-flags/all.ts index fff6cd855..71efd5425 100644 --- a/packages/lib/server-only/feature-flags/all.ts +++ b/packages/lib/server-only/feature-flags/all.ts @@ -1,20 +1,17 @@ import { NextRequest, NextResponse } from 'next/server'; import { getToken } from 'next-auth/jwt'; -import { env } from 'next-runtime-env'; import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags'; import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; +import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { extractDistinctUserId, mapJwtToFlagProperties } from './get'; /** * Get all the evaluated feature flags based on the current user if possible. */ export default async function handlerFeatureFlagAll(req: Request) { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); - const requestHeaders = Object.fromEntries(req.headers.entries()); const nextReq = new NextRequest(req, { @@ -42,11 +39,11 @@ export default async function handlerFeatureFlagAll(req: Request) { const origin = req.headers.get('origin'); if (origin) { - if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { + if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000')) { res.headers.set('Access-Control-Allow-Origin', origin); } - if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { + if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) { res.headers.set('Access-Control-Allow-Origin', origin); } } diff --git a/packages/lib/server-only/feature-flags/get.ts b/packages/lib/server-only/feature-flags/get.ts index 6f8a2c011..5b2b3b7fd 100644 --- a/packages/lib/server-only/feature-flags/get.ts +++ b/packages/lib/server-only/feature-flags/get.ts @@ -3,11 +3,12 @@ import { NextRequest, NextResponse } from 'next/server'; import { nanoid } from 'nanoid'; import type { JWT } from 'next-auth/jwt'; import { getToken } from 'next-auth/jwt'; -import { env } from 'next-runtime-env'; import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags'; import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; +import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; + /** * Evaluate a single feature flag based on the current user if possible. * @@ -15,9 +16,6 @@ import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-po * @returns A Response with the feature flag value. */ export default async function handleFeatureFlagGet(req: Request) { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - const NEXT_PUBLIC_MARKETING_URL = env('NEXT_PUBLIC_MARKETING_URL'); - const { searchParams } = new URL(req.url ?? ''); const flag = searchParams.get('flag'); @@ -62,11 +60,11 @@ export default async function handleFeatureFlagGet(req: Request) { const origin = req.headers.get('Origin'); if (origin) { - if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000')) { + if (origin.startsWith(NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000')) { res.headers.set('Access-Control-Allow-Origin', origin); } - if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001')) { + if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) { res.headers.set('Access-Control-Allow-Origin', origin); } } diff --git a/packages/lib/server-only/pdf/insert-text-in-pdf.ts b/packages/lib/server-only/pdf/insert-text-in-pdf.ts index 248702b6e..d5120341c 100644 --- a/packages/lib/server-only/pdf/insert-text-in-pdf.ts +++ b/packages/lib/server-only/pdf/insert-text-in-pdf.ts @@ -12,7 +12,7 @@ export async function insertTextInPDF( useHandwritingFont = true, ): Promise { // Fetch the font file from the public URL. - const fontResponse = await fetch(CAVEAT_FONT_PATH); + const fontResponse = await fetch(CAVEAT_FONT_PATH()); const fontCaveat = await fontResponse.arrayBuffer(); const pdfDoc = await PDFDocument.load(pdfAsBase64); diff --git a/packages/lib/universal/get-base-url.ts b/packages/lib/universal/get-base-url.ts index b7c9d4ea4..7f9380047 100644 --- a/packages/lib/universal/get-base-url.ts +++ b/packages/lib/universal/get-base-url.ts @@ -1,7 +1,5 @@ /* eslint-disable turbo/no-undeclared-env-vars */ -import { env } from 'next-runtime-env'; - -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); +import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app'; export const getBaseUrl = () => { if (typeof window !== 'undefined') { @@ -12,8 +10,10 @@ export const getBaseUrl = () => { return `https://${process.env.VERCEL_URL}`; } - if (NEXT_PUBLIC_WEBAPP_URL) { - return NEXT_PUBLIC_WEBAPP_URL; + const webAppUrl = NEXT_PUBLIC_WEBAPP_URL(); + + if (webAppUrl) { + return webAppUrl; } return `http://localhost:${process.env.PORT ?? 3000}`; diff --git a/packages/lib/universal/get-feature-flag.ts b/packages/lib/universal/get-feature-flag.ts index 0fc0aa131..f4650f691 100644 --- a/packages/lib/universal/get-feature-flag.ts +++ b/packages/lib/universal/get-feature-flag.ts @@ -22,7 +22,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(`${APP_BASE_URL()}/api/feature-flag/get`); url.searchParams.set('flag', flag); const response = await fetch(url, { @@ -55,7 +55,7 @@ export const getAllFlags = async ( return LOCAL_FEATURE_FLAGS; } - const url = new URL(`${APP_BASE_URL}/api/feature-flag/all`); + const url = new URL(`${APP_BASE_URL()}/api/feature-flag/all`); return fetch(url, { headers: { @@ -80,7 +80,7 @@ export const getAllAnonymousFlags = async (): Promise Promise; }; -const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); - export const putFile = async (file: File) => { + const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); + const { type, data } = await match(NEXT_PUBLIC_UPLOAD_TRANSPORT) .with('s3', async () => putFileInS3(file)) .otherwise(async () => putFileInDatabase(file)); diff --git a/packages/lib/universal/upload/server-actions.ts b/packages/lib/universal/upload/server-actions.ts index 61429cd86..fd4bfc57a 100644 --- a/packages/lib/universal/upload/server-actions.ts +++ b/packages/lib/universal/upload/server-actions.ts @@ -18,8 +18,6 @@ import { APP_BASE_URL } from '../../constants/app'; import { ONE_HOUR, ONE_SECOND } from '../../constants/time'; import { alphaid } from '../id'; -const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); - export const getPresignPostUrl = async (fileName: string, contentType: string) => { const client = getS3Client(); @@ -28,8 +26,10 @@ export const getPresignPostUrl = async (fileName: string, contentType: string) = let token: JWT | null = null; try { + const baseUrl = APP_BASE_URL() ?? 'http://localhost:3000'; + token = await getToken({ - req: new NextRequest(APP_BASE_URL ?? 'http://localhost:3000', { + req: new NextRequest(baseUrl, { headers: headers(), }), }); @@ -120,6 +120,8 @@ export const deleteS3File = async (key: string) => { }; const getS3Client = () => { + const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT'); + if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') { throw new Error('Invalid upload transport'); } diff --git a/packages/trpc/server/auth-router/router.ts b/packages/trpc/server/auth-router/router.ts index 0debd7f8d..65fe8d296 100644 --- a/packages/trpc/server/auth-router/router.ts +++ b/packages/trpc/server/auth-router/router.ts @@ -9,12 +9,12 @@ import { sendConfirmationToken } from '@documenso/lib/server-only/user/send-conf import { authenticatedProcedure, procedure, router } from '../trpc'; import { ZSignUpMutationSchema, ZVerifyPasswordMutationSchema } from './schema'; -const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); +const NEXT_PUBLIC_DISABLE_SIGNUP = () => env('NEXT_PUBLIC_DISABLE_SIGNUP'); export const authRouter = router({ signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => { try { - if (NEXT_PUBLIC_DISABLE_SIGNUP === 'true') { + if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') { throw new TRPCError({ code: 'BAD_REQUEST', message: 'Signups are disabled.', diff --git a/packages/trpc/server/singleplayer-router/router.ts b/packages/trpc/server/singleplayer-router/router.ts index cb92aa7a4..4b4c12c0a 100644 --- a/packages/trpc/server/singleplayer-router/router.ts +++ b/packages/trpc/server/singleplayer-router/router.ts @@ -1,11 +1,11 @@ import { createElement } from 'react'; -import { env } from 'next-runtime-env'; import { PDFDocument } from 'pdf-lib'; import { mailer } from '@documenso/email/mailer'; import { renderAsync } from '@documenso/email/render'; import { DocumentSelfSignedEmailTemplate } from '@documenso/email/templates/document-self-signed'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { FROM_ADDRESS, FROM_NAME, SERVICE_USER_EMAIL } from '@documenso/lib/constants/email'; import { insertFieldInPDF } from '@documenso/lib/server-only/pdf/insert-field-in-pdf'; import { alphaid } from '@documenso/lib/universal/id'; @@ -25,8 +25,6 @@ import { procedure, router } from '../trpc'; import { mapField } from './helper'; import { ZCreateSinglePlayerDocumentMutationSchema } from './schema'; -const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); - export const singleplayerRouter = router({ createSinglePlayerDocument: procedure .input(ZCreateSinglePlayerDocumentMutationSchema) @@ -151,7 +149,7 @@ export const singleplayerRouter = router({ const template = createElement(DocumentSelfSignedEmailTemplate, { documentName: documentName, - assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000', + assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000', }); const [html, text] = await Promise.all([ diff --git a/packages/ui/components/document/document-share-button.tsx b/packages/ui/components/document/document-share-button.tsx index f304d92c0..e20e677c7 100644 --- a/packages/ui/components/document/document-share-button.tsx +++ b/packages/ui/components/document/document-share-button.tsx @@ -4,10 +4,10 @@ import type { HTMLAttributes } from 'react'; import React, { useState } from 'react'; import { Copy, Sparkles } from 'lucide-react'; -import { env } from 'next-runtime-env'; import { FaXTwitter } from 'react-icons/fa6'; import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { TOAST_DOCUMENT_SHARE_ERROR, TOAST_DOCUMENT_SHARE_SUCCESS, @@ -39,7 +39,6 @@ export const DocumentShareButton = ({ className, trigger, }: DocumentShareButtonProps) => { - const NEXT_PUBLIC_WEBAPP_URL = env('NEXT_PUBLIC_WEBAPP_URL'); const { toast } = useToast(); const { copyShareLink, createAndCopyShareLink, isCopyingShareLink } = useCopyShareLink({ @@ -70,7 +69,7 @@ export const DocumentShareButton = ({ const onCopyClick = async () => { if (shareLink) { - await copyShareLink(`${NEXT_PUBLIC_WEBAPP_URL}/share/${shareLink.slug}`); + await copyShareLink(`${NEXT_PUBLIC_WEBAPP_URL()}/share/${shareLink.slug}`); } else { await createAndCopyShareLink({ token, @@ -94,7 +93,7 @@ export const DocumentShareButton = ({ } // Ensuring we've prewarmed the opengraph image for the Twitter - await fetch(`${NEXT_PUBLIC_WEBAPP_URL}/share/${slug}/opengraph`, { + await fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/share/${slug}/opengraph`, { // We don't care about the response, so we can use no-cors mode: 'no-cors', }); @@ -102,7 +101,7 @@ export const DocumentShareButton = ({ window.open( generateTwitterIntent( `I just ${token ? 'signed' : 'sent'} a document in style with @documenso. Check it out!`, - `${NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`, + `${NEXT_PUBLIC_WEBAPP_URL()}/share/${slug}`, ), '_blank', ); @@ -150,7 +149,7 @@ export const DocumentShareButton = ({ 'animate-pulse': !shareLink?.slug, })} > - {NEXT_PUBLIC_WEBAPP_URL}/share/{shareLink?.slug || '...'} + {NEXT_PUBLIC_WEBAPP_URL()}/share/{shareLink?.slug || '...'}

{shareLink?.slug && ( sharing link From 748bf6de6b4a37ce42de728c2ad99085959ba550 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Thu, 8 Feb 2024 22:12:04 +1100 Subject: [PATCH 3/5] fix: add dropped constants from merge --- packages/lib/constants/app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/constants/app.ts b/packages/lib/constants/app.ts index 71f227764..2355c4b07 100644 --- a/packages/lib/constants/app.ts +++ b/packages/lib/constants/app.ts @@ -9,6 +9,7 @@ export const NEXT_PUBLIC_MARKETING_URL = () => env('NEXT_PUBLIC_MARKETING_URL'); export const IS_APP_MARKETING = () => NEXT_PUBLIC_PROJECT() === 'marketing'; export const IS_APP_WEB = () => NEXT_PUBLIC_PROJECT() === 'web'; +export const IS_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED'); export const APP_FOLDER = () => (IS_APP_MARKETING() ? 'marketing' : 'web'); From 20e297673168b81c4fc5f807f892603e30866a28 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Mon, 12 Feb 2024 01:29:22 +0000 Subject: [PATCH 4/5] fix: build issues --- .../content/blog/commodifying-signing.mdx | 2 +- apps/marketing/content/blog/linear-gh.mdx | 2 +- .../content/blog/why-i-started-documenso.mdx | 2 +- .../src/app/(marketing)/[content]/page.tsx | 2 +- .../src/app/(marketing)/blog/[post]/page.tsx | 6 ++- .../src/app/(marketing)/claimed/page.tsx | 2 + .../src/app/(marketing)/pricing/page.tsx | 2 + .../src/app/(marketing)/singleplayer/page.tsx | 1 + apps/marketing/src/app/layout.tsx | 52 ++++++++++--------- .../web/src/app/(share)/share/[slug]/page.tsx | 6 +-- .../src/app/(unauthenticated)/signin/page.tsx | 2 +- apps/web/src/app/layout.tsx | 51 +++++++++--------- .../(teams)/dialogs/transfer-team-dialog.tsx | 2 +- .../(teams)/settings/layout/desktop-nav.tsx | 2 +- .../(teams)/settings/layout/mobile-nav.tsx | 2 +- packages/ee/server-only/limits/server.ts | 2 +- packages/lib/constants/app.ts | 2 +- .../server-only/document/send-document.tsx | 7 +-- .../team/accept-team-invitation.ts | 2 +- .../team/create-team-billing-portal.ts | 2 +- packages/lib/server-only/team/create-team.ts | 4 +- .../server-only/team/delete-team-members.ts | 2 +- packages/lib/server-only/team/leave-team.ts | 2 +- .../team/transfer-team-ownership.ts | 2 +- packages/lib/server-only/user/create-user.ts | 4 +- 25 files changed, 88 insertions(+), 77 deletions(-) diff --git a/apps/marketing/content/blog/commodifying-signing.mdx b/apps/marketing/content/blog/commodifying-signing.mdx index 0a9cf4050..2bf1b2799 100644 --- a/apps/marketing/content/blog/commodifying-signing.mdx +++ b/apps/marketing/content/blog/commodifying-signing.mdx @@ -5,7 +5,7 @@ authorName: 'Timur Ercan' authorImage: '/blog/blog-author-timur.jpeg' authorRole: 'Co-Founder' date: 2024-01-25 -Tags: +tags: - Vision - Mission - Open Source diff --git a/apps/marketing/content/blog/linear-gh.mdx b/apps/marketing/content/blog/linear-gh.mdx index 1267931d6..fc5282851 100644 --- a/apps/marketing/content/blog/linear-gh.mdx +++ b/apps/marketing/content/blog/linear-gh.mdx @@ -5,7 +5,7 @@ authorName: 'Timur Ercan' authorImage: '/blog/blog-author-timur.jpeg' authorRole: 'Co-Founder' date: 2024-01-10 -Tags: +tags: - GitHub - Backlog - Roadmap diff --git a/apps/marketing/content/blog/why-i-started-documenso.mdx b/apps/marketing/content/blog/why-i-started-documenso.mdx index 2fceddd25..9ef83b8d1 100644 --- a/apps/marketing/content/blog/why-i-started-documenso.mdx +++ b/apps/marketing/content/blog/why-i-started-documenso.mdx @@ -5,7 +5,7 @@ authorName: 'Timur Ercan' authorImage: '/blog/blog-author-timur.jpeg' authorRole: 'Co-Founder' date: 2024-02-06 -Tags: +tags: - Founders - Mission - Open Source diff --git a/apps/marketing/src/app/(marketing)/[content]/page.tsx b/apps/marketing/src/app/(marketing)/[content]/page.tsx index 62c83f400..ba23e6b81 100644 --- a/apps/marketing/src/app/(marketing)/[content]/page.tsx +++ b/apps/marketing/src/app/(marketing)/[content]/page.tsx @@ -12,7 +12,7 @@ export const generateMetadata = ({ params }: { params: { content: string } }) => const document = allDocuments.find((post) => post._raw.flattenedPath === params.content); if (!document) { - notFound(); + return { title: 'Not Found' }; } return { title: document.title }; diff --git a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx index 866539a92..495b8946e 100644 --- a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx +++ b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx @@ -7,6 +7,8 @@ import { ChevronLeft } from 'lucide-react'; import type { MDXComponents } from 'mdx/types'; import { useMDXComponent } from 'next-contentlayer/hooks'; +export const dynamic = 'force-dynamic'; + export const generateStaticParams = () => allBlogPosts.map((post) => ({ post: post._raw.flattenedPath })); @@ -14,7 +16,9 @@ export const generateMetadata = ({ params }: { params: { post: string } }) => { const blogPost = allBlogPosts.find((post) => post._raw.flattenedPath === `blog/${params.post}`); if (!blogPost) { - notFound(); + return { + title: 'Not Found', + }; } return { diff --git a/apps/marketing/src/app/(marketing)/claimed/page.tsx b/apps/marketing/src/app/(marketing)/claimed/page.tsx index 7f85b5d2e..730a99377 100644 --- a/apps/marketing/src/app/(marketing)/claimed/page.tsx +++ b/apps/marketing/src/app/(marketing)/claimed/page.tsx @@ -13,6 +13,8 @@ import { Button } from '@documenso/ui/primitives/button'; import { PasswordReveal } from '~/components/(marketing)/password-reveal'; +export const dynamic = 'force-dynamic'; + const fontCaveat = Caveat({ weight: ['500'], subsets: ['latin'], diff --git a/apps/marketing/src/app/(marketing)/pricing/page.tsx b/apps/marketing/src/app/(marketing)/pricing/page.tsx index e4c7b776a..d926ec675 100644 --- a/apps/marketing/src/app/(marketing)/pricing/page.tsx +++ b/apps/marketing/src/app/(marketing)/pricing/page.tsx @@ -15,6 +15,8 @@ export const metadata: Metadata = { title: 'Pricing', }; +export const dynamic = 'force-dynamic'; + export type PricingPageProps = { searchParams?: { planId?: string; diff --git a/apps/marketing/src/app/(marketing)/singleplayer/page.tsx b/apps/marketing/src/app/(marketing)/singleplayer/page.tsx index aafad32a8..5e8a07040 100644 --- a/apps/marketing/src/app/(marketing)/singleplayer/page.tsx +++ b/apps/marketing/src/app/(marketing)/singleplayer/page.tsx @@ -7,6 +7,7 @@ export const metadata: Metadata = { }; export const revalidate = 0; +export const dynamic = 'force-dynamic'; // !: This entire file is a hack to get around failed prerendering of // !: the Single Player Mode page. This regression was introduced during diff --git a/apps/marketing/src/app/layout.tsx b/apps/marketing/src/app/layout.tsx index 1745149c6..57da42c3f 100644 --- a/apps/marketing/src/app/layout.tsx +++ b/apps/marketing/src/app/layout.tsx @@ -3,6 +3,7 @@ import { Suspense } from 'react'; import { Caveat, Inter } from 'next/font/google'; import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag'; +import { NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app'; import { getAllAnonymousFlags } from '@documenso/lib/universal/get-feature-flag'; import { TrpcProvider } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; @@ -17,32 +18,35 @@ import './globals.css'; const fontInter = Inter({ subsets: ['latin'], variable: '--font-sans' }); const fontCaveat = Caveat({ subsets: ['latin'], variable: '--font-signature' }); -export const metadata = { - title: { - template: '%s - Documenso', - default: 'Documenso', - }, - description: - 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - keywords: - 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', - authors: { name: 'Documenso, Inc.' }, - robots: 'index, follow', - openGraph: { - title: 'Documenso - The Open Source DocuSign Alternative', +export function generateMetadata() { + return { + title: { + template: '%s - Documenso', + default: 'Documenso', + }, description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - type: 'website', - images: [`${process.env.NEXT_PUBLIC_MARKETING_URL}/opengraph-image.jpg`], - }, - twitter: { - site: '@documenso', - card: 'summary_large_image', - images: [`${process.env.NEXT_PUBLIC_MARKETING_URL}/opengraph-image.jpg`], - description: - 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - }, -}; + keywords: + 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', + authors: { name: 'Documenso, Inc.' }, + robots: 'index, follow', + metadataBase: new URL(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3000'), + openGraph: { + title: 'Documenso - The Open Source DocuSign Alternative', + description: + 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', + type: 'website', + images: ['/opengraph-image.jpg'], + }, + twitter: { + site: '@documenso', + card: 'summary_large_image', + images: ['/opengraph-image.jpg'], + description: + 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', + }, + }; +} export default async function RootLayout({ children }: { children: React.ReactNode }) { const flags = await getAllAnonymousFlags(); diff --git a/apps/web/src/app/(share)/share/[slug]/page.tsx b/apps/web/src/app/(share)/share/[slug]/page.tsx index 80d991934..efcd75a41 100644 --- a/apps/web/src/app/(share)/share/[slug]/page.tsx +++ b/apps/web/src/app/(share)/share/[slug]/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from 'next'; import { headers } from 'next/headers'; import { redirect } from 'next/navigation'; -import { APP_BASE_URL, NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app'; +import { NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app'; type SharePageProps = { params: { slug: string }; @@ -16,12 +16,12 @@ export function generateMetadata({ params: { slug } }: SharePageProps) { title: 'Documenso - Join the open source signing revolution', description: 'I just signed with Documenso!', type: 'website', - images: [`${APP_BASE_URL()}/share/${slug}/opengraph`], + images: [`/share/${slug}/opengraph`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${APP_BASE_URL()}/share/${slug}/opengraph`], + images: [`/share/${slug}/opengraph`], description: 'I just signed with Documenso!', }, } satisfies Metadata; diff --git a/apps/web/src/app/(unauthenticated)/signin/page.tsx b/apps/web/src/app/(unauthenticated)/signin/page.tsx index 71a558bc0..50356a5bb 100644 --- a/apps/web/src/app/(unauthenticated)/signin/page.tsx +++ b/apps/web/src/app/(unauthenticated)/signin/page.tsx @@ -21,7 +21,7 @@ type SignInPageProps = { export default function SignInPage({ searchParams }: SignInPageProps) { const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP'); - + const rawEmail = typeof searchParams.email === 'string' ? searchParams.email : undefined; const email = rawEmail ? decryptSecondaryData(rawEmail) : null; diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 606aa0f10..7753e1e53 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -22,32 +22,35 @@ import './globals.css'; const fontInter = Inter({ subsets: ['latin'], variable: '--font-sans' }); const fontCaveat = Caveat({ subsets: ['latin'], variable: '--font-signature' }); -export const metadata = { - title: { - template: '%s - Documenso', - default: 'Documenso', - }, - description: - 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - keywords: - 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', - authors: { name: 'Documenso, Inc.' }, - robots: 'index, follow', - openGraph: { - title: 'Documenso - The Open Source DocuSign Alternative', +export function generateMetadata() { + return { + title: { + template: '%s - Documenso', + default: 'Documenso', + }, description: 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - type: 'website', - images: [`${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`], - }, - twitter: { - site: '@documenso', - card: 'summary_large_image', - images: [`${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`], - description: - 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', - }, -}; + keywords: + 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', + authors: { name: 'Documenso, Inc.' }, + robots: 'index, follow', + metadataBase: new URL(NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000'), + openGraph: { + title: 'Documenso - The Open Source DocuSign Alternative', + description: + 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', + type: 'website', + images: ['/opengraph-image.jpg'], + }, + twitter: { + site: '@documenso', + card: 'summary_large_image', + images: ['/opengraph-image.jpg'], + description: + 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', + }, + }; +} export default async function RootLayout({ children }: { children: React.ReactNode }) { const flags = await getServerComponentAllFlags(); diff --git a/apps/web/src/components/(teams)/dialogs/transfer-team-dialog.tsx b/apps/web/src/components/(teams)/dialogs/transfer-team-dialog.tsx index e5dd8ca17..fb2d1bdfd 100644 --- a/apps/web/src/components/(teams)/dialogs/transfer-team-dialog.tsx +++ b/apps/web/src/components/(teams)/dialogs/transfer-team-dialog.tsx @@ -238,7 +238,7 @@ export const TransferTeamDialog = ({
    - {IS_BILLING_ENABLED && ( + {IS_BILLING_ENABLED() && ( // Temporary removed. //
  • // {form.getValues('clearPaymentMethods') diff --git a/apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx b/apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx index be68f6c03..98df7416e 100644 --- a/apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx +++ b/apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx @@ -48,7 +48,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => { - {IS_BILLING_ENABLED && ( + {IS_BILLING_ENABLED() && ( - {IS_BILLING_ENABLED && ( + {IS_BILLING_ENABLED() && (