diff --git a/.env.example b/.env similarity index 100% rename from .env.example rename to .env diff --git a/src/app/api/trpc/[trpc]/route.ts b/src/app/api/trpc/[trpc]/route.ts index 267aad0b7..805668a9f 100644 --- a/src/app/api/trpc/[trpc]/route.ts +++ b/src/app/api/trpc/[trpc]/route.ts @@ -1,7 +1,7 @@ import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import type { NextRequest } from "next/server"; -import { env } from "@/env"; +import { env, envUtils } from "@/env"; import { appRouter } from "@/trpc/api/root"; import { createTRPCContext } from "@/trpc/api/trpc"; @@ -22,7 +22,7 @@ const handler = (req: NextRequest) => router: appRouter, createContext: () => createContext(req), onError: - env.NODE_ENV === "development" + envUtils.isDevelopment() ? ({ path, error }) => { console.error( `❌ tRPC failed on ${path ?? ""}: ${error.message}`, diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3e899f6a1..2cfec265b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import logo from "@/assets/logo.svg"; import { PublicEnvScript } from "@/components/public-env-script"; import ScreenSize from "@/components/screen-size"; import { constants } from "@/lib/constants"; +import { envUtils } from "@/env"; import { cn } from "@/lib/utils"; import { NextAuthProvider } from "@/providers/next-auth"; import { ProgressBarProvider } from "@/providers/progress-bar"; @@ -30,7 +31,6 @@ export default async function RootLayout({ children: React.ReactNode; }) { const session = await getServerComponentAuthSession(); - const nodeEnv = process.env.NODE_ENV; return ( @@ -43,7 +43,7 @@ export default async function RootLayout({
{children}
- {nodeEnv === "development" && } + {envUtils.isDevelopment() && }
diff --git a/src/components/onboarding/signin/index.tsx b/src/components/onboarding/signin/index.tsx index e6c1df9fc..229f12d11 100644 --- a/src/components/onboarding/signin/index.tsx +++ b/src/components/onboarding/signin/index.tsx @@ -27,6 +27,7 @@ import { useForm } from "react-hook-form"; import { toast } from "sonner"; import * as z from "zod"; import { AuthFormHeader } from "../auth-form-header"; +import { envUtils } from "@/env"; const loginSchema = z.object({ email: z.string().email(), @@ -47,8 +48,8 @@ const SignInForm = ({ isGoogleAuthEnabled }: LoginFormProps) => { const form = useForm>({ resolver: zodResolver(loginSchema), defaultValues: { - email: process.env.NODE_ENV === "development" ? "ceo@example.com" : "", - password: process.env.NODE_ENV === "development" ? "P@ssw0rd!" : "", + email: envUtils.isDevelopment() ? "ceo@example.com" : "", + password: envUtils.isDevelopment() ? "P@ssw0rd!" : "", }, }); const isSubmitting = form.formState.isSubmitting; diff --git a/src/env.js b/src/env.js index 6a914b993..5658aafbb 100644 --- a/src/env.js +++ b/src/env.js @@ -16,6 +16,75 @@ const readRuntimePublicEnvVariable = (key) => { return process.env[key]; }; +// Utility functions to check environment +function isDevelopment() { + // First check the custom environment variable from runtime + if (isBrowser() && window?.[PUBLIC_ENV_KEY]?.NEXT_PUBLIC_APP_ENV) { + return window[PUBLIC_ENV_KEY].NEXT_PUBLIC_APP_ENV === "development"; + } + + // Then check process.env + if (process.env.NEXT_PUBLIC_APP_ENV) { + return process.env.NEXT_PUBLIC_APP_ENV === "development"; + } + + // Fall back to NODE_ENV + return process.env.NODE_ENV === "development"; +} + +function isProduction() { + // First check the custom environment variable from runtime + if (isBrowser() && window?.[PUBLIC_ENV_KEY]?.NEXT_PUBLIC_APP_ENV) { + return window[PUBLIC_ENV_KEY].NEXT_PUBLIC_APP_ENV === "production"; + } + + // Then check process.env + if (process.env.NEXT_PUBLIC_APP_ENV) { + return process.env.NEXT_PUBLIC_APP_ENV === "production"; + } + + // Fall back to NODE_ENV + return process.env.NODE_ENV === "production"; +} + +function isTest() { + // First check the custom environment variable from runtime + if (isBrowser() && window?.[PUBLIC_ENV_KEY]?.NEXT_PUBLIC_APP_ENV) { + return window[PUBLIC_ENV_KEY].NEXT_PUBLIC_APP_ENV === "test"; + } + + // Then check process.env + if (process.env.NEXT_PUBLIC_APP_ENV) { + return process.env.NEXT_PUBLIC_APP_ENV === "test"; + } + + // Fall back to NODE_ENV + return process.env.NODE_ENV === "test"; +} + +function isStaging() { + // First check the custom environment variable from runtime + if (isBrowser() && window?.[PUBLIC_ENV_KEY]?.NEXT_PUBLIC_APP_ENV) { + return window[PUBLIC_ENV_KEY].NEXT_PUBLIC_APP_ENV === "staging"; + } + + // Then check process.env + if (process.env.NEXT_PUBLIC_APP_ENV) { + return process.env.NEXT_PUBLIC_APP_ENV === "staging"; + } + + // Fall back to NODE_ENV + return process.env.NODE_ENV === "staging"; +} + +// Export utility functions for checking environment +export const envUtils = { + isDevelopment, + isProduction, + isTest, + isStaging +}; + export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app @@ -73,6 +142,7 @@ export const env = createEnv({ client: { NEXT_PUBLIC_BASE_URL: z.string(), NEXT_PUBLIC_UPLOAD_DOMAIN: z.string().optional(), + NEXT_PUBLIC_APP_ENV: z.enum(["development", "test", "production", "staging"]).default("production"), // stripe NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().optional(), @@ -122,6 +192,7 @@ export const env = createEnv({ /// job queue WORKER_DATABASE_URL: process.env.WORKER_DATABASE_URL, + NEXT_PUBLIC_APP_ENV: readRuntimePublicEnvVariable("NEXT_PUBLIC_APP_ENV"), }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx index cadb403ee..279ee3dd2 100644 --- a/src/trpc/react.tsx +++ b/src/trpc/react.tsx @@ -7,6 +7,7 @@ import { useState } from "react"; import { type AppRouter } from "@/trpc/api/root"; import { getUrl, transformer } from "./shared"; +import { envUtils } from "@/env"; export const api = createTRPCReact(); @@ -22,7 +23,7 @@ export function TRPCReactProvider(props: { links: [ loggerLink({ enabled: (op) => - process.env.NODE_ENV === "development" || + envUtils.isDevelopment() || (op.direction === "down" && op.result instanceof Error), }), unstable_httpBatchStreamLink({