var express = require("express"); var hbs = require('express-hbs'); require('handlebars-form-helpers').register(hbs.handlebars); var nodemailer = require('nodemailer'); var courtsopenUtils = require('./courtsopenUtils.js'); var fs = require("fs"); var bodyParser = require("body-parser"); var app = express(); var winston = require('winston'); winston.add(winston.transports.File, { filename: './logs/courtsopen.log', maxsize: 5000000 }); // 5MB // Setup email var transporter = nodemailer.createTransport({ service: 'Gmail', auth: { user: 'alertmonitorfl@gmail.com', pass: 'ZPW!KdxkdTSmyH99' } }); var mailOptions = { from: 'Alert Monitor ', //to: 'jody@kaplon.us,don@gettner.com', to: 'jody@kaplon.us', subject: 'Device did not wish me a GoodMorning or GoodEvening', }; var file = "./db/courtsopen.db"; var exists = fs.existsSync(file); if(!exists) { console.log("Creating DB file."); fs.openSync(file, "w"); } var sqlite3 = require("sqlite3").verbose(); var db = new sqlite3.Database(file); var pg = require("pg"); var conString = "postgres://courtsopen:courtsopen@db/courtsopen"; app.use(bodyParser.json()); // Needed for JSON POST requests from Particle Cores. app.use(bodyParser.urlencoded({ extended: false })); // Needed for web POST requests from edit form. // Use `.hbs` for extensions and find partials in `views/partials`. app.engine('hbs', hbs.express4({ partialsDir: __dirname + '/views/partials' })); app.set('view engine', 'hbs'); app.set('views', __dirname + '/views'); app.get('/', function(req, res){ var d = new Date(); console.log("GET /, " + JSON.stringify(d, 4)); winston.info("GET /"); pg.connect(conString, function(err, client, done) { if(err) { return console.error('error fetching client from pool', err); } var devIndexQry = "select status, published_at " + "from alerts " + "where status in ('Open', 'Closed') " + "order by published_at desc " + "limit 2" client.query(devIndexQry, function(err, result) { //call `done()` to release the client back to the pool done(); if(err) { return console.error('error running query', err); } // Loop over elements in rows array, convert ugly UTC times to pretty local times. result.rows.forEach(function(row){ row.pubdate = courtsopenUtils.getLocDateFromUTC(row.published_at); row.pubtime = courtsopenUtils.getLocTimeFromUTC(row.published_at); if(row.status.toLowerCase().indexOf('closed') > -1){ row.statusclass = 'closed'; } else { row.statusclass = 'open'; } }); res.render('index', {values: result.rows}, function(err, html) { if(err !== null) { console.log(err); } else { res.send(html); } }); }); }); }); app.post('/', function(req, res){ var postEvent = req.body.postEvent; var source = req.body.source; console.log(req.body); // If it's stripped down JSON sent by cell modem, there won't be a req.body.data key w/full-nested JSON as value!!! // ???how to fix??? // If post body includes coreid and published_at, assume it's from a particle devcie and parse accordingly. if (req.body.coreid !== undefined && req.body.published_at !== undefined) { var innerDataJSON = JSON.parse(req.body.data); var status = JSON.stringify(innerDataJSON.status, null, 4).slice(1,-1); var coreid = JSON.stringify(req.body.coreid, null, 4).slice(1,-1); var pubAt = JSON.stringify(req.body.published_at, null, 4).slice(1,-1); pg.connect(conString, function(err, client, done) { if(err) { return console.error('error fetching client from pool', err); } client.query( "INSERT INTO alerts (origjson, coreid, published_at, status) VALUES ($1, $2, $3, $4);", [JSON.stringify(req.body, null, 4), coreid, pubAt, status] ); }); } else { // parse for minimal data from cell modem. var deviceid = JSON.stringify(req.body.data, null, 4).slice(1,4); var statusCode = JSON.stringify(req.body.data, null, 4).slice(5,7); var statusFromCode; switch (statusCode) { case "01": statusFromCode = "Open"; break; case "02": statusFromCode = "Closed"; break; case "03": statusFromCode = "GoodMorning"; break; case "04": statusFromCode = "GoodEvening"; break; default: statusFromCode = "UnknownStatus" }; var received_at = new Date(Date.now()); received_at = received_at.toISOString(); winston.info(statusFromCode + ' ' + received_at); pg.connect(conString, function(err, client, done) { if(err) { return console.error('error fetching client from pool', err); } client.query( "INSERT INTO alerts (origjson, coreid, published_at, status) VALUES ($1, $2, $3, $4);", [JSON.stringify(req.body, null, 4), deviceid, received_at, statusFromCode] ); }); } res.status(204).send('POST received'); }); setInterval(function() { // Check every hour to see if GoodMorning or GoodEvening has gone missing. pg.connect(conString, function(err, client, done) { if(err) { return winston.error('error fetching client from pool', err); } var deadManQry = "select published_at from alerts where to_timestamp(published_at, 'YYYY-MM-DD HH24:MI:SS') > (now() - interval '14.5 hours') limit 1"; client.query(deadManQry, function(err, result) { //call `done()` to release the client back to the pool done(); if(err) { return winston.error('error running query', err); } else if (result.rowCount == 0) { mailOptions.text = "It's been too long since the last data transmission from device. \n\n"; winston.info(mailOptions.text); // Don't include any other details for now, will need to change DB query to get details on last message received. //mailOptions.text = mailOptions.text + 'Status message, ' + status + '\n'; //mailOptions.text = mailOptions.text + 'Published at, ' + courtsopenUtils.getLocDateFromUTC(pubAt) + ' ' + courtsopenUtils.getLocTimeFromUTC(pubAt) + '\n'; transporter.sendMail(mailOptions, function(error, info){ if(error){ winston.error(error); }else{ winston.info('Message sent: ' + info.response); } }); } }); }); }, 60 * 60 * 1000); app.listen(3000, function() { winston.info("Started on PORT 3000"); });