const express = require('express'); const mysql = require('mysql2'); const path = require('path'); const app = express(); const PORT = process.env.PORT || 3000; // Database connection pool const pool = mysql.createPool({ host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || 3306, user: process.env.DB_USER || '570005354', password: process.env.DB_PASS || '570005354', database: process.env.DB_NAME || 'shopdb', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); // Promisify for async/await const promisePool = pool.promise(); // Serve static files app.use(express.static('public')); // API endpoint to get notifications app.get('/api/notifications', async (req, res) => { try { const now = new Date(); const future = new Date(now.getTime() + (72 * 60 * 60 * 1000)); // 72 hours from now const [rows] = await promisePool.query( `SELECT n.notificationid, n.notification, n.starttime, n.endtime, n.ticketnumber, n.link, n.isactive, n.isshopfloor, nt.typename, nt.typecolor FROM notifications n LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid WHERE n.isactive = 1 AND n.isshopfloor = 1 AND ( (n.starttime <= ? AND (n.endtime IS NULL OR n.endtime >= ?)) OR (n.starttime BETWEEN ? AND ?) ) ORDER BY n.starttime ASC`, [future, now, now, future] ); // Categorize notifications const currentEvents = []; const upcomingEvents = []; rows.forEach(notification => { const start = new Date(notification.starttime); const end = notification.endtime ? new Date(notification.endtime) : null; if (start <= now && (end === null || end >= now)) { currentEvents.push(notification); } else { upcomingEvents.push(notification); } }); // Sort current events by severity priority, then by starttime // Priority: Incident (danger) > Change (warning) > Awareness/TBD (success) const severityPriority = { 'danger': 1, // Incident - highest priority 'warning': 2, // Change 'success': 3, // Awareness/TBD - lowest priority 'secondary': 4 // Fallback }; currentEvents.sort((a, b) => { const priorityA = severityPriority[a.typecolor] || 4; const priorityB = severityPriority[b.typecolor] || 4; // First sort by priority if (priorityA !== priorityB) { return priorityA - priorityB; } // If same priority, sort by starttime (earliest first) return new Date(a.starttime) - new Date(b.starttime); }); // Upcoming events stay sorted by starttime only upcomingEvents.sort((a, b) => { return new Date(a.starttime) - new Date(b.starttime); }); res.json({ success: true, timestamp: new Date().toISOString(), current: currentEvents, upcoming: upcomingEvents }); } catch (error) { console.error('Database error:', error); res.status(500).json({ success: false, error: 'Database error: ' + error.message }); } }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Start server app.listen(PORT, () => { console.log(`Shopfloor Dashboard running on port ${PORT}`); console.log(`Access at: http://localhost:${PORT}`); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM signal received: closing HTTP server'); pool.end(() => { console.log('Database pool closed'); process.exit(0); }); });