NeahStable/scripts/validate-env.ts
2026-01-16 18:22:20 +01:00

162 lines
3.8 KiB
TypeScript

#!/usr/bin/env ts-node
/**
* Environment Variables Validation Script
*
* Validates that all required environment variables are set
* before deployment to production.
*
* Usage:
* ts-node scripts/validate-env.ts
* or
* npm run validate:env
*/
const requiredVars = [
// Core
'DATABASE_URL',
'NEXTAUTH_URL',
'NEXTAUTH_SECRET',
// Keycloak
'KEYCLOAK_BASE_URL',
'KEYCLOAK_REALM',
'KEYCLOAK_CLIENT_ID',
'KEYCLOAK_CLIENT_SECRET',
'KEYCLOAK_ISSUER',
'NEXT_PUBLIC_KEYCLOAK_ISSUER',
];
const optionalButRecommended = [
// Redis
'REDIS_HOST',
'REDIS_PORT',
'REDIS_PASSWORD',
// External Services
'N8N_API_KEY',
'N8N_WEBHOOK_URL',
'LEANTIME_API_URL',
'LEANTIME_TOKEN',
'ROCKET_CHAT_TOKEN',
'ROCKET_CHAT_USER_ID',
];
interface ValidationResult {
valid: boolean;
missing: string[];
warnings: string[];
errors: string[];
}
function validateEnvironment(): ValidationResult {
const result: ValidationResult = {
valid: true,
missing: [],
warnings: [],
errors: [],
};
// Check required variables
for (const varName of requiredVars) {
const value = process.env[varName];
if (!value || value.trim() === '') {
result.missing.push(varName);
result.valid = false;
}
}
// Check optional but recommended
for (const varName of optionalButRecommended) {
const value = process.env[varName];
if (!value || value.trim() === '') {
result.warnings.push(varName);
}
}
// Validate DATABASE_URL format
const dbUrl = process.env.DATABASE_URL;
if (dbUrl) {
if (!dbUrl.startsWith('postgresql://') && !dbUrl.startsWith('postgres://')) {
result.errors.push('DATABASE_URL must start with postgresql:// or postgres://');
result.valid = false;
}
// Check for connection pool parameters
if (!dbUrl.includes('connection_limit')) {
result.warnings.push(
'DATABASE_URL should include connection_limit parameter (e.g., ?connection_limit=10&pool_timeout=20)'
);
}
}
// Validate NEXTAUTH_SECRET strength
const nextAuthSecret = process.env.NEXTAUTH_SECRET;
if (nextAuthSecret && nextAuthSecret.length < 32) {
result.warnings.push('NEXTAUTH_SECRET should be at least 32 characters long');
}
// Validate URLs
const urlVars = ['NEXTAUTH_URL', 'KEYCLOAK_BASE_URL', 'NEXT_PUBLIC_KEYCLOAK_ISSUER'];
for (const varName of urlVars) {
const value = process.env[varName];
if (value) {
try {
new URL(value);
} catch {
result.errors.push(`${varName} is not a valid URL: ${value}`);
result.valid = false;
}
}
}
return result;
}
function main() {
console.log('🔍 Validating environment variables...\n');
const result = validateEnvironment();
if (result.missing.length > 0) {
console.error('❌ Missing required environment variables:');
result.missing.forEach((varName) => {
console.error(` - ${varName}`);
});
console.error('');
}
if (result.errors.length > 0) {
console.error('❌ Validation errors:');
result.errors.forEach((error) => {
console.error(` - ${error}`);
});
console.error('');
}
if (result.warnings.length > 0) {
console.warn('⚠️ Warnings:');
result.warnings.forEach((warning) => {
console.warn(` - ${warning}`);
});
console.warn('');
}
if (result.valid) {
console.log('✅ All required environment variables are set!\n');
if (result.warnings.length > 0) {
console.log('⚠️ Some optional variables are missing, but deployment can proceed.\n');
}
process.exit(0);
} else {
console.error('❌ Environment validation failed!\n');
console.error('Please set all required variables before deploying.\n');
process.exit(1);
}
}
if (require.main === module) {
main();
}
export { validateEnvironment };