mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 14:54:05 +00:00
add telegram notification
This commit is contained in:
parent
04ec91d7a9
commit
3bdf174e90
11 changed files with 418 additions and 85 deletions
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -14,6 +14,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz",
|
||||
"integrity": "sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA=="
|
||||
},
|
||||
"@babel/standalone": {
|
||||
"version": "7.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.14.7.tgz",
|
||||
"integrity": "sha512-7RlfMPR4604SbYpj5zvs2ZK587hVhixgU9Pd9Vs8lB8KYtT3U0apXSf0vZXhy8XRh549eUmJOHXhWKTO3ObzOQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz",
|
||||
|
@ -48,6 +54,19 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz",
|
||||
"integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA=="
|
||||
},
|
||||
"@vitejs/plugin-legacy": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.3.tgz",
|
||||
"integrity": "sha512-lxZUJaMWYMQuqvZM1wPzDP6KABQgA/drVL5fnaygEPcz9adc2OHhfFNN/SvvHQ1V0rP8gybIc7uA+iI1gAdkVQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/standalone": "^7.14.7",
|
||||
"core-js": "^3.15.1",
|
||||
"magic-string": "^0.25.7",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"systemjs": "^6.10.1"
|
||||
}
|
||||
},
|
||||
"@vitejs/plugin-vue": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.3.tgz",
|
||||
|
@ -618,6 +637,12 @@
|
|||
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
|
||||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.15.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz",
|
||||
"integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
|
@ -2682,6 +2707,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
||||
"dev": true
|
||||
},
|
||||
"regex-not": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
||||
|
@ -3203,6 +3234,12 @@
|
|||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"systemjs": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.10.2.tgz",
|
||||
"integrity": "sha512-PwaC0Z6Y1E6gFekY2u38EC5+5w2M65jYVrD1aAcOptpHVhCwPIwPFJvYJyryQKUyeuQ5bKKI3PBHWNjdE9aizg==",
|
||||
"dev": true
|
||||
},
|
||||
"tarn": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz",
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"dev-server": "node server/server.js",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview --host"
|
||||
"start-server": "node server/server.js",
|
||||
"update": "",
|
||||
"build": "npm install && vite build",
|
||||
"vite-preview-dist": "vite preview --host"
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.2",
|
||||
|
@ -25,8 +26,10 @@
|
|||
"vue-toastification": "^2.0.0-rc.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-legacy": "^1.4.3",
|
||||
"@vitejs/plugin-vue": "^1.2.3",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"core-js": "^3.15.2",
|
||||
"sass": "^1.35.1",
|
||||
"vite": "^2.3.7"
|
||||
}
|
||||
|
|
|
@ -141,71 +141,52 @@ class Monitor extends BeanModel {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Uptime with calculation
|
||||
* Calculation based on:
|
||||
* https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime
|
||||
* @param duration : int Hours
|
||||
*/
|
||||
static async sendUptime(duration, io, monitorID, userID) {
|
||||
let sec = duration * 3600;
|
||||
|
||||
let downtimeList = await R.getAll(`
|
||||
SELECT duration, time
|
||||
SELECT duration, time, status
|
||||
FROM heartbeat
|
||||
WHERE time > DATE('now', ? || ' hours')
|
||||
AND status = 0
|
||||
AND monitor_id = ? `, [
|
||||
-duration,
|
||||
monitorID
|
||||
]);
|
||||
|
||||
let downtime = 0;
|
||||
let uptime = 0;
|
||||
let total = 0;
|
||||
let uptime;
|
||||
|
||||
if (downtimeList.length === 0) {
|
||||
for (let row of downtimeList) {
|
||||
let value = parseInt(row.duration)
|
||||
let time = row.time
|
||||
for (let row of downtimeList) {
|
||||
let value = parseInt(row.duration)
|
||||
let time = row.time
|
||||
|
||||
// Handle if heartbeat duration longer than the target duration
|
||||
// e.g. Heartbeat duration = 28hrs, but target duration = 24hrs
|
||||
if (value <= sec) {
|
||||
downtime += value;
|
||||
} else {
|
||||
let trim = dayjs.utc().diff(dayjs(time), 'second');
|
||||
// Handle if heartbeat duration longer than the target duration
|
||||
// e.g. Heartbeat duration = 28hrs, but target duration = 24hrs
|
||||
if (value > sec) {
|
||||
let trim = dayjs.utc().diff(dayjs(time), 'second');
|
||||
value = sec - trim;
|
||||
|
||||
value = sec - trim;
|
||||
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
downtime += value;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uptime = (sec - downtime) / sec;
|
||||
|
||||
if (uptime < 0) {
|
||||
uptime = 0;
|
||||
total += value;
|
||||
if (row.status === 0) {
|
||||
downtime += value;
|
||||
}
|
||||
} else {
|
||||
// This case for someone who are not running UptimeKuma 24x7.
|
||||
// If there is no heartbeat in this time range, use last heartbeat as reference
|
||||
// If is down, uptime = 0
|
||||
// If is up, uptime = 1
|
||||
}
|
||||
|
||||
let lastHeartbeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
||||
monitorID
|
||||
]);
|
||||
uptime = (total - downtime) / total;
|
||||
|
||||
if (lastHeartbeat) {
|
||||
if (lastHeartbeat.status === 1) {
|
||||
uptime = 1;
|
||||
} else {
|
||||
uptime = 0;
|
||||
}
|
||||
} else {
|
||||
// No heartbeat is found, assume 100%
|
||||
uptime = 1;
|
||||
}
|
||||
if (uptime < 0) {
|
||||
uptime = 0;
|
||||
}
|
||||
|
||||
io.to(userID).emit("uptime", monitorID, duration, uptime);
|
||||
|
|
58
server/notification.js
Normal file
58
server/notification.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
const axios = require("axios");
|
||||
const {R} = require("redbean-node");
|
||||
|
||||
class Notification {
|
||||
static async send(notification, msg) {
|
||||
if (notification.type === "telegram") {
|
||||
let res = await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
|
||||
params: {
|
||||
chat_id: notification.telegramChatID,
|
||||
text: msg,
|
||||
}
|
||||
})
|
||||
return true;
|
||||
} else {
|
||||
throw new Error("Notification type is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
static async save(notification, notificationID, userID) {
|
||||
let bean
|
||||
|
||||
if (notificationID) {
|
||||
bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
|
||||
notificationID,
|
||||
userID,
|
||||
])
|
||||
|
||||
if (! bean) {
|
||||
throw new Error("notification not found")
|
||||
}
|
||||
|
||||
} else {
|
||||
bean = R.dispense("notification")
|
||||
}
|
||||
|
||||
bean.name = notification.name;
|
||||
bean.user_id = userID;
|
||||
bean.config = JSON.stringify(notification)
|
||||
await R.store(bean)
|
||||
}
|
||||
|
||||
static async delete(notificationID, userID) {
|
||||
let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
|
||||
notificationID,
|
||||
userID,
|
||||
])
|
||||
|
||||
if (! bean) {
|
||||
throw new Error("notification not found")
|
||||
}
|
||||
|
||||
await R.trash(bean)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Notification,
|
||||
}
|
|
@ -10,6 +10,7 @@ const passwordHash = require('password-hash');
|
|||
const jwt = require('jsonwebtoken');
|
||||
const Monitor = require("./model/monitor");
|
||||
const {getSettings} = require("./util-server");
|
||||
const {Notification} = require("./notification")
|
||||
|
||||
let totalClient = 0;
|
||||
let jwtSecret = null;
|
||||
|
@ -26,6 +27,10 @@ let monitorList = {};
|
|||
|
||||
app.use('/', express.static("dist"));
|
||||
|
||||
app.get('*', function(request, response, next) {
|
||||
response.sendFile(process.cwd() + '/dist/index.html');
|
||||
});
|
||||
|
||||
io.on('connection', async (socket) => {
|
||||
console.log('a user connected');
|
||||
totalClient++;
|
||||
|
@ -318,6 +323,65 @@ let monitorList = {};
|
|||
}
|
||||
});
|
||||
|
||||
// Add or Edit
|
||||
socket.on("addNotification", async (notification, notificationID, callback) => {
|
||||
try {
|
||||
checkLogin(socket)
|
||||
|
||||
await Notification.save(notification, notificationID, socket.userID)
|
||||
await sendNotificationList(socket)
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Saved",
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("deleteNotification", async (notificationID, callback) => {
|
||||
try {
|
||||
checkLogin(socket)
|
||||
|
||||
await Notification.delete(notificationID, socket.userID)
|
||||
await sendNotificationList(socket)
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Deleted",
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("testNotification", async (notification, callback) => {
|
||||
try {
|
||||
checkLogin(socket)
|
||||
|
||||
await Notification.send(notification, notification.name + " Testing")
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Sent Successfully"
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(3001, () => {
|
||||
|
@ -344,6 +408,20 @@ async function sendMonitorList(socket) {
|
|||
return list;
|
||||
}
|
||||
|
||||
async function sendNotificationList(socket) {
|
||||
let result = [];
|
||||
let list = await R.find("notification", " user_id = ? ", [
|
||||
socket.userID
|
||||
]);
|
||||
|
||||
for (let bean of list) {
|
||||
result.push(bean.export())
|
||||
}
|
||||
|
||||
io.to(socket.userID).emit("notificationList", result)
|
||||
return list;
|
||||
}
|
||||
|
||||
async function afterLogin(socket, user) {
|
||||
socket.userID = user.id;
|
||||
socket.join(user.id)
|
||||
|
@ -355,6 +433,8 @@ async function afterLogin(socket, user) {
|
|||
await sendImportantHeartbeatList(socket, monitorID);
|
||||
await Monitor.sendStats(io, monitorID, user.id)
|
||||
}
|
||||
|
||||
await sendNotificationList(socket)
|
||||
}
|
||||
|
||||
async function getMonitorJSONList(userID) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const tcpp = require('tcp-ping');
|
||||
const Ping = require("./ping-lite");
|
||||
const {R} = require("redbean-node");
|
||||
|
||||
exports.tcping = function (hostname, port) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -37,3 +38,25 @@ exports.ping = function (hostname) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.setting = async function (key) {
|
||||
return await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
|
||||
key
|
||||
])
|
||||
}
|
||||
|
||||
exports.getSettings = async function (type) {
|
||||
let list = await R.getAll("SELECT * FROM setting WHERE `type` = ? ", [
|
||||
type
|
||||
])
|
||||
|
||||
let result = {};
|
||||
|
||||
for (let row of list) {
|
||||
result[row.key] = row.value;
|
||||
}
|
||||
|
||||
console.log(result)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ export function sleep(ms) {
|
|||
}
|
||||
|
||||
export function ucfirst(str) {
|
||||
if (! str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const firstLetter = str.substr(0, 1);
|
||||
return firstLetter.toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
|
|
@ -1,42 +1,81 @@
|
|||
<template>
|
||||
<div class="modal fade" tabindex="-1" ref="modal" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Setup Notification</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form @submit.prevent="submit">
|
||||
<form @submit.prevent="submit">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Notification Type</label>
|
||||
<select class="form-select" id="type" v-model="type">
|
||||
<option value="email">Email</option>
|
||||
<option value="webhook">Webhook</option>
|
||||
<option value="telegram">Telegram</option>
|
||||
<option value="discord">Discord</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal fade" tabindex="-1" ref="modal" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Setup Notification</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Friendly Name</label>
|
||||
<input type="text" class="form-control" id="name" required v-model="name">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" @click="yes" data-bs-dismiss="modal">Save</button>
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Notification Type</label>
|
||||
<select class="form-select" id="type" v-model="notification.type">
|
||||
<option value="telegram">Telegram</option>
|
||||
<option value="webhook">Webhook</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="discord">Discord</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Friendly Name</label>
|
||||
<input type="text" class="form-control" id="name" required v-model="notification.name">
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="notification.type === 'telegram'">
|
||||
<label for="telegram-bot-token" class="form-label">Bot Token</label>
|
||||
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
|
||||
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="notification.type === 'telegram'">
|
||||
<label for="telegram-chat-id" class="form-label">Chat ID</label>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
|
||||
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
|
||||
</div>
|
||||
|
||||
<div class="form-text">
|
||||
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
|
||||
|
||||
<p style="margin-top: 8px;">
|
||||
|
||||
<template v-if="notification.telegramBotToken">
|
||||
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ telegramGetUpdatesURL }}
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" @click="deleteNotification" :disabled="processing" v-if="id">Delete</button>
|
||||
<button type="button" class="btn btn-warning" @click="test" :disabled="processing">Test</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="processing">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Modal } from 'bootstrap'
|
||||
import { ucfirst } from "../../server/util";
|
||||
import axios from "axios";
|
||||
import { useToast } from 'vue-toastification'
|
||||
const toast = useToast()
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -45,33 +84,123 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
model: null,
|
||||
type: null,
|
||||
name: "",
|
||||
processing: false,
|
||||
id: null,
|
||||
notification: {
|
||||
name: "",
|
||||
type: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.modal = new Modal(this.$refs.modal)
|
||||
|
||||
// TODO: for edit
|
||||
this.$root.getSocket().emit("getSettings", "notification", (data) => {
|
||||
// this.notification = data
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
|
||||
show(notificationID) {
|
||||
if (notificationID) {
|
||||
this.id = notificationID;
|
||||
|
||||
for (let n of this.$root.notificationList) {
|
||||
if (n.id === notificationID) {
|
||||
this.notification = JSON.parse(n.config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.id = null;
|
||||
this.notification = {
|
||||
name: "",
|
||||
type: null,
|
||||
}
|
||||
|
||||
// Default set to Telegram
|
||||
this.notification.type = "telegram"
|
||||
}
|
||||
|
||||
this.modal.show()
|
||||
},
|
||||
submit() {
|
||||
|
||||
}
|
||||
submit() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
|
||||
this.$root.toastRes(res)
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.modal.hide()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
test() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("testNotification", this.notification, (res) => {
|
||||
this.$root.toastRes(res)
|
||||
this.processing = false;
|
||||
})
|
||||
},
|
||||
|
||||
deleteNotification() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("deleteNotification", this.id, (res) => {
|
||||
this.$root.toastRes(res)
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.modal.hide()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async autoGetTelegramChatID() {
|
||||
try {
|
||||
let res = await axios.get(this.telegramGetUpdatesURL)
|
||||
|
||||
if (res.data.result.length >= 1) {
|
||||
let update = res.data.result[res.data.result.length - 1]
|
||||
this.notification.telegramChatID = update.message.chat.id;
|
||||
} else {
|
||||
throw new Error("Chat ID is not found, please send a message to this bot first")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
toast.error(error.message)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
computed: {
|
||||
telegramGetUpdatesURL() {
|
||||
let token = "<YOUR BOT TOKEN HERE>"
|
||||
|
||||
if (this.notification.telegramBotToken) {
|
||||
token = this.notification.telegramBotToken;
|
||||
}
|
||||
|
||||
return `https://api.telegram.org/bot${token}/getUpdates`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
type(to, from) {
|
||||
"notification.type"(to, from) {
|
||||
let oldName;
|
||||
|
||||
if (from) {
|
||||
oldName = `My ${ucfirst(from)} Notification`;
|
||||
oldName = `My ${ucfirst(from)} Alert (1)`;
|
||||
} else {
|
||||
oldName = "";
|
||||
}
|
||||
|
||||
if (! this.name || this.name === oldName) {
|
||||
this.name = `My ${ucfirst(to)} Alert (1)`
|
||||
if (! this.notification.name || this.notification.name === oldName) {
|
||||
this.notification.name = `My ${ucfirst(to)} Alert (1)`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ export default {
|
|||
heartbeatList: { },
|
||||
importantHeartbeatList: { },
|
||||
avgPingList: { },
|
||||
uptimeList: { }
|
||||
uptimeList: { },
|
||||
notificationList: [],
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -36,6 +37,10 @@ export default {
|
|||
this.monitorList = data;
|
||||
});
|
||||
|
||||
socket.on('notificationList', (data) => {
|
||||
this.notificationList = data;
|
||||
});
|
||||
|
||||
socket.on('heartbeat', (data) => {
|
||||
if (! (data.monitorID in this.heartbeatList)) {
|
||||
this.heartbeatList[data.monitorID] = [];
|
||||
|
|
|
@ -55,7 +55,13 @@
|
|||
|
||||
<div class="col-md-6">
|
||||
<h2>Notifications</h2>
|
||||
<p>Not available, please setup.</p>
|
||||
<p v-if="$root.notificationList.length === 0">Not available, please setup.</p>
|
||||
|
||||
<div class="form-check form-switch mb-3" v-for="notification in $root.notificationList">
|
||||
<input class="form-check-input" type="checkbox" :id=" 'notification' + notification.id">
|
||||
<label class="form-check-label" :for=" 'notification' + notification.id">{{ notification.name }} <a href="#" @click="$refs.notificationDialog.show(notification.id)">Edit</a></label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" @click="$refs.notificationDialog.show()" type="button">Setup Notification</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import legacy from '@vitejs/plugin-legacy'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()]
|
||||
plugins: [
|
||||
vue(),
|
||||
legacy({
|
||||
targets: ['ie >= 11'],
|
||||
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
|
||||
})
|
||||
]
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue