add Push-based monitoring (#279)

This commit is contained in:
LouisLam
2021-10-01 00:09:43 +08:00
parent 9e95d568c2
commit 1ed4ac9494
13 changed files with 292 additions and 30 deletions

View File

@@ -48,6 +48,7 @@ class Database {
"patch-add-retry-interval-monitor.sql": true,
"patch-incident-table.sql": true,
"patch-group-table.sql": true,
"patch-monitor-push_token.sql": true,
}
/**

View File

@@ -69,6 +69,7 @@ class Monitor extends BeanModel {
dns_resolve_type: this.dns_resolve_type,
dns_resolve_server: this.dns_resolve_server,
dns_last_result: this.dns_last_result,
pushToken: this.pushToken,
notificationIDList,
tags: tags,
};
@@ -236,6 +237,28 @@ class Monitor extends BeanModel {
bean.msg = dnsMessage;
bean.status = UP;
} else if (this.type === "push") { // Type: Push
const time = R.isoDateTime(dayjs.utc().subtract(this.interval, "second"));
let heartbeatCount = await R.count("heartbeat", " monitor_id = ? AND time > ? ", [
this.id,
time
]);
debug("heartbeatCount" + heartbeatCount + " " + time);
if (heartbeatCount <= 0) {
throw new Error("No heartbeat in the time window");
} else {
// No need to insert successful heartbeat for push type, so end here
retries = 0;
this.heartbeatInterval = setTimeout(beat, this.interval * 1000);
return;
}
} else {
bean.msg = "Unknown Monitor Type";
bean.status = PENDING;
}
if (this.isUpsideDown()) {
@@ -263,6 +286,8 @@ class Monitor extends BeanModel {
}
}
let beatInterval = this.interval;
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
@@ -312,8 +337,6 @@ class Monitor extends BeanModel {
bean.important = false;
}
let beatInterval = this.interval;
if (bean.status === UP) {
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
} else if (bean.status === PENDING) {
@@ -339,7 +362,14 @@ class Monitor extends BeanModel {
};
beat();
// Delay Push Type
if (this.type === "push") {
setTimeout(() => {
beat();
}, this.interval * 1000);
} else {
beat();
}
}
stop() {

View File

@@ -4,15 +4,53 @@ const { R } = require("redbean-node");
const server = require("../server");
const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor");
const dayjs = require("dayjs");
const { UP } = require("../../src/util");
let router = express.Router();
let cache = apicache.middleware;
let io = server.io;
router.get("/api/entry-page", async (_, response) => {
allowDevAllOrigin(response);
response.json(server.entryPage);
});
router.get("/api/push/:pushToken", async (request, response) => {
try {
let pushToken = request.params.pushToken;
let msg = request.query.msg || "OK";
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
pushToken
]);
if (! monitor) {
throw new Error("Monitor not found or not active.");
}
let bean = R.dispense("heartbeat");
bean.monitor_id = monitor.id;
bean.time = R.isoDateTime(dayjs.utc());
bean.status = UP;
bean.msg = msg;
await R.store(bean);
io.to(monitor.user_id).emit("heartbeat", bean.toJSON());
Monitor.sendStats(io, monitor.id, monitor.user_id);
response.json({
ok: true,
});
} catch (e) {
response.json({
ok: false,
msg: e.message
});
}
});
// Status Page Config
router.get("/api/status-page/config", async (_request, response) => {
allowDevAllOrigin(response);

View File

@@ -6,7 +6,7 @@ if (! process.env.NODE_ENV) {
console.log("Node Env: " + process.env.NODE_ENV);
const { sleep, debug, TimeLogger, getRandomInt } = require("../src/util");
const { sleep, debug, getRandomInt, genSecret } = require("../src/util");
console.log("Importing Node libraries");
const fs = require("fs");
@@ -37,7 +37,7 @@ console.log("Importing this project modules");
debug("Importing Monitor");
const Monitor = require("./model/monitor");
debug("Importing Settings");
const { getSettings, setSettings, setting, initJWTSecret, genSecret, allowDevAllOrigin, checkLogin } = require("./util-server");
const { getSettings, setSettings, setting, initJWTSecret, checkLogin } = require("./util-server");
debug("Importing Notification");
const { Notification } = require("./notification");
@@ -71,7 +71,7 @@ if (demoMode) {
console.log("==== Demo Mode ====");
}
console.log("Creating express and socket.io instance")
console.log("Creating express and socket.io instance");
const app = express();
let server;
@@ -511,6 +511,7 @@ exports.entryPage = "dashboard";
bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
bean.dns_resolve_type = monitor.dns_resolve_type;
bean.dns_resolve_server = monitor.dns_resolve_server;
bean.pushToken = monitor.pushToken;
await R.store(bean);

View File

@@ -272,16 +272,6 @@ exports.getTotalClientInRoom = (io, roomName) => {
}
};
exports.genSecret = () => {
let secret = "";
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let charsLength = chars.length;
for ( let i = 0; i < 64; i++ ) {
secret += chars.charAt(Math.floor(Math.random() * charsLength));
}
return secret;
};
exports.allowDevAllOrigin = (res) => {
if (process.env.NODE_ENV === "development") {
exports.allowAllOrigin(res);