summaryrefslogtreecommitdiff
path: root/modules/subd-buien/subd-buien.js
blob: 1d599ebb515cb974ba042db3ce92323488b96960 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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();
	});
};