const cmn = require("../$common.js"); const fs = require("fs"); const express = require("express"); const child_process = require("child_process"); // NOTE: // This module expects a buienradar-store instance in the 'buienradar-store' // directory within this module's directory. The database therein should be // named 'db', and the background image should be stored under key 'mode' // (which should probably be the cmpref). // cb : (err, status, stdout : Buffer) -> () function runDbmanage(bdir, args, cb) { const options = { cwd: bdir, stdio: ["inherit", "pipe", "inherit"], timeout: 5000, // milliseconds }; const proc = child_process.spawn(bdir + "/dbmanage", args, options); const outbufs = []; proc.stdout.on("data", data => { outbufs.push(data); }); let callbackInvoked = false; proc.on("error", err => { if (callbackInvoked) return; callbackInvoked = true; cb(err, null, null); }); proc.on("exit", (code, sig) => { if (callbackInvoked) return; callbackInvoked = true; if (code != null) cb(null, code, Buffer.concat(outbufs)); else cb("Process terminated by signal", null, null); }); } // cb : (err, frames : [String]) -> () function getFramesList(bdir, cb) { runDbmanage(bdir, ["list", bdir + "/db"], (err, status, out) => { if (err) cb(err, null); else if (status != 0) cb("Error getting list of frames from DB", null); else cb(null, out.toString("utf8").trim().split("\n").filter(s => s != "mode")); }); } // cb : (err, pngs : Buffer) -> () function getFramesPng(bdir, stamps, cb) { runDbmanage(bdir, ["getmultiple", bdir + "/db"].concat(stamps), (err, status, out) => { if (err) { cb(err, null); return; } if (status != 0) { cb("Error getting frames from DB", null); return; } cb(null, out); // const pngs = []; // let cursor = 0; // while (cursor < out.length) { // let pnglen = out.readBigUInt64LE(cursor); // if (Number(pnglen) != pnglen) { // cb("PNG too large", null); // return; // } // pnglen = Number(pnglen); // pngs.push(out.slice(cursor + 8, cursor + 8 + pnglen)); // cursor += 8 + pnglen; // } // cb(null, pngs); }); } // cb : (err, present : Bool) -> () function checkFramePresent(bdir, stamp, cb) { runDbmanage(bdir, ["has", bdir + "/db", stamp], (err, status, out) => { if (err) { cb(err, null); return; } if (status == 1) { cb("Error getting status from DB", null); return; } if (status == 0) cb(null, true); else cb(null, false); }); } module.exports = function (app, io, moddir) { const buiendir = moddir + "/buienradar-store"; console.log(`buien: Using buienradar-store directory '${buiendir}'`); const router = express.Router(); router.get("/", function (req, res) { res.sendFile(moddir + "/index.html"); }); router.get("/api/list/json", function (req, res) { getFramesList(buiendir, (err, frames) => { if (err) { console.log(err); res.sendStatus(500); } else res.json(frames); }); }); router.get("/api/images", function (req, res) { const url = new URL(req.url, `https://${req.headers.host}`); const stamps = url.searchParams.getAll("s").flatMap(s => s.split(",")); getFramesPng(buiendir, stamps, (err, buf) => { if (err) { console.log(err); res.sendStatus(500); } else res.send(buf); }); }); router.get("/api/has/:stamp", function (req, res) { checkFramePresent(buiendir, req.params.stamp, (err, present) => { if (err) { console.log(err); res.sendStatus(500); } else if (present) res.sendStatus(200); else res.sendStatus(404); }); }); app.use(function (req, res, next) { if (req.subdomains.length && req.subdomains[req.subdomains.length - 1] == "buien") { router.handle(req, res, next); } else next(); }); };