// node_api/server.js const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const morgan = require('morgan'); const rateLimit = require('express-rate-limit'); require('dotenv').config(); const { authMiddleware } = require('./middleware/auth'); const assetsRoutes = require('./routes/assets'); const healthRoutes = require('./routes/health'); const assetTypesRoutes = require('./routes/asset_types'); const maintenanceRecordsRoutes = require('./routes/maintenance_records'); const jobsRoutes = require('./routes/jobs'); const assetComponentsRoutes = require('./routes/asset_components'); const assetComponentTypesRoutes = require('./routes/asset_component_types'); const { jobManager } = require('./services/jobManager'); const app = express(); const PORT = process.env.PORT || 3001; // Security middleware app.use(helmet()); app.use(compression()); // CORS configuration const corsOrigins = (process.env.CORS_ORIGIN || 'http://localhost:5173').split(','); app.use(cors({ origin: corsOrigins, credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'] })); // Rate limiting const limiter = rateLimit({ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100 }); app.use('/api/', limiter); // Logging app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev')); // Body parsing app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Serve static files app.use(express.static('public')); // Health check (no auth required) app.use('/health', healthRoutes); // Protected API routes app.use('/api', authMiddleware); app.use('/api/assets', assetsRoutes); app.use('/api/asset-types', assetTypesRoutes); app.use('/api/maintenance-records', maintenanceRecordsRoutes); app.use('/api/jobs', jobsRoutes); app.use('/api/asset-components', assetComponentsRoutes); app.use('/api/asset-component-types', assetComponentTypesRoutes); // Error handling middleware app.use((err, req, res, next) => { console.error('Error:', err); if (err.name === 'JsonWebTokenError') { return res.status(401).json({ error: 'Invalid token' }); } if (err.name === 'TokenExpiredError') { return res.status(401).json({ error: 'Token expired' }); } res.status(err.status || 500).json({ error: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message }); }); // 404 handler app.use('*', (req, res) => { res.status(404).json({ error: 'Route not found' }); }); app.listen(PORT, async () => { console.log(`🚀 Asset Management API running on port ${PORT}`); console.log(`📊 Environment: ${process.env.NODE_ENV}`); console.log(`🔗 CORS Origin: ${process.env.CORS_ORIGIN}`); // Initialize and start job manager try { await jobManager.initialize(); await jobManager.start(); console.log('📅 Job manager initialized and started'); } catch (error) { console.error('❌ Failed to initialize job manager:', error); } }); // Graceful shutdown process.on('SIGTERM', async () => { console.log('SIGTERM received, shutting down gracefully...'); try { await jobManager.shutdown(); console.log('Job manager shut down successfully'); } catch (error) { console.error('Error during job manager shutdown:', error); } process.exit(0); }); process.on('SIGINT', async () => { console.log('SIGINT received, shutting down gracefully...'); try { await jobManager.shutdown(); console.log('Job manager shut down successfully'); } catch (error) { console.error('Error during job manager shutdown:', error); } process.exit(0); });