courtsopen/server.js

175 lines
6.5 KiB
JavaScript

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 <alertmonitorfl@gmail.com>',
//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);
db.serialize(function() {
if(!exists) {
// Can't create multiple tables w/one statement, semicolons not allowed.
db.run(
"CREATE TABLE Alerts (" +
"origJSON TEXT," +
"coreId TEXT," +
"status TEXT," +
"published_at TEXT)"
);
db.run(
"CREATE TABLE Cores (" +
"coreId TEXT PRIMARY KEY," +
"coreName TEXT," +
"locationDesc TEXT)"
);
}
});
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 /");
var devIndexQry =
"select status, published_at " +
"from Alerts " +
"where status in ('Open', 'Closed') " +
"order by published_at desc " +
"limit 2"
db.all(devIndexQry, function(err, rows){
if(err !== null) {
console.log(err);
} else {
//console.log(rows);
// Loop over elements in rows array, convert ugly UTC times to pretty local times.
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: 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);
var stmt = db.prepare("INSERT INTO Alerts (OrigJSON, coreid, published_at, status) VALUES (?, ?, ?, ?)");
stmt.run(
JSON.stringify(req.body, null, 4),
coreid,
pubAt,
status
);
stmt.finalize();
} 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();
console.log(statusFromCode + ' ' + received_at);
var stmtCell = db.prepare("INSERT INTO Alerts (OrigJSON, coreid, published_at, status) VALUES (?, ?, ?, ?)");
stmtCell.run(
JSON.stringify(req.body, null, 4),
deviceid,
received_at,
statusFromCode
);
stmtCell.finalize();
}
res.status(204).send('POST received');
});
setInterval(function() {
// Check every hour to see if GoodMorning or GoodEvening has gone missing.
var deadManQry = "select published_at from Alerts where datetime(published_at) > datetime('now', '-14.5 hours') order by datetime(published_at) limit 1";
db.get(deadManQry, function(err, row){
if (err !== null) { winston.error(err); }
else if (typeof row == "undefined") {
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.info(error);
}else{
winston.info('Message sent: ' + info.response);
}
});
}
});
}, 60 * 60 * 1000);
app.listen(3000, function() {
winston.info("Started on PORT 3000");
});