export const USER_ROLES = ["admin", "ap_processor", "approver", "viewer"] as const; export type UserRole = (typeof USER_ROLES)[number]; export function isUserRole(value: string): value is UserRole { return (USER_ROLES as readonly string[]).includes(value); } export type ApiEnv = { nodeEnv: string; port: number; databaseDriver: "postgres" | "data-api"; databaseUrl: string; awsRegion: string; rdsClusterArn: string; rdsSecretArn: string; rdsDatabase: string; cognitoIssuer: string; cognitoAudience: string; devAuthBypass: boolean; devAuthSub: string; devAuthEmail: string; devAuthName: string; devAuthRole: UserRole; }; function required(name: string, value: string | undefined): string { if (!value) { throw new Error(`Missing required env var ${name}.`); } return value; } export function loadEnv(env: NodeJS.ProcessEnv = process.env): ApiEnv { const nodeEnv = env.NODE_ENV ?? "development"; const databaseDriver = (env.DATABASE_DRIVER ?? "postgres") as ApiEnv["databaseDriver"]; if (databaseDriver !== "postgres" && databaseDriver !== "data-api") { throw new Error(`Invalid DATABASE_DRIVER: ${databaseDriver}`); } const devAuthBypass = env.DEV_AUTH_BYPASS === "true"; const localNodeEnvs = new Set(["development", "test"]); if (devAuthBypass && !localNodeEnvs.has(nodeEnv)) { throw new Error("DEV_AUTH_BYPASS is only allowed when NODE_ENV is development or test."); } const rawRole = env.DEV_AUTH_ROLE ?? "admin"; if (!isUserRole(rawRole)) { throw new Error(`Invalid DEV_AUTH_ROLE: ${rawRole}`); } const base: ApiEnv = { nodeEnv, port: Number(env.API_PORT ?? "8787"), databaseDriver, databaseUrl: env.DATABASE_URL ?? "postgresql://seahaven:seahaven@127.0.0.1:5432/seahaven_ap", awsRegion: env.AWS_REGION ?? "us-east-1", rdsClusterArn: env.RDS_CLUSTER_ARN ?? "", rdsSecretArn: env.RDS_SECRET_ARN ?? "", rdsDatabase: env.RDS_DATABASE ?? "seahaven_ap", cognitoIssuer: env.COGNITO_ISSUER ?? "", cognitoAudience: env.COGNITO_AUDIENCE ?? "", devAuthBypass, devAuthSub: env.DEV_AUTH_SUB ?? "seed-sub-admin", devAuthEmail: env.DEV_AUTH_EMAIL ?? "admin@seahavenind.com", devAuthName: env.DEV_AUTH_NAME ?? "Dev Admin", devAuthRole: rawRole, }; if (databaseDriver === "data-api") { required("RDS_CLUSTER_ARN", base.rdsClusterArn); required("RDS_SECRET_ARN", base.rdsSecretArn); } if (!devAuthBypass && nodeEnv !== "test") { required("COGNITO_ISSUER", base.cognitoIssuer); required("COGNITO_AUDIENCE", base.cognitoAudience); } return base; }