import 'dotenv/config'; import { z } from 'zod'; const optionalEnvironmentValue = (schema: T) => z.preprocess((value) => value === '' ? undefined : value, schema.optional()); const environmentSchema = z.object({ API_PORT: z.coerce.number().int().positive().default(3001), DATABASE_URL: z.string().url().default('postgres://compor:compor_local_password@localhost:5433/compor_hub'), FRONTEND_ORIGIN: z.string().url().default('http://localhost:3000'), APP_ENV: z.enum(['development', 'test', 'production']).default('development'), JWT_SECRET: z.string().min(32).default('development-only-secret-change-before-production'), SUPERADMIN_EMAIL: optionalEnvironmentValue(z.string().email()), SUPERADMIN_PASSWORD: optionalEnvironmentValue(z.string().min(8)), SUPERADMIN_NAME: z.string().min(1).max(120).default('Compor HUB Superadmin'), AUTH_RATE_LIMIT_MAX: z.coerce.number().int().min(1).max(1000).default(10), AUTH_RATE_LIMIT_WINDOW_SECONDS: z.coerce.number().int().min(60).max(86_400).default(900), JWT_SESSION_TTL: z.string().regex(/^\d+[smhd]$/).default('7d'), INVITATION_TTL_HOURS: z.coerce.number().int().min(1).max(24 * 90).default(24 * 7), PASSWORD_RESET_TTL_HOURS: z.coerce.number().int().min(1).max(24 * 30).default(24), AUDIT_LOG_PAGE_SIZE: z.coerce.number().int().min(10).max(500).default(50), SMTP_HOST: optionalEnvironmentValue(z.string().trim().min(1).max(255)), SMTP_PORT: optionalEnvironmentValue(z.coerce.number().int().min(1).max(65_535)), SMTP_USER: optionalEnvironmentValue(z.string().trim().min(1).max(320)), SMTP_PASS: optionalEnvironmentValue(z.string().min(1).max(1_000)), MAIL_FROM: optionalEnvironmentValue(z.string().trim().min(3).max(320)), BUNNY_STREAM_LIBRARY_ID: optionalEnvironmentValue(z.coerce.number().int().positive()), BUNNY_STREAM_API_KEY: optionalEnvironmentValue(z.string().min(20)), BUNNY_EMBED_TOKEN_KEY: optionalEnvironmentValue(z.string().min(20)), BUNNY_WEBHOOK_SECRET: optionalEnvironmentValue(z.string().min(20)), BUNNY_EMBED_TOKEN_TTL_SECONDS: z.coerce.number().int().min(60).max(86_400).default(600), BUNNY_MAX_UPLOAD_MB: z.coerce.number().int().min(1).max(5120).default(5120), BUNNY_STORAGE_ZONE: optionalEnvironmentValue(z.string().trim().min(1).max(120)), BUNNY_STORAGE_PASSWORD: optionalEnvironmentValue(z.string().min(1)), BUNNY_STORAGE_ENDPOINT: optionalEnvironmentValue(z.string().url()), BUNNY_STORAGE_CDN_HOST: optionalEnvironmentValue(z.string().url()), BUNNY_COVER_MAX_UPLOAD_MB: z.coerce.number().int().min(1).max(50).default(10), BUNNY_ASSET_MAX_UPLOAD_MB: z.coerce.number().int().min(1).max(512).default(100), }); export const config = environmentSchema.parse(process.env); if (config.APP_ENV === 'production' && config.JWT_SECRET === 'development-only-secret-change-before-production') { throw new Error('JWT_SECRET deve ter um valor único em produção.'); } const bunnyConfigurationValues = [ config.BUNNY_STREAM_LIBRARY_ID, config.BUNNY_STREAM_API_KEY, config.BUNNY_EMBED_TOKEN_KEY, ]; if (bunnyConfigurationValues.some(Boolean) && !bunnyConfigurationValues.every(Boolean)) { throw new Error('BUNNY_STREAM_LIBRARY_ID, BUNNY_STREAM_API_KEY e BUNNY_EMBED_TOKEN_KEY devem ser configurados juntos.'); } const bunnyStorageConfigurationValues = [ config.BUNNY_STORAGE_ZONE, config.BUNNY_STORAGE_PASSWORD, config.BUNNY_STORAGE_ENDPOINT, config.BUNNY_STORAGE_CDN_HOST, ]; if (bunnyStorageConfigurationValues.some(Boolean) && !bunnyStorageConfigurationValues.every(Boolean)) { throw new Error('BUNNY_STORAGE_ZONE, BUNNY_STORAGE_PASSWORD, BUNNY_STORAGE_ENDPOINT e BUNNY_STORAGE_CDN_HOST devem ser configurados juntos.'); } const smtpConfigurationValues = [config.SMTP_HOST, config.SMTP_PORT, config.SMTP_USER, config.SMTP_PASS, config.MAIL_FROM]; if (smtpConfigurationValues.some(Boolean) && !smtpConfigurationValues.every(Boolean)) { throw new Error('SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS e MAIL_FROM devem ser configurados juntos.'); }