const express = require('express'); const cors = require('cors'); const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); const { corsMiddleware, corsErrorHandler } = require('./middleware/cors'); const { initPool } = require('./utils/db'); const productsRouter = require('./routes/products'); const dashboardRouter = require('./routes/dashboard'); const ordersRouter = require('./routes/orders'); const csvRouter = require('./routes/data-management'); const analyticsRouter = require('./routes/analytics'); const purchaseOrdersRouter = require('./routes/purchase-orders'); const configRouter = require('./routes/config'); const metricsRouter = require('./routes/metrics'); const importRouter = require('./routes/import'); const aiValidationRouter = require('./routes/ai-validation'); const templatesRouter = require('./routes/templates'); const aiPromptsRouter = require('./routes/ai-prompts'); const reusableImagesRouter = require('./routes/reusable-images'); const categoriesAggregateRouter = require('./routes/categoriesAggregate'); const vendorsAggregateRouter = require('./routes/vendorsAggregate'); const brandsAggregateRouter = require('./routes/brandsAggregate'); // Get the absolute path to the .env file const envPath = '/var/www/html/inventory/.env'; console.log('Looking for .env file at:', envPath); console.log('.env file exists:', fs.existsSync(envPath)); try { require('dotenv').config({ path: envPath }); console.log('.env file loaded successfully'); console.log('Environment check:', { NODE_ENV: process.env.NODE_ENV || 'not set', PORT: process.env.PORT || 'not set', DB_HOST: process.env.DB_HOST || 'not set', DB_USER: process.env.DB_USER || 'not set', DB_NAME: process.env.DB_NAME || 'not set', DB_PASSWORD: process.env.DB_PASSWORD ? '[password set]' : 'not set', DB_PORT: process.env.DB_PORT || 'not set', DB_SSL: process.env.DB_SSL || 'not set' }); } catch (error) { console.error('Error loading .env file:', error); } // Ensure required directories exist ['logs', 'uploads'].forEach(dir => { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } }); const app = express(); // Debug middleware to log request details app.use((req, res, next) => { console.log('Request details:', { method: req.method, url: req.url, origin: req.get('Origin'), headers: req.headers }); next(); }); // Apply CORS middleware first, before any other middleware app.use(corsMiddleware); // Body parser middleware app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Initialize database pool and start server async function startServer() { try { // Initialize database pool const pool = await initPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, port: process.env.DB_PORT || 5432, max: process.env.NODE_ENV === 'production' ? 20 : 10, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : false }); // Make pool available to routes app.locals.pool = pool; // Set up routes after pool is initialized app.use('/api/products', productsRouter); app.use('/api/dashboard', dashboardRouter); app.use('/api/orders', ordersRouter); app.use('/api/csv', csvRouter); app.use('/api/analytics', analyticsRouter); app.use('/api/purchase-orders', purchaseOrdersRouter); app.use('/api/config', configRouter); app.use('/api/metrics', metricsRouter); // Use only the aggregate routes for vendors and categories app.use('/api/vendors', vendorsAggregateRouter); app.use('/api/categories', categoriesAggregateRouter); // Keep the aggregate-specific endpoints for backward compatibility app.use('/api/categories-aggregate', categoriesAggregateRouter); app.use('/api/vendors-aggregate', vendorsAggregateRouter); app.use('/api/brands-aggregate', brandsAggregateRouter); app.use('/api/import', importRouter); app.use('/api/ai-validation', aiValidationRouter); app.use('/api/templates', templatesRouter); app.use('/api/ai-prompts', aiPromptsRouter); app.use('/api/reusable-images', reusableImagesRouter); // Basic health check route app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV }); }); // CORS error handler - must be before other error handlers app.use(corsErrorHandler); // Error handling middleware - MUST be after routes and CORS error handler app.use((err, req, res, next) => { console.error(`[${new Date().toISOString()}] Error:`, err); // Send detailed error in development, generic in production const error = process.env.NODE_ENV === 'production' ? 'An internal server error occurred' : err.message || err; res.status(err.status || 500).json({ error }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`[Server] Running in ${process.env.NODE_ENV || 'development'} mode on port ${PORT}`); }); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } // Handle uncaught exceptions process.on('uncaughtException', (err) => { console.error(`[${new Date().toISOString()}] Uncaught Exception:`, err); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error(`[${new Date().toISOString()}] Unhandled Rejection at:`, promise, 'reason:', reason); }); // Initialize client sets for SSE const importClients = new Set(); const updateClients = new Set(); const resetClients = new Set(); const resetMetricsClients = new Set(); // Helper function to send progress to SSE clients const sendProgressToClients = (clients, data) => { clients.forEach(client => { try { client.write(`data: ${JSON.stringify(data)}\n\n`); } catch (error) { console.error('Error sending SSE update:', error); } }); }; // Setup SSE connection const setupSSE = (req, res) => { const { type } = req.params; // Set headers for SSE res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': req.headers.origin || '*', 'Access-Control-Allow-Credentials': 'true' }); // Send initial message res.write('data: {"status":"connected"}\n\n'); // Add client to appropriate set const clientSet = type === 'import' ? importClients : type === 'update' ? updateClients : type === 'reset' ? resetClients : type === 'reset-metrics' ? resetMetricsClients : null; if (clientSet) { clientSet.add(res); // Remove client when connection closes req.on('close', () => { clientSet.delete(res); }); } }; // Start the server startServer();