From 771042c9ceaac88361c456d133639656cbf01f37 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 14 Sep 2023 15:00:14 +1000 Subject: [PATCH] feat: add vercel build script --- .env.example | 4 +- .eslintignore | 1 + apps/marketing/next.config.js | 1 + apps/marketing/process-env.d.ts | 3 +- .../src/app/(marketing)/claimed/page.tsx | 2 +- apps/marketing/src/app/layout.tsx | 4 +- .../src/pages/api/claim-plan/index.ts | 6 +- apps/web/next.config.js | 1 + apps/web/process-env.d.ts | 3 +- .../app/(dashboard)/settings/billing/page.tsx | 2 +- apps/web/src/app/layout.tsx | 4 +- apps/web/src/helpers/get-feature-flag.ts | 4 +- apps/web/src/pages/api/claim-plan/index.ts | 6 +- assets/example.pdf | Bin 0 -> 39842 bytes package-lock.json | 36 +++--- .../server-only/document/send-document.tsx | 4 +- packages/lib/universal/get-base-url.ts | 4 +- packages/prisma/helper.ts | 52 +++++++++ packages/prisma/index.ts | 10 +- packages/prisma/package.json | 14 ++- packages/prisma/seed-database.ts | 82 ++++++++++++++ packages/tsconfig/process-env.d.ts | 17 ++- scripts/remap-vercel-env.cjs | 45 ++++++++ scripts/vercel.sh | 107 ++++++++++++++++++ turbo.json | 30 +++-- 25 files changed, 391 insertions(+), 51 deletions(-) create mode 100644 assets/example.pdf create mode 100644 packages/prisma/helper.ts create mode 100644 packages/prisma/seed-database.ts create mode 100644 scripts/remap-vercel-env.cjs create mode 100755 scripts/vercel.sh diff --git a/.env.example b/.env.example index 6f32b5a63..fb22bbedf 100644 --- a/.env.example +++ b/.env.example @@ -7,8 +7,8 @@ NEXT_PRIVATE_GOOGLE_CLIENT_ID="" NEXT_PRIVATE_GOOGLE_CLIENT_SECRET="" # [[APP]] -NEXT_PUBLIC_SITE_URL="http://localhost:3000" -NEXT_PUBLIC_APP_URL="http://localhost:3000" +NEXT_PUBLIC_WEBAPP_URL="http://localhost:3000" +NEXT_PUBLIC_MARKETING_URL="http://localhost:3001" # [[DATABASE]] NEXT_PRIVATE_DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/documenso" diff --git a/.eslintignore b/.eslintignore index f80dc7f80..b7f7e638f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ # Statically hosted javascript files apps/*/public/*.js apps/*/public/*.cjs +scripts/ diff --git a/apps/marketing/next.config.js b/apps/marketing/next.config.js index 2783e4063..9944b561f 100644 --- a/apps/marketing/next.config.js +++ b/apps/marketing/next.config.js @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); const { withContentlayer } = require('next-contentlayer'); +const { remapVercelEnv } = require('../../scripts/remap-vercel-env.cjs'); const { parsed: env } = require('dotenv').config({ path: path.join(__dirname, '../../.env.local'), diff --git a/apps/marketing/process-env.d.ts b/apps/marketing/process-env.d.ts index ac170a616..3dfdcb30f 100644 --- a/apps/marketing/process-env.d.ts +++ b/apps/marketing/process-env.d.ts @@ -1,6 +1,7 @@ declare namespace NodeJS { export interface ProcessEnv { - NEXT_PUBLIC_SITE_URL?: string; + NEXT_PUBLIC_WEBAPP_URL?: string; + NEXT_PUBLIC_MARKETING_URL?: string; NEXT_PRIVATE_DATABASE_URL: string; diff --git a/apps/marketing/src/app/(marketing)/claimed/page.tsx b/apps/marketing/src/app/(marketing)/claimed/page.tsx index f56ae2b26..b1636e2be 100644 --- a/apps/marketing/src/app/(marketing)/claimed/page.tsx +++ b/apps/marketing/src/app/(marketing)/claimed/page.tsx @@ -161,7 +161,7 @@ export default async function ClaimedPlanPage({ searchParams = {} }: ClaimedPlan

diff --git a/apps/marketing/src/app/layout.tsx b/apps/marketing/src/app/layout.tsx index ea21ed3c3..46d9a3d32 100644 --- a/apps/marketing/src/app/layout.tsx +++ b/apps/marketing/src/app/layout.tsx @@ -21,12 +21,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_SITE_URL}/opengraph-image.jpg`], + images: [`${process.env.NEXT_PUBLIC_MARKETING_URL}/opengraph-image.jpg`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${process.env.NEXT_PUBLIC_SITE_URL}/opengraph-image.jpg`], + 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.', }, diff --git a/apps/marketing/src/pages/api/claim-plan/index.ts b/apps/marketing/src/pages/api/claim-plan/index.ts index abad354a8..3d2d8679b 100644 --- a/apps/marketing/src/pages/api/claim-plan/index.ts +++ b/apps/marketing/src/pages/api/claim-plan/index.ts @@ -43,7 +43,7 @@ export default async function handler( if (user && user.Subscription.length > 0) { return res.status(200).json({ - redirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`, + redirectUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/login`, }); } @@ -103,8 +103,8 @@ export default async function handler( mode: 'subscription', metadata, allow_promotion_codes: true, - success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, - cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing?email=${encodeURIComponent( + success_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, + cancel_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}/pricing?email=${encodeURIComponent( email, )}&name=${encodeURIComponent(name)}&planId=${planId}&cancelled=true`, }); diff --git a/apps/web/next.config.js b/apps/web/next.config.js index c3c3e1b85..d23272725 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); const { version } = require('./package.json'); +const { remapVercelEnv } = require('../../scripts/remap-vercel-env.cjs'); const { parsed: env } = require('dotenv').config({ path: path.join(__dirname, '../../.env.local'), diff --git a/apps/web/process-env.d.ts b/apps/web/process-env.d.ts index 1cb0018ac..4149423dd 100644 --- a/apps/web/process-env.d.ts +++ b/apps/web/process-env.d.ts @@ -1,6 +1,7 @@ declare namespace NodeJS { export interface ProcessEnv { - NEXT_PUBLIC_SITE_URL?: string; + NEXT_PUBLIC_WEBAPP_URL?: string; + NEXT_PUBLIC_MARKETING_URL?: string; NEXT_PRIVATE_DATABASE_URL: string; diff --git a/apps/web/src/app/(dashboard)/settings/billing/page.tsx b/apps/web/src/app/(dashboard)/settings/billing/page.tsx index 555c645ce..bd2659e62 100644 --- a/apps/web/src/app/(dashboard)/settings/billing/page.tsx +++ b/apps/web/src/app/(dashboard)/settings/billing/page.tsx @@ -35,7 +35,7 @@ export default async function BillingSettingsPage() { if (subscription.customerId) { billingPortalUrl = await getPortalSession({ customerId: subscription.customerId, - returnUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/settings/billing`, + returnUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/settings/billing`, }); } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 2ce8744d4..2a1d082f9 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -33,12 +33,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_SITE_URL}/opengraph-image.jpg`], + images: [`${process.env.NEXT_PUBLIC_WEBAPP_URL}/opengraph-image.jpg`], }, twitter: { site: '@documenso', card: 'summary_large_image', - images: [`${process.env.NEXT_PUBLIC_SITE_URL}/opengraph-image.jpg`], + 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.', }, diff --git a/apps/web/src/helpers/get-feature-flag.ts b/apps/web/src/helpers/get-feature-flag.ts index 3b6c66528..d5cd26c33 100644 --- a/apps/web/src/helpers/get-feature-flag.ts +++ b/apps/web/src/helpers/get-feature-flag.ts @@ -21,7 +21,7 @@ export const getFlag = async ( return LOCAL_FEATURE_FLAGS[flag] ?? true; } - const url = new URL(`${process.env.NEXT_PUBLIC_SITE_URL}/api/feature-flag/get`); + const url = new URL(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/feature-flag/get`); url.searchParams.set('flag', flag); const response = await fetch(url, { @@ -54,7 +54,7 @@ export const getAllFlags = async ( return LOCAL_FEATURE_FLAGS; } - const url = new URL(`${process.env.NEXT_PUBLIC_SITE_URL}/api/feature-flag/all`); + const url = new URL(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/feature-flag/all`); return fetch(url, { headers: { diff --git a/apps/web/src/pages/api/claim-plan/index.ts b/apps/web/src/pages/api/claim-plan/index.ts index abad354a8..3d2d8679b 100644 --- a/apps/web/src/pages/api/claim-plan/index.ts +++ b/apps/web/src/pages/api/claim-plan/index.ts @@ -43,7 +43,7 @@ export default async function handler( if (user && user.Subscription.length > 0) { return res.status(200).json({ - redirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`, + redirectUrl: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/login`, }); } @@ -103,8 +103,8 @@ export default async function handler( mode: 'subscription', metadata, allow_promotion_codes: true, - success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, - cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing?email=${encodeURIComponent( + success_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, + cancel_url: `${process.env.NEXT_PUBLIC_MARKETING_URL}/pricing?email=${encodeURIComponent( email, )}&name=${encodeURIComponent(name)}&planId=${planId}&cancelled=true`, }); diff --git a/assets/example.pdf b/assets/example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f908d84e124bc485ffe198f9d5949caa12ed2d17 GIT binary patch literal 39842 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 Za5@759sL7WP9_qM#Gv`*<&6&R{TJLMdWZl3 literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index f4cf47807..39b1a1841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3489,12 +3489,12 @@ } }, "node_modules/@prisma/client": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.0.0.tgz", - "integrity": "sha512-XlO5ELNAQ7rV4cXIDJUNBEgdLwX3pjtt9Q/RHqDpGf43szpNJx2hJnggfFs7TKNx0cOFsl6KJCSfqr5duEU/bQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz", + "integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584" + "@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" }, "engines": { "node": ">=16.13" @@ -3509,15 +3509,15 @@ } }, "node_modules/@prisma/engines": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.0.0.tgz", - "integrity": "sha512-kyT/8fd0OpWmhAU5YnY7eP31brW1q1YrTGoblWrhQJDiN/1K+Z8S1kylcmtjqx5wsUGcP1HBWutayA/jtyt+sg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz", + "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==", "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584.tgz", - "integrity": "sha512-HHiUF6NixsldsP3JROq07TYBLEjXFKr6PdH8H4gK/XAoTmIplOJBCgrIUMrsRAnEuGyRoRLXKXWUb943+PFoKQ==" + "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", + "integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w==" }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", @@ -14820,12 +14820,12 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.0.0.tgz", - "integrity": "sha512-KYWk83Fhi1FH59jSpavAYTt2eoMVW9YKgu8ci0kuUnt6Dup5Qy47pcB4/TLmiPAbhGrxxSz7gsSnJcCmkyPANA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.3.1.tgz", + "integrity": "sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.0.0" + "@prisma/engines": "5.3.1" }, "bin": { "prisma": "build/index.js" @@ -18417,8 +18417,12 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@prisma/client": "5.0.0", - "prisma": "5.0.0" + "@prisma/client": "5.3.1", + "prisma": "5.3.1" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.1.6" } }, "packages/tailwind-config": { diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index 37ecc66b7..83a88f24a 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -48,8 +48,8 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) return; } - const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; - const signDocumentLink = `${process.env.NEXT_PUBLIC_SITE_URL}/sign/${recipient.token}`; + const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; + const signDocumentLink = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`; const template = createElement(DocumentInviteEmailTemplate, { documentName: document.title, diff --git a/packages/lib/universal/get-base-url.ts b/packages/lib/universal/get-base-url.ts index aa8884088..2120c9f54 100644 --- a/packages/lib/universal/get-base-url.ts +++ b/packages/lib/universal/get-base-url.ts @@ -8,8 +8,8 @@ export const getBaseUrl = () => { return `https://${process.env.VERCEL_URL}`; } - if (process.env.NEXT_PUBLIC_SITE_URL) { - return `https://${process.env.NEXT_PUBLIC_SITE_URL}`; + if (process.env.NEXT_PUBLIC_WEBAPP_URL) { + return process.env.NEXT_PUBLIC_WEBAPP_URL; } return `http://localhost:${process.env.PORT ?? 3000}`; diff --git a/packages/prisma/helper.ts b/packages/prisma/helper.ts new file mode 100644 index 000000000..d436771d1 --- /dev/null +++ b/packages/prisma/helper.ts @@ -0,0 +1,52 @@ +/// + +export const getDatabaseUrl = () => { + if (process.env.NEXT_PRIVATE_DATABASE_URL) { + return process.env.NEXT_PRIVATE_DATABASE_URL; + } + + if (process.env.POSTGRES_URL) { + process.env.NEXT_PRIVATE_DATABASE_URL = process.env.POSTGRES_URL; + process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.POSTGRES_URL; + } + + if (process.env.DATABASE_URL) { + process.env.NEXT_PRIVATE_DATABASE_URL = process.env.DATABASE_URL; + process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.DATABASE_URL; + } + + if (process.env.POSTGRES_PRISMA_URL) { + process.env.NEXT_PRIVATE_DATABASE_URL = process.env.POSTGRES_PRISMA_URL; + } + + if (process.env.POSTGRES_URL_NON_POOLING) { + process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.POSTGRES_URL_NON_POOLING; + } + + // We change the protocol from `postgres:` to `https:` so we can construct a easily + // mofifiable URL. + const url = new URL(process.env.NEXT_PRIVATE_DATABASE_URL.replace('postgres://', 'https://')); + + // If we're using a connection pool, we need to let Prisma know that + // we're using PgBouncer. + if (process.env.NEXT_PRIVATE_DATABASE_URL !== process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL) { + url.searchParams.set('pgbouncer', 'true'); + + process.env.NEXT_PRIVATE_DATABASE_URL = url.toString().replace('https://', 'postgres://'); + } + + // Support for neon.tech (Neon Database) + if (url.hostname.endsWith('neon.tech')) { + const [projectId, ...rest] = url.hostname.split('.'); + + if (!projectId.endsWith('-bouncer')) { + url.hostname = `${projectId}-pooler.${rest.join('.')}`; + } + + url.searchParams.set('pgbouncer', 'true'); + + process.env.NEXT_PRIVATE_DATABASE_URL = url.toString().replace('https://', 'postgres://'); + } + + return process.env.NEXT_PRIVATE_DATABASE_URL; +}; diff --git a/packages/prisma/index.ts b/packages/prisma/index.ts index 93a334caa..b9e290add 100644 --- a/packages/prisma/index.ts +++ b/packages/prisma/index.ts @@ -1,5 +1,7 @@ import { PrismaClient } from '@prisma/client'; +import { getDatabaseUrl } from './helper'; + declare global { // We need `var` to declare a global variable in TypeScript // eslint-disable-next-line no-var @@ -7,9 +9,13 @@ declare global { } if (!globalThis.prisma) { - globalThis.prisma = new PrismaClient(); + globalThis.prisma = new PrismaClient({ datasourceUrl: getDatabaseUrl() }); } -export const prisma = globalThis.prisma || new PrismaClient(); +export const prisma = + globalThis.prisma || + new PrismaClient({ + datasourceUrl: getDatabaseUrl(), + }); export const getPrismaClient = () => prisma; diff --git a/packages/prisma/package.json b/packages/prisma/package.json index 3ef12787a..958bcde17 100644 --- a/packages/prisma/package.json +++ b/packages/prisma/package.json @@ -9,10 +9,18 @@ "format": "prisma format", "prisma:generate": "prisma generate", "prisma:migrate-dev": "prisma migrate dev", - "prisma:migrate-deploy": "prisma migrate deploy" + "prisma:migrate-deploy": "prisma migrate deploy", + "prisma:seed": "prisma db seed" + }, + "prisma": { + "seed": "ts-node --transpileOnly --skipProject ./seed-database.ts" }, "dependencies": { - "@prisma/client": "5.0.0", - "prisma": "5.0.0" + "@prisma/client": "5.3.1", + "prisma": "5.3.1" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.1.6" } } diff --git a/packages/prisma/seed-database.ts b/packages/prisma/seed-database.ts new file mode 100644 index 000000000..65daa357e --- /dev/null +++ b/packages/prisma/seed-database.ts @@ -0,0 +1,82 @@ +import { DocumentDataType, Role } from '@prisma/client'; +import fs from 'node:fs'; +import path from 'node:path'; + +import { hashSync } from '@documenso/lib/server-only/auth/hash'; + +import { prisma } from './index'; + +const seedDatabase = async () => { + const examplePdf = fs + .readFileSync(path.join(__dirname, '../../assets/example.pdf')) + .toString('base64'); + + const exampleUser = await prisma.user.upsert({ + where: { + email: 'example@documenso.com', + }, + create: { + name: 'Example User', + email: 'example@documenso.com', + password: hashSync('password'), + roles: [Role.USER], + }, + update: {}, + }); + + const adminUser = await prisma.user.upsert({ + where: { + email: 'admin@documenso.com', + }, + create: { + name: 'Admin User', + email: 'admin@documenso.com', + password: hashSync('password'), + roles: [Role.USER, Role.ADMIN], + }, + update: {}, + }); + + const examplePdfData = await prisma.documentData.upsert({ + where: { + id: 'clmn0kv5k0000pe04vcqg5zla', + }, + create: { + id: 'clmn0kv5k0000pe04vcqg5zla', + type: DocumentDataType.BYTES_64, + data: examplePdf, + initialData: examplePdf, + }, + update: {}, + }); + + await prisma.document.upsert({ + where: { + id: 1, + }, + create: { + id: 1, + title: 'Example Document', + documentDataId: examplePdfData.id, + userId: exampleUser.id, + Recipient: { + create: { + name: String(adminUser.name), + email: adminUser.email, + token: Math.random().toString(36).slice(2, 9), + }, + }, + }, + update: {}, + }); +}; + +seedDatabase() + .then(() => { + console.log('Database seeded'); + process.exit(0); + }) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/packages/tsconfig/process-env.d.ts b/packages/tsconfig/process-env.d.ts index b0852b4f4..d32b9984a 100644 --- a/packages/tsconfig/process-env.d.ts +++ b/packages/tsconfig/process-env.d.ts @@ -1,6 +1,7 @@ declare namespace NodeJS { export interface ProcessEnv { - NEXT_PUBLIC_SITE_URL?: string; + NEXT_PUBLIC_WEBAPP_URL?: string; + NEXT_PUBLIC_MARKETING_URL?: string; NEXT_PRIVATE_GOOGLE_CLIENT_ID?: string; NEXT_PRIVATE_GOOGLE_CLIENT_SECRET?: string; @@ -40,5 +41,19 @@ declare namespace NodeJS { NEXT_PRIVATE_SMTP_FROM_NAME?: string; NEXT_PRIVATE_SMTP_FROM_ADDRESS?: string; + + /** + * Vercel environment variables + */ + VERCEL?: string; + VERCEL_ENV?: 'production' | 'development' | 'preview'; + VERCEL_URL?: string; + + DEPLOYMENT_TARGET?: 'webapp' | 'marketing'; + + POSTGRES_URL?: string; + DATABASE_URL?: string; + POSTGRES_PRISMA_URL?: string; + POSTGRES_URL_NON_POOLING?: string; } } diff --git a/scripts/remap-vercel-env.cjs b/scripts/remap-vercel-env.cjs new file mode 100644 index 000000000..95e60cde8 --- /dev/null +++ b/scripts/remap-vercel-env.cjs @@ -0,0 +1,45 @@ +/** @typedef {import('@documenso/tsconfig/process-env')} */ + +/** + * Remap Vercel environment variables to our defined Next.js environment variables. + * + * @deprecated This is no longer needed because we can't inject runtime environment variables via next.config.js + * + * @returns {void} + */ +const remapVercelEnv = () => { + if (!process.env.VERCEL || !process.env.DEPLOYMENT_TARGET) { + return; + } + + if (process.env.POSTGRES_URL) { + process.env.NEXT_PRIVATE_DATABASE_URL = process.env.POSTGRES_URL; + } + + if (process.env.POSTGRES_URL_NON_POOLING) { + process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.POSTGRES_URL_NON_POOLING; + } + + // If we're using a connection pool, we need to let Prisma know that + // we're using PgBouncer. + if (process.env.NEXT_PRIVATE_DATABASE_URL !== process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL) { + const url = new URL(process.env.NEXT_PRIVATE_DATABASE_URL); + + url.searchParams.set('pgbouncer', 'true'); + + process.env.NEXT_PRIVATE_DATABASE_URL = url.toString(); + } + + if (process.env.VERCEL_ENV !== 'production' && process.env.DEPLOYMENT_TARGET === 'webapp') { + process.env.NEXTAUTH_URL = `https://${process.env.VERCEL_URL}`; + process.env.NEXT_PUBLIC_WEBAPP_URL = `https://${process.env.VERCEL_URL}`; + } + + if (process.env.VERCEL_ENV !== 'production' && process.env.DEPLOYMENT_TARGET === 'marketing') { + process.env.NEXT_PUBLIC_MARKETING_URL = `https://${process.env.VERCEL_URL}`; + } +}; + +module.exports = { + remapVercelEnv, +}; diff --git a/scripts/vercel.sh b/scripts/vercel.sh new file mode 100755 index 000000000..e4ab23622 --- /dev/null +++ b/scripts/vercel.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Exit on error. +set -eo pipefail + +# Get the directory of this script, regardless of where it is called from. +SCRIPT_DIR="$(readlink -f "$(dirname "$0")")" + + +function log() { + echo "[VercelBuild]: $1" +} + +function build_webapp() { + log "Building webapp for $VERCEL_ENV" + + remap_database_integration + + npm run prisma:generate --workspace=@documenso/prisma + npm run prisma:migrate-deploy --workspace=@documenso/prisma + + if [[ "$VERCEL_ENV" != "production" ]]; then + log "Seeding database for $VERCEL_ENV" + + npm run prisma:seed --workspace=@documenso/prisma + fi + + npm run build -- --filter @documenso/web +} + +function remap_webapp_env() { + if [[ "$VERCEL_ENV" != "production" ]]; then + log "Remapping webapp environment variables for $VERCEL_ENV" + + export NEXTAUTH_URL="https://$VERCEL_URL" + export NEXT_PUBLIC_WEBAPP_URL="https://$VERCEL_URL" + fi +} + +function build_marketing() { + log "Building marketing for $VERCEL_ENV" + + remap_database_integration + + npm run prisma:generate --workspace=@documenso/prisma + npm run build -- --filter @documenso/marketing +} + +function remap_marketing_env() { + if [[ "$VERCEL_ENV" != "production" ]]; then + log "Remapping marketing environment variables for $VERCEL_ENV" + + export NEXT_PUBLIC_MARKETING_URL="https://$VERCEL_URL" + fi +} + +function remap_database_integration() { + log "Remapping Supabase integration for $VERCEL_ENV" + + if [[ ! -z "$POSTGRES_URL" ]]; then + export NEXT_PRIVATE_DATABASE_URL="$POSTGRES_URL" + export NEXT_PRIVATE_DIRECT_DATABASE_URL="$POSTGRES_URL" + fi + + if [[ ! -z "$DATABASE_URL" ]]; then + export NEXT_PRIVATE_DATABASE_URL="$DATABASE_URL" + export NEXT_PRIVATE_DIRECT_DATABASE_URL="$DATABASE_URL" + fi + + if [[ ! -z "$POSTGRES_URL_NON_POOLING" ]]; then + export NEXT_PRIVATE_DATABASE_URL="$POSTGRES_URL?pgbouncer=true" + export NEXT_PRIVATE_DIRECT_DATABASE_URL="$POSTGRES_URL_NON_POOLING" + fi + + + if [[ "$NEXT_PRIVATE_DATABASE_URL" == *"neon.tech"* ]]; then + log "Remapping for Neon integration" + + PROJECT_ID="$(echo "$PGHOST" | cut -d'.' -f1)" + PGBOUNCER_HOST="$(echo "$PGHOST" | sed "s/${PROJECT_ID}/${PROJECT_ID}-pooler/")" + + export NEXT_PRIVATE_DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGBOUNCER_HOST}/${PGDATABASE}?pgbouncer=true" + fi +} + +# Navigate to the root of the project. +cd "$SCRIPT_DIR/.." + +# Check if the script is running on Vercel. +if [[ -z "$VERCEL" ]]; then + log "ERROR - This script must be run as part of the Vercel build process." + exit 1 +fi + +case "$DEPLOYMENT_TARGET" in + "webapp") + build_webapp + ;; + "marketing") + build_marketing + ;; + *) + log "ERROR - Missing or invalid DEPLOYMENT_TARGET environment variable." + log "ERROR - DEPLOYMENT_TARGET must be either 'webapp' or 'marketing'." + exit 1 + ;; +esac diff --git a/turbo.json b/turbo.json index a5b333c66..01b8bd487 100644 --- a/turbo.json +++ b/turbo.json @@ -2,8 +2,13 @@ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { - "dependsOn": ["^build"], - "outputs": [".next/**", "!.next/cache/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + ".next/**", + "!.next/cache/**" + ] }, "lint": {}, "dev": { @@ -11,20 +16,22 @@ "persistent": true } }, - "globalDependencies": ["**/.env.*local"], + "globalDependencies": [ + "**/.env.*local" + ], "globalEnv": [ "APP_VERSION", "NEXTAUTH_URL", "NEXTAUTH_SECRET", - "NEXT_PUBLIC_APP_URL", - "NEXT_PUBLIC_SITE_URL", + "NEXT_PUBLIC_WEBAPP_URL", + "NEXT_PUBLIC_MARKETING_URL", "NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST", "NEXT_PUBLIC_FEATURE_BILLING_ENABLED", "NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID", "NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID", "NEXT_PRIVATE_DATABASE_URL", - "NEXT_PRIVATE_NEXT_AUTH_SECRET", + "NEXT_PRIVATE_DIRECT_DATABASE_URL", "NEXT_PRIVATE_GOOGLE_CLIENT_ID", "NEXT_PRIVATE_GOOGLE_CLIENT_SECRET", "NEXT_PUBLIC_UPLOAD_TRANSPORT", @@ -48,6 +55,15 @@ "NEXT_PRIVATE_SMTP_SECURE", "NEXT_PRIVATE_SMTP_FROM_NAME", "NEXT_PRIVATE_SMTP_FROM_ADDRESS", - "NEXT_PRIVATE_STRIPE_API_KEY" + "NEXT_PRIVATE_STRIPE_API_KEY", + + "VERCEL", + "VERCEL_ENV", + "VERCEL_URL", + "DEPLOYMENT_TARGET", + "POSTGRES_URL", + "DATABASE_URL", + "POSTGRES_PRISMA_URL", + "POSTGRES_URL_NON_POOLING" ] }