add changes

This commit is contained in:
AD2025
2025-12-25 00:24:11 +02:00
parent 079c10e843
commit efb4f69e20
64 changed files with 576 additions and 568 deletions

View File

@@ -0,0 +1,338 @@
require('dotenv').config();
/**
* Environment Configuration Validator
* Validates all required environment variables and their formats
*/
const REQUIRED_VARS = {
// Server Configuration
NODE_ENV: {
required: true,
type: 'string',
allowedValues: ['development', 'test', 'production'],
default: 'development'
},
PORT: {
required: true,
type: 'number',
min: 1000,
max: 65535,
default: 3000
},
API_PREFIX: {
required: true,
type: 'string',
default: '/api'
},
// Database Configuration
DB_HOST: {
required: true,
type: 'string',
default: 'localhost'
},
DB_PORT: {
required: true,
type: 'number',
default: 3306
},
DB_NAME: {
required: true,
type: 'string',
minLength: 3
},
DB_USER: {
required: true,
type: 'string'
},
DB_PASSWORD: {
required: false, // Optional for development
type: 'string',
warning: 'Database password is not set. This is only acceptable in development.'
},
DB_DIALECT: {
required: true,
type: 'string',
allowedValues: ['mysql', 'postgres', 'sqlite', 'mssql'],
default: 'mysql'
},
// Database Pool Configuration
DB_POOL_MAX: {
required: false,
type: 'number',
default: 10
},
DB_POOL_MIN: {
required: false,
type: 'number',
default: 0
},
DB_POOL_ACQUIRE: {
required: false,
type: 'number',
default: 30000
},
DB_POOL_IDLE: {
required: false,
type: 'number',
default: 10000
},
// JWT Configuration
JWT_SECRET: {
required: true,
type: 'string',
minLength: 32,
warning: 'JWT_SECRET should be a long, random string (64+ characters recommended)'
},
JWT_EXPIRE: {
required: true,
type: 'string',
default: '24h'
},
// Rate Limiting
RATE_LIMIT_WINDOW_MS: {
required: false,
type: 'number',
default: 900000
},
RATE_LIMIT_MAX_REQUESTS: {
required: false,
type: 'number',
default: 100
},
// CORS Configuration
CORS_ORIGIN: {
required: true,
type: 'string',
default: 'http://localhost:4200'
},
// Guest Configuration
GUEST_SESSION_EXPIRE_HOURS: {
required: false,
type: 'number',
default: 24
},
GUEST_MAX_QUIZZES: {
required: false,
type: 'number',
default: 3
},
// Logging
LOG_LEVEL: {
required: false,
type: 'string',
allowedValues: ['error', 'warn', 'info', 'debug'],
default: 'info'
},
// Redis Configuration (Optional - for caching)
REDIS_HOST: {
required: false,
type: 'string',
default: 'localhost'
},
REDIS_PORT: {
required: false,
type: 'number',
default: 6379
},
REDIS_PASSWORD: {
required: false,
type: 'string'
},
REDIS_DB: {
required: false,
type: 'number',
default: 0
}
};
class ValidationError extends Error {
constructor(variable, message) {
super(`${variable}: ${message}`);
this.variable = variable;
}
}
/**
* Validate a single environment variable
*/
function validateVariable(name, config) {
const value = process.env[name];
const errors = [];
const warnings = [];
// Check if required and missing
if (config.required && !value) {
if (config.default !== undefined) {
warnings.push(`${name} is not set. Using default: ${config.default}`);
process.env[name] = String(config.default);
return { errors, warnings };
}
errors.push(`${name} is required but not set`);
return { errors, warnings };
}
// If not set and not required, use default if available
if (!value && config.default !== undefined) {
process.env[name] = String(config.default);
return { errors, warnings };
}
// Skip further validation if not set and not required
if (!value && !config.required) {
return { errors, warnings };
}
// Type validation
if (config.type === 'number') {
const numValue = Number(value);
if (isNaN(numValue)) {
errors.push(`${name} must be a number. Got: ${value}`);
} else {
if (config.min !== undefined && numValue < config.min) {
errors.push(`${name} must be >= ${config.min}. Got: ${numValue}`);
}
if (config.max !== undefined && numValue > config.max) {
errors.push(`${name} must be <= ${config.max}. Got: ${numValue}`);
}
}
}
// String length validation
if (config.type === 'string' && config.minLength && value.length < config.minLength) {
errors.push(`${name} must be at least ${config.minLength} characters. Got: ${value.length}`);
}
// Allowed values validation
if (config.allowedValues && !config.allowedValues.includes(value)) {
errors.push(`${name} must be one of: ${config.allowedValues.join(', ')}. Got: ${value}`);
}
// Custom warnings
if (config.warning && value) {
const needsWarning = config.minLength ? value.length < 64 : true;
if (needsWarning) {
warnings.push(`${name}: ${config.warning}`);
}
}
// Warning for missing optional password in production
if (name === 'DB_PASSWORD' && !value && process.env.NODE_ENV === 'production') {
errors.push('DB_PASSWORD is required in production');
}
return { errors, warnings };
}
/**
* Validate all environment variables
*/
function validateEnvironment() {
console.log('\n🔍 Validating Environment Configuration...\n');
const allErrors = [];
const allWarnings = [];
let validCount = 0;
// Validate each variable
Object.entries(REQUIRED_VARS).forEach(([name, config]) => {
const { errors, warnings } = validateVariable(name, config);
if (errors.length > 0) {
allErrors.push(...errors);
console.log(`${name}: INVALID`);
errors.forEach(err => console.log(` ${err}`));
} else if (warnings.length > 0) {
allWarnings.push(...warnings);
console.log(`⚠️ ${name}: WARNING`);
warnings.forEach(warn => console.log(` ${warn}`));
validCount++;
} else {
console.log(`${name}: OK`);
validCount++;
}
});
// Summary
console.log('\n' + '='.repeat(60));
console.log('VALIDATION SUMMARY');
console.log('='.repeat(60));
console.log(`Total Variables: ${Object.keys(REQUIRED_VARS).length}`);
console.log(`✅ Valid: ${validCount}`);
console.log(`⚠️ Warnings: ${allWarnings.length}`);
console.log(`❌ Errors: ${allErrors.length}`);
console.log('='.repeat(60));
if (allWarnings.length > 0) {
console.log('\n⚠ WARNINGS:');
allWarnings.forEach(warning => console.log(` - ${warning}`));
}
if (allErrors.length > 0) {
console.log('\n❌ ERRORS:');
allErrors.forEach(error => console.log(` - ${error}`));
console.log('\nPlease fix the above errors before starting the application.\n');
return false;
}
console.log('\n✅ All environment variables are valid!\n');
return true;
}
/**
* Get current environment configuration summary
*/
function getEnvironmentSummary() {
return {
environment: process.env.NODE_ENV || 'development',
server: {
port: process.env.PORT || 3000,
apiPrefix: process.env.API_PREFIX || '/api'
},
database: {
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
name: process.env.DB_NAME,
dialect: process.env.DB_DIALECT || 'mysql',
pool: {
max: parseInt(process.env.DB_POOL_MAX) || 10,
min: parseInt(process.env.DB_POOL_MIN) || 0
}
},
security: {
jwtExpire: process.env.JWT_EXPIRE || '24h',
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:4200'
},
guest: {
maxQuizzes: parseInt(process.env.GUEST_MAX_QUIZZES) || 3,
sessionExpireHours: parseInt(process.env.GUEST_SESSION_EXPIRE_HOURS) || 24
}
};
}
// Run validation if called directly
if (require.main === module) {
const isValid = validateEnvironment();
if (isValid) {
console.log('Current Configuration:');
console.log(JSON.stringify(getEnvironmentSummary(), null, 2));
console.log('\n');
}
process.exit(isValid ? 0 : 1);
}
module.exports = {
validateEnvironment,
getEnvironmentSummary,
REQUIRED_VARS
};