This commit is contained in:
2025-12-26 20:54:20 +01:00
parent 904feb41b8
commit 6389be4f0c
26 changed files with 745 additions and 195 deletions

View File

@@ -0,0 +1,96 @@
import { z } from 'zod';
const urlOptional = z.string().url().optional();
const stringOptional = z.string().optional();
const publicEnvSchema = z.object({
NEXT_PUBLIC_GRIDPILOT_MODE: z.enum(['pre-launch', 'alpha']).optional(),
NEXT_PUBLIC_SITE_URL: urlOptional,
NEXT_PUBLIC_API_BASE_URL: urlOptional,
NEXT_PUBLIC_SITE_NAME: stringOptional,
NEXT_PUBLIC_SUPPORT_EMAIL: stringOptional,
NEXT_PUBLIC_SPONSOR_EMAIL: stringOptional,
NEXT_PUBLIC_LEGAL_COMPANY_NAME: stringOptional,
NEXT_PUBLIC_LEGAL_VAT_ID: stringOptional,
NEXT_PUBLIC_LEGAL_REGISTERED_COUNTRY: stringOptional,
NEXT_PUBLIC_LEGAL_REGISTERED_ADDRESS: stringOptional,
NEXT_PUBLIC_DISCORD_URL: stringOptional,
NEXT_PUBLIC_X_URL: stringOptional,
});
const serverEnvSchema = z.object({
API_BASE_URL: urlOptional,
KV_REST_API_URL: urlOptional,
KV_REST_API_TOKEN: stringOptional,
CI: stringOptional,
DOCKER: stringOptional,
NODE_ENV: z.enum(['development', 'production', 'test']).optional(),
});
export type WebsitePublicEnv = z.infer<typeof publicEnvSchema>;
export type WebsiteServerEnv = z.infer<typeof serverEnvSchema>;
function formatZodIssues(issues: z.ZodIssue[]): string {
return issues
.map((issue) => {
const path = issue.path.join('.') || '(root)';
return `${path}: ${issue.message}`;
})
.join('; ');
}
/**
* Parses Website env vars (server-side safe).
* Only validates the variables we explicitly support.
*/
export function getWebsiteServerEnv(): WebsiteServerEnv {
const result = serverEnvSchema.safeParse(process.env);
if (!result.success) {
throw new Error(`Invalid website server env: ${formatZodIssues(result.error.issues)}`);
}
return result.data;
}
/**
* Parses Website public env vars (safe on both server + client).
* Note: on the client, only `NEXT_PUBLIC_*` vars are actually present.
*/
export function getWebsitePublicEnv(): WebsitePublicEnv {
const result = publicEnvSchema.safeParse(process.env);
if (!result.success) {
throw new Error(`Invalid website public env: ${formatZodIssues(result.error.issues)}`);
}
return result.data;
}
export function isTruthyEnv(value: string | undefined): boolean {
if (!value) return false;
return value !== '0' && value.toLowerCase() !== 'false';
}
/**
* Matches the semantics used in [`getWebsiteApiBaseUrl()`](apps/website/lib/config/apiBaseUrl.ts:6).
*/
export function isTestLikeEnvironment(): boolean {
const { NODE_ENV, CI, DOCKER } = getWebsiteServerEnv();
return NODE_ENV === 'test' || CI === 'true' || DOCKER === 'true';
}
export function isProductionEnvironment(): boolean {
return getWebsiteServerEnv().NODE_ENV === 'production';
}
export function isKvConfigured(): boolean {
const { KV_REST_API_URL, KV_REST_API_TOKEN } = getWebsiteServerEnv();
return Boolean(KV_REST_API_URL && KV_REST_API_TOKEN);
}
export function assertKvConfiguredInProduction(): void {
if (isProductionEnvironment() && !isKvConfigured()) {
throw new Error('Missing KV_REST_API_URL/KV_REST_API_TOKEN in production environment');
}
}