From ce852dfa022057587b5182cf8b0cfb93695a9dd8 Mon Sep 17 00:00:00 2001 From: Lakr Aream Date: Sun, 24 Oct 2021 00:20:24 +0800 Subject: [PATCH 01/23] Support for Bark (APN) notifications Update bark.js --- server/notification-providers/bark.js | 89 +++++++++++++++++++++++++++ server/notification.js | 2 + src/components/notifications/Bark.vue | 15 +++++ src/components/notifications/index.js | 4 +- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 server/notification-providers/bark.js create mode 100644 src/components/notifications/Bark.vue diff --git a/server/notification-providers/bark.js b/server/notification-providers/bark.js new file mode 100644 index 00000000..4ebe978a --- /dev/null +++ b/server/notification-providers/bark.js @@ -0,0 +1,89 @@ +// +// bark.js +// UptimeKuma +// +// Created by Lakr Aream on 2021/10/24. +// Copyright © 2021 Lakr Aream. All rights reserved. +// + +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const { default: axios } = require("axios"); + +// bark is an APN bridge that sends notifications to Apple devices. + +const barkNotificationGroup = "UptimeKuma"; +const barkNotificationAvatar = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; +const barkNotificationSound = "telegraph"; +const successMessage = "Successes!"; + +class Bark extends NotificationProvider { + name = "Bark"; + + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + var barkEndpoint = notification.barkEndpoint; + + // check if the endpoint has a "/" suffix, if so, delete it first + if (barkEndpoint.endsWith("/")) { + barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1); + } + + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) { + let title = "UptimeKuma Monitor Up"; + return await this.postNotification(title, msg, barkEndpoint); + } + + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) { + let title = "UptimeKuma Monitor Down"; + return await this.postNotification(title, msg, barkEndpoint); + } + + if (msg != null) { + let title = "UptimeKuma Message"; + return await this.postNotification(title, msg, barkEndpoint); + } + + } catch (error) { + throw error; + } + } + + // add additional parameter for better on device styles (iOS 15 optimized) + appendAdditionalParameters(postUrl) { + // grouping all our notifications + postUrl += "?group=" + barkNotificationGroup; + // set icon to uptime kuma icon, 11kb should be fine + postUrl += "&icon=" + barkNotificationAvatar; + // picked a sound, this should follow system's mute status when arrival + postUrl += "&sound=" + barkNotificationSound; + return postUrl; + } + + // thrown if failed to check result, result code should be in range 2xx + checkResult(result) { + if (result.status == null) { + throw new Error("Bark notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("Bark notification failed with status code " + result.status); + } + } + + async postNotification(title, subtitle, endpoint) { + // url encode title and subtitle + title = encodeURIComponent(title); + subtitle = encodeURIComponent(subtitle); + let postUrl = endpoint + "/" + title + "/" + subtitle; + postUrl = this.appendAdditionalParameters(postUrl); + let result = await axios.get(postUrl); + this.checkResult(result); + if (result.statusText != null) { + return "Bark notification succeed: " + result.statusText; + } + // because returned in range 200 ..< 300 + return successMessage; + } +} + +module.exports = Bark; diff --git a/server/notification.js b/server/notification.js index 658216f9..57453c4a 100644 --- a/server/notification.js +++ b/server/notification.js @@ -21,6 +21,7 @@ const Webhook = require("./notification-providers/webhook"); const Feishu = require("./notification-providers/feishu"); const AliyunSms = require("./notification-providers/aliyun-sms"); const DingDing = require("./notification-providers/dingding"); +const Bark = require("./notification-providers/bark"); class Notification { @@ -54,6 +55,7 @@ class Notification { new SMTP(), new Telegram(), new Webhook(), + new Bark(), ]; for (let item of list) { diff --git a/src/components/notifications/Bark.vue b/src/components/notifications/Bark.vue new file mode 100644 index 00000000..014450de --- /dev/null +++ b/src/components/notifications/Bark.vue @@ -0,0 +1,15 @@ + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 96ee2824..2d8c9c09 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -20,6 +20,7 @@ import Mattermost from "./Mattermost.vue"; import Matrix from "./Matrix.vue"; import AliyunSMS from "./AliyunSms.vue"; import DingDing from "./DingDing.vue"; +import Bark from "./Bark.vue"; /** * Manage all notification form. @@ -48,7 +49,8 @@ const NotificationFormList = { "line": Line, "mattermost": Mattermost, "matrix": Matrix, - "DingDing": DingDing + "DingDing": DingDing, + "Bark": Bark } export default NotificationFormList From b83c59e308c1f97e5c3d305d5f7e96d0f7b6f2c4 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Mon, 18 Oct 2021 19:00:39 +0800 Subject: [PATCH 02/23] WIP: Add options for chart period Fix: Fix callback, add toast on error Fix: Improve styling Fix: Restore default chart behavior Fix: Replace 1h with 3h draft only --- server/client.js | 37 ++++++++++++++------ server/server.js | 19 ++++++++++ src/components/PingChart.vue | 67 ++++++++++++++++++++++++++++++++---- src/mixins/socket.js | 4 +++ 4 files changed, 111 insertions(+), 16 deletions(-) diff --git a/server/client.js b/server/client.js index c7b3bc16..951ffd8a 100644 --- a/server/client.js +++ b/server/client.js @@ -31,19 +31,36 @@ async function sendNotificationList(socket) { * @param toUser True = send to all browsers with the same user id, False = send to the current browser only * @param overwrite Overwrite client-side's heartbeat list */ -async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { +async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false, period = null) { const timeLogger = new TimeLogger(); - let list = await R.getAll(` - SELECT * FROM heartbeat - WHERE monitor_id = ? - ORDER BY time DESC - LIMIT 100 - `, [ - monitorID, - ]); + let result; - let result = list.reverse(); + if (period) { + let list = await R.getAll(` + SELECT * FROM heartbeat + WHERE monitor_id = ? AND + time > DATETIME('now', '-' || ? || ' hours') + ORDER BY time ASC + `, [ + monitorID, + period, + ]); + + result = list; + + } else { + let list = await R.getAll(` + SELECT * FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT 100 + `, [ + monitorID, + ]); + + result = list.reverse(); + } if (toUser) { io.to(socket.userID).emit("heartbeatList", monitorID, result, overwrite); diff --git a/server/server.js b/server/server.js index 3084cad2..4209b45c 100644 --- a/server/server.js +++ b/server/server.js @@ -644,6 +644,25 @@ exports.entryPage = "dashboard"; } }); + socket.on("getMonitorBeats", async (monitorID, period, callback) => { + try { + checkLogin(socket); + + console.log(`Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); + + await sendHeartbeatList(socket, monitorID, true, true, period); + + callback({ + ok: true + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + // Start or Resume the monitor socket.on("resumeMonitor", async (monitorID, callback) => { try { diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 0baa9881..4818bba2 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -1,5 +1,18 @@ + + diff --git a/src/mixins/socket.js b/src/mixins/socket.js index d7ac8bcb..67979449 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -328,6 +328,10 @@ export default { clearStatistics(callback) { socket.emit("clearStatistics", callback); }, + + getMonitorBeats(monitorID, period, callback) { + socket.emit("getMonitorBeats", monitorID, period, callback); + } }, computed: { From 2f7b60f5e50b6c2c370739e376580d74b172f39e Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 22 Oct 2021 18:38:41 +0800 Subject: [PATCH 03/23] Feat: Use separate storage for custom chart period Fix: Fix import error --- server/client.js | 37 ++++++--------------- server/server.js | 17 ++++++++-- src/components/PingChart.vue | 64 +++++++++++++++++++++++------------- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/server/client.js b/server/client.js index 951ffd8a..c7b3bc16 100644 --- a/server/client.js +++ b/server/client.js @@ -31,36 +31,19 @@ async function sendNotificationList(socket) { * @param toUser True = send to all browsers with the same user id, False = send to the current browser only * @param overwrite Overwrite client-side's heartbeat list */ -async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false, period = null) { +async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { const timeLogger = new TimeLogger(); - let result; - - if (period) { - let list = await R.getAll(` - SELECT * FROM heartbeat - WHERE monitor_id = ? AND - time > DATETIME('now', '-' || ? || ' hours') - ORDER BY time ASC - `, [ - monitorID, - period, - ]); - - result = list; + let list = await R.getAll(` + SELECT * FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT 100 + `, [ + monitorID, + ]); - } else { - let list = await R.getAll(` - SELECT * FROM heartbeat - WHERE monitor_id = ? - ORDER BY time DESC - LIMIT 100 - `, [ - monitorID, - ]); - - result = list.reverse(); - } + let result = list.reverse(); if (toUser) { io.to(socket.userID).emit("heartbeatList", monitorID, result, overwrite); diff --git a/server/server.js b/server/server.js index 4209b45c..49e867da 100644 --- a/server/server.js +++ b/server/server.js @@ -650,10 +650,23 @@ exports.entryPage = "dashboard"; console.log(`Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); - await sendHeartbeatList(socket, monitorID, true, true, period); + if (period == null) { + throw new Error("Invalid period."); + } + + let list = await R.getAll(` + SELECT * FROM heartbeat + WHERE monitor_id = ? AND + time > DATETIME('now', '-' || ? || ' hours') + ORDER BY time ASC + `, [ + monitorID, + period, + ]); callback({ - ok: true + ok: true, + data: list, }); } catch (e) { callback({ diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 4818bba2..0c76721c 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -15,7 +15,7 @@ - @@ -217,7 +237,7 @@ export default { } .period-options { - padding: 0.3em 1.5em; + padding: 0.3em 2.2em; margin-bottom: -1.5em; float: right; position: relative; From 445674aacb0c58ca5eb96dc2c7c033a4d70c8670 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 22 Oct 2021 18:44:11 +0800 Subject: [PATCH 04/23] Chore: Improve code formatting & comments --- src/components/PingChart.vue | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 0c76721c..cd5e239c 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -1,7 +1,12 @@