diff --git a/db/patch-monitor-push_token.sql b/db/patch-monitor-push_token.sql
new file mode 100644
index 000000000..8c2e7a42c
--- /dev/null
+++ b/db/patch-monitor-push_token.sql
@@ -0,0 +1,7 @@
+-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
+BEGIN TRANSACTION;
+
+ALTER TABLE monitor
+ ADD push_token VARCHAR(20) DEFAULT NULL;
+
+COMMIT;
diff --git a/server/database.js b/server/database.js
index 4cf1e3933..47eca2835 100644
--- a/server/database.js
+++ b/server/database.js
@@ -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,
}
/**
diff --git a/server/model/monitor.js b/server/model/monitor.js
index a50baccfd..c551fa7d7 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -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() {
diff --git a/server/routers/api-router.js b/server/routers/api-router.js
index b56efcb22..6c87fb179 100644
--- a/server/routers/api-router.js
+++ b/server/routers/api-router.js
@@ -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);
diff --git a/server/server.js b/server/server.js
index ddd686951..2384acf57 100644
--- a/server/server.js
+++ b/server/server.js
@@ -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);
diff --git a/server/util-server.js b/server/util-server.js
index 4d2b6cbe1..29e4b11fd 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -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);
diff --git a/src/assets/app.scss b/src/assets/app.scss
index f4707df95..bc2949327 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -180,6 +180,11 @@ h2 {
border-color: $dark-border-color;
}
+ .form-control:disabled, .form-control[readonly] {
+ background-color: #232f3b;
+ opacity: 1;
+ }
+
.table-hover > tbody > tr:hover {
--bs-table-accent-bg: #070a10;
color: $dark-font-color;
diff --git a/src/components/CopyableInput.vue b/src/components/CopyableInput.vue
new file mode 100644
index 000000000..d777f4a0e
--- /dev/null
+++ b/src/components/CopyableInput.vue
@@ -0,0 +1,122 @@
+
+