diff --git a/server.js b/server.js index dced90f..9b013b7 100644 --- a/server.js +++ b/server.js @@ -14,7 +14,12 @@ const PORT = process.env.PORT || 3000; const shiftCounters = {}; // ─── Helpers ───────────────────────────────────────────────────────────────── -// Format an epoch ms timestamp for Slack: "M/D/YY @HH:mm" in America/New_York +// pad two digits +function pad2(n) { + return n.toString().padStart(2, '0'); +} + +// Format an epoch‐ms timestamp for Slack: "M/D/YY @HH:mm" in America/New_York function formatForSlack(epoch) { return new Date(epoch).toLocaleString('en-US', { timeZone: 'America/New_York', @@ -39,24 +44,27 @@ function computeHeatIndex(T, R) { return Math.round(HI * 100) / 100; } -// Determine Day/Night shift and rolling period key +// Determine Day/Night shift & rolling period key based on a JS epoch function getShiftInfo(epoch) { - // first get a string in NY time, then parse back to Date - const estString = new Date(epoch).toLocaleString('en-US', { timeZone: 'America/New_York' }); + // convert epoch to an EST Date object by round‐trip through toLocaleString + const estString = new Date(epoch) + .toLocaleString('en-US', { timeZone: 'America/New_York' }); const est = new Date(estString); const h = est.getHours(), m = est.getMinutes(); let shift, start = new Date(est); if (h > 7 || (h === 7 && m >= 0)) { if (h < 17 || (h === 17 && m < 30)) { - shift = 'Day'; start.setHours(7,0,0,0); + shift = 'Day'; + start.setHours(7, 0, 0, 0); } else { - shift = 'Night'; start.setHours(17,30,0,0); + shift = 'Night'; + start.setHours(17, 30, 0, 0); } } else { shift = 'Night'; start.setDate(start.getDate() - 1); - start.setHours(17,30,0,0); + start.setHours(17, 30, 0, 0); } const key = `${shift}-${start.toISOString().slice(0,10)}-${start.getHours()}${start.getMinutes()}`; @@ -70,7 +78,7 @@ async function fetchCurrentWeather() { try { const { data } = await axios.get( 'https://api.openweathermap.org/data/2.5/weather', - { params: { zip: `${zip},us`, appid: key, units: 'imperial' } } + { params: { zip:`${zip},us`, appid:key, units:'imperial' } } ); const desc = data.weather[0].description.replace(/^\w/, c => c.toUpperCase()); const hi = Math.round(data.main.temp_max); @@ -85,7 +93,7 @@ async function fetchCurrentWeather() { // ─── MariaDB Pool & Table Setup ─────────────────────────────────────────────── const pool = mysql.createPool({ host: process.env.DB_HOST, - port: parseInt(process.env.DB_PORT,10) || 3306, + port: parseInt(process.env.DB_PORT, 10) || 3306, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, @@ -95,14 +103,14 @@ const pool = mysql.createPool({ connectTimeout: 10000 }); -// Ensure readings table exists (with epoch_ms instead of timestamp) (async () => { + // Create readings table with epoch_ms instead of timestamp await pool.execute(` CREATE TABLE IF NOT EXISTS readings ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - location VARCHAR(20) NOT NULL, - stationDockDoor VARCHAR(10) NOT NULL, - epoch_ms BIGINT NOT NULL, + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + location VARCHAR(20) NOT NULL, + stationDockDoor VARCHAR(10) NOT NULL, + epoch_ms BIGINT NOT NULL, temperature DOUBLE, humidity DOUBLE, heatIndex DOUBLE, @@ -114,7 +122,7 @@ const pool = mysql.createPool({ // ─── Middleware & Static ───────────────────────────────────────────────────── app.use(bodyParser.json()); -app.use(express.static(path.join(__dirname,'public'), { index: 'heatmap.html' })); +app.use(express.static(path.join(__dirname, 'public'), { index: 'heatmap.html' })); // ─── SSE Setup ──────────────────────────────────────────────────────────────── let clients = []; @@ -150,13 +158,13 @@ app.post('/api/readings', async (req, res) => { shiftCounters[key] = (shiftCounters[key] || 0) + 1; const period = shiftCounters[key]; - // Insert inbound + outbound - const sql = ` + // Insert inbound and outbound + const insertSQL = ` INSERT INTO readings(location,stationDockDoor,epoch_ms,temperature,humidity,heatIndex) VALUES (?, ?, ?, ?, ?, ?) `; - await pool.execute(sql, ['Inbound', String(inD), epoch, inT, inH, hiIn]); - await pool.execute(sql, ['Outbound', String(outD), epoch, outT, outH, hiOut]); + await pool.execute(insertSQL, ['Inbound', String(inD), epoch, inT, inH, hiIn]); + await pool.execute(insertSQL, ['Outbound', String(outD), epoch, outT, outH, hiOut]); // Broadcast via SSE const slackTs = formatForSlack(epoch); @@ -177,15 +185,20 @@ app.post('/api/readings', async (req, res) => { heatIndex: hiOut }); - // CSV upload for today's readings + // Upload today's CSV const y = estNow.getFullYear(), m = pad2(estNow.getMonth()+1), d = pad2(estNow.getDate()); const dateKey = `${y}${m}${d}`; const [rows] = await pool.execute( - `SELECT * FROM readings WHERE DATE(FROM_UNIXTIME(epoch_ms/1000)) = CURDATE() ORDER BY epoch_ms` + `SELECT * FROM readings + WHERE DATE(FROM_UNIXTIME(epoch_ms/1000)) = CURDATE() + ORDER BY epoch_ms` ); let csvUrl = null; - try { csvUrl = await uploadTrendsCsv(dateKey, rows); } - catch (e) { console.error('CSV upload error:', e); } + try { + csvUrl = await uploadTrendsCsv(dateKey, rows); + } catch (e) { + console.error('CSV upload error:', e); + } // Send Slack notification const weather = await fetchCurrentWeather(); @@ -223,7 +236,7 @@ app.post('/api/area-readings', async (req, res) => { } const epoch = Date.now(); - const hi = computeHeatIndex(T, H); + const hi = computeHeatIndex(T, H); const { shift, key, estNow } = getShiftInfo(epoch); await pool.execute( @@ -245,11 +258,16 @@ app.post('/api/area-readings', async (req, res) => { const y = estNow.getFullYear(), m = pad2(estNow.getMonth()+1), d = pad2(estNow.getDate()); const dateKey = `${y}${m}${d}`; const [rows] = await pool.execute( - `SELECT * FROM readings WHERE DATE(FROM_UNIXTIME(epoch_ms/1000)) = CURDATE() ORDER BY epoch_ms` + `SELECT * FROM readings + WHERE DATE(FROM_UNIXTIME(epoch_ms/1000)) = CURDATE() + ORDER BY epoch_ms` ); let csvUrl = null; - try { csvUrl = await uploadTrendsCsv(dateKey, rows); } - catch (e) { console.error('CSV upload error:', e); } + try { + csvUrl = await uploadTrendsCsv(dateKey, rows); + } catch (e) { + console.error('CSV upload error:', e); + } const weather = await fetchCurrentWeather(); const text = @@ -292,7 +310,10 @@ app.get('/api/export', async (req, res) => { res.set('Content-Type', 'text/csv'); res.write('id,location,stationDockDoor,epoch_ms,temperature,humidity,heatIndex\n'); rows.forEach(r => { - res.write(`${r.id},${r.location},${r.stationDockDoor},${r.epoch_ms},${r.temperature},${r.humidity},${r.heatIndex}\n`); + res.write( + `${r.id},${r.location},${r.stationDockDoor},${r.epoch_ms},` + + `${r.temperature},${r.humidity},${r.heatIndex}\n` + ); }); res.end(); } catch (err) { @@ -305,8 +326,3 @@ app.get('/api/export', async (req, res) => { app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); }); - -// Utility pad2 used above -function pad2(n) { - return n.toString().padStart(2,'0'); -}