diff --git a/next.config.mjs b/next.config.mjs
index 811a2e2..8949b40 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -2,9 +2,10 @@
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
* for Docker builds.
*/
-import './src/env.js'
+
import { createMDX } from 'fumadocs-mdx/next'
import createNextIntlPlugin from 'next-intl/plugin'
+import './src/env.js'
const withMDX = createMDX()
diff --git a/src/app/api/neatqueue-webhook/route.ts b/src/app/api/neatqueue-webhook/route.ts
index f48c594..ef9c3ee 100644
--- a/src/app/api/neatqueue-webhook/route.ts
+++ b/src/app/api/neatqueue-webhook/route.ts
@@ -1,11 +1,12 @@
import crypto from 'node:crypto'
import { type NextRequest, NextResponse } from 'next/server'
+import { env } from '@/env'
import { globalEmitter } from '@/lib/events'
import type { PlayerState } from '@/server/api/routers/player-state'
import { PLAYER_STATE_KEY, redis } from '@/server/redis'
import { leaderboardService } from '@/server/services/leaderboard'
-const EXPECTED_QUERY_SECRET = process.env.WEBHOOK_QUERY_SECRET
+const EXPECTED_QUERY_SECRET = env.WEBHOOK_QUERY_SECRET
type WebhookPlayer = {
id?: string | number
diff --git a/src/app/docs/[[...slug]]/page.tsx b/src/app/docs/[[...slug]]/page.tsx
index 880a566..fa9bf1a 100644
--- a/src/app/docs/[[...slug]]/page.tsx
+++ b/src/app/docs/[[...slug]]/page.tsx
@@ -19,6 +19,7 @@ import { Nemesis } from '@/app/_components/nemesis'
import { Spectral } from '@/app/_components/spectral'
import { Xmult } from '@/app/_components/xmult'
import { Button } from '@/components/ui/button'
+import { env } from '@/env'
import { CDN_URL } from '@/shared/constants'
import { metadataImage } from '../../../../lib/metadata'
import { source } from '../../../../lib/source'
@@ -73,8 +74,7 @@ export default async function Page(props: {
...defaultMdxComponents,
img: (props: ImageZoomProps) => {
const isDev =
- process.env.NODE_ENV === 'development' ||
- process.env.IS_PREVIEW === 'true'
+ env.NODE_ENV === 'development' || env.IS_PREVIEW === 'true'
if (isDev) {
return
}
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
index 50c0242..9c26760 100644
--- a/src/components/ui/avatar.tsx
+++ b/src/components/ui/avatar.tsx
@@ -3,6 +3,7 @@
import * as AvatarPrimitive from '@radix-ui/react-avatar'
import type * as React from 'react'
+import { env } from '@/env'
import { cn } from '@/lib/utils'
import { CDN_URL } from '@/shared/constants'
@@ -27,7 +28,7 @@ function AvatarImage({
src,
...props
}: React.ComponentProps) {
- const isDev = process.env.NODE_ENV === 'development'
+ const isDev = env.NODE_ENV === 'development'
let finalSrc = src
if (typeof src === 'string' && src.startsWith('/') && !isDev) {
finalSrc = `${CDN_URL}${src}`
diff --git a/src/env.js b/src/env.js
index 75527f8..91fd853 100644
--- a/src/env.js
+++ b/src/env.js
@@ -24,6 +24,16 @@ export const env = createEnv({
MINIO_BUCKET_NAME: z.string(),
MINIO_LEADERBOARD_BUCKET_NAME: z.string(),
MINIO_USE_SSL: z.enum(['true', 'false']).default('false'),
+ API_TOKEN: z.string(),
+ VERCEL_URL: z.string().optional(),
+ PORT: z.string().optional(),
+ IS_PREVIEW: z.string().optional(),
+ },
+
+ /**
+ * Shared variables available on both client and server (e.g. NODE_ENV).
+ */
+ shared: {
NODE_ENV: z
.enum(['development', 'test', 'production'])
.default('development'),
@@ -59,6 +69,10 @@ export const env = createEnv({
MINIO_LEADERBOARD_BUCKET_NAME: process.env.MINIO_LEADERBOARD_BUCKET_NAME,
MINIO_USE_SSL: process.env.MINIO_USE_SSL,
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
+ API_TOKEN: process.env.API_TOKEN,
+ VERCEL_URL: process.env.VERCEL_URL,
+ PORT: process.env.PORT,
+ IS_PREVIEW: process.env.IS_PREVIEW,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts
index b35648f..a341c63 100644
--- a/src/server/api/trpc.ts
+++ b/src/server/api/trpc.ts
@@ -11,6 +11,7 @@ import { initTRPC, TRPCError } from '@trpc/server'
import superjson from 'superjson'
import { ZodError } from 'zod'
+import { env } from '@/env'
import { auth } from '@/server/auth'
import { db } from '@/server/db'
@@ -58,7 +59,7 @@ const t = initTRPC.context().create({
}
// Clear stack trace in production to prevent retention
- if (process.env.NODE_ENV === 'production') {
+ if (env.NODE_ENV === 'production') {
const { stack: _stack, ...safeFormatted } =
formatted as typeof formatted & {
stack?: unknown
diff --git a/src/server/services/botlatro.service.ts b/src/server/services/botlatro.service.ts
index 38904c3..0366281 100644
--- a/src/server/services/botlatro.service.ts
+++ b/src/server/services/botlatro.service.ts
@@ -1,4 +1,5 @@
import { eq } from 'drizzle-orm'
+import { env } from '@/env'
import { db } from '../db'
import { transcripts } from '../db/schema'
import { redis } from '../redis'
@@ -13,7 +14,7 @@ export const botlatro_service = {
get_active_matches: async (): Promise => {
const response = await fetch(`${BOTLATRO_URL}api/matches/active-counts`, {
headers: {
- Authorization: `Bearer ${process.env.API_TOKEN}`,
+ Authorization: `Bearer ${env.API_TOKEN}`,
},
})
if (!response.ok) {
@@ -112,7 +113,7 @@ export const botlatro_service = {
`${BOTLATRO_URL}api/transcripts/view/${gameNumber}`,
{
headers: {
- Authorization: `Bearer ${process.env.API_TOKEN}`,
+ Authorization: `Bearer ${env.API_TOKEN}`,
},
}
)
diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx
index 72580c2..d930982 100644
--- a/src/trpc/react.tsx
+++ b/src/trpc/react.tsx
@@ -12,6 +12,7 @@ import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'
import { useState } from 'react'
import SuperJSON from 'superjson'
+import { env } from '@/env'
import type { AppRouter } from '@/server/api/root'
import { createQueryClient } from './query-client'
@@ -51,7 +52,7 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) {
links: [
loggerLink({
enabled: (op) =>
- process.env.NODE_ENV === 'development' ||
+ env.NODE_ENV === 'development' ||
(op.direction === 'down' && op.result instanceof Error),
}),
@@ -87,9 +88,9 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) {
}
function getBaseUrl() {
- if (process.env.NODE_ENV === 'production') return 'https://balatromp.com'
+ if (env.NODE_ENV === 'production') return 'https://balatromp.com'
if (typeof window !== 'undefined') return window.location.origin
- if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`
+ if (env.VERCEL_URL) return `https://${env.VERCEL_URL}`
- return `http://localhost:${process.env.PORT ?? 3000}`
+ return `http://localhost:${env.PORT ?? 3000}`
}
diff --git a/test.ts b/test.ts
index 5d042f6..3b4aeb3 100644
--- a/test.ts
+++ b/test.ts
@@ -1,9 +1,10 @@
import { desc, eq } from 'drizzle-orm'
import { createClient } from 'redis'
+import { env } from '@/env'
import { db } from '@/server/db'
import { player_games } from '@/server/db/schema'
-const redisClient = await createClient({ url: process.env.REDIS_URL })
+const redisClient = await createClient({ url: env.REDIS_URL })
.on('error', (err) => console.error('Redis Client Error', err))
.connect()