import { z } from "zod"; // Minimal validated env for the foundation. When the Next.js app is added this // will move to @t3-oss/env-nextjs (the habbo-next pattern), but the data layer // only needs the DB connection + a couple of values today. const schema = z.object({ NODE_ENV: z.enum(["development", "test", "production"]).default("development"), DATABASE_URL: z.string().url(), DATABASE_POOL_SIZE: z.coerce.number().int().positive().default(40), DATABASE_IDLE_TIMEOUT_MS: z.coerce.number().int().positive().default(300_000), DATABASE_CONNECT_TIMEOUT_MS: z.coerce.number().int().positive().default(10_000), HOTEL_NAME: z.string().default("Atom"), APP_URL: z.string().url().default("http://localhost:3000"), // SMTP (password reset / notifications). Email features no-op if unset. SMTP_HOST: z.string().optional(), SMTP_PORT: z.coerce.number().int().positive().optional(), SMTP_SECURE: z .string() .optional() .transform((v) => v === "true" || v === "1"), SMTP_USER: z.string().optional(), SMTP_PASSWORD: z.string().optional(), SMTP_FROM: z.string().optional(), // RCON link to the Arcturus emulator (raw-JSON TCP protocol). RCON_HOST: z.string().default("127.0.0.1"), RCON_PORT: z.coerce.number().int().positive().default(3001), RCON_TIMEOUT_MS: z.coerce.number().int().positive().default(10_000), RCON_MAX_RETRIES: z.coerce.number().int().positive().default(3), // NextAuth v5 reads AUTH_SECRET itself; declared here for documentation/typing. AUTH_SECRET: z.string().min(1).optional(), // Laravel APP_KEY (base64:...) — needed to read existing 2FA secrets. APP_KEY: z.string().optional(), // Optional OAuth providers (enabled only when both id+secret are set). DISCORD_CLIENT_ID: z.string().optional(), DISCORD_CLIENT_SECRET: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(), // Mirrors Laravel config('habbo.site.convert_passwords') — enables md5->argon2id. CONVERT_PASSWORDS: z .string() .optional() .transform((v) => v === "true" || v === "1"), // Hashing driver for NEW passwords: bcrypt (default, fits varchar(64)) | argon2id. PASSWORD_HASH: z.enum(["bcrypt", "argon2id"]).optional(), // Filesystem dir the badge uploader writes .gif into (the emulator's // badge image folder, e.g. .../assets/c_images/album1584). Upload is disabled // when unset. BADGE_UPLOAD_DIR: z.string().optional(), // Optional AI content moderation (comments / guestbook). OPENAI_API_KEY: z.string().optional(), // Optional alerting (jobs worker / alert service). DISCORD_WEBHOOK_URL: z.string().url().optional(), ALERT_EMAIL: z.string().optional(), // Optional PayPal top-up. PAYPAL_CLIENT_ID: z.string().optional(), PAYPAL_SECRET: z.string().optional(), PAYPAL_API: z.string().url().optional(), }); type Env = z.infer; // SKIP_ENV_VALIDATION lets tooling (typecheck, tests that don't touch the DB) // import modules transitively without a populated .env. export const env: Env = process.env.SKIP_ENV_VALIDATION ? (process.env as unknown as Env) : schema.parse(process.env);