UX-agent / backend /server.js
AUXteam's picture
Set Gemini model to gemini-2.0-flash and enable trust proxy
838da4d verified
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();