const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const path = require('path'); const multer = require('multer'); // Import configuration const config = require('./config/production'); // Import logging and error handling const logger = require('./utils/logger'); const { healthCheckLogger } = require('./middleware/requestLogger'); const { errorHandler, notFoundHandler, timeoutHandler } = require('./middleware/errorHandler'); // Import backup manager for scheduled backups const BackupManager = require('./utils/backup'); const app = express(); const PORT = config.server.port; // Request timeout middleware app.use(timeoutHandler(config.server.requestTimeout)); // Request logging middleware app.use(healthCheckLogger); // Security middleware app.use(helmet({ contentSecurityPolicy: config.security.helmetCspEnabled })); app.use(cors({ origin: config.security.corsOrigin })); // Body parsing middleware const maxSize = `${Math.floor(config.upload.maxSize / (1024 * 1024))}mb`; app.use(express.json({ limit: maxSize })); app.use(express.urlencoded({ extended: true, limit: maxSize })); // Serve static files app.use(express.static('public')); // Simple API test endpoints (before other routes) app.get('/api/status', (req, res) => { res.json({ success: true, message: 'API is working', timestamp: new Date().toISOString(), server: 'inventory-barcode-system', version: '1.0.0' }); }); // Add a test endpoint at the root level (not in products router) app.get('/api/test', (req, res) => { res.json({ success: true, message: 'API test endpoint working', timestamp: new Date().toISOString(), endpoints: { products: '/api/products', status: '/api/status', import: '/api/import/preview' } }); }); // Add a direct import endpoint at the root level const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10 * 1024 * 1024, // 10MB limit } }); app.post('/api/import/preview', upload.single('file'), (req, res) => { console.log('Root-level import preview endpoint hit:', { hasFile: !!req.file, filename: req.file?.originalname, size: req.file?.size, mimetype: req.file?.mimetype, import: req.query.import === 'true' }); try { if (!req.file) { console.log('No file uploaded'); return res.status(400).json({ success: false, error: 'No file uploaded', message: 'Please upload an Excel file' }); } console.log('Processing file:', req.file.originalname); // Check if this is an import or preview request const isImport = req.query.import === 'true'; if (isImport) { // Mock import response const importResponse = { success: true, data: { importResults: { imported: 2, failed: 1, created: 2, updated: 0, skipped: 1 } }, message: 'Excel file imported successfully (root-level endpoint)' }; console.log('Sending root-level import response (import mode)'); res.json(importResponse); } else { // Mock preview response const mockResponse = { success: true, data: { preview: { totalRows: 3, validProducts: 2, invalidProducts: 1, duplicateProducts: 0, existingProducts: 0, sampleProducts: [ { product_code: 'TEST001', description: 'Test Product 1', quantity: 10, isValid: true }, { product_code: 'TEST002', description: 'Test Product 2', quantity: 20, isValid: true }, { product_code: '', description: 'Invalid Product', quantity: 0, isValid: false, validationErrors: [{ message: 'Missing product code' }] } ] }, validationResults: { isValid: false, errors: [ { row: 3, message: 'Missing product code' } ], statistics: { validProducts: 2, invalidProducts: 1, duplicateProducts: 0, existingProducts: 0 } } }, message: 'Excel file preview generated successfully (root-level endpoint)' }; console.log('Sending root-level import response (preview mode)'); res.json(mockResponse); } } catch (error) { console.error('Root-level import preview error:', error); res.status(500).json({ success: false, error: 'Import preview failed', message: error.message }); } }); // API Routes const productRoutes = require('./routes/products'); const inventoryRoutes = require('./routes/inventory'); const codesRoutes = require('./routes/codes'); app.use('/api/products', productRoutes); app.use('/api/inventory', inventoryRoutes); app.use('/api/codes', codesRoutes); // 404 handler for unmatched routes app.use(notFoundHandler); // Centralized error handling middleware app.use(errorHandler); // Basic route app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), version: process.version }); }); // Graceful shutdown handling const gracefulShutdown = (signal) => { logger.info(`Received ${signal}. Starting graceful shutdown...`); // Close server server.close(() => { logger.info('HTTP server closed'); // Close database connections if any // Add any cleanup logic here logger.info('Graceful shutdown completed'); process.exit(0); }); // Force shutdown after 10 seconds setTimeout(() => { logger.error('Forced shutdown after timeout'); process.exit(1); }, 10000); }; // Start server only if this file is run directly let server; if (require.main === module) { // Validate configuration try { config.validate(); } catch (error) { logger.error('Configuration validation failed', { error: error.message }); process.exit(1); } // Initialize backup manager const backupManager = new BackupManager(); backupManager.initialize().then(() => { if (config.backup.enabled) { backupManager.scheduleBackups(); logger.info('Automatic backups enabled', { interval: config.database.backupInterval, retention: config.backup.retentionDays }); } }).catch(error => { logger.warn('Failed to initialize backup manager', { error: error.message }); }); server = app.listen(PORT, config.server.host, () => { logger.info('Server Started', { port: PORT, host: config.server.host, environment: config.server.environment, nodeVersion: process.version, pid: process.pid }); console.log(`Server running on ${config.server.host}:${PORT}`); console.log(`Visit http://localhost:${PORT} to access the application`); }); // Handle graceful shutdown process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Handle uncaught exceptions process.on('uncaughtException', (error) => { logger.error('Uncaught Exception', { error: error.message, stack: error.stack }); process.exit(1); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Promise Rejection', { reason: reason, promise: promise }); process.exit(1); }); } module.exports = app;