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 931045bd2..730a99377 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'; @@ -12,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'], @@ -175,11 +178,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 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 = ({