diff --git a/apps/dashboard/.env b/apps/dashboard/.env index 787a424..f7321ea 100644 --- a/apps/dashboard/.env +++ b/apps/dashboard/.env @@ -1,2 +1,2 @@ -NEXT_PUBLIC_SUPABASE_URL= -NEXT_PUBLIC_SUPABASE_ANON_KEY= \ No newline at end of file +SUPABASE_URL= +SUPABASE_ANON_KEY= \ No newline at end of file diff --git a/apps/dashboard/globals.d.ts b/apps/dashboard/globals.d.ts index 3fafc0e..f824a2f 100644 --- a/apps/dashboard/globals.d.ts +++ b/apps/dashboard/globals.d.ts @@ -6,11 +6,4 @@ declare module '*.svg' { export default content; } -declare global { - namespace globalThis { - // eslint-disable-next-line no-var - var EdgeRuntime: string; - } -} - export {}; diff --git a/apps/dashboard/next.config.mjs b/apps/dashboard/next.config.mjs index c3205db..eb0fe09 100644 --- a/apps/dashboard/next.config.mjs +++ b/apps/dashboard/next.config.mjs @@ -5,8 +5,8 @@ const jiti = createJiti(fileURLToPath(import.meta.url)); import { rewrites } from './src/lib/rewrites.mjs'; import { headers } from './src/lib/headers.mjs'; -jiti('./src/env/client'); -jiti('./src/env/server'); +jiti('./src/env/client-env'); +const serverEnv = jiti('./src/env/server-env'); /** @type {import('next').NextConfig} */ const nextConfig = { @@ -43,6 +43,8 @@ export default withSentryConfig(nextConfig, { org: 'ds-pro', project: 'engine', + authToken: serverEnv.SENTRY_AUTH_TOKEN, + // Only print logs for uploading source maps in CI silent: !process.env.CI, @@ -69,4 +71,9 @@ export default withSentryConfig(nextConfig, { // https://docs.sentry.io/product/crons/ // https://vercel.com/docs/cron-jobs automaticVercelMonitors: true, + + sourcemaps: { + // Only upload source maps in production + disable: serverEnv.VERCEL_ENV !== 'production', + }, }); diff --git a/apps/dashboard/sentry.client.config.ts b/apps/dashboard/sentry.client.config.ts index e478b68..6cc451a 100644 --- a/apps/dashboard/sentry.client.config.ts +++ b/apps/dashboard/sentry.client.config.ts @@ -2,7 +2,7 @@ // The config you add here will be used whenever a users loads a page in their browser. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import { clientEnv } from '@/env/client'; +import { clientEnv } from '@/env/client-env'; import * as Sentry from '@sentry/nextjs'; Sentry.init({ diff --git a/apps/dashboard/sentry.edge.config.ts b/apps/dashboard/sentry.edge.config.ts index 9c3e91e..4bdb87b 100644 --- a/apps/dashboard/sentry.edge.config.ts +++ b/apps/dashboard/sentry.edge.config.ts @@ -3,11 +3,11 @@ // Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import { clientEnv } from '@/env/client'; +import { serverEnv } from '@/env/server-env'; import * as Sentry from '@sentry/nextjs'; Sentry.init({ - enabled: clientEnv.NEXT_PUBLIC_VERCEL_ENV === 'production', + enabled: serverEnv.VERCEL_ENV === 'production', dsn: 'https://bee796b3d7d7f0364e1dc326183331f0@o4507860870299648.ingest.de.sentry.io/4507860871872592', // Adjust this value in production, or use tracesSampler for greater control diff --git a/apps/dashboard/sentry.server.config.ts b/apps/dashboard/sentry.server.config.ts index 61250bd..21ba4ad 100644 --- a/apps/dashboard/sentry.server.config.ts +++ b/apps/dashboard/sentry.server.config.ts @@ -2,11 +2,11 @@ // The config you add here will be used whenever the server handles a request. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import { clientEnv } from '@/env/client'; +import { serverEnv } from '@/env/server-env'; import * as Sentry from '@sentry/nextjs'; Sentry.init({ - enabled: clientEnv.NEXT_PUBLIC_VERCEL_ENV === 'production', + enabled: serverEnv.VERCEL_ENV === 'production', dsn: 'https://bee796b3d7d7f0364e1dc326183331f0@o4507860870299648.ingest.de.sentry.io/4507860871872592', // Adjust this value in production, or use tracesSampler for greater control diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index 626501f..31c86bd 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -6,6 +6,7 @@ import { Toaster } from '@ds-project/components'; import { Favicon } from '@/components'; import dynamic from 'next/dynamic'; import { getMetadata } from '@/lib/metadata'; +import { clientEnv } from '@/env/client-env'; const inter = Inter({ subsets: ['latin'] }); @@ -31,7 +32,9 @@ export default function RootLayout({ - + import('react-json-view'), { export function JsonBlock(props: ComponentProps) { return ( -
+
); diff --git a/apps/dashboard/src/config.ts b/apps/dashboard/src/config.ts index 8fb9c47..97b7357 100644 --- a/apps/dashboard/src/config.ts +++ b/apps/dashboard/src/config.ts @@ -1,11 +1,12 @@ -import { clientEnv } from './env/client'; +import { clientEnv } from './env/client-env'; +import { serverEnv } from './env/server-env'; const pageUrl = (() => { - switch (clientEnv.NEXT_PUBLIC_VERCEL_ENV) { + switch (serverEnv.VERCEL_ENV) { case 'production': return 'https://getds.pro'; case 'preview': - return `https://${clientEnv.NEXT_PUBLIC_VERCEL_URL}`; + return `https://${serverEnv.VERCEL_URL}`; default: return 'http://localhost:3000'; } diff --git a/apps/dashboard/src/env/client-env.ts b/apps/dashboard/src/env/client-env.ts new file mode 100644 index 0000000..59dfcee --- /dev/null +++ b/apps/dashboard/src/env/client-env.ts @@ -0,0 +1,16 @@ +/* eslint-disable no-restricted-properties */ +import { createEnv } from '@t3-oss/env-nextjs'; +import { z } from 'zod'; + +export const clientEnv = createEnv({ + client: { + NEXT_PUBLIC_VERCEL_ENV: z + .enum(['development', 'preview', 'production']) + .optional(), + NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1), + }, + runtimeEnv: { + NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV, + NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, + }, +}); diff --git a/apps/dashboard/src/env/client.ts b/apps/dashboard/src/env/client.ts deleted file mode 100644 index d3a40c7..0000000 --- a/apps/dashboard/src/env/client.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable no-restricted-properties */ -import { createEnv } from '@t3-oss/env-nextjs'; -import { z } from 'zod'; -import { vercel } from '@t3-oss/env-core/presets'; - -export const clientEnv = createEnv({ - extends: [vercel()], - client: { - NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1), - NEXT_PUBLIC_SUPABASE_URL: z.string().url(), - NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1), - NEXT_PUBLIC_VERCEL_ENV: z.union([ - z.literal('development'), - z.literal('preview'), - z.literal('production'), - ]), - NEXT_PUBLIC_VERCEL_URL: z.string().min(1).optional(), - }, - runtimeEnv: { - NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, - NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, - NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV, - NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL, - }, -}); diff --git a/apps/dashboard/src/env/server.ts b/apps/dashboard/src/env/server-env.ts similarity index 71% rename from apps/dashboard/src/env/server.ts rename to apps/dashboard/src/env/server-env.ts index dfef3b9..d831613 100644 --- a/apps/dashboard/src/env/server.ts +++ b/apps/dashboard/src/env/server-env.ts @@ -1,10 +1,13 @@ /* eslint-disable no-restricted-properties */ import { createEnv } from '@t3-oss/env-nextjs'; import { z } from 'zod'; +import { vercel } from '@t3-oss/env-core/presets'; export const serverEnv = createEnv({ + extends: [vercel()], isServer: typeof window === 'undefined', server: { + NEXT_RUNTIME: z.enum(['nodejs', 'edge']).optional(), NODE_ENV: z.enum(['development', 'test', 'production']).optional(), GITHUB_APP_ID: z.string().min(1), GITHUB_APP_PRIVATE_KEY: z.string().min(1), @@ -13,6 +16,9 @@ export const serverEnv = createEnv({ FIGMA_APP_CLIENT_ID: z.string().min(1), FIGMA_APP_CLIENT_SECRET: z.string().min(1), POSTGRES_URL: z.string().min(1), + SUPABASE_URL: z.string().url(), + SUPABASE_ANON_KEY: z.string().min(1), + SENTRY_AUTH_TOKEN: z.string().min(1).optional(), }, experimental__runtimeEnv: process.env, }); diff --git a/apps/dashboard/src/instrumentation.ts b/apps/dashboard/src/instrumentation.ts index 9339d50..b830b42 100644 --- a/apps/dashboard/src/instrumentation.ts +++ b/apps/dashboard/src/instrumentation.ts @@ -1,7 +1,11 @@ +import { serverEnv } from './env/server-env'; + export async function register() { - if (typeof globalThis.EdgeRuntime !== 'string') { + if (serverEnv.NEXT_RUNTIME === 'nodejs') { await import('../sentry.server.config'); - } else { + } + + if (serverEnv.NEXT_RUNTIME === 'edge') { await import('../sentry.edge.config'); } } diff --git a/apps/dashboard/src/lib/analytics/provider.tsx b/apps/dashboard/src/lib/analytics/provider.tsx index dce6c97..f2f626c 100644 --- a/apps/dashboard/src/lib/analytics/provider.tsx +++ b/apps/dashboard/src/lib/analytics/provider.tsx @@ -1,29 +1,33 @@ 'use client'; -import { clientEnv } from '@/env/client'; +import { clientEnv } from '@/env/client-env'; import posthog from 'posthog-js'; import { PostHogProvider } from 'posthog-js/react'; - -if ( - typeof window !== 'undefined' && - clientEnv.NEXT_PUBLIC_VERCEL_ENV === 'production' -) { - posthog.init(clientEnv.NEXT_PUBLIC_POSTHOG_KEY, { - api_host: '/_proxy/posthog', - ui_host: 'https://eu.posthog.com', - person_profiles: 'identified_only', - capture_pageview: false, - capture_pageleave: true, - loaded: (posthog) => { - if (clientEnv.NEXT_PUBLIC_VERCEL_ENV === 'development') - posthog.debug(true); - }, - }); -} +import { useEffect } from 'react'; interface AnalyticsProviderProps { children: React.ReactNode; + enabled?: boolean; } -export function AnalyticsProvider({ children }: AnalyticsProviderProps) { +export function AnalyticsProvider({ + children, + enabled = false, +}: AnalyticsProviderProps) { + useEffect(() => { + if (enabled && typeof window !== 'undefined') { + posthog.init(clientEnv.NEXT_PUBLIC_POSTHOG_KEY, { + api_host: '/_proxy/posthog', + ui_host: 'https://eu.posthog.com', + person_profiles: 'identified_only', + capture_pageview: false, + capture_pageleave: true, + loaded: (posthog) => { + if (clientEnv.NEXT_PUBLIC_VERCEL_ENV === 'development') + posthog.debug(true); + }, + }); + } + }, [enabled]); + return {children}; } diff --git a/apps/dashboard/src/lib/figma/figma.ts b/apps/dashboard/src/lib/figma/figma.ts index 2dee3a7..cdcb18d 100644 --- a/apps/dashboard/src/lib/figma/figma.ts +++ b/apps/dashboard/src/lib/figma/figma.ts @@ -16,7 +16,7 @@ import { import { database } from '@ds-project/database/client'; import { cache } from 'react'; import { api } from '@ds-project/api/rsc'; -import { serverEnv } from '@/env/server'; +import { serverEnv } from '@/env/server-env'; class Figma { private apiUrl = 'https://api.figma.com'; diff --git a/apps/dashboard/src/lib/metadata.ts b/apps/dashboard/src/lib/metadata.ts index a4a631f..8e997e9 100644 --- a/apps/dashboard/src/lib/metadata.ts +++ b/apps/dashboard/src/lib/metadata.ts @@ -1,4 +1,4 @@ -import { clientEnv } from '@/env/client'; +import { config } from '@/config'; import type { Metadata } from 'next'; interface GetMetadataArgs { @@ -12,8 +12,8 @@ export function getMetadata({ title }: GetMetadataArgs = {}): Metadata { keywords: 'design system, devops, engine, design, system, design tokens', openGraph: { siteName: 'DS Pro', - url: `https://${clientEnv.NEXT_PUBLIC_VERCEL_URL}/api/og`, - images: [`https://${clientEnv.NEXT_PUBLIC_VERCEL_URL}/api/og`], + url: `${config.pageUrl}/api/og`, + images: [`${config.pageUrl}/api/og`], }, }; } diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts index 2043aa8..0093f3f 100644 --- a/apps/dashboard/src/middleware.ts +++ b/apps/dashboard/src/middleware.ts @@ -1,7 +1,6 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { createMiddlewareClient } from '@ds-project/auth/middleware'; -import { clientEnv } from '@/env/client'; import { handleFigmaAuth, exchangeApiKey, @@ -10,14 +9,15 @@ import { isFigmaAuthPath, hasOnGoingFigmaAuth, } from './lib/middleware/utils'; +import { serverEnv } from './env/server-env'; export async function middleware(request: NextRequest) { const response = NextResponse.next({ request }); const url = request.nextUrl.clone(); const supabase = createMiddlewareClient(request, response, { - supabaseAnonKey: clientEnv.NEXT_PUBLIC_SUPABASE_ANON_KEY, - supabaseUrl: clientEnv.NEXT_PUBLIC_SUPABASE_URL, + supabaseAnonKey: serverEnv.SUPABASE_ANON_KEY, + supabaseUrl: serverEnv.SUPABASE_URL, }); // Figma middleware logic diff --git a/packages/auth/src/client/client.ts b/packages/auth/src/client/client.ts index f5fd79c..7a04cd8 100644 --- a/packages/auth/src/client/client.ts +++ b/packages/auth/src/client/client.ts @@ -4,8 +4,5 @@ import { env } from '@/config'; export function createBrowserClient(): ReturnType< typeof createClient > { - return createClient( - env.NEXT_PUBLIC_SUPABASE_URL, - env.NEXT_PUBLIC_SUPABASE_ANON_KEY - ); + return createClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY); } diff --git a/packages/auth/src/config.ts b/packages/auth/src/config.ts index 119f4e0..404f544 100644 --- a/packages/auth/src/config.ts +++ b/packages/auth/src/config.ts @@ -6,8 +6,8 @@ loadEnvConfig(process.cwd()); export const env = createEnv({ server: { - NEXT_PUBLIC_SUPABASE_URL: z.string().url(), - NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1), + SUPABASE_URL: z.string().url(), + SUPABASE_ANON_KEY: z.string().min(1), }, runtimeEnv: process.env, emptyStringAsUndefined: true, diff --git a/packages/auth/src/server/client.ts b/packages/auth/src/server/client.ts index 4b3ef3b..e63c1ac 100644 --- a/packages/auth/src/server/client.ts +++ b/packages/auth/src/server/client.ts @@ -8,26 +8,22 @@ export function createServerClient(): ReturnType< > { const cookieStore = cookies(); - return createClient( - env.NEXT_PUBLIC_SUPABASE_URL, - env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - { - cookies: { - getAll() { - return cookieStore.getAll(); - }, - setAll(cookiesToSet) { - try { - cookiesToSet.forEach(({ name, value, options }) => - cookieStore.set(name, value, options as ResponseCookie) - ); - } catch { - // The `setAll` method was called from a Server Component. - // This can be ignored if you have middleware refreshing - // user sessions. - } - }, + return createClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY, { + cookies: { + getAll() { + return cookieStore.getAll(); }, - } - ); + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => + cookieStore.set(name, value, options as ResponseCookie) + ); + } catch { + // The `setAll` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } + }, + }, + }); } diff --git a/turbo.json b/turbo.json index 362a736..924e051 100644 --- a/turbo.json +++ b/turbo.json @@ -2,6 +2,7 @@ "$schema": "https://turbo.build/schema.json", "ui": "tui", "globalPassThroughEnv": [ + "NEXT_RUNTIME", "GITHUB_APP_ID", "GITHUB_APP_PRIVATE_KEY", "GITHUB_APP_CLIENT_ID", @@ -9,8 +10,8 @@ "FIGMA_APP_CLIENT_ID", "FIGMA_APP_CLIENT_SECRET", "POSTGRES_URL", - "NEXT_PUBLIC_SUPABASE_ANON_KEY", - "NEXT_PUBLIC_SUPABASE_URL", + "SUPABASE_URL", + "SUPABASE_ANON_KEY", "NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST", "NEXT_PUBLIC_VERCEL_ENV",