Spaces:
Paused
Paused
| const express = require('express'); | |
| const cors = require('cors'); | |
| const helmet = require('helmet'); | |
| const rateLimit = require('express-rate-limit'); | |
| require('dotenv').config(); | |
| const { initializeDatabase } = require('./database/init'); | |
| const path = require('path'); | |
| const analysisRoutes = require('./routes/analysis'); | |
| const healthRoutes = require('./routes/health'); | |
| const docRoutes = require('./routes/docs'); | |
| // New robust architecture imports | |
| const configManager = require('./config/ConfigManager'); | |
| const serviceContainer = require('./core/ServiceContainer'); | |
| const ScreenshotService = require('./services/screenshotService'); | |
| const AICritiqueService = require('./services/aiCritiqueService'); | |
| const VisualDesignAnalyzer = require('./services/visualDesignAnalyzer'); | |
| const AnalysisService = require('./services/analysisService'); | |
| const CodeGenerationService = require('./services/codeGenerationService'); | |
| const app = express(); | |
| app.set('trust proxy', 1); | |
| let config = null; | |
| let PORT = process.env.PORT || 3000; | |
| // Configure middleware and routes after initialization | |
| function setupMiddleware() { | |
| // Security middleware | |
| app.use(helmet()); | |
| app.use(cors()); | |
| // Rate limiting (disabled in development) | |
| if (config.server.environment === 'production') { | |
| const limiter = rateLimit({ | |
| windowMs: config.server.rateLimitWindowMs || 900000, // 15 minutes | |
| max: config.server.rateLimitMaxRequests || 10, | |
| message: 'Too many requests, please try again later.', | |
| }); | |
| app.use('/api/', limiter); | |
| } | |
| // Body parsing | |
| app.use(express.json({ limit: '10mb' })); | |
| app.use(express.urlencoded({ extended: true })); | |
| // Make services available to routes | |
| app.use((req, res, next) => { | |
| req.services = serviceContainer; | |
| req.config = config; | |
| next(); | |
| }); | |
| // Routes | |
| app.use('/api/health', healthRoutes); | |
| app.use('/api/analyze', analysisRoutes); | |
| app.use('/api-docs', docRoutes); | |
| // HF Space required health check at root | |
| app.get('/health', (req, res) => { | |
| res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() }); | |
| }); | |
| // Serve static frontend files | |
| app.use(express.static(path.join(__dirname, '../frontend/dist'))); | |
| // Static files for screenshots | |
| app.use('/screenshots', express.static(config.screenshots.storagePath || 'data/screenshots')); | |
| // Error handling | |
| app.use((err, req, res, next) => { | |
| console.error(err.stack); | |
| res.status(500).json({ | |
| error: 'Something went wrong!', | |
| message: config.server.environment === 'development' ? err.message : undefined | |
| }); | |
| }); | |
| // 404 handler for API routes | |
| app.use('/api/*', (req, res) => { | |
| res.status(404).json({ error: 'Not found' }); | |
| }); | |
| // Fallback to index.html for frontend routing (SPA) | |
| app.get('*', (req, res) => { | |
| res.sendFile(path.join(__dirname, '../frontend/dist/index.html')); | |
| }); | |
| } | |
| // Initialize robust architecture and start server | |
| async function startServer() { | |
| try { | |
| console.log('π Starting UX Analyst AI server with robust architecture...'); | |
| // 1. Initialize configuration | |
| console.log('π Initializing configuration...'); | |
| await configManager.initialize(); | |
| configManager.validateRequiredEnvVars(); | |
| config = configManager.get(); | |
| PORT = config.server.port; | |
| console.log('β Configuration loaded successfully'); | |
| // 2. Initialize database | |
| console.log('ποΈ Initializing database...'); | |
| await initializeDatabase(); | |
| console.log('β Database initialized'); | |
| // 3. Register services with dependency injection | |
| console.log('π§ Registering services...'); | |
| await registerServices(); | |
| // 4. Start all services | |
| console.log('βΆοΈ Starting services...'); | |
| await serviceContainer.startServices(); | |
| console.log('β All services started successfully'); | |
| // 5. Setup Express middleware and routes | |
| console.log('π Setting up Express middleware and routes...'); | |
| setupMiddleware(); | |
| // 6. Start HTTP server | |
| await startServerWithPortHandling(); | |
| // 7. Health check | |
| console.log('π Performing initial health check...'); | |
| const healthStatus = await serviceContainer.getHealthStatus(); | |
| console.log(`π₯ System health: ${healthStatus.status}`); | |
| // 8. Graceful shutdown handling | |
| process.on('SIGTERM', gracefulShutdown); | |
| process.on('SIGINT', gracefulShutdown); | |
| console.log('π UX Analyst AI server startup complete!'); | |
| } catch (error) { | |
| console.error('β Failed to start server:', error); | |
| await gracefulShutdown(); | |
| process.exit(1); | |
| } | |
| } | |
| // Register all services with the service container | |
| async function registerServices() { | |
| // Register ScreenshotService | |
| serviceContainer.register('screenshotService', (deps, container) => { | |
| return new ScreenshotService(config); | |
| }, { | |
| singleton: true, | |
| startupPriority: 10, | |
| dependencies: [], | |
| circuitBreaker: { | |
| failureThreshold: 3, | |
| recoveryTimeout: 30000 | |
| }, | |
| healthCheck: async (service) => service.getHealthStatus() | |
| }); | |
| // Register VisualDesignAnalyzer | |
| serviceContainer.register('visualDesignAnalyzer', (deps, container) => { | |
| return new VisualDesignAnalyzer(); | |
| }, { | |
| singleton: true, | |
| startupPriority: 20, | |
| dependencies: [], | |
| healthCheck: async (service) => ({ status: 'healthy' }) | |
| }); | |
| // Register AICritiqueService | |
| serviceContainer.register('aiCritiqueService', (deps, container) => { | |
| return new AICritiqueService(config); | |
| }, { | |
| singleton: true, | |
| startupPriority: 30, | |
| dependencies: [], | |
| circuitBreaker: { | |
| failureThreshold: 5, | |
| recoveryTimeout: 60000, | |
| expectedErrors: ['rate limit', '429', '503', 'timeout', 'overloaded'] | |
| }, | |
| healthCheck: async (service) => service.getHealthStatus() | |
| }); | |
| // Register CodeGenerationService | |
| serviceContainer.register('codeGenerationService', (deps, container) => { | |
| return new CodeGenerationService({ | |
| geminiApiKey: config.ai.geminiApiKey, | |
| logger: console | |
| }); | |
| }, { | |
| singleton: true, | |
| startupPriority: 35, | |
| dependencies: [], | |
| circuitBreaker: { | |
| failureThreshold: 3, | |
| recoveryTimeout: 30000, | |
| expectedErrors: ['rate limit', '429', '503', 'timeout'] | |
| }, | |
| healthCheck: async (service) => service.getHealthStatus() | |
| }); | |
| // Register AnalysisService (depends on other services) | |
| serviceContainer.register('analysisService', (deps, container) => { | |
| const screenshotService = container.get('screenshotService'); | |
| const aiCritiqueService = container.get('aiCritiqueService'); | |
| const visualDesignAnalyzer = container.get('visualDesignAnalyzer'); | |
| const codeGenerationService = container.get('codeGenerationService'); | |
| return new AnalysisService({ | |
| screenshotService, | |
| aiCritiqueService, | |
| visualDesignAnalyzer, | |
| codeGenerationService, | |
| config | |
| }); | |
| }, { | |
| singleton: true, | |
| startupPriority: 40, | |
| dependencies: ['screenshotService', 'aiCritiqueService', 'visualDesignAnalyzer', 'codeGenerationService'], | |
| healthCheck: async (service) => service.getHealthStatus ? service.getHealthStatus() : { status: 'healthy' } | |
| }); | |
| console.log('β All services registered with dependency injection'); | |
| } | |
| async function startServerWithPortHandling() { | |
| return new Promise((resolve, reject) => { | |
| const server = app.listen(PORT) | |
| .on('listening', () => { | |
| console.log(`π UX Analyst AI server running on port ${PORT}`); | |
| console.log(`π Environment: ${config.server.environment}`); | |
| console.log(`π Service endpoints:`); | |
| console.log(` β’ Health: http://${config.server.host}:${PORT}/api/health`); | |
| console.log(` β’ Analysis: http://${config.server.host}:${PORT}/api/analyze`); | |
| console.log(` β’ Screenshots: http://${config.server.host}:${PORT}/screenshots/`); | |
| console.log(`π‘οΈ Circuit breakers active for fault tolerance`); | |
| console.log(`β»οΈ Resource cleanup and monitoring enabled`); | |
| resolve(server); | |
| }) | |
| .on('error', (error) => { | |
| if (error.code === 'EADDRINUSE') { | |
| console.warn(`Port ${PORT} is in use. Attempting to detect and clean up stuck processes...`); | |
| // In production, you could add more sophisticated port cleanup here | |
| // For development, we'll provide helpful information | |
| console.error(` | |
| β Port ${PORT} is already in use! | |
| This usually means: | |
| 1. Another instance of this server is already running | |
| 2. A previous server instance crashed and didn't release the port | |
| 3. Another application is using this port | |
| π§ To resolve this: | |
| 1. Check running processes: netstat -ano | findstr :${PORT} | |
| 2. Kill the process: taskkill /PID <process_id> | |
| 3. Or restart your terminal/system | |
| For production deployment, consider implementing automatic port cleanup or using process managers like PM2. | |
| `); | |
| reject(error); | |
| } else { | |
| reject(error); | |
| } | |
| }); | |
| // Store server reference for graceful shutdown | |
| global.server = server; | |
| }); | |
| } | |
| async function gracefulShutdown() { | |
| console.log('π Received shutdown signal, cleaning up...'); | |
| try { | |
| // Stop watching configuration files | |
| if (configManager) { | |
| configManager.stopWatching(); | |
| console.log('π Configuration manager stopped'); | |
| } | |
| // Shutdown all services through service container | |
| await serviceContainer.shutdown(); | |
| console.log('π§ All services shut down'); | |
| // Close HTTP server | |
| if (global.server) { | |
| await new Promise((resolve, reject) => { | |
| global.server.close((error) => { | |
| if (error) { | |
| reject(error); | |
| } else { | |
| console.log('π HTTP server closed'); | |
| resolve(); | |
| } | |
| }); | |
| }); | |
| } | |
| console.log('β Graceful shutdown complete'); | |
| process.exit(0); | |
| } catch (error) { | |
| console.error('β Error during graceful shutdown:', error); | |
| // Force shutdown after error | |
| setTimeout(() => { | |
| console.error('β οΈ Forcing shutdown after error'); | |
| process.exit(1); | |
| }, 5000); | |
| } | |
| } | |
| startServer(); |