import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import session from 'express-session'; import rateLimit from 'express-rate-limit'; import dotenv from 'dotenv'; import { testConnection } from './config.js'; import { sessionConfig, securityConfig, serverConfig } from './config.js'; import { notFoundHandler, errorHandler } from './middleware/errorHandler.js'; // Routes import authRoutes from './routes/auth.js'; import analyzeRoutes from './routes/analyze.js'; import adminRoutes from './routes/admin.js'; import llmConfigRoutes from './routes/llmConfig.js'; // 載入環境變數 dotenv.config(); const app = express(); const PORT = serverConfig.port; // ============================================ // Middleware Setup // ============================================ // Security Headers app.use(helmet({ contentSecurityPolicy: false, // 暫時關閉 CSP 以便開發 })); // CORS app.use(cors({ origin: [`http://localhost:${serverConfig.clientPort}`, 'http://localhost:5173'], credentials: true })); // Body Parser app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Session app.use(session({ secret: sessionConfig.secret, resave: sessionConfig.resave, saveUninitialized: sessionConfig.saveUninitialized, cookie: sessionConfig.cookie, name: '5why.sid' })); // Rate Limiting const limiter = rateLimit({ windowMs: securityConfig.rateLimitWindowMs, max: securityConfig.rateLimitMax, message: { success: false, error: '請求過於頻繁,請稍後再試' }, standardHeaders: true, legacyHeaders: false }); app.use('/api/', limiter); // Request Logging (Development) if (process.env.NODE_ENV === 'development') { app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); } // ============================================ // Routes // ============================================ // Health Check app.get('/health', (req, res) => { res.json({ status: 'ok', message: 'Server is running', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV || 'development' }); }); // Database Health Check app.get('/health/db', async (req, res) => { try { const isConnected = await testConnection(); res.json({ status: isConnected ? 'ok' : 'error', database: isConnected ? 'connected' : 'disconnected' }); } catch (error) { res.status(500).json({ status: 'error', database: 'error', message: error.message }); } }); // API Routes app.use('/api/auth', authRoutes); app.use('/api/analyze', analyzeRoutes); app.use('/api/admin', adminRoutes); app.use('/api/llm-config', llmConfigRoutes); // Root Endpoint app.get('/', (req, res) => { res.json({ message: '5 Why Root Cause Analyzer API', version: '1.0.0', endpoints: { health: '/health', auth: { login: 'POST /api/auth/login', logout: 'POST /api/auth/logout', me: 'GET /api/auth/me' }, analyze: { create: 'POST /api/analyze', history: 'GET /api/analyze/history', detail: 'GET /api/analyze/:id', translate: 'POST /api/analyze/translate' }, admin: { dashboard: 'GET /api/admin/dashboard', users: 'GET /api/admin/users', analyses: 'GET /api/admin/analyses', auditLogs: 'GET /api/admin/audit-logs' }, llmConfig: { list: 'GET /api/llm-config', active: 'GET /api/llm-config/active', create: 'POST /api/llm-config', update: 'PUT /api/llm-config/:id', activate: 'PUT /api/llm-config/:id/activate', delete: 'DELETE /api/llm-config/:id', test: 'POST /api/llm-config/test' } } }); }); // ============================================ // Error Handling // ============================================ // 404 Handler app.use(notFoundHandler); // Global Error Handler app.use(errorHandler); // ============================================ // Server Startup // ============================================ async function startServer() { try { console.log('\n╔════════════════════════════════════════════╗'); console.log('║ 5 Why Analyzer - Server Starting... ║'); console.log('╚════════════════════════════════════════════╝\n'); // Test database connection console.log('📡 Testing database connection...'); const dbConnected = await testConnection(); if (!dbConnected) { console.warn('⚠️ Warning: Database connection failed'); console.warn(' Server will start but database features will not work'); } // Start server app.listen(PORT, () => { console.log('\n✅ Server is running!'); console.log(` URL: http://localhost:${PORT}`); console.log(` Environment: ${process.env.NODE_ENV || 'development'}`); console.log(` Database: ${dbConnected ? 'Connected ✓' : 'Disconnected ✗'}`); console.log(`\n📚 API Documentation: http://localhost:${PORT}/`); console.log(`🔍 Health Check: http://localhost:${PORT}/health`); console.log('\n💡 Press Ctrl+C to stop the server\n'); }); } catch (error) { console.error('\n❌ Failed to start server:'); console.error(' Error:', error.message); process.exit(1); } } // Handle uncaught exceptions process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); process.exit(1); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); process.exit(1); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('\n📴 SIGTERM received. Shutting down gracefully...'); process.exit(0); }); process.on('SIGINT', () => { console.log('\n📴 SIGINT received. Shutting down gracefully...'); process.exit(0); }); // Start the server startServer();