/** * Environment variable validation. * Import this at the top of your app to get clear startup errors * instead of cryptic runtime failures. * * Usage: import '@/lib/env' in layout.tsx or next.config.ts */ type EnvVar = { name: string required: boolean description: string } const ENV_VARS: EnvVar[] = [ // Required { name: 'DATABASE_URL', required: true, description: 'PostgreSQL connection string (Neon)' }, { name: 'NEXT_PUBLIC_STACK_PROJECT_ID', required: true, description: 'Stack Auth project ID' }, { name: 'NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY', required: true, description: 'Stack Auth publishable key' }, { name: 'STACK_SECRET_SERVER_KEY', required: true, description: 'Stack Auth server secret key' }, // AI Providers (at least one required) { name: 'OPENAI_API_KEY', required: false, description: 'OpenAI API key (for GPT-4o models)' }, { name: 'ANTHROPIC_API_KEY', required: false, description: 'Anthropic API key (for Claude models)' }, { name: 'GOOGLE_AI_API_KEY', required: false, description: 'Google AI API key (for Gemini models)' }, // Optional services { name: 'UPSTASH_REDIS_REST_URL', required: false, description: 'Upstash Redis URL (caching & rate limiting)' }, { name: 'UPSTASH_REDIS_REST_TOKEN', required: false, description: 'Upstash Redis auth token' }, { name: 'NEXT_PUBLIC_TURNSTILE_SITE_KEY', required: false, description: 'Cloudflare Turnstile site key (bot protection)' }, { name: 'TURNSTILE_SECRET_KEY', required: false, description: 'Cloudflare Turnstile secret key' }, { name: 'STRIPE_SECRET_KEY', required: false, description: 'Stripe secret key (payments)' }, { name: 'STRIPE_WEBHOOK_SECRET', required: false, description: 'Stripe webhook signing secret' }, { name: 'NEXT_PUBLIC_APP_URL', required: false, description: 'Public URL of the app (defaults to open-prompt.netlify.app)' }, ] export function validateEnv(): { valid: boolean; errors: string[]; warnings: string[] } { const errors: string[] = [] const warnings: string[] = [] for (const envVar of ENV_VARS) { const value = process.env[envVar.name] if (envVar.required && !value) { errors.push(`❌ Missing required: ${envVar.name} — ${envVar.description}`) } else if (!envVar.required && !value) { warnings.push(`⚠️ Optional missing: ${envVar.name} — ${envVar.description}`) } } // Check that at least one AI provider is configured const hasAiProvider = !!( process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.GOOGLE_AI_API_KEY ) if (!hasAiProvider) { errors.push('❌ No AI provider configured. Set at least one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_AI_API_KEY') } // Check Redis pair (both or neither) const hasRedisUrl = !!process.env.UPSTASH_REDIS_REST_URL const hasRedisToken = !!process.env.UPSTASH_REDIS_REST_TOKEN if (hasRedisUrl !== hasRedisToken) { warnings.push('⚠️ Redis partially configured. Set both UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN, or neither.') } // Check Turnstile pair const hasTurnstileSite = !!process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY const hasTurnstileSecret = !!process.env.TURNSTILE_SECRET_KEY if (hasTurnstileSite !== hasTurnstileSecret) { warnings.push('⚠️ Turnstile partially configured. Set both NEXT_PUBLIC_TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY, or neither.') } return { valid: errors.length === 0, errors, warnings, } } /** * Log environment validation results during startup. * Call this in development to catch issues early. */ export function logEnvValidation(): void { // Only validate in development or on first build if (process.env.NODE_ENV === 'production' && process.env.NEXT_PHASE !== 'phase-production-build') { return } const { valid, errors, warnings } = validateEnv() if (errors.length > 0) { console.error('\n🔴 Environment Validation Failed:\n') errors.forEach(e => console.error(` ${e}`)) console.error('') } if (warnings.length > 0 && process.env.NODE_ENV === 'development') { console.warn('\n🟡 Environment Warnings:\n') warnings.forEach(w => console.warn(` ${w}`)) console.warn('') } if (valid && process.env.NODE_ENV === 'development') { console.log('\n✅ Environment validation passed\n') } } // Auto-run validation when this module is imported logEnvValidation()