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 d4305a04c..fe7502d27 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)/settings/billing/create-billing-portal.action.ts b/apps/web/src/app/(dashboard)/settings/billing/create-billing-portal.action.ts
index 885414515..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
@@ -2,6 +2,7 @@
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 () => {
@@ -11,6 +12,6 @@ export const createBillingPortal = async () => {
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..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
@@ -3,6 +3,7 @@
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';
@@ -27,13 +28,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..27f39a6b1 100644
--- a/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx
+++ b/apps/web/src/app/(share)/share/[slug]/opengraph/route.tsx
@@ -3,6 +3,8 @@ import { NextResponse } from 'next/server';
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';
@@ -37,7 +39,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..efcd75a41 100644
--- a/apps/web/src/app/(share)/share/[slug]/page.tsx
+++ b/apps/web/src/app/(share)/share/[slug]/page.tsx
@@ -1,8 +1,8 @@
-import { Metadata } from 'next';
+import type { Metadata } from 'next';
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
-import { APP_BASE_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;
@@ -35,5 +35,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 8331e7c03..50356a5bb 100644
--- a/apps/web/src/app/(unauthenticated)/signin/page.tsx
+++ b/apps/web/src/app/(unauthenticated)/signin/page.tsx
@@ -2,6 +2,8 @@ import type { Metadata } from 'next';
import Link from 'next/link';
import { redirect } from 'next/navigation';
+import { env } from 'next-runtime-env';
+
import { IS_GOOGLE_SSO_ENABLED } from '@documenso/lib/constants/auth';
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
@@ -18,6 +20,8 @@ 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;
@@ -39,7 +43,7 @@ export default function SignInPage({ searchParams }: SignInPageProps) {
isGoogleSSOEnabled={IS_GOOGLE_SSO_ENABLED}
/>
- {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 dbbbcdba9..b9365e1d5 100644
--- a/apps/web/src/app/(unauthenticated)/signup/page.tsx
+++ b/apps/web/src/app/(unauthenticated)/signup/page.tsx
@@ -2,6 +2,8 @@ import type { Metadata } from 'next';
import Link from 'next/link';
import { redirect } from 'next/navigation';
+import { env } from 'next-runtime-env';
+
import { IS_GOOGLE_SSO_ENABLED } from '@documenso/lib/constants/auth';
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
@@ -18,7 +20,9 @@ type SignUpPageProps = {
};
export default function SignUpPage({ searchParams }: SignUpPageProps) {
- 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 17f92fa2b..7753e1e53 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -2,8 +2,11 @@ import { Suspense } from 'react';
import { Caveat, Inter } from 'next/font/google';
+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';
@@ -19,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: [`${process.env.NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`],
- },
- twitter: {
- site: '@documenso',
- card: 'summary_large_image',
- images: [`${process.env.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();
@@ -62,6 +68,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 46182c36e..69dd88d79 100644
--- a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx
+++ b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx
@@ -4,6 +4,7 @@ import React from 'react';
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 { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
import type { Recipient } from '@documenso/prisma/client';
@@ -25,7 +26,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/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() && (