feat: implement comprehensive startup system and fix authentication
Major improvements: - Created startup orchestration system with health monitoring and graceful shutdown - Fixed user registration and login with simplified authentication flow - Rebuilt authentication forms from scratch with direct API integration - Implemented comprehensive debugging and error handling - Added Redis fallback functionality for disabled environments - Fixed CORS configuration for cross-origin frontend requests - Simplified password validation to 6+ characters (removed complexity requirements) - Added toast notifications at app level for better UX feedback - Created comprehensive startup/shutdown scripts with OODA methodology - Fixed database validation and connection issues - Implemented TokenService memory fallback when Redis is disabled Technical details: - New SimpleLoginForm.tsx and SimpleRegisterForm.tsx components - Enhanced CORS middleware with additional allowed origins - Simplified auth validators and removed strict password requirements - Added extensive logging and diagnostic capabilities - Fixed authentication middleware token validation - Implemented graceful Redis error handling throughout the stack - Created modular startup system with configurable health checks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d41d1e8125
commit
e681c446b6
36 changed files with 7719 additions and 183 deletions
380
config/startup.config.js
Normal file
380
config/startup.config.js
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/**
|
||||
* Shattered Void MMO - Startup Configuration
|
||||
*
|
||||
* Central configuration file for the startup system, allowing easy customization
|
||||
* of startup behavior, timeouts, and service settings.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Default startup configuration
|
||||
*/
|
||||
const defaultConfig = {
|
||||
// Environment settings
|
||||
environment: {
|
||||
mode: process.env.NODE_ENV || 'development',
|
||||
logLevel: process.env.LOG_LEVEL || 'info',
|
||||
enableDebug: process.env.NODE_ENV === 'development'
|
||||
},
|
||||
|
||||
// Backend server configuration
|
||||
backend: {
|
||||
port: parseInt(process.env.PORT) || 3000,
|
||||
host: process.env.HOST || '0.0.0.0',
|
||||
script: 'src/server.js',
|
||||
startupTimeout: 30000,
|
||||
healthEndpoint: '/health',
|
||||
gracefulShutdownTimeout: 10000
|
||||
},
|
||||
|
||||
// Frontend configuration
|
||||
frontend: {
|
||||
enabled: process.env.ENABLE_FRONTEND !== 'false',
|
||||
port: parseInt(process.env.FRONTEND_PORT) || 5173,
|
||||
host: process.env.FRONTEND_HOST || '0.0.0.0',
|
||||
directory: './frontend',
|
||||
buildDirectory: './frontend/dist',
|
||||
startupTimeout: 45000,
|
||||
buildTimeout: 120000,
|
||||
devCommand: 'dev',
|
||||
buildCommand: 'build',
|
||||
previewCommand: 'preview'
|
||||
},
|
||||
|
||||
// Database configuration
|
||||
database: {
|
||||
enabled: process.env.DISABLE_DATABASE !== 'true',
|
||||
connectionTimeout: 10000,
|
||||
migrationTimeout: 60000,
|
||||
seedTimeout: 30000,
|
||||
autoMigrate: process.env.AUTO_MIGRATE !== 'false',
|
||||
autoSeed: process.env.AUTO_SEED === 'true',
|
||||
integrityChecks: process.env.SKIP_DB_INTEGRITY !== 'true',
|
||||
retryAttempts: 3,
|
||||
retryDelay: 2000
|
||||
},
|
||||
|
||||
// Redis configuration
|
||||
redis: {
|
||||
enabled: process.env.DISABLE_REDIS !== 'true',
|
||||
optional: true,
|
||||
connectionTimeout: 5000,
|
||||
retryAttempts: 3,
|
||||
retryDelay: 1000,
|
||||
host: process.env.REDIS_HOST || 'localhost',
|
||||
port: parseInt(process.env.REDIS_PORT) || 6379
|
||||
},
|
||||
|
||||
// Health monitoring configuration
|
||||
healthMonitoring: {
|
||||
enabled: process.env.ENABLE_HEALTH_MONITORING !== 'false',
|
||||
interval: parseInt(process.env.HEALTH_CHECK_INTERVAL) || 30000,
|
||||
timeout: 5000,
|
||||
alertThresholds: {
|
||||
responseTime: 5000,
|
||||
memoryUsage: 80,
|
||||
cpuUsage: 90,
|
||||
errorRate: 10,
|
||||
consecutiveFailures: 3
|
||||
},
|
||||
systemMetricsInterval: 10000,
|
||||
historySize: 100
|
||||
},
|
||||
|
||||
// Startup process configuration
|
||||
startup: {
|
||||
maxRetries: parseInt(process.env.STARTUP_MAX_RETRIES) || 3,
|
||||
retryDelay: parseInt(process.env.STARTUP_RETRY_DELAY) || 2000,
|
||||
enableBanner: process.env.DISABLE_BANNER !== 'true',
|
||||
enableColors: process.env.DISABLE_COLORS !== 'true',
|
||||
verboseLogging: process.env.VERBOSE_STARTUP === 'true',
|
||||
failFast: process.env.FAIL_FAST === 'true',
|
||||
gracefulShutdown: true
|
||||
},
|
||||
|
||||
// Pre-flight checks configuration
|
||||
preflightChecks: {
|
||||
enabled: process.env.SKIP_PREFLIGHT !== 'true',
|
||||
timeout: 30000,
|
||||
required: {
|
||||
nodeVersion: true,
|
||||
npmAvailability: true,
|
||||
environmentConfig: true,
|
||||
directoryStructure: true,
|
||||
packageDependencies: true,
|
||||
portAvailability: true,
|
||||
databaseConfig: true,
|
||||
logDirectories: true,
|
||||
filePermissions: true,
|
||||
systemMemory: true,
|
||||
diskSpace: true
|
||||
},
|
||||
optional: {
|
||||
redisConfig: true,
|
||||
frontendDependencies: true
|
||||
},
|
||||
requirements: {
|
||||
nodeMinVersion: 18,
|
||||
memoryMinGB: 1,
|
||||
diskSpaceMaxUsage: 90
|
||||
}
|
||||
},
|
||||
|
||||
// Logging configuration
|
||||
logging: {
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
colorize: process.env.DISABLE_COLORS !== 'true',
|
||||
timestamp: true,
|
||||
includeProcessId: true,
|
||||
startupLog: true,
|
||||
errorStackTrace: process.env.NODE_ENV === 'development'
|
||||
},
|
||||
|
||||
// Performance configuration
|
||||
performance: {
|
||||
measureStartupTime: true,
|
||||
measurePhaseTime: true,
|
||||
memoryMonitoring: true,
|
||||
cpuMonitoring: process.env.NODE_ENV === 'development',
|
||||
performanceReporting: process.env.PERFORMANCE_REPORTING === 'true'
|
||||
},
|
||||
|
||||
// Security configuration
|
||||
security: {
|
||||
hidePasswords: true,
|
||||
sanitizeEnvironment: true,
|
||||
validatePorts: true,
|
||||
checkFilePermissions: true
|
||||
},
|
||||
|
||||
// Development specific settings
|
||||
development: {
|
||||
hotReload: true,
|
||||
autoRestart: process.env.AUTO_RESTART === 'true',
|
||||
debugEndpoints: process.env.ENABLE_DEBUG_ENDPOINTS === 'true',
|
||||
verboseErrors: true,
|
||||
showDeprecations: true
|
||||
},
|
||||
|
||||
// Production specific settings
|
||||
production: {
|
||||
compressionEnabled: true,
|
||||
cachingEnabled: true,
|
||||
minifyAssets: true,
|
||||
enableCDN: process.env.ENABLE_CDN === 'true',
|
||||
healthEndpoints: true,
|
||||
metricsCollection: true
|
||||
},
|
||||
|
||||
// Service dependencies
|
||||
dependencies: {
|
||||
required: ['database'],
|
||||
optional: ['redis', 'frontend'],
|
||||
order: ['database', 'redis', 'backend', 'frontend', 'healthMonitoring']
|
||||
},
|
||||
|
||||
// Error handling
|
||||
errorHandling: {
|
||||
retryFailedServices: true,
|
||||
continueOnOptionalFailure: true,
|
||||
detailedErrorMessages: process.env.NODE_ENV === 'development',
|
||||
errorNotifications: process.env.ERROR_NOTIFICATIONS === 'true',
|
||||
crashReporting: process.env.CRASH_REPORTING === 'true'
|
||||
},
|
||||
|
||||
// Paths and directories
|
||||
paths: {
|
||||
root: process.cwd(),
|
||||
src: path.join(process.cwd(), 'src'),
|
||||
config: path.join(process.cwd(), 'config'),
|
||||
logs: path.join(process.cwd(), 'logs'),
|
||||
scripts: path.join(process.cwd(), 'scripts'),
|
||||
frontend: path.join(process.cwd(), 'frontend'),
|
||||
database: path.join(process.cwd(), 'src', 'database'),
|
||||
migrations: path.join(process.cwd(), 'src', 'database', 'migrations'),
|
||||
seeds: path.join(process.cwd(), 'src', 'database', 'seeds')
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Environment-specific configurations
|
||||
*/
|
||||
const environmentConfigs = {
|
||||
development: {
|
||||
backend: {
|
||||
startupTimeout: 20000
|
||||
},
|
||||
frontend: {
|
||||
startupTimeout: 30000
|
||||
},
|
||||
database: {
|
||||
integrityChecks: false,
|
||||
autoSeed: true
|
||||
},
|
||||
healthMonitoring: {
|
||||
interval: 15000
|
||||
},
|
||||
logging: {
|
||||
level: 'debug'
|
||||
},
|
||||
startup: {
|
||||
verboseLogging: true,
|
||||
failFast: false
|
||||
}
|
||||
},
|
||||
|
||||
production: {
|
||||
backend: {
|
||||
startupTimeout: 45000
|
||||
},
|
||||
frontend: {
|
||||
enabled: false // Assume pre-built assets are served by nginx/CDN
|
||||
},
|
||||
database: {
|
||||
integrityChecks: true,
|
||||
autoSeed: false,
|
||||
retryAttempts: 5
|
||||
},
|
||||
healthMonitoring: {
|
||||
interval: 60000,
|
||||
alertThresholds: {
|
||||
responseTime: 3000,
|
||||
memoryUsage: 85,
|
||||
cpuUsage: 85
|
||||
}
|
||||
},
|
||||
logging: {
|
||||
level: 'warn'
|
||||
},
|
||||
startup: {
|
||||
verboseLogging: false,
|
||||
failFast: true
|
||||
},
|
||||
errorHandling: {
|
||||
retryFailedServices: true,
|
||||
continueOnOptionalFailure: false
|
||||
}
|
||||
},
|
||||
|
||||
staging: {
|
||||
backend: {
|
||||
startupTimeout: 30000
|
||||
},
|
||||
database: {
|
||||
integrityChecks: true,
|
||||
autoSeed: true
|
||||
},
|
||||
healthMonitoring: {
|
||||
interval: 30000
|
||||
},
|
||||
logging: {
|
||||
level: 'info'
|
||||
}
|
||||
},
|
||||
|
||||
testing: {
|
||||
backend: {
|
||||
port: 0, // Use random available port
|
||||
startupTimeout: 10000
|
||||
},
|
||||
frontend: {
|
||||
enabled: false
|
||||
},
|
||||
database: {
|
||||
autoMigrate: true,
|
||||
autoSeed: true,
|
||||
integrityChecks: false
|
||||
},
|
||||
healthMonitoring: {
|
||||
enabled: false
|
||||
},
|
||||
preflightChecks: {
|
||||
enabled: false
|
||||
},
|
||||
startup: {
|
||||
enableBanner: false,
|
||||
verboseLogging: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge configurations based on environment
|
||||
*/
|
||||
function mergeConfigs(base, override) {
|
||||
const result = { ...base };
|
||||
|
||||
for (const [key, value] of Object.entries(override)) {
|
||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||
result[key] = mergeConfigs(result[key] || {}, value);
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for current environment
|
||||
*/
|
||||
function getConfig() {
|
||||
const environment = process.env.NODE_ENV || 'development';
|
||||
const envConfig = environmentConfigs[environment] || {};
|
||||
|
||||
return mergeConfigs(defaultConfig, envConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate configuration
|
||||
*/
|
||||
function validateConfig(config) {
|
||||
const errors = [];
|
||||
|
||||
// Validate ports
|
||||
if (config.backend.port < 1 || config.backend.port > 65535) {
|
||||
errors.push(`Invalid backend port: ${config.backend.port}`);
|
||||
}
|
||||
|
||||
if (config.frontend.enabled && (config.frontend.port < 1 || config.frontend.port > 65535)) {
|
||||
errors.push(`Invalid frontend port: ${config.frontend.port}`);
|
||||
}
|
||||
|
||||
// Validate timeouts
|
||||
if (config.backend.startupTimeout < 1000) {
|
||||
errors.push('Backend startup timeout too low (minimum 1000ms)');
|
||||
}
|
||||
|
||||
if (config.database.connectionTimeout < 1000) {
|
||||
errors.push('Database connection timeout too low (minimum 1000ms)');
|
||||
}
|
||||
|
||||
// Validate required paths
|
||||
const requiredPaths = ['root', 'src', 'config'];
|
||||
for (const pathKey of requiredPaths) {
|
||||
if (!config.paths[pathKey]) {
|
||||
errors.push(`Missing required path: ${pathKey}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export configuration
|
||||
*/
|
||||
const config = getConfig();
|
||||
validateConfig(config);
|
||||
|
||||
module.exports = {
|
||||
config,
|
||||
getConfig,
|
||||
validateConfig,
|
||||
defaultConfig,
|
||||
environmentConfigs
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue