diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..cc0f11863 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,73 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es2017: true, + node: true, + }, + extends: [ + "eslint:recommended", + "plugin:vue/vue3-recommended", + ], + parserOptions: { + ecmaVersion: 2018, + sourceType: "module", + }, + rules: { + // override/add rules settings here, such as: + // 'vue/no-unused-vars': 'error' + indent: ["error", 4], + quotes: ["warn", "double"], + //semi: ['off', 'never'], + "vue/html-indent": ["warn", 4], // default: 2 + "vue/max-attributes-per-line": "off", + "no-multi-spaces": ["error", { + ignoreEOLComments: true, + }], + "curly": "error", + "object-curly-spacing": ["error", "always"], + "object-curly-newline": ["error", { + "ObjectExpression": { + "minProperties": 1, + }, + "ObjectPattern": { + "multiline": true, + "minProperties": 2, + }, + "ImportDeclaration": { + "multiline": true, + }, + "ExportDeclaration": { + "multiline": true, + //'minProperties': 2, + }, + }], + "object-property-newline": "error", + "comma-spacing": "error", + "brace-style": "error", + "no-var": "error", + "key-spacing": "warn", + "keyword-spacing": "warn", + "space-infix-ops": "warn", + "arrow-spacing": "warn", + "no-trailing-spaces": "warn", + "space-before-blocks": "warn", + //'no-console': 'warn', + "no-extra-boolean-cast": "off", + "no-multiple-empty-lines": ["warn", { + "max": 1, + "maxBOF": 0, + }], + "lines-between-class-members": ["warn", "always", { + exceptAfterSingleLine: true, + }], + "no-unneeded-ternary": "error", + "no-else-return": ["error", { + "allowElseIf": false, + }], + "array-bracket-newline": ["error", "consistent"], + "eol-last": ["error", "always"], + //'prefer-template': 'error', + "comma-dangle": ["warn", "always-multiline"], + }, +} diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 000000000..4d3d9d1f8 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,3 @@ +{ + "extends": "stylelint-config-recommended", +} diff --git a/package.json b/package.json index b54c73ff9..7cfd9e408 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,16 @@ "vue-toastification": "^2.0.0-rc.1" }, "devDependencies": { - "@vitejs/plugin-legacy": "^1.4.4", - "@vitejs/plugin-vue": "^1.2.5", + "@vitejs/plugin-legacy": "^1.5.0", + "@vitejs/plugin-vue": "^1.3.0", "@vue/compiler-sfc": "^3.1.5", "core-js": "^3.15.2", - "sass": "^1.35.2", - "vite": "^2.4.2" + "eslint": "^7.31.0", + "eslint-plugin-vue": "^7.14.0", + "sass": "^1.36.0", + "stylelint": "^13.13.1", + "stylelint-config-recommended": "^5.0.0", + "stylelint-config-standard": "^22.0.0", + "vite": "^2.4.4" } } diff --git a/server/auth.js b/server/auth.js index b4d13d68a..54182bdef 100644 --- a/server/auth.js +++ b/server/auth.js @@ -1,6 +1,6 @@ -const basicAuth = require('express-basic-auth') -const passwordHash = require('./password-hash'); -const {R} = require("redbean-node"); +const basicAuth = require("express-basic-auth") +const passwordHash = require("./password-hash"); +const { R } = require("redbean-node"); /** * @@ -10,7 +10,7 @@ const {R} = require("redbean-node"); */ exports.login = async function (username, password) { let user = await R.findOne("user", " username = ? AND active = 1 ", [ - username + username, ]) if (user && passwordHash.verify(password, user.password)) { @@ -18,13 +18,13 @@ exports.login = async function (username, password) { if (passwordHash.needRehash(user.password)) { await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ passwordHash.generate(password), - user.id + user.id, ]); } return user; - } else { - return null; } + + return null; } function myAuthorizer(username, password, callback) { @@ -36,5 +36,5 @@ function myAuthorizer(username, password, callback) { exports.basicAuth = basicAuth({ authorizer: myAuthorizer, authorizeAsync: true, - challenge: true + challenge: true, }); diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index 01fb71ff9..546794140 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -1,15 +1,15 @@ const dayjs = require("dayjs"); -const utc = require('dayjs/plugin/utc') -var timezone = require('dayjs/plugin/timezone') +const utc = require("dayjs/plugin/utc") +let timezone = require("dayjs/plugin/timezone") dayjs.extend(utc) dayjs.extend(timezone) -const {BeanModel} = require("redbean-node/dist/bean-model"); - +const { BeanModel } = require("redbean-node/dist/bean-model"); /** * status: * 0 = DOWN * 1 = UP + * 2 = PENDING */ class Heartbeat extends BeanModel { diff --git a/server/model/monitor.js b/server/model/monitor.js index 27f3f9bab..b725f43c6 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -22,6 +22,7 @@ const customAgent = new https.Agent({ * status: * 0 = DOWN * 1 = UP + * 2 = PENDING */ class Monitor extends BeanModel { async toJSON() { @@ -197,7 +198,7 @@ class Monitor extends BeanModel { if (bean.status === UP) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) } else if (bean.status === PENDING) { - console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Type: ${this.type}`) + console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Type: ${this.type}`) } else { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } diff --git a/server/notification.js b/server/notification.js index 9da8a0dc8..f25ca80a7 100644 --- a/server/notification.js +++ b/server/notification.js @@ -1,6 +1,6 @@ const axios = require("axios"); -const {R} = require("redbean-node"); -const FormData = require('form-data'); +const { R } = require("redbean-node"); +const FormData = require("form-data"); const nodemailer = require("nodemailer"); const child_process = require("child_process"); @@ -24,7 +24,7 @@ class Notification { params: { chat_id: notification.telegramChatID, text: msg, - } + }, }) return okMsg; @@ -41,7 +41,7 @@ class Notification { await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, { "message": msg, "priority": notification.gotifyPriority || 8, - "title": "Uptime-Kuma" + "title": "Uptime-Kuma", }) return okMsg; @@ -62,10 +62,10 @@ class Notification { if (notification.webhookContentType === "form-data") { finalData = new FormData(); - finalData.append('data', JSON.stringify(data)); + finalData.append("data", JSON.stringify(data)); config = { - headers: finalData.getHeaders() + headers: finalData.getHeaders(), } } else { @@ -84,63 +84,68 @@ class Notification { } else if (notification.type === "discord") { try { - // If heartbeatJSON is null, assume we're testing. - if(heartbeatJSON == null) { + // If heartbeatJSON is null, assume we're testing. + if (heartbeatJSON == null) { + let data = { + username: "Uptime-Kuma", + content: msg, + } + await axios.post(notification.discordWebhookUrl, data) + return okMsg; + } + // If heartbeatJSON is not null, we go into the normal alerting loop. + if (heartbeatJSON["status"] == 0) { + var alertColor = "16711680"; + } else if (heartbeatJSON["status"] == 1) { + var alertColor = "65280"; + } let data = { - username: 'Uptime-Kuma', - content: msg + username: "Uptime-Kuma", + embeds: [{ + title: "Uptime-Kuma Alert", + color: alertColor, + fields: [ + { + name: "Time (UTC)", + value: heartbeatJSON["time"], + }, + { + name: "Message", + value: msg, + }, + ], + }], } await axios.post(notification.discordWebhookUrl, data) return okMsg; - } - // If heartbeatJSON is not null, we go into the normal alerting loop. - if(heartbeatJSON['status'] == 0) { - var alertColor = "16711680"; - } else if(heartbeatJSON['status'] == 1) { - var alertColor = "65280"; - } - let data = { - username: 'Uptime-Kuma', - embeds: [{ - title: "Uptime-Kuma Alert", - color: alertColor, - fields: [ - { - name: "Time (UTC)", - value: heartbeatJSON["time"] - }, - { - name: "Message", - value: msg - } - ] - }] - } - await axios.post(notification.discordWebhookUrl, data) - return okMsg; - } catch(error) { - throwGeneralAxiosError(error) + } catch (error) { + throwGeneralAxiosError(error) } } else if (notification.type === "signal") { - try { - let data = { - "message": msg, - "number": notification.signalNumber, - "recipients": notification.signalRecipients.replace(/\s/g, '').split(",") - }; - let config = {}; + try { + let data = { + "message": msg, + "number": notification.signalNumber, + "recipients": notification.signalRecipients.replace(/\s/g, "").split(","), + }; + let config = {}; - await axios.post(notification.signalURL, data, config) - return okMsg; - } catch (error) { - throwGeneralAxiosError(error) - } + await axios.post(notification.signalURL, data, config) + return okMsg; + } catch (error) { + throwGeneralAxiosError(error) + } } else if (notification.type === "slack") { try { if (heartbeatJSON == null) { - let data = {'text': "Uptime Kuma Slack testing successful.", 'channel': notification.slackchannel, 'username': notification.slackusername, 'icon_emoji': notification.slackiconemo} + let data = { + "text": "Uptime Kuma Slack testing successful.", + "channel": notification.slackchannel, + "username": notification.slackusername, + "icon_emoji": notification.slackiconemo, + } await axios.post(notification.slackwebhookURL, data) return okMsg; } @@ -148,44 +153,42 @@ class Notification { const time = heartbeatJSON["time"]; let data = { "text": "Uptime Kuma Alert", - "channel":notification.slackchannel, + "channel": notification.slackchannel, "username": notification.slackusername, "icon_emoji": notification.slackiconemo, "blocks": [{ - "type": "header", - "text": { - "type": "plain_text", - "text": "Uptime Kuma Alert" - } + "type": "header", + "text": { + "type": "plain_text", + "text": "Uptime Kuma Alert", + }, + }, + { + "type": "section", + "fields": [{ + "type": "mrkdwn", + "text": "*Message*\n" + msg, }, { - "type": "section", - "fields": [{ - "type": "mrkdwn", - "text": '*Message*\n'+msg + "type": "mrkdwn", + "text": "*Time (UTC)*\n" + time, + }], + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Visit Uptime Kuma", }, - { - "type": "mrkdwn", - "text": "*Time (UTC)*\n"+time - } - ] - }, - { - "type": "actions", - "elements": [ - { - "type": "button", - "text": { - "type": "plain_text", - "text": "Visit Uptime Kuma", - }, - "value": "Uptime-Kuma", - "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma" - } - ] - } - ] - } + "value": "Uptime-Kuma", + "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma", + }, + ], + }], + } await axios.post(notification.slackwebhookURL, data) return okMsg; } catch (error) { @@ -193,27 +196,35 @@ class Notification { } } else if (notification.type === "pushover") { - var pushoverlink = 'https://api.pushover.net/1/messages.json' + let pushoverlink = "https://api.pushover.net/1/messages.json" try { if (heartbeatJSON == null) { - let data = {'message': "Uptime Kuma Pushover testing successful.", - 'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds, - 'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1} + let data = { + "message": "Uptime Kuma Pushover testing successful.", + "user": notification.pushoveruserkey, + "token": notification.pushoverapptoken, + "sound": notification.pushoversounds, + "priority": notification.pushoverpriority, + "title": notification.pushovertitle, + "retry": "30", + "expire": "3600", + "html": 1, + } await axios.post(pushoverlink, data) return okMsg; } let data = { - "message": "Uptime Kuma Alert\n\nMessage:"+msg+ '\nTime (UTC):' +heartbeatJSON["time"], - "user":notification.pushoveruserkey, + "message": "Uptime Kuma Alert\n\nMessage:" + msg + "\nTime (UTC):" + heartbeatJSON["time"], + "user": notification.pushoveruserkey, "token": notification.pushoverapptoken, "sound": notification.pushoversounds, "priority": notification.pushoverpriority, "title": notification.pushovertitle, "retry": "30", "expire": "3600", - "html": 1 - } + "html": 1, + } await axios.post(pushoverlink, data) return okMsg; } catch (error) { @@ -291,24 +302,23 @@ class Notification { static async apprise(notification, msg) { let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL]) - - let output = (s.stdout) ? s.stdout.toString() : 'ERROR: maybe apprise not found'; + let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; if (output) { if (! output.includes("ERROR")) { return "Sent Successfully"; - } else { - throw new Error(output) } + + throw new Error(output) } else { return "" } } static checkApprise() { - let commandExistsSync = require('command-exists').sync; - let exists = commandExistsSync('apprise'); + let commandExistsSync = require("command-exists").sync; + let exists = commandExistsSync("apprise"); return exists; } diff --git a/server/password-hash.js b/server/password-hash.js index 39bc0c20c..52e26b959 100644 --- a/server/password-hash.js +++ b/server/password-hash.js @@ -1,5 +1,5 @@ -const passwordHashOld = require('password-hash'); -const bcrypt = require('bcrypt'); +const passwordHashOld = require("password-hash"); +const bcrypt = require("bcrypt"); const saltRounds = 10; exports.generate = function (password) { @@ -9,9 +9,9 @@ exports.generate = function (password) { exports.verify = function (password, hash) { if (isSHA1(hash)) { return passwordHashOld.verify(password, hash) - } else { - return bcrypt.compareSync(password, hash); } + + return bcrypt.compareSync(password, hash); } function isSHA1(hash) { diff --git a/server/ping-lite.js b/server/ping-lite.js index e290d8876..0b9a7401a 100644 --- a/server/ping-lite.js +++ b/server/ping-lite.js @@ -1,9 +1,9 @@ // https://github.com/ben-bradley/ping-lite/blob/master/ping-lite.js // Fixed on Windows -var spawn = require('child_process').spawn, - events = require('events'), - fs = require('fs'), +let spawn = require("child_process").spawn, + events = require("events"), + fs = require("fs"), WIN = /^win/.test(process.platform), LIN = /^linux/.test(process.platform), MAC = /^darwin/.test(process.platform); @@ -11,8 +11,9 @@ var spawn = require('child_process').spawn, module.exports = Ping; function Ping(host, options) { - if (!host) - throw new Error('You must specify a host to ping!'); + if (!host) { + throw new Error("You must specify a host to ping!"); + } this._host = host; this._options = options = (options || {}); @@ -20,26 +21,24 @@ function Ping(host, options) { events.EventEmitter.call(this); if (WIN) { - this._bin = 'c:/windows/system32/ping.exe'; - this._args = (options.args) ? options.args : [ '-n', '1', '-w', '5000', host ]; + this._bin = "c:/windows/system32/ping.exe"; + this._args = (options.args) ? options.args : [ "-n", "1", "-w", "5000", host ]; this._regmatch = /[><=]([0-9.]+?)ms/; - } - else if (LIN) { - this._bin = '/bin/ping'; - this._args = (options.args) ? options.args : [ '-n', '-w', '2', '-c', '1', host ]; + } else if (LIN) { + this._bin = "/bin/ping"; + this._args = (options.args) ? options.args : [ "-n", "-w", "2", "-c", "1", host ]; this._regmatch = /=([0-9.]+?) ms/; // need to verify this - } - else if (MAC) { - this._bin = '/sbin/ping'; - this._args = (options.args) ? options.args : [ '-n', '-t', '2', '-c', '1', host ]; + } else if (MAC) { + this._bin = "/sbin/ping"; + this._args = (options.args) ? options.args : [ "-n", "-t", "2", "-c", "1", host ]; this._regmatch = /=([0-9.]+?) ms/; - } - else { - throw new Error('Could not detect your ping binary.'); + } else { + throw new Error("Could not detect your ping binary."); } - if (!fs.existsSync(this._bin)) - throw new Error('Could not detect '+this._bin+' on your system'); + if (!fs.existsSync(this._bin)) { + throw new Error("Could not detect " + this._bin + " on your system"); + } this._i = 0; @@ -51,48 +50,57 @@ Ping.prototype.__proto__ = events.EventEmitter.prototype; // SEND A PING // =========== Ping.prototype.send = function(callback) { - var self = this; + let self = this; callback = callback || function(err, ms) { - if (err) return self.emit('error', err); - else return self.emit('result', ms); + if (err) { + return self.emit("error", err); + } + return self.emit("result", ms); }; - var _ended, _exited, _errored; + let _ended, _exited, _errored; this._ping = spawn(this._bin, this._args); // spawn the binary - this._ping.on('error', function(err) { // handle binary errors + this._ping.on("error", function(err) { // handle binary errors _errored = true; callback(err); }); - this._ping.stdout.on('data', function(data) { // log stdout - this._stdout = (this._stdout || '') + data; + this._ping.stdout.on("data", function(data) { // log stdout + this._stdout = (this._stdout || "") + data; }); - this._ping.stdout.on('end', function() { + this._ping.stdout.on("end", function() { _ended = true; - if (_exited && !_errored) onEnd.call(self._ping); + if (_exited && !_errored) { + onEnd.call(self._ping); + } }); - this._ping.stderr.on('data', function(data) { // log stderr - this._stderr = (this._stderr || '') + data; + this._ping.stderr.on("data", function(data) { // log stderr + this._stderr = (this._stderr || "") + data; }); - this._ping.on('exit', function(code) { // handle complete + this._ping.on("exit", function(code) { // handle complete _exited = true; - if (_ended && !_errored) onEnd.call(self._ping); + if (_ended && !_errored) { + onEnd.call(self._ping); + } }); function onEnd() { - var stdout = this.stdout._stdout, + let stdout = this.stdout._stdout, stderr = this.stderr._stderr, ms; - if (stderr) + if (stderr) { return callback(new Error(stderr)); - else if (!stdout) - return callback(new Error('No stdout detected')); + } + + if (!stdout) { + return callback(new Error("No stdout detected")); + } ms = stdout.match(self._regmatch); // parse out the ##ms response ms = (ms && ms[1]) ? Number(ms[1]) : ms; @@ -104,7 +112,7 @@ Ping.prototype.send = function(callback) { // CALL Ping#send(callback) ON A TIMER // =================================== Ping.prototype.start = function(callback) { - var self = this; + let self = this; this._i = setInterval(function() { self.send(callback); }, (self._options.interval || 5000)); diff --git a/server/server.js b/server/server.js index b3837c920..661d9807d 100644 --- a/server/server.js +++ b/server/server.js @@ -1,24 +1,24 @@ console.log("Welcome to Uptime Kuma ") console.log("Importing libraries") -const express = require('express'); -const http = require('http'); +const express = require("express"); +const http = require("http"); const { Server } = require("socket.io"); const dayjs = require("dayjs"); -const {R} = require("redbean-node"); -const jwt = require('jsonwebtoken'); +const { R } = require("redbean-node"); +const jwt = require("jsonwebtoken"); const Monitor = require("./model/monitor"); const fs = require("fs"); -const {getSettings} = require("./util-server"); -const {Notification} = require("./notification") -const gracefulShutdown = require('http-graceful-shutdown'); +const { getSettings } = require("./util-server"); +const { Notification } = require("./notification") +const gracefulShutdown = require("http-graceful-shutdown"); const Database = require("./database"); -const {sleep} = require("./util"); -const args = require('args-parser')(process.argv); -const prometheusAPIMetrics = require('prometheus-api-metrics'); +const { sleep } = require("./util"); +const args = require("args-parser")(process.argv); +const prometheusAPIMetrics = require("prometheus-api-metrics"); const { basicAuth } = require("./auth"); -const {login} = require("./auth"); +const { login } = require("./auth"); const passwordHash = require('./password-hash'); -const version = require('../package.json').version; +const version = require("../package.json").version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 @@ -64,12 +64,12 @@ let needSetup = false; // Normal Router here - app.use('/', express.static("dist")); + app.use("/", express.static("dist")); // Basic Auth Router here // For testing - basicAuthRouter.get('/test-auth', (req, res) => { + basicAuthRouter.get("/test-auth", (req, res) => { res.end("OK") }); @@ -78,12 +78,12 @@ let needSetup = false; basicAuthRouter.use(prometheusAPIMetrics()) // Universal Route Handler, must be at the end - app.get('*', function(request, response, next) { - response.sendFile(process.cwd() + '/dist/index.html'); + app.get("*", function(request, response, next) { + response.sendFile(process.cwd() + "/dist/index.html"); }); console.log("Adding socket handler") - io.on('connection', async (socket) => { + io.on("connection", async (socket) => { socket.emit("info", { version, @@ -96,7 +96,7 @@ let needSetup = false; socket.emit("setup") } - socket.on('disconnect', () => { + socket.on("disconnect", () => { totalClient--; }); @@ -110,7 +110,7 @@ let needSetup = false; console.log("Username from JWT: " + decoded.username) let user = await R.findOne("user", " username = ? AND active = 1 ", [ - decoded.username + decoded.username, ]) if (user) { @@ -122,13 +122,13 @@ let needSetup = false; } else { callback({ ok: false, - msg: "The user is inactive or deleted." + msg: "The user is inactive or deleted.", }) } } catch (error) { callback({ ok: false, - msg: "Invalid token." + msg: "Invalid token.", }) } @@ -145,13 +145,13 @@ let needSetup = false; callback({ ok: true, token: jwt.sign({ - username: data.username - }, jwtSecret) + username: data.username, + }, jwtSecret), }) } else { callback({ ok: false, - msg: "Incorrect username or password." + msg: "Incorrect username or password.", }) } @@ -182,13 +182,13 @@ let needSetup = false; callback({ ok: true, - msg: "Added Successfully." + msg: "Added Successfully.", }); } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -215,13 +215,13 @@ let needSetup = false; callback({ ok: true, msg: "Added Successfully.", - monitorID: bean.id + monitorID: bean.id, }); } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -258,14 +258,14 @@ let needSetup = false; callback({ ok: true, msg: "Saved.", - monitorID: bean.id + monitorID: bean.id, }); } catch (e) { console.error(e) callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -289,7 +289,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -303,13 +303,13 @@ let needSetup = false; callback({ ok: true, - msg: "Resumed Successfully." + msg: "Resumed Successfully.", }); } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -322,14 +322,13 @@ let needSetup = false; callback({ ok: true, - msg: "Paused Successfully." + msg: "Paused Successfully.", }); - } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -347,12 +346,12 @@ let needSetup = false; await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [ monitorID, - socket.userID + socket.userID, ]); callback({ ok: true, - msg: "Deleted Successfully." + msg: "Deleted Successfully.", }); await sendMonitorList(socket); @@ -360,7 +359,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -374,19 +373,19 @@ let needSetup = false; } let user = await R.findOne("user", " id = ? AND active = 1 ", [ - socket.userID + socket.userID, ]) if (user && passwordHash.verify(password.currentPassword, user.password)) { await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ passwordHash.generate(password.newPassword), - socket.userID + socket.userID, ]); callback({ ok: true, - msg: "Password has been updated successfully." + msg: "Password has been updated successfully.", }) } else { throw new Error("Incorrect current password") @@ -395,7 +394,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -404,7 +403,6 @@ let needSetup = false; try { checkLogin(socket) - callback({ ok: true, data: await getSettings(type), @@ -413,7 +411,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -434,7 +432,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -454,7 +452,7 @@ let needSetup = false; } catch (e) { callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -467,7 +465,7 @@ let needSetup = false; callback({ ok: true, - msg + msg, }); } catch (e) { @@ -475,7 +473,7 @@ let needSetup = false; callback({ ok: false, - msg: e.message + msg: e.message, }); } }); @@ -500,7 +498,7 @@ let needSetup = false; async function updateMonitorNotification(monitorID, notificationIDList) { R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ - monitorID + monitorID, ]) for (let notificationID in notificationIDList) { @@ -533,7 +531,7 @@ async function sendMonitorList(socket) { async function sendNotificationList(socket) { let result = []; let list = await R.find("notification", " user_id = ? ", [ - socket.userID + socket.userID, ]); for (let bean of list) { @@ -563,7 +561,7 @@ async function getMonitorJSONList(userID) { let result = {}; let monitorList = await R.find("monitor", " user_id = ? ", [ - userID + userID, ]) for (let monitor of monitorList) { @@ -586,8 +584,8 @@ async function initDatabase() { } console.log("Connecting to Database") - R.setup('sqlite', { - filename: Database.path + R.setup("sqlite", { + filename: Database.path, }); console.log("Connected") @@ -599,7 +597,7 @@ async function initDatabase() { await R.autoloadModels("./server/model"); let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [ - "jwtSecret" + "jwtSecret", ]); if (! jwtSecretBean) { @@ -630,11 +628,11 @@ async function startMonitor(userID, monitorID) { await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [ monitorID, - userID + userID, ]); let monitor = await R.findOne("monitor", " id = ? ", [ - monitorID + monitorID, ]) if (monitor.id in monitorList) { @@ -656,7 +654,7 @@ async function pauseMonitor(userID, monitorID) { await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [ monitorID, - userID + userID, ]); if (monitorID in monitorList) { @@ -685,13 +683,13 @@ async function sendHeartbeatList(socket, monitorID) { ORDER BY time DESC LIMIT 100 `, [ - monitorID + monitorID, ]) let result = []; for (let bean of list) { - result.unshift(bean.toJSON()) + result.unshift(bean.toJSON()) } socket.emit("heartbeatList", monitorID, result) @@ -704,23 +702,20 @@ async function sendImportantHeartbeatList(socket, monitorID) { ORDER BY time DESC LIMIT 500 `, [ - monitorID + monitorID, ]) socket.emit("importantHeartbeatList", monitorID, list) } - - const startGracefulShutdown = async () => { - console.log('Shutdown requested'); - + console.log("Shutdown requested"); await (new Promise((resolve) => { server.close(async function () { - console.log('Stopped Express.'); + console.log("Stopped Express."); process.exit(0) - setTimeout(async () =>{ + setTimeout(async () => { await R.close(); console.log("Stopped DB") @@ -730,11 +725,10 @@ const startGracefulShutdown = async () => { }); })); - } async function shutdownFunction(signal) { - console.log('Called signal: ' + signal); + console.log("Called signal: " + signal); console.log("Stopping all monitors") for (let id in monitorList) { @@ -746,14 +740,14 @@ async function shutdownFunction(signal) { } function finalFunction() { - console.log('Graceful Shutdown') + console.log("Graceful Shutdown") } gracefulShutdown(server, { - signals: 'SIGINT SIGTERM', + signals: "SIGINT SIGTERM", timeout: 30000, // timeout: 30 secs development: false, // not in dev mode forceExit: true, // triggers process.exit() at the end of shutdown process onShutdown: shutdownFunction, // shutdown function (async) - e.g. for cleanup DB, ... - finally: finalFunction // finally function (sync) - e.g. for logging + finally: finalFunction, // finally function (sync) - e.g. for logging }); diff --git a/server/util-server.js b/server/util-server.js index f03823d3a..43aa5ccfb 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -1,6 +1,6 @@ -const tcpp = require('tcp-ping'); +const tcpp = require("tcp-ping"); const Ping = require("./ping-lite"); -const {R} = require("redbean-node"); +const { R } = require("redbean-node"); exports.tcping = function (hostname, port) { return new Promise((resolve, reject) => { @@ -41,13 +41,13 @@ exports.ping = function (hostname) { exports.setting = async function (key) { return await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ - key + key, ]) } exports.setSetting = async function (key, value) { let bean = await R.findOne("setting", " `key` = ? ", [ - key + key, ]) if (! bean) { bean = R.dispense("setting") @@ -59,7 +59,7 @@ exports.setSetting = async function (key, value) { exports.getSettings = async function (type) { let list = await R.getAll("SELECT * FROM setting WHERE `type` = ? ", [ - type + type, ]) let result = {}; @@ -71,7 +71,6 @@ exports.getSettings = async function (type) { return result; } - // ssl-checker by @dyaa // param: res - response object from axios // return an object containing the certificate information @@ -97,7 +96,9 @@ exports.checkCertificate = function (res) { } = res.request.res.socket.getPeerCertificate(false); if (!valid_from || !valid_to || !subjectaltname) { - throw { message: 'No TLS certificate in response' }; + throw { + message: "No TLS certificate in response", + }; } const valid = res.request.res.socket.authorized || false; @@ -118,4 +119,4 @@ exports.checkCertificate = function (res) { issuer, fingerprint, }; -} \ No newline at end of file +} diff --git a/server/util.js b/server/util.js index 081561bf9..6e90dc42b 100644 --- a/server/util.js +++ b/server/util.js @@ -23,4 +23,3 @@ exports.debug = (msg) => { console.log(msg) } } - diff --git a/src/App.vue b/src/App.vue index 1f05560e6..a16d42085 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,11 +3,5 @@ - - diff --git a/src/components/Confirm.vue b/src/components/Confirm.vue index 063ece257..36d3781a6 100644 --- a/src/components/Confirm.vue +++ b/src/components/Confirm.vue @@ -1,17 +1,23 @@ - + - Confirm - + + Confirm + + - + @@ -19,17 +25,17 @@ - - diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue index 33904b6a9..c63aea14b 100644 --- a/src/components/CountUp.vue +++ b/src/components/CountUp.vue @@ -5,24 +5,20 @@ - - diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue index 3e5516597..84093afb1 100644 --- a/src/components/Datetime.vue +++ b/src/components/Datetime.vue @@ -5,8 +5,8 @@ - - diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 03cdceca6..bfcb90850 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -1,28 +1,27 @@ - + - + :title="beat.msg" + /> diff --git a/src/components/Login.vue b/src/components/Login.vue index 017261ccd..4b08de066 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -2,31 +2,32 @@ - - + - + Username - + Password - + Remember me - Login + + Login + - + {{ res.msg }} @@ -52,8 +53,8 @@ export default { this.processing = false; this.res = res; }) - } - } + }, + }, } diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index afbfc0d4e..83a0d1800 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -1,48 +1,70 @@ - - + - Setup Notification - + + Setup Notification + + - Notification Type - - Telegram - Webhook - Email (SMTP) - Discord - Signal - Gotify - Slack - Pushover - Apprise (Support 50+ Notification services) + + + Telegram + + + Webhook + + + Email (SMTP) + + + Discord + + + Signal + + + Gotify + + + Slack + + + Pushover + + + Apprise (Support 50+ Notification services) + Friendly Name - + Bot Token - - You can get a token from https://t.me/BotFather. + + + You can get a token from https://t.me/BotFather. + Chat ID - - Auto Get + + + Auto Get + @@ -53,7 +75,6 @@ - {{ telegramGetUpdatesURL }} @@ -69,15 +90,18 @@ Post URL - - + Content Type - - application/json - multipart/form-data + + + application/json + + + multipart/form-data + @@ -90,70 +114,71 @@ Hostname - + Port - + - + Secure - Generally, true for 465, false for other ports. + + Generally, true for 465, false for other ports. + Username - + Password - + From Email - + To Email - + - Discord Webhook URL - - You can get this by going to Server Settings -> Integrations -> Create Webhook + + + You can get this by going to Server Settings -> Integrations -> Create Webhook + Post URL - - + Number - - + Recipients - + You need to have a signal client with REST API. @@ -174,37 +199,37 @@ - - Application Token - - - - Server URL - - - + + Application Token + + + + Server URL + + + - - Priority - - - + + Priority + + + Webhook URL* - + Username - + Icon Emoji - + Channel Name - + Uptime Kuma URL - + - *Required + *Required More info about webhooks on: https://api.slack.com/messaging/webhooks @@ -224,15 +249,15 @@ User Key* - + Application Token* - + Device - + Message Title - + Priority - + -2 -1 0 @@ -240,7 +265,7 @@ 2 Notification Sound - + pushover bike bugle @@ -265,16 +290,16 @@ none - *Required - + *Required + More info on: https://pushover.net/api - - + + Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour. - - + + If you want to send notifications to different devices, fill out Device field. - + @@ -282,7 +307,7 @@ Apprise URL - + Example: twilio://AccountSid:AuthToken@FromPhoneNo @@ -293,40 +318,46 @@ Status: - Apprise is installed - Apprise is not installed. Read more + Apprise is installed + Apprise is not installed. Read more - - - Are you sure want to delete this notification for all monitors? + + Are you sure want to delete this notification for all monitors? + - - diff --git a/src/components/Status.vue b/src/components/Status.vue index 1eaf17f2d..ff430558e 100644 --- a/src/components/Status.vue +++ b/src/components/Status.vue @@ -5,34 +5,42 @@ diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index 322b35f70..393496238 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -5,10 +5,10 @@ - - diff --git a/src/icon.js b/src/icon.js index 02c5d8180..d8ea36d61 100644 --- a/src/icon.js +++ b/src/icon.js @@ -1,12 +1,10 @@ -import { library } from '@fortawesome/fontawesome-svg-core' +import { library } from "@fortawesome/fontawesome-svg-core" +import { faCog, faEdit, faList, faPause, faPlay, faPlus, faTachometerAlt, faTrash } from "@fortawesome/free-solid-svg-icons" //import { fa } from '@fortawesome/free-regular-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -import { faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" // Add Free Font Awesome Icons here // https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free library.add(faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList) -export { - FontAwesomeIcon -} +export { FontAwesomeIcon } diff --git a/src/layouts/EmptyLayout.vue b/src/layouts/EmptyLayout.vue index 7b7204477..a16d42085 100644 --- a/src/layouts/EmptyLayout.vue +++ b/src/layouts/EmptyLayout.vue @@ -3,11 +3,5 @@ - - diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index bc99854ac..8e725500a 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -1,28 +1,35 @@ - - + Lost connection to the socket server. Reconnecting... - + - + Uptime Kuma - - Dashboard - Settings + + + + Dashboard + + + + + Settings + + - + - + Uptime Kuma @@ -42,9 +49,8 @@ - - - + + Dashboard @@ -64,7 +70,6 @@ Settings - @@ -73,23 +78,19 @@ import Login from "../components/Login.vue"; export default { components: { - Login + Login, }, data() { - return { - - } - }, - computed: { - - }, - mounted() { - this.init(); + return {} }, + computed: {}, watch: { $route (to, from) { this.init(); - } + }, + }, + mounted() { + this.init(); }, methods: { init() { @@ -98,7 +99,7 @@ export default { } }, - } + }, } @@ -154,10 +155,6 @@ export default { color: white; } -main { - -} - footer { color: #AAA; font-size: 13px; diff --git a/src/main.js b/src/main.js index 59672f8d6..6eb296dc2 100644 --- a/src/main.js +++ b/src/main.js @@ -1,59 +1,58 @@ -import {createApp, h} from "vue"; -import {createRouter, createWebHistory} from 'vue-router' - -import App from './App.vue' -import Layout from './layouts/Layout.vue' -import EmptyLayout from './layouts/EmptyLayout.vue' -import Settings from "./pages/Settings.vue"; +import "bootstrap"; +import { createApp, h } from "vue"; +import { createRouter, createWebHistory } from "vue-router"; +import Toast from "vue-toastification"; +import "vue-toastification/dist/index.css"; +import App from "./App.vue"; +import "./assets/app.scss"; +import { FontAwesomeIcon } from "./icon.js"; +import EmptyLayout from "./layouts/EmptyLayout.vue"; +import Layout from "./layouts/Layout.vue"; +import socket from "./mixins/socket"; import Dashboard from "./pages/Dashboard.vue"; import DashboardHome from "./pages/DashboardHome.vue"; import Details from "./pages/Details.vue"; -import socket from "./mixins/socket" -import "./assets/app.scss" import EditMonitor from "./pages/EditMonitor.vue"; -import Toast from "vue-toastification"; -import "vue-toastification/dist/index.css"; -import "bootstrap" +import Settings from "./pages/Settings.vue"; import Setup from "./pages/Setup.vue"; -import {FontAwesomeIcon} from "./icon.js" const routes = [ { - path: '/', + path: "/", component: Layout, children: [ { name: "root", - path: '', + path: "", component: Dashboard, children: [ { name: "DashboardHome", - path: '/dashboard', + path: "/dashboard", component: DashboardHome, children: [ { - path: '/dashboard/:id', + path: "/dashboard/:id", component: EmptyLayout, children: [ { - path: '', + path: "", component: Details, }, { - path: '/edit/:id', + path: "/edit/:id", component: EditMonitor, }, - ] + ], }, { - path: '/add', + path: "/add", component: EditMonitor, }, - ] + ], }, { - path: '/settings', + path: "/settings", component: Settings, }, ], @@ -63,13 +62,13 @@ const routes = [ }, { - path: '/setup', + path: "/setup", component: Setup, }, ] const router = createRouter({ - linkActiveClass: 'active', + linkActiveClass: "active", history: createWebHistory(), routes, }) @@ -78,18 +77,17 @@ const app = createApp({ mixins: [ socket, ], - render: ()=>h(App) + render: () => h(App), }) app.use(router) const options = { - position: "bottom-right" + position: "bottom-right", }; app.use(Toast, options); -app.component('font-awesome-icon', FontAwesomeIcon) - -app.mount('#app') +app.component("FontAwesomeIcon", FontAwesomeIcon) +app.mount("#app") diff --git a/src/mixins/socket.js b/src/mixins/socket.js index f36a770e3..585b929ea 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -1,6 +1,6 @@ -import {io} from "socket.io-client"; -import { useToast } from 'vue-toastification' import dayjs from "dayjs"; +import { io } from "socket.io-client"; +import { useToast } from "vue-toastification"; const toast = useToast() let socket; @@ -33,7 +33,7 @@ export default { }, created() { - window.addEventListener('resize', this.onResize); + window.addEventListener("resize", this.onResize); let wsHost; const env = process.env.NODE_ENV || "production"; @@ -44,18 +44,18 @@ export default { } socket = io(wsHost, { - transports: ['websocket'] + transports: ["websocket"], }); socket.on("connect_error", (err) => { console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`); }); - socket.on('info', (info) => { + socket.on("info", (info) => { this.info = info; }); - socket.on('setup', (monitorID, data) => { + socket.on("setup", (monitorID, data) => { this.$router.push("/setup") }); @@ -73,11 +73,11 @@ export default { this.monitorList = data; }); - socket.on('notificationList', (data) => { + socket.on("notificationList", (data) => { this.notificationList = data; }); - socket.on('heartbeat', (data) => { + socket.on("heartbeat", (data) => { if (! (data.monitorID in this.heartbeatList)) { this.heartbeatList[data.monitorID] = []; } @@ -100,7 +100,6 @@ export default { toast(`[${this.monitorList[data.monitorID].name}] ${data.msg}`); } - if (! (data.monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[data.monitorID] = []; } @@ -109,7 +108,7 @@ export default { } }); - socket.on('heartbeatList', (monitorID, data) => { + socket.on("heartbeatList", (monitorID, data) => { if (! (monitorID in this.heartbeatList)) { this.heartbeatList[monitorID] = data; } else { @@ -117,19 +116,19 @@ export default { } }); - socket.on('avgPing', (monitorID, data) => { + socket.on("avgPing", (monitorID, data) => { this.avgPingList[monitorID] = data }); - socket.on('uptime', (monitorID, type, data) => { + socket.on("uptime", (monitorID, type, data) => { this.uptimeList[`${monitorID}_${type}`] = data }); - socket.on('certInfo', (monitorID, data) => { + socket.on("certInfo", (monitorID, data) => { this.certInfoList[monitorID] = JSON.parse(data) }); - socket.on('importantHeartbeatList', (monitorID, data) => { + socket.on("importantHeartbeatList", (monitorID, data) => { if (! (monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[monitorID] = data; } else { @@ -137,12 +136,12 @@ export default { } }); - socket.on('disconnect', () => { + socket.on("disconnect", () => { console.log("disconnect") this.socket.connected = false; }); - socket.on('connect', () => { + socket.on("connect", () => { console.log("connect") this.socket.connectCount++; this.socket.connected = true; @@ -201,7 +200,7 @@ export default { this.loggedIn = true; // Trigger Chrome Save Password - history.pushState({}, '') + history.pushState({}, "") } callback(res) @@ -254,10 +253,9 @@ export default { if (this.userTimezone === "auto") { return dayjs.tz.guess() - } else { - return this.userTimezone } + return this.userTimezone }, lastHeartbeatList() { @@ -276,7 +274,7 @@ export default { let unknown = { text: "Unknown", - color: "secondary" + color: "secondary", } for (let monitorID in this.lastHeartbeatList) { @@ -287,17 +285,17 @@ export default { } else if (lastHeartBeat.status === 1) { result[monitorID] = { text: "Up", - color: "primary" + color: "primary", }; } else if (lastHeartBeat.status === 0) { result[monitorID] = { text: "Down", - color: "danger" + color: "danger", }; } else if (lastHeartBeat.status === 2) { result[monitorID] = { text: "Pending", - color: "warning" + color: "warning", }; } else { result[monitorID] = unknown; @@ -305,23 +303,22 @@ export default { } return result; - } + }, }, watch: { // Reload the SPA if the server version is changed. "info.version"(to, from) { - if (from && from !== to) { - window.location.reload() - } + if (from && from !== to) { + window.location.reload() + } }, remember() { localStorage.remember = (this.remember) ? "1" : "0" - } + }, - } + }, } - diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue index d24d9fc07..7df7ec7dd 100644 --- a/src/pages/Dashboard.vue +++ b/src/pages/Dashboard.vue @@ -1,36 +1,33 @@ - - Add New Monitor + + Add New Monitor + - - - - No Monitors, please add one. + + + No Monitors, please + add one + . - - + - - + {{ item.name }} - - + - - @@ -38,7 +35,6 @@ - diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue index 1fa784ff0..1fd4f6028 100644 --- a/src/pages/DashboardHome.vue +++ b/src/pages/DashboardHome.vue @@ -1,15 +1,16 @@ - - Quick Stats + + Quick Stats + - + Up {{ stats.up }} - + Down {{ stats.down }} @@ -22,16 +23,16 @@ {{ stats.pause }} - + Uptime (24-hour) - + Uptime (30-day) - + @@ -39,32 +40,35 @@ - - Name - Status - DateTime - Message - + + Name + Status + DateTime + Message + - - {{ beat.name }} - - - {{ beat.msg }} - + + {{ beat.name }} + + + {{ beat.msg }} + - - No important events - + + + No important events + + + :records="importantHeartBeatList.length" + :per-page="perPage" + /> @@ -111,7 +115,7 @@ export default { } else if (beat.status === 0) { result.down++; } else if (beat.status === 2) { - result.up++; + result.up++; } else { result.unknown++; } @@ -127,7 +131,7 @@ export default { let result = []; for (let monitorID in this.$root.importantHeartbeatList) { - let list = this.$root.importantHeartbeatList[monitorID] + let list = this.$root.importantHeartbeatList[monitorID] result = result.concat(list); } @@ -142,11 +146,13 @@ export default { result.sort((a, b) => { if (a.time > b.time) { return -1; - } else if (a.time < b.time) { - return 1; - } else { - return 0; } + + if (a.time < b.time) { + return 1; + } + + return 0; }); this.heartBeatList = result; @@ -159,7 +165,7 @@ export default { const endIndex = startIndex + this.perPage; return this.heartBeatList.slice(startIndex, endIndex); }, - } + }, } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index b1f396132..20ce046df 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -1,20 +1,28 @@ {{ monitor.name }} - {{ monitor.url }} + {{ monitor.url }} TCP Ping {{ monitor.hostname }}:{{ monitor.port }} Ping: {{ monitor.hostname }} - + Keyword: {{ monitor.keyword }} - Pause - Resume - Edit - Delete + + Pause + + + Resume + + + Edit + + + Delete + @@ -37,7 +45,7 @@ - Avg.{{ pingTitle }} + Avg. {{ pingTitle }} (24-hour) @@ -52,40 +60,50 @@ - - CertExp. + + Cert Exp. () - - {{certInfo.daysRemaining}} days + + {{ certInfo.daysRemaining }} days - + Certificate Info - Valid: + + Valid: + {{ certInfo.valid }} - Valid To: + + Valid To: + - Days Remaining: + + Days Remaining: + {{ certInfo.daysRemaining }} - Issuer: + + Issuer: + {{ certInfo.issuer }} - Fingerprint: + + Fingerprint: + {{ certInfo.fingerprint }} @@ -111,7 +129,9 @@ - No important events + + No important events + @@ -119,8 +139,9 @@ + :records="importantHeartBeatList.length" + :per-page="perPage" + /> @@ -128,13 +149,13 @@ Are you sure want to pause? - + Are you sure want to delete this monitor? diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 75d7d4b9b..b79c6cb11 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -1,85 +1,102 @@ - {{ pageName }} + + {{ pageName }} + - - - - - General + + + + General Monitor Type - - HTTP(s) - TCP Port - Ping - HTTP(s) - Keyword + + + HTTP(s) + + + TCP Port + + + Ping + + + HTTP(s) - Keyword + Friendly Name - + - + URL - + - + Keyword - - Search keyword in plain html or JSON response and it is case-sensitive + + + Search keyword in plain html or JSON response and it is case-sensitive + - + Hostname - + - + Port - + Heartbeat Interval (Every {{ monitor.interval }} seconds) - + Retries - - Maximum retries before the service is marked as down and a notification is sent + + + Maximum retries before the service is marked as down and a notification is sent + - Save + + Save + - - - - - - - - Notifications - Not available, please setup. - - - - - - {{ notification.name }} - Edit - - Setup Notification + + + + Notifications + + Not available, please setup. + + + + + + + {{ notification.name }} + Edit + + + + + Setup Notification + + - @@ -87,15 +104,12 @@ diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index f4bf156a8..d1d3599ba 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -1,22 +1,29 @@ - Settings + + Settings + - General Timezone - - Auto: {{ guessTimezone }} - {{ timezone.name }} + + + Auto: {{ guessTimezone }} + + + {{ timezone.name }} + - Save + + Save + @@ -24,51 +31,58 @@ Current Password - + New Password - + Repeat New Password - + The repeat password does not match. - Update Password + + Update Password + - Logout + + Logout + - - + Notifications - Not available, please setup. - Please assign a notification to monitor(s) to get it to work. + + Not available, please setup. + + + Please assign a notification to monitor(s) to get it to work. + - - {{ notification.name }} + + {{ notification.name }} Edit - Setup Notification + + Setup Notification + - - @@ -77,18 +91,18 @@ diff --git a/src/pages/Setup.vue b/src/pages/Setup.vue index fa68945d1..85a8147c6 100644 --- a/src/pages/Setup.vue +++ b/src/pages/Setup.vue @@ -2,38 +2,42 @@ - - - Uptime Kuma + + + Uptime Kuma + - Create your admin account + + Create your admin account + - + Username - + Password - + Repeat Password - Create - + + Create + diff --git a/src/util-frontend.js b/src/util-frontend.js index 3cfab2dfa..d80d4385e 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -1,6 +1,6 @@ import dayjs from "dayjs"; -import utc from 'dayjs/plugin/utc' -import timezone from 'dayjs/plugin/timezone' +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; dayjs.extend(utc) dayjs.extend(timezone) @@ -18,11 +18,12 @@ export function ucfirst(str) { return firstLetter.toUpperCase() + str.substr(1); } - function getTimezoneOffset(timeZone) { const now = new Date(); - const tzString = now.toLocaleString('en-US', { timeZone }); - const localString = now.toLocaleString('en-US'); + const tzString = now.toLocaleString("en-US", { + timeZone, + }); + const localString = now.toLocaleString("en-US"); const diff = (Date.parse(localString) - Date.parse(tzString)) / 3600000; const offset = diff + now.getTimezoneOffset() / 60; return -offset; @@ -31,355 +32,354 @@ function getTimezoneOffset(timeZone) { // From: https://stackoverflow.com/questions/38399465/how-to-get-list-of-all-timezones-in-javascript // TODO: Move to separate file const aryIannaTimeZones = [ - 'Europe/Andorra', - 'Asia/Dubai', - 'Asia/Kabul', - 'Europe/Tirane', - 'Asia/Yerevan', - 'Antarctica/Casey', - 'Antarctica/Davis', - 'Antarctica/Mawson', - 'Antarctica/Palmer', - 'Antarctica/Rothera', - 'Antarctica/Syowa', - 'Antarctica/Troll', - 'Antarctica/Vostok', - 'America/Argentina/Buenos_Aires', - 'America/Argentina/Cordoba', - 'America/Argentina/Salta', - 'America/Argentina/Jujuy', - 'America/Argentina/Tucuman', - 'America/Argentina/Catamarca', - 'America/Argentina/La_Rioja', - 'America/Argentina/San_Juan', - 'America/Argentina/Mendoza', - 'America/Argentina/San_Luis', - 'America/Argentina/Rio_Gallegos', - 'America/Argentina/Ushuaia', - 'Pacific/Pago_Pago', - 'Europe/Vienna', - 'Australia/Lord_Howe', - 'Antarctica/Macquarie', - 'Australia/Hobart', - 'Australia/Currie', - 'Australia/Melbourne', - 'Australia/Sydney', - 'Australia/Broken_Hill', - 'Australia/Brisbane', - 'Australia/Lindeman', - 'Australia/Adelaide', - 'Australia/Darwin', - 'Australia/Perth', - 'Australia/Eucla', - 'Asia/Baku', - 'America/Barbados', - 'Asia/Dhaka', - 'Europe/Brussels', - 'Europe/Sofia', - 'Atlantic/Bermuda', - 'Asia/Brunei', - 'America/La_Paz', - 'America/Noronha', - 'America/Belem', - 'America/Fortaleza', - 'America/Recife', - 'America/Araguaina', - 'America/Maceio', - 'America/Bahia', - 'America/Sao_Paulo', - 'America/Campo_Grande', - 'America/Cuiaba', - 'America/Santarem', - 'America/Porto_Velho', - 'America/Boa_Vista', - 'America/Manaus', - 'America/Eirunepe', - 'America/Rio_Branco', - 'America/Nassau', - 'Asia/Thimphu', - 'Europe/Minsk', - 'America/Belize', - 'America/St_Johns', - 'America/Halifax', - 'America/Glace_Bay', - 'America/Moncton', - 'America/Goose_Bay', - 'America/Blanc-Sablon', - 'America/Toronto', - 'America/Nipigon', - 'America/Thunder_Bay', - 'America/Iqaluit', - 'America/Pangnirtung', - 'America/Atikokan', - 'America/Winnipeg', - 'America/Rainy_River', - 'America/Resolute', - 'America/Rankin_Inlet', - 'America/Regina', - 'America/Swift_Current', - 'America/Edmonton', - 'America/Cambridge_Bay', - 'America/Yellowknife', - 'America/Inuvik', - 'America/Creston', - 'America/Dawson_Creek', - 'America/Fort_Nelson', - 'America/Vancouver', - 'America/Whitehorse', - 'America/Dawson', - 'Indian/Cocos', - 'Europe/Zurich', - 'Africa/Abidjan', - 'Pacific/Rarotonga', - 'America/Santiago', - 'America/Punta_Arenas', - 'Pacific/Easter', - 'Asia/Shanghai', - 'Asia/Urumqi', - 'America/Bogota', - 'America/Costa_Rica', - 'America/Havana', - 'Atlantic/Cape_Verde', - 'America/Curacao', - 'Indian/Christmas', - 'Asia/Nicosia', - 'Asia/Famagusta', - 'Europe/Prague', - 'Europe/Berlin', - 'Europe/Copenhagen', - 'America/Santo_Domingo', - 'Africa/Algiers', - 'America/Guayaquil', - 'Pacific/Galapagos', - 'Europe/Tallinn', - 'Africa/Cairo', - 'Africa/El_Aaiun', - 'Europe/Madrid', - 'Africa/Ceuta', - 'Atlantic/Canary', - 'Europe/Helsinki', - 'Pacific/Fiji', - 'Atlantic/Stanley', - 'Pacific/Chuuk', - 'Pacific/Pohnpei', - 'Pacific/Kosrae', - 'Atlantic/Faroe', - 'Europe/Paris', - 'Europe/London', - 'Asia/Tbilisi', - 'America/Cayenne', - 'Africa/Accra', - 'Europe/Gibraltar', - 'America/Godthab', - 'America/Danmarkshavn', - 'America/Scoresbysund', - 'America/Thule', - 'Europe/Athens', - 'Atlantic/South_Georgia', - 'America/Guatemala', - 'Pacific/Guam', - 'Africa/Bissau', - 'America/Guyana', - 'Asia/Hong_Kong', - 'America/Tegucigalpa', - 'America/Port-au-Prince', - 'Europe/Budapest', - 'Asia/Jakarta', - 'Asia/Pontianak', - 'Asia/Makassar', - 'Asia/Jayapura', - 'Europe/Dublin', - 'Asia/Jerusalem', - 'Asia/Kolkata', - 'Indian/Chagos', - 'Asia/Baghdad', - 'Asia/Tehran', - 'Atlantic/Reykjavik', - 'Europe/Rome', - 'America/Jamaica', - 'Asia/Amman', - 'Asia/Tokyo', - 'Africa/Nairobi', - 'Asia/Bishkek', - 'Pacific/Tarawa', - 'Pacific/Enderbury', - 'Pacific/Kiritimati', - 'Asia/Pyongyang', - 'Asia/Seoul', - 'Asia/Almaty', - 'Asia/Qyzylorda', - 'Asia/Aqtobe', - 'Asia/Aqtau', - 'Asia/Atyrau', - 'Asia/Oral', - 'Asia/Beirut', - 'Asia/Colombo', - 'Africa/Monrovia', - 'Europe/Vilnius', - 'Europe/Luxembourg', - 'Europe/Riga', - 'Africa/Tripoli', - 'Africa/Casablanca', - 'Europe/Monaco', - 'Europe/Chisinau', - 'Pacific/Majuro', - 'Pacific/Kwajalein', - 'Asia/Yangon', - 'Asia/Ulaanbaatar', - 'Asia/Hovd', - 'Asia/Choibalsan', - 'Asia/Macau', - 'America/Martinique', - 'Europe/Malta', - 'Indian/Mauritius', - 'Indian/Maldives', - 'America/Mexico_City', - 'America/Cancun', - 'America/Merida', - 'America/Monterrey', - 'America/Matamoros', - 'America/Mazatlan', - 'America/Chihuahua', - 'America/Ojinaga', - 'America/Hermosillo', - 'America/Tijuana', - 'America/Bahia_Banderas', - 'Asia/Kuala_Lumpur', - 'Asia/Kuching', - 'Africa/Maputo', - 'Africa/Windhoek', - 'Pacific/Noumea', - 'Pacific/Norfolk', - 'Africa/Lagos', - 'America/Managua', - 'Europe/Amsterdam', - 'Europe/Oslo', - 'Asia/Kathmandu', - 'Pacific/Nauru', - 'Pacific/Niue', - 'Pacific/Auckland', - 'Pacific/Chatham', - 'America/Panama', - 'America/Lima', - 'Pacific/Tahiti', - 'Pacific/Marquesas', - 'Pacific/Gambier', - 'Pacific/Port_Moresby', - 'Pacific/Bougainville', - 'Asia/Manila', - 'Asia/Karachi', - 'Europe/Warsaw', - 'America/Miquelon', - 'Pacific/Pitcairn', - 'America/Puerto_Rico', - 'Asia/Gaza', - 'Asia/Hebron', - 'Europe/Lisbon', - 'Atlantic/Madeira', - 'Atlantic/Azores', - 'Pacific/Palau', - 'America/Asuncion', - 'Asia/Qatar', - 'Indian/Reunion', - 'Europe/Bucharest', - 'Europe/Belgrade', - 'Europe/Kaliningrad', - 'Europe/Moscow', - 'Europe/Simferopol', - 'Europe/Kirov', - 'Europe/Astrakhan', - 'Europe/Volgograd', - 'Europe/Saratov', - 'Europe/Ulyanovsk', - 'Europe/Samara', - 'Asia/Yekaterinburg', - 'Asia/Omsk', - 'Asia/Novosibirsk', - 'Asia/Barnaul', - 'Asia/Tomsk', - 'Asia/Novokuznetsk', - 'Asia/Krasnoyarsk', - 'Asia/Irkutsk', - 'Asia/Chita', - 'Asia/Yakutsk', - 'Asia/Khandyga', - 'Asia/Vladivostok', - 'Asia/Ust-Nera', - 'Asia/Magadan', - 'Asia/Sakhalin', - 'Asia/Srednekolymsk', - 'Asia/Kamchatka', - 'Asia/Anadyr', - 'Asia/Riyadh', - 'Pacific/Guadalcanal', - 'Indian/Mahe', - 'Africa/Khartoum', - 'Europe/Stockholm', - 'Asia/Singapore', - 'America/Paramaribo', - 'Africa/Juba', - 'Africa/Sao_Tome', - 'America/El_Salvador', - 'Asia/Damascus', - 'America/Grand_Turk', - 'Africa/Ndjamena', - 'Indian/Kerguelen', - 'Asia/Bangkok', - 'Asia/Dushanbe', - 'Pacific/Fakaofo', - 'Asia/Dili', - 'Asia/Ashgabat', - 'Africa/Tunis', - 'Pacific/Tongatapu', - 'Europe/Istanbul', - 'America/Port_of_Spain', - 'Pacific/Funafuti', - 'Asia/Taipei', - 'Europe/Kiev', - 'Europe/Uzhgorod', - 'Europe/Zaporozhye', - 'Pacific/Wake', - 'America/New_York', - 'America/Detroit', - 'America/Kentucky/Louisville', - 'America/Kentucky/Monticello', - 'America/Indiana/Indianapolis', - 'America/Indiana/Vincennes', - 'America/Indiana/Winamac', - 'America/Indiana/Marengo', - 'America/Indiana/Petersburg', - 'America/Indiana/Vevay', - 'America/Chicago', - 'America/Indiana/Tell_City', - 'America/Indiana/Knox', - 'America/Menominee', - 'America/North_Dakota/Center', - 'America/North_Dakota/New_Salem', - 'America/North_Dakota/Beulah', - 'America/Denver', - 'America/Boise', - 'America/Phoenix', - 'America/Los_Angeles', - 'America/Anchorage', - 'America/Juneau', - 'America/Sitka', - 'America/Metlakatla', - 'America/Yakutat', - 'America/Nome', - 'America/Adak', - 'Pacific/Honolulu', - 'America/Montevideo', - 'Asia/Samarkand', - 'Asia/Tashkent', - 'America/Caracas', - 'Asia/Ho_Chi_Minh', - 'Pacific/Efate', - 'Pacific/Wallis', - 'Pacific/Apia', - 'Africa/Johannesburg', + "Europe/Andorra", + "Asia/Dubai", + "Asia/Kabul", + "Europe/Tirane", + "Asia/Yerevan", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/Mawson", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "America/Argentina/Buenos_Aires", + "America/Argentina/Cordoba", + "America/Argentina/Salta", + "America/Argentina/Jujuy", + "America/Argentina/Tucuman", + "America/Argentina/Catamarca", + "America/Argentina/La_Rioja", + "America/Argentina/San_Juan", + "America/Argentina/Mendoza", + "America/Argentina/San_Luis", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Ushuaia", + "Pacific/Pago_Pago", + "Europe/Vienna", + "Australia/Lord_Howe", + "Antarctica/Macquarie", + "Australia/Hobart", + "Australia/Currie", + "Australia/Melbourne", + "Australia/Sydney", + "Australia/Broken_Hill", + "Australia/Brisbane", + "Australia/Lindeman", + "Australia/Adelaide", + "Australia/Darwin", + "Australia/Perth", + "Australia/Eucla", + "Asia/Baku", + "America/Barbados", + "Asia/Dhaka", + "Europe/Brussels", + "Europe/Sofia", + "Atlantic/Bermuda", + "Asia/Brunei", + "America/La_Paz", + "America/Noronha", + "America/Belem", + "America/Fortaleza", + "America/Recife", + "America/Araguaina", + "America/Maceio", + "America/Bahia", + "America/Sao_Paulo", + "America/Campo_Grande", + "America/Cuiaba", + "America/Santarem", + "America/Porto_Velho", + "America/Boa_Vista", + "America/Manaus", + "America/Eirunepe", + "America/Rio_Branco", + "America/Nassau", + "Asia/Thimphu", + "Europe/Minsk", + "America/Belize", + "America/St_Johns", + "America/Halifax", + "America/Glace_Bay", + "America/Moncton", + "America/Goose_Bay", + "America/Blanc-Sablon", + "America/Toronto", + "America/Nipigon", + "America/Thunder_Bay", + "America/Iqaluit", + "America/Pangnirtung", + "America/Atikokan", + "America/Winnipeg", + "America/Rainy_River", + "America/Resolute", + "America/Rankin_Inlet", + "America/Regina", + "America/Swift_Current", + "America/Edmonton", + "America/Cambridge_Bay", + "America/Yellowknife", + "America/Inuvik", + "America/Creston", + "America/Dawson_Creek", + "America/Fort_Nelson", + "America/Vancouver", + "America/Whitehorse", + "America/Dawson", + "Indian/Cocos", + "Europe/Zurich", + "Africa/Abidjan", + "Pacific/Rarotonga", + "America/Santiago", + "America/Punta_Arenas", + "Pacific/Easter", + "Asia/Shanghai", + "Asia/Urumqi", + "America/Bogota", + "America/Costa_Rica", + "America/Havana", + "Atlantic/Cape_Verde", + "America/Curacao", + "Indian/Christmas", + "Asia/Nicosia", + "Asia/Famagusta", + "Europe/Prague", + "Europe/Berlin", + "Europe/Copenhagen", + "America/Santo_Domingo", + "Africa/Algiers", + "America/Guayaquil", + "Pacific/Galapagos", + "Europe/Tallinn", + "Africa/Cairo", + "Africa/El_Aaiun", + "Europe/Madrid", + "Africa/Ceuta", + "Atlantic/Canary", + "Europe/Helsinki", + "Pacific/Fiji", + "Atlantic/Stanley", + "Pacific/Chuuk", + "Pacific/Pohnpei", + "Pacific/Kosrae", + "Atlantic/Faroe", + "Europe/Paris", + "Europe/London", + "Asia/Tbilisi", + "America/Cayenne", + "Africa/Accra", + "Europe/Gibraltar", + "America/Godthab", + "America/Danmarkshavn", + "America/Scoresbysund", + "America/Thule", + "Europe/Athens", + "Atlantic/South_Georgia", + "America/Guatemala", + "Pacific/Guam", + "Africa/Bissau", + "America/Guyana", + "Asia/Hong_Kong", + "America/Tegucigalpa", + "America/Port-au-Prince", + "Europe/Budapest", + "Asia/Jakarta", + "Asia/Pontianak", + "Asia/Makassar", + "Asia/Jayapura", + "Europe/Dublin", + "Asia/Jerusalem", + "Asia/Kolkata", + "Indian/Chagos", + "Asia/Baghdad", + "Asia/Tehran", + "Atlantic/Reykjavik", + "Europe/Rome", + "America/Jamaica", + "Asia/Amman", + "Asia/Tokyo", + "Africa/Nairobi", + "Asia/Bishkek", + "Pacific/Tarawa", + "Pacific/Enderbury", + "Pacific/Kiritimati", + "Asia/Pyongyang", + "Asia/Seoul", + "Asia/Almaty", + "Asia/Qyzylorda", + "Asia/Aqtobe", + "Asia/Aqtau", + "Asia/Atyrau", + "Asia/Oral", + "Asia/Beirut", + "Asia/Colombo", + "Africa/Monrovia", + "Europe/Vilnius", + "Europe/Luxembourg", + "Europe/Riga", + "Africa/Tripoli", + "Africa/Casablanca", + "Europe/Monaco", + "Europe/Chisinau", + "Pacific/Majuro", + "Pacific/Kwajalein", + "Asia/Yangon", + "Asia/Ulaanbaatar", + "Asia/Hovd", + "Asia/Choibalsan", + "Asia/Macau", + "America/Martinique", + "Europe/Malta", + "Indian/Mauritius", + "Indian/Maldives", + "America/Mexico_City", + "America/Cancun", + "America/Merida", + "America/Monterrey", + "America/Matamoros", + "America/Mazatlan", + "America/Chihuahua", + "America/Ojinaga", + "America/Hermosillo", + "America/Tijuana", + "America/Bahia_Banderas", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Africa/Maputo", + "Africa/Windhoek", + "Pacific/Noumea", + "Pacific/Norfolk", + "Africa/Lagos", + "America/Managua", + "Europe/Amsterdam", + "Europe/Oslo", + "Asia/Kathmandu", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Auckland", + "Pacific/Chatham", + "America/Panama", + "America/Lima", + "Pacific/Tahiti", + "Pacific/Marquesas", + "Pacific/Gambier", + "Pacific/Port_Moresby", + "Pacific/Bougainville", + "Asia/Manila", + "Asia/Karachi", + "Europe/Warsaw", + "America/Miquelon", + "Pacific/Pitcairn", + "America/Puerto_Rico", + "Asia/Gaza", + "Asia/Hebron", + "Europe/Lisbon", + "Atlantic/Madeira", + "Atlantic/Azores", + "Pacific/Palau", + "America/Asuncion", + "Asia/Qatar", + "Indian/Reunion", + "Europe/Bucharest", + "Europe/Belgrade", + "Europe/Kaliningrad", + "Europe/Moscow", + "Europe/Simferopol", + "Europe/Kirov", + "Europe/Astrakhan", + "Europe/Volgograd", + "Europe/Saratov", + "Europe/Ulyanovsk", + "Europe/Samara", + "Asia/Yekaterinburg", + "Asia/Omsk", + "Asia/Novosibirsk", + "Asia/Barnaul", + "Asia/Tomsk", + "Asia/Novokuznetsk", + "Asia/Krasnoyarsk", + "Asia/Irkutsk", + "Asia/Chita", + "Asia/Yakutsk", + "Asia/Khandyga", + "Asia/Vladivostok", + "Asia/Ust-Nera", + "Asia/Magadan", + "Asia/Sakhalin", + "Asia/Srednekolymsk", + "Asia/Kamchatka", + "Asia/Anadyr", + "Asia/Riyadh", + "Pacific/Guadalcanal", + "Indian/Mahe", + "Africa/Khartoum", + "Europe/Stockholm", + "Asia/Singapore", + "America/Paramaribo", + "Africa/Juba", + "Africa/Sao_Tome", + "America/El_Salvador", + "Asia/Damascus", + "America/Grand_Turk", + "Africa/Ndjamena", + "Indian/Kerguelen", + "Asia/Bangkok", + "Asia/Dushanbe", + "Pacific/Fakaofo", + "Asia/Dili", + "Asia/Ashgabat", + "Africa/Tunis", + "Pacific/Tongatapu", + "Europe/Istanbul", + "America/Port_of_Spain", + "Pacific/Funafuti", + "Asia/Taipei", + "Europe/Kiev", + "Europe/Uzhgorod", + "Europe/Zaporozhye", + "Pacific/Wake", + "America/New_York", + "America/Detroit", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Indiana/Indianapolis", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Vevay", + "America/Chicago", + "America/Indiana/Tell_City", + "America/Indiana/Knox", + "America/Menominee", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/North_Dakota/Beulah", + "America/Denver", + "America/Boise", + "America/Phoenix", + "America/Los_Angeles", + "America/Anchorage", + "America/Juneau", + "America/Sitka", + "America/Metlakatla", + "America/Yakutat", + "America/Nome", + "America/Adak", + "Pacific/Honolulu", + "America/Montevideo", + "Asia/Samarkand", + "Asia/Tashkent", + "America/Caracas", + "Asia/Ho_Chi_Minh", + "Pacific/Efate", + "Pacific/Wallis", + "Pacific/Apia", + "Africa/Johannesburg", ]; - export function timezoneList() { let result = []; @@ -404,12 +404,14 @@ export function timezoneList() { result.sort((a, b) => { if (a.time > b.time) { return 1; - } else if (b.time > a.time) { - return -1; - } else { - return 0; } + + if (b.time > a.time) { + return -1; + } + + return 0; }) return result; -}; +}
- {{ telegramGetUpdatesURL }} @@ -69,15 +90,18 @@ Post URL - - + Content Type - - application/json - multipart/form-data + + + application/json + + + multipart/form-data + @@ -90,70 +114,71 @@ Hostname - + Port - + - + Secure - Generally, true for 465, false for other ports. + + Generally, true for 465, false for other ports. + Username - + Password - + From Email - + To Email - + - Discord Webhook URL - - You can get this by going to Server Settings -> Integrations -> Create Webhook + + + You can get this by going to Server Settings -> Integrations -> Create Webhook + Post URL - - + Number - - + Recipients - + You need to have a signal client with REST API. @@ -174,37 +199,37 @@ - - Application Token - - - - Server URL - - - + + Application Token + + + + Server URL + + + - - Priority - - - + + Priority + + + Webhook URL* - + Username - + Icon Emoji - + Channel Name - + Uptime Kuma URL - + - *Required + *Required More info about webhooks on: https://api.slack.com/messaging/webhooks @@ -224,15 +249,15 @@ User Key* - + Application Token* - + Device - + Message Title - + Priority - + -2 -1 0 @@ -240,7 +265,7 @@ 2 Notification Sound - + pushover bike bugle @@ -265,16 +290,16 @@ none - *Required - + *Required + More info on: https://pushover.net/api - - + + Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour. - - + + If you want to send notifications to different devices, fill out Device field. - + @@ -282,7 +307,7 @@ Apprise URL - + Example: twilio://AccountSid:AuthToken@FromPhoneNo @@ -293,40 +318,46 @@ Status: - Apprise is installed - Apprise is not installed. Read more + Apprise is installed + Apprise is not installed. Read more - - - Are you sure want to delete this notification for all monitors? + + Are you sure want to delete this notification for all monitors? + - - diff --git a/src/components/Status.vue b/src/components/Status.vue index 1eaf17f2d..ff430558e 100644 --- a/src/components/Status.vue +++ b/src/components/Status.vue @@ -5,34 +5,42 @@ diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index 322b35f70..393496238 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -5,10 +5,10 @@ - - diff --git a/src/icon.js b/src/icon.js index 02c5d8180..d8ea36d61 100644 --- a/src/icon.js +++ b/src/icon.js @@ -1,12 +1,10 @@ -import { library } from '@fortawesome/fontawesome-svg-core' +import { library } from "@fortawesome/fontawesome-svg-core" +import { faCog, faEdit, faList, faPause, faPlay, faPlus, faTachometerAlt, faTrash } from "@fortawesome/free-solid-svg-icons" //import { fa } from '@fortawesome/free-regular-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -import { faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" // Add Free Font Awesome Icons here // https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free library.add(faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList) -export { - FontAwesomeIcon -} +export { FontAwesomeIcon } diff --git a/src/layouts/EmptyLayout.vue b/src/layouts/EmptyLayout.vue index 7b7204477..a16d42085 100644 --- a/src/layouts/EmptyLayout.vue +++ b/src/layouts/EmptyLayout.vue @@ -3,11 +3,5 @@ - - diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index bc99854ac..8e725500a 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -1,28 +1,35 @@ - - + Lost connection to the socket server. Reconnecting... - + - + Uptime Kuma - - Dashboard - Settings + + + + Dashboard + + + + + Settings + + - + - + Uptime Kuma @@ -42,9 +49,8 @@ - - - + + Dashboard @@ -64,7 +70,6 @@ Settings - @@ -73,23 +78,19 @@ import Login from "../components/Login.vue"; export default { components: { - Login + Login, }, data() { - return { - - } - }, - computed: { - - }, - mounted() { - this.init(); + return {} }, + computed: {}, watch: { $route (to, from) { this.init(); - } + }, + }, + mounted() { + this.init(); }, methods: { init() { @@ -98,7 +99,7 @@ export default { } }, - } + }, } @@ -154,10 +155,6 @@ export default { color: white; } -main { - -} - footer { color: #AAA; font-size: 13px; diff --git a/src/main.js b/src/main.js index 59672f8d6..6eb296dc2 100644 --- a/src/main.js +++ b/src/main.js @@ -1,59 +1,58 @@ -import {createApp, h} from "vue"; -import {createRouter, createWebHistory} from 'vue-router' - -import App from './App.vue' -import Layout from './layouts/Layout.vue' -import EmptyLayout from './layouts/EmptyLayout.vue' -import Settings from "./pages/Settings.vue"; +import "bootstrap"; +import { createApp, h } from "vue"; +import { createRouter, createWebHistory } from "vue-router"; +import Toast from "vue-toastification"; +import "vue-toastification/dist/index.css"; +import App from "./App.vue"; +import "./assets/app.scss"; +import { FontAwesomeIcon } from "./icon.js"; +import EmptyLayout from "./layouts/EmptyLayout.vue"; +import Layout from "./layouts/Layout.vue"; +import socket from "./mixins/socket"; import Dashboard from "./pages/Dashboard.vue"; import DashboardHome from "./pages/DashboardHome.vue"; import Details from "./pages/Details.vue"; -import socket from "./mixins/socket" -import "./assets/app.scss" import EditMonitor from "./pages/EditMonitor.vue"; -import Toast from "vue-toastification"; -import "vue-toastification/dist/index.css"; -import "bootstrap" +import Settings from "./pages/Settings.vue"; import Setup from "./pages/Setup.vue"; -import {FontAwesomeIcon} from "./icon.js" const routes = [ { - path: '/', + path: "/", component: Layout, children: [ { name: "root", - path: '', + path: "", component: Dashboard, children: [ { name: "DashboardHome", - path: '/dashboard', + path: "/dashboard", component: DashboardHome, children: [ { - path: '/dashboard/:id', + path: "/dashboard/:id", component: EmptyLayout, children: [ { - path: '', + path: "", component: Details, }, { - path: '/edit/:id', + path: "/edit/:id", component: EditMonitor, }, - ] + ], }, { - path: '/add', + path: "/add", component: EditMonitor, }, - ] + ], }, { - path: '/settings', + path: "/settings", component: Settings, }, ], @@ -63,13 +62,13 @@ const routes = [ }, { - path: '/setup', + path: "/setup", component: Setup, }, ] const router = createRouter({ - linkActiveClass: 'active', + linkActiveClass: "active", history: createWebHistory(), routes, }) @@ -78,18 +77,17 @@ const app = createApp({ mixins: [ socket, ], - render: ()=>h(App) + render: () => h(App), }) app.use(router) const options = { - position: "bottom-right" + position: "bottom-right", }; app.use(Toast, options); -app.component('font-awesome-icon', FontAwesomeIcon) - -app.mount('#app') +app.component("FontAwesomeIcon", FontAwesomeIcon) +app.mount("#app") diff --git a/src/mixins/socket.js b/src/mixins/socket.js index f36a770e3..585b929ea 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -1,6 +1,6 @@ -import {io} from "socket.io-client"; -import { useToast } from 'vue-toastification' import dayjs from "dayjs"; +import { io } from "socket.io-client"; +import { useToast } from "vue-toastification"; const toast = useToast() let socket; @@ -33,7 +33,7 @@ export default { }, created() { - window.addEventListener('resize', this.onResize); + window.addEventListener("resize", this.onResize); let wsHost; const env = process.env.NODE_ENV || "production"; @@ -44,18 +44,18 @@ export default { } socket = io(wsHost, { - transports: ['websocket'] + transports: ["websocket"], }); socket.on("connect_error", (err) => { console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`); }); - socket.on('info', (info) => { + socket.on("info", (info) => { this.info = info; }); - socket.on('setup', (monitorID, data) => { + socket.on("setup", (monitorID, data) => { this.$router.push("/setup") }); @@ -73,11 +73,11 @@ export default { this.monitorList = data; }); - socket.on('notificationList', (data) => { + socket.on("notificationList", (data) => { this.notificationList = data; }); - socket.on('heartbeat', (data) => { + socket.on("heartbeat", (data) => { if (! (data.monitorID in this.heartbeatList)) { this.heartbeatList[data.monitorID] = []; } @@ -100,7 +100,6 @@ export default { toast(`[${this.monitorList[data.monitorID].name}] ${data.msg}`); } - if (! (data.monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[data.monitorID] = []; } @@ -109,7 +108,7 @@ export default { } }); - socket.on('heartbeatList', (monitorID, data) => { + socket.on("heartbeatList", (monitorID, data) => { if (! (monitorID in this.heartbeatList)) { this.heartbeatList[monitorID] = data; } else { @@ -117,19 +116,19 @@ export default { } }); - socket.on('avgPing', (monitorID, data) => { + socket.on("avgPing", (monitorID, data) => { this.avgPingList[monitorID] = data }); - socket.on('uptime', (monitorID, type, data) => { + socket.on("uptime", (monitorID, type, data) => { this.uptimeList[`${monitorID}_${type}`] = data }); - socket.on('certInfo', (monitorID, data) => { + socket.on("certInfo", (monitorID, data) => { this.certInfoList[monitorID] = JSON.parse(data) }); - socket.on('importantHeartbeatList', (monitorID, data) => { + socket.on("importantHeartbeatList", (monitorID, data) => { if (! (monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[monitorID] = data; } else { @@ -137,12 +136,12 @@ export default { } }); - socket.on('disconnect', () => { + socket.on("disconnect", () => { console.log("disconnect") this.socket.connected = false; }); - socket.on('connect', () => { + socket.on("connect", () => { console.log("connect") this.socket.connectCount++; this.socket.connected = true; @@ -201,7 +200,7 @@ export default { this.loggedIn = true; // Trigger Chrome Save Password - history.pushState({}, '') + history.pushState({}, "") } callback(res) @@ -254,10 +253,9 @@ export default { if (this.userTimezone === "auto") { return dayjs.tz.guess() - } else { - return this.userTimezone } + return this.userTimezone }, lastHeartbeatList() { @@ -276,7 +274,7 @@ export default { let unknown = { text: "Unknown", - color: "secondary" + color: "secondary", } for (let monitorID in this.lastHeartbeatList) { @@ -287,17 +285,17 @@ export default { } else if (lastHeartBeat.status === 1) { result[monitorID] = { text: "Up", - color: "primary" + color: "primary", }; } else if (lastHeartBeat.status === 0) { result[monitorID] = { text: "Down", - color: "danger" + color: "danger", }; } else if (lastHeartBeat.status === 2) { result[monitorID] = { text: "Pending", - color: "warning" + color: "warning", }; } else { result[monitorID] = unknown; @@ -305,23 +303,22 @@ export default { } return result; - } + }, }, watch: { // Reload the SPA if the server version is changed. "info.version"(to, from) { - if (from && from !== to) { - window.location.reload() - } + if (from && from !== to) { + window.location.reload() + } }, remember() { localStorage.remember = (this.remember) ? "1" : "0" - } + }, - } + }, } - diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue index d24d9fc07..7df7ec7dd 100644 --- a/src/pages/Dashboard.vue +++ b/src/pages/Dashboard.vue @@ -1,36 +1,33 @@ - - Add New Monitor + + Add New Monitor + - - - - No Monitors, please add one. + + + No Monitors, please + add one + . - - + - - + {{ item.name }} - - + - - @@ -38,7 +35,6 @@ - diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue index 1fa784ff0..1fd4f6028 100644 --- a/src/pages/DashboardHome.vue +++ b/src/pages/DashboardHome.vue @@ -1,15 +1,16 @@ - - Quick Stats + + Quick Stats + - + Up {{ stats.up }} - + Down {{ stats.down }} @@ -22,16 +23,16 @@ {{ stats.pause }} - + Uptime (24-hour) - + Uptime (30-day) - + @@ -39,32 +40,35 @@ - - Name - Status - DateTime - Message - + + Name + Status + DateTime + Message + - - {{ beat.name }} - - - {{ beat.msg }} - + + {{ beat.name }} + + + {{ beat.msg }} + - - No important events - + + + No important events + + + :records="importantHeartBeatList.length" + :per-page="perPage" + /> @@ -111,7 +115,7 @@ export default { } else if (beat.status === 0) { result.down++; } else if (beat.status === 2) { - result.up++; + result.up++; } else { result.unknown++; } @@ -127,7 +131,7 @@ export default { let result = []; for (let monitorID in this.$root.importantHeartbeatList) { - let list = this.$root.importantHeartbeatList[monitorID] + let list = this.$root.importantHeartbeatList[monitorID] result = result.concat(list); } @@ -142,11 +146,13 @@ export default { result.sort((a, b) => { if (a.time > b.time) { return -1; - } else if (a.time < b.time) { - return 1; - } else { - return 0; } + + if (a.time < b.time) { + return 1; + } + + return 0; }); this.heartBeatList = result; @@ -159,7 +165,7 @@ export default { const endIndex = startIndex + this.perPage; return this.heartBeatList.slice(startIndex, endIndex); }, - } + }, } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index b1f396132..20ce046df 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -1,20 +1,28 @@ {{ monitor.name }} - {{ monitor.url }} + {{ monitor.url }} TCP Ping {{ monitor.hostname }}:{{ monitor.port }} Ping: {{ monitor.hostname }} - + Keyword: {{ monitor.keyword }} - Pause - Resume - Edit - Delete + + Pause + + + Resume + + + Edit + + + Delete + @@ -37,7 +45,7 @@ - Avg.{{ pingTitle }} + Avg. {{ pingTitle }} (24-hour) @@ -52,40 +60,50 @@ - - CertExp. + + Cert Exp. () - - {{certInfo.daysRemaining}} days + + {{ certInfo.daysRemaining }} days - + Certificate Info - Valid: + + Valid: + {{ certInfo.valid }} - Valid To: + + Valid To: + - Days Remaining: + + Days Remaining: + {{ certInfo.daysRemaining }} - Issuer: + + Issuer: + {{ certInfo.issuer }} - Fingerprint: + + Fingerprint: + {{ certInfo.fingerprint }} @@ -111,7 +129,9 @@ - No important events + + No important events + @@ -119,8 +139,9 @@ + :records="importantHeartBeatList.length" + :per-page="perPage" + /> @@ -128,13 +149,13 @@ Are you sure want to pause? - + Are you sure want to delete this monitor? diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 75d7d4b9b..b79c6cb11 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -1,85 +1,102 @@ - {{ pageName }} + + {{ pageName }} + - - - - - General + + + + General Monitor Type - - HTTP(s) - TCP Port - Ping - HTTP(s) - Keyword + + + HTTP(s) + + + TCP Port + + + Ping + + + HTTP(s) - Keyword + Friendly Name - + - + URL - + - + Keyword - - Search keyword in plain html or JSON response and it is case-sensitive + + + Search keyword in plain html or JSON response and it is case-sensitive + - + Hostname - + - + Port - + Heartbeat Interval (Every {{ monitor.interval }} seconds) - + Retries - - Maximum retries before the service is marked as down and a notification is sent + + + Maximum retries before the service is marked as down and a notification is sent + - Save + + Save + - - - - - - - - Notifications - Not available, please setup. - - - - - - {{ notification.name }} - Edit - - Setup Notification + + + + Notifications + + Not available, please setup. + + + + + + + {{ notification.name }} + Edit + + + + + Setup Notification + + - @@ -87,15 +104,12 @@ diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index f4bf156a8..d1d3599ba 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -1,22 +1,29 @@ - Settings + + Settings + - General Timezone - - Auto: {{ guessTimezone }} - {{ timezone.name }} + + + Auto: {{ guessTimezone }} + + + {{ timezone.name }} + - Save + + Save + @@ -24,51 +31,58 @@ Current Password - + New Password - + Repeat New Password - + The repeat password does not match. - Update Password + + Update Password + - Logout + + Logout + - - + Notifications - Not available, please setup. - Please assign a notification to monitor(s) to get it to work. + + Not available, please setup. + + + Please assign a notification to monitor(s) to get it to work. + - - {{ notification.name }} + + {{ notification.name }} Edit - Setup Notification + + Setup Notification + - - @@ -77,18 +91,18 @@ diff --git a/src/pages/Setup.vue b/src/pages/Setup.vue index fa68945d1..85a8147c6 100644 --- a/src/pages/Setup.vue +++ b/src/pages/Setup.vue @@ -2,38 +2,42 @@ - - - Uptime Kuma + + + Uptime Kuma + - Create your admin account + + Create your admin account + - + Username - + Password - + Repeat Password - Create - + + Create + diff --git a/src/util-frontend.js b/src/util-frontend.js index 3cfab2dfa..d80d4385e 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -1,6 +1,6 @@ import dayjs from "dayjs"; -import utc from 'dayjs/plugin/utc' -import timezone from 'dayjs/plugin/timezone' +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; dayjs.extend(utc) dayjs.extend(timezone) @@ -18,11 +18,12 @@ export function ucfirst(str) { return firstLetter.toUpperCase() + str.substr(1); } - function getTimezoneOffset(timeZone) { const now = new Date(); - const tzString = now.toLocaleString('en-US', { timeZone }); - const localString = now.toLocaleString('en-US'); + const tzString = now.toLocaleString("en-US", { + timeZone, + }); + const localString = now.toLocaleString("en-US"); const diff = (Date.parse(localString) - Date.parse(tzString)) / 3600000; const offset = diff + now.getTimezoneOffset() / 60; return -offset; @@ -31,355 +32,354 @@ function getTimezoneOffset(timeZone) { // From: https://stackoverflow.com/questions/38399465/how-to-get-list-of-all-timezones-in-javascript // TODO: Move to separate file const aryIannaTimeZones = [ - 'Europe/Andorra', - 'Asia/Dubai', - 'Asia/Kabul', - 'Europe/Tirane', - 'Asia/Yerevan', - 'Antarctica/Casey', - 'Antarctica/Davis', - 'Antarctica/Mawson', - 'Antarctica/Palmer', - 'Antarctica/Rothera', - 'Antarctica/Syowa', - 'Antarctica/Troll', - 'Antarctica/Vostok', - 'America/Argentina/Buenos_Aires', - 'America/Argentina/Cordoba', - 'America/Argentina/Salta', - 'America/Argentina/Jujuy', - 'America/Argentina/Tucuman', - 'America/Argentina/Catamarca', - 'America/Argentina/La_Rioja', - 'America/Argentina/San_Juan', - 'America/Argentina/Mendoza', - 'America/Argentina/San_Luis', - 'America/Argentina/Rio_Gallegos', - 'America/Argentina/Ushuaia', - 'Pacific/Pago_Pago', - 'Europe/Vienna', - 'Australia/Lord_Howe', - 'Antarctica/Macquarie', - 'Australia/Hobart', - 'Australia/Currie', - 'Australia/Melbourne', - 'Australia/Sydney', - 'Australia/Broken_Hill', - 'Australia/Brisbane', - 'Australia/Lindeman', - 'Australia/Adelaide', - 'Australia/Darwin', - 'Australia/Perth', - 'Australia/Eucla', - 'Asia/Baku', - 'America/Barbados', - 'Asia/Dhaka', - 'Europe/Brussels', - 'Europe/Sofia', - 'Atlantic/Bermuda', - 'Asia/Brunei', - 'America/La_Paz', - 'America/Noronha', - 'America/Belem', - 'America/Fortaleza', - 'America/Recife', - 'America/Araguaina', - 'America/Maceio', - 'America/Bahia', - 'America/Sao_Paulo', - 'America/Campo_Grande', - 'America/Cuiaba', - 'America/Santarem', - 'America/Porto_Velho', - 'America/Boa_Vista', - 'America/Manaus', - 'America/Eirunepe', - 'America/Rio_Branco', - 'America/Nassau', - 'Asia/Thimphu', - 'Europe/Minsk', - 'America/Belize', - 'America/St_Johns', - 'America/Halifax', - 'America/Glace_Bay', - 'America/Moncton', - 'America/Goose_Bay', - 'America/Blanc-Sablon', - 'America/Toronto', - 'America/Nipigon', - 'America/Thunder_Bay', - 'America/Iqaluit', - 'America/Pangnirtung', - 'America/Atikokan', - 'America/Winnipeg', - 'America/Rainy_River', - 'America/Resolute', - 'America/Rankin_Inlet', - 'America/Regina', - 'America/Swift_Current', - 'America/Edmonton', - 'America/Cambridge_Bay', - 'America/Yellowknife', - 'America/Inuvik', - 'America/Creston', - 'America/Dawson_Creek', - 'America/Fort_Nelson', - 'America/Vancouver', - 'America/Whitehorse', - 'America/Dawson', - 'Indian/Cocos', - 'Europe/Zurich', - 'Africa/Abidjan', - 'Pacific/Rarotonga', - 'America/Santiago', - 'America/Punta_Arenas', - 'Pacific/Easter', - 'Asia/Shanghai', - 'Asia/Urumqi', - 'America/Bogota', - 'America/Costa_Rica', - 'America/Havana', - 'Atlantic/Cape_Verde', - 'America/Curacao', - 'Indian/Christmas', - 'Asia/Nicosia', - 'Asia/Famagusta', - 'Europe/Prague', - 'Europe/Berlin', - 'Europe/Copenhagen', - 'America/Santo_Domingo', - 'Africa/Algiers', - 'America/Guayaquil', - 'Pacific/Galapagos', - 'Europe/Tallinn', - 'Africa/Cairo', - 'Africa/El_Aaiun', - 'Europe/Madrid', - 'Africa/Ceuta', - 'Atlantic/Canary', - 'Europe/Helsinki', - 'Pacific/Fiji', - 'Atlantic/Stanley', - 'Pacific/Chuuk', - 'Pacific/Pohnpei', - 'Pacific/Kosrae', - 'Atlantic/Faroe', - 'Europe/Paris', - 'Europe/London', - 'Asia/Tbilisi', - 'America/Cayenne', - 'Africa/Accra', - 'Europe/Gibraltar', - 'America/Godthab', - 'America/Danmarkshavn', - 'America/Scoresbysund', - 'America/Thule', - 'Europe/Athens', - 'Atlantic/South_Georgia', - 'America/Guatemala', - 'Pacific/Guam', - 'Africa/Bissau', - 'America/Guyana', - 'Asia/Hong_Kong', - 'America/Tegucigalpa', - 'America/Port-au-Prince', - 'Europe/Budapest', - 'Asia/Jakarta', - 'Asia/Pontianak', - 'Asia/Makassar', - 'Asia/Jayapura', - 'Europe/Dublin', - 'Asia/Jerusalem', - 'Asia/Kolkata', - 'Indian/Chagos', - 'Asia/Baghdad', - 'Asia/Tehran', - 'Atlantic/Reykjavik', - 'Europe/Rome', - 'America/Jamaica', - 'Asia/Amman', - 'Asia/Tokyo', - 'Africa/Nairobi', - 'Asia/Bishkek', - 'Pacific/Tarawa', - 'Pacific/Enderbury', - 'Pacific/Kiritimati', - 'Asia/Pyongyang', - 'Asia/Seoul', - 'Asia/Almaty', - 'Asia/Qyzylorda', - 'Asia/Aqtobe', - 'Asia/Aqtau', - 'Asia/Atyrau', - 'Asia/Oral', - 'Asia/Beirut', - 'Asia/Colombo', - 'Africa/Monrovia', - 'Europe/Vilnius', - 'Europe/Luxembourg', - 'Europe/Riga', - 'Africa/Tripoli', - 'Africa/Casablanca', - 'Europe/Monaco', - 'Europe/Chisinau', - 'Pacific/Majuro', - 'Pacific/Kwajalein', - 'Asia/Yangon', - 'Asia/Ulaanbaatar', - 'Asia/Hovd', - 'Asia/Choibalsan', - 'Asia/Macau', - 'America/Martinique', - 'Europe/Malta', - 'Indian/Mauritius', - 'Indian/Maldives', - 'America/Mexico_City', - 'America/Cancun', - 'America/Merida', - 'America/Monterrey', - 'America/Matamoros', - 'America/Mazatlan', - 'America/Chihuahua', - 'America/Ojinaga', - 'America/Hermosillo', - 'America/Tijuana', - 'America/Bahia_Banderas', - 'Asia/Kuala_Lumpur', - 'Asia/Kuching', - 'Africa/Maputo', - 'Africa/Windhoek', - 'Pacific/Noumea', - 'Pacific/Norfolk', - 'Africa/Lagos', - 'America/Managua', - 'Europe/Amsterdam', - 'Europe/Oslo', - 'Asia/Kathmandu', - 'Pacific/Nauru', - 'Pacific/Niue', - 'Pacific/Auckland', - 'Pacific/Chatham', - 'America/Panama', - 'America/Lima', - 'Pacific/Tahiti', - 'Pacific/Marquesas', - 'Pacific/Gambier', - 'Pacific/Port_Moresby', - 'Pacific/Bougainville', - 'Asia/Manila', - 'Asia/Karachi', - 'Europe/Warsaw', - 'America/Miquelon', - 'Pacific/Pitcairn', - 'America/Puerto_Rico', - 'Asia/Gaza', - 'Asia/Hebron', - 'Europe/Lisbon', - 'Atlantic/Madeira', - 'Atlantic/Azores', - 'Pacific/Palau', - 'America/Asuncion', - 'Asia/Qatar', - 'Indian/Reunion', - 'Europe/Bucharest', - 'Europe/Belgrade', - 'Europe/Kaliningrad', - 'Europe/Moscow', - 'Europe/Simferopol', - 'Europe/Kirov', - 'Europe/Astrakhan', - 'Europe/Volgograd', - 'Europe/Saratov', - 'Europe/Ulyanovsk', - 'Europe/Samara', - 'Asia/Yekaterinburg', - 'Asia/Omsk', - 'Asia/Novosibirsk', - 'Asia/Barnaul', - 'Asia/Tomsk', - 'Asia/Novokuznetsk', - 'Asia/Krasnoyarsk', - 'Asia/Irkutsk', - 'Asia/Chita', - 'Asia/Yakutsk', - 'Asia/Khandyga', - 'Asia/Vladivostok', - 'Asia/Ust-Nera', - 'Asia/Magadan', - 'Asia/Sakhalin', - 'Asia/Srednekolymsk', - 'Asia/Kamchatka', - 'Asia/Anadyr', - 'Asia/Riyadh', - 'Pacific/Guadalcanal', - 'Indian/Mahe', - 'Africa/Khartoum', - 'Europe/Stockholm', - 'Asia/Singapore', - 'America/Paramaribo', - 'Africa/Juba', - 'Africa/Sao_Tome', - 'America/El_Salvador', - 'Asia/Damascus', - 'America/Grand_Turk', - 'Africa/Ndjamena', - 'Indian/Kerguelen', - 'Asia/Bangkok', - 'Asia/Dushanbe', - 'Pacific/Fakaofo', - 'Asia/Dili', - 'Asia/Ashgabat', - 'Africa/Tunis', - 'Pacific/Tongatapu', - 'Europe/Istanbul', - 'America/Port_of_Spain', - 'Pacific/Funafuti', - 'Asia/Taipei', - 'Europe/Kiev', - 'Europe/Uzhgorod', - 'Europe/Zaporozhye', - 'Pacific/Wake', - 'America/New_York', - 'America/Detroit', - 'America/Kentucky/Louisville', - 'America/Kentucky/Monticello', - 'America/Indiana/Indianapolis', - 'America/Indiana/Vincennes', - 'America/Indiana/Winamac', - 'America/Indiana/Marengo', - 'America/Indiana/Petersburg', - 'America/Indiana/Vevay', - 'America/Chicago', - 'America/Indiana/Tell_City', - 'America/Indiana/Knox', - 'America/Menominee', - 'America/North_Dakota/Center', - 'America/North_Dakota/New_Salem', - 'America/North_Dakota/Beulah', - 'America/Denver', - 'America/Boise', - 'America/Phoenix', - 'America/Los_Angeles', - 'America/Anchorage', - 'America/Juneau', - 'America/Sitka', - 'America/Metlakatla', - 'America/Yakutat', - 'America/Nome', - 'America/Adak', - 'Pacific/Honolulu', - 'America/Montevideo', - 'Asia/Samarkand', - 'Asia/Tashkent', - 'America/Caracas', - 'Asia/Ho_Chi_Minh', - 'Pacific/Efate', - 'Pacific/Wallis', - 'Pacific/Apia', - 'Africa/Johannesburg', + "Europe/Andorra", + "Asia/Dubai", + "Asia/Kabul", + "Europe/Tirane", + "Asia/Yerevan", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/Mawson", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "America/Argentina/Buenos_Aires", + "America/Argentina/Cordoba", + "America/Argentina/Salta", + "America/Argentina/Jujuy", + "America/Argentina/Tucuman", + "America/Argentina/Catamarca", + "America/Argentina/La_Rioja", + "America/Argentina/San_Juan", + "America/Argentina/Mendoza", + "America/Argentina/San_Luis", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Ushuaia", + "Pacific/Pago_Pago", + "Europe/Vienna", + "Australia/Lord_Howe", + "Antarctica/Macquarie", + "Australia/Hobart", + "Australia/Currie", + "Australia/Melbourne", + "Australia/Sydney", + "Australia/Broken_Hill", + "Australia/Brisbane", + "Australia/Lindeman", + "Australia/Adelaide", + "Australia/Darwin", + "Australia/Perth", + "Australia/Eucla", + "Asia/Baku", + "America/Barbados", + "Asia/Dhaka", + "Europe/Brussels", + "Europe/Sofia", + "Atlantic/Bermuda", + "Asia/Brunei", + "America/La_Paz", + "America/Noronha", + "America/Belem", + "America/Fortaleza", + "America/Recife", + "America/Araguaina", + "America/Maceio", + "America/Bahia", + "America/Sao_Paulo", + "America/Campo_Grande", + "America/Cuiaba", + "America/Santarem", + "America/Porto_Velho", + "America/Boa_Vista", + "America/Manaus", + "America/Eirunepe", + "America/Rio_Branco", + "America/Nassau", + "Asia/Thimphu", + "Europe/Minsk", + "America/Belize", + "America/St_Johns", + "America/Halifax", + "America/Glace_Bay", + "America/Moncton", + "America/Goose_Bay", + "America/Blanc-Sablon", + "America/Toronto", + "America/Nipigon", + "America/Thunder_Bay", + "America/Iqaluit", + "America/Pangnirtung", + "America/Atikokan", + "America/Winnipeg", + "America/Rainy_River", + "America/Resolute", + "America/Rankin_Inlet", + "America/Regina", + "America/Swift_Current", + "America/Edmonton", + "America/Cambridge_Bay", + "America/Yellowknife", + "America/Inuvik", + "America/Creston", + "America/Dawson_Creek", + "America/Fort_Nelson", + "America/Vancouver", + "America/Whitehorse", + "America/Dawson", + "Indian/Cocos", + "Europe/Zurich", + "Africa/Abidjan", + "Pacific/Rarotonga", + "America/Santiago", + "America/Punta_Arenas", + "Pacific/Easter", + "Asia/Shanghai", + "Asia/Urumqi", + "America/Bogota", + "America/Costa_Rica", + "America/Havana", + "Atlantic/Cape_Verde", + "America/Curacao", + "Indian/Christmas", + "Asia/Nicosia", + "Asia/Famagusta", + "Europe/Prague", + "Europe/Berlin", + "Europe/Copenhagen", + "America/Santo_Domingo", + "Africa/Algiers", + "America/Guayaquil", + "Pacific/Galapagos", + "Europe/Tallinn", + "Africa/Cairo", + "Africa/El_Aaiun", + "Europe/Madrid", + "Africa/Ceuta", + "Atlantic/Canary", + "Europe/Helsinki", + "Pacific/Fiji", + "Atlantic/Stanley", + "Pacific/Chuuk", + "Pacific/Pohnpei", + "Pacific/Kosrae", + "Atlantic/Faroe", + "Europe/Paris", + "Europe/London", + "Asia/Tbilisi", + "America/Cayenne", + "Africa/Accra", + "Europe/Gibraltar", + "America/Godthab", + "America/Danmarkshavn", + "America/Scoresbysund", + "America/Thule", + "Europe/Athens", + "Atlantic/South_Georgia", + "America/Guatemala", + "Pacific/Guam", + "Africa/Bissau", + "America/Guyana", + "Asia/Hong_Kong", + "America/Tegucigalpa", + "America/Port-au-Prince", + "Europe/Budapest", + "Asia/Jakarta", + "Asia/Pontianak", + "Asia/Makassar", + "Asia/Jayapura", + "Europe/Dublin", + "Asia/Jerusalem", + "Asia/Kolkata", + "Indian/Chagos", + "Asia/Baghdad", + "Asia/Tehran", + "Atlantic/Reykjavik", + "Europe/Rome", + "America/Jamaica", + "Asia/Amman", + "Asia/Tokyo", + "Africa/Nairobi", + "Asia/Bishkek", + "Pacific/Tarawa", + "Pacific/Enderbury", + "Pacific/Kiritimati", + "Asia/Pyongyang", + "Asia/Seoul", + "Asia/Almaty", + "Asia/Qyzylorda", + "Asia/Aqtobe", + "Asia/Aqtau", + "Asia/Atyrau", + "Asia/Oral", + "Asia/Beirut", + "Asia/Colombo", + "Africa/Monrovia", + "Europe/Vilnius", + "Europe/Luxembourg", + "Europe/Riga", + "Africa/Tripoli", + "Africa/Casablanca", + "Europe/Monaco", + "Europe/Chisinau", + "Pacific/Majuro", + "Pacific/Kwajalein", + "Asia/Yangon", + "Asia/Ulaanbaatar", + "Asia/Hovd", + "Asia/Choibalsan", + "Asia/Macau", + "America/Martinique", + "Europe/Malta", + "Indian/Mauritius", + "Indian/Maldives", + "America/Mexico_City", + "America/Cancun", + "America/Merida", + "America/Monterrey", + "America/Matamoros", + "America/Mazatlan", + "America/Chihuahua", + "America/Ojinaga", + "America/Hermosillo", + "America/Tijuana", + "America/Bahia_Banderas", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Africa/Maputo", + "Africa/Windhoek", + "Pacific/Noumea", + "Pacific/Norfolk", + "Africa/Lagos", + "America/Managua", + "Europe/Amsterdam", + "Europe/Oslo", + "Asia/Kathmandu", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Auckland", + "Pacific/Chatham", + "America/Panama", + "America/Lima", + "Pacific/Tahiti", + "Pacific/Marquesas", + "Pacific/Gambier", + "Pacific/Port_Moresby", + "Pacific/Bougainville", + "Asia/Manila", + "Asia/Karachi", + "Europe/Warsaw", + "America/Miquelon", + "Pacific/Pitcairn", + "America/Puerto_Rico", + "Asia/Gaza", + "Asia/Hebron", + "Europe/Lisbon", + "Atlantic/Madeira", + "Atlantic/Azores", + "Pacific/Palau", + "America/Asuncion", + "Asia/Qatar", + "Indian/Reunion", + "Europe/Bucharest", + "Europe/Belgrade", + "Europe/Kaliningrad", + "Europe/Moscow", + "Europe/Simferopol", + "Europe/Kirov", + "Europe/Astrakhan", + "Europe/Volgograd", + "Europe/Saratov", + "Europe/Ulyanovsk", + "Europe/Samara", + "Asia/Yekaterinburg", + "Asia/Omsk", + "Asia/Novosibirsk", + "Asia/Barnaul", + "Asia/Tomsk", + "Asia/Novokuznetsk", + "Asia/Krasnoyarsk", + "Asia/Irkutsk", + "Asia/Chita", + "Asia/Yakutsk", + "Asia/Khandyga", + "Asia/Vladivostok", + "Asia/Ust-Nera", + "Asia/Magadan", + "Asia/Sakhalin", + "Asia/Srednekolymsk", + "Asia/Kamchatka", + "Asia/Anadyr", + "Asia/Riyadh", + "Pacific/Guadalcanal", + "Indian/Mahe", + "Africa/Khartoum", + "Europe/Stockholm", + "Asia/Singapore", + "America/Paramaribo", + "Africa/Juba", + "Africa/Sao_Tome", + "America/El_Salvador", + "Asia/Damascus", + "America/Grand_Turk", + "Africa/Ndjamena", + "Indian/Kerguelen", + "Asia/Bangkok", + "Asia/Dushanbe", + "Pacific/Fakaofo", + "Asia/Dili", + "Asia/Ashgabat", + "Africa/Tunis", + "Pacific/Tongatapu", + "Europe/Istanbul", + "America/Port_of_Spain", + "Pacific/Funafuti", + "Asia/Taipei", + "Europe/Kiev", + "Europe/Uzhgorod", + "Europe/Zaporozhye", + "Pacific/Wake", + "America/New_York", + "America/Detroit", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Indiana/Indianapolis", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Vevay", + "America/Chicago", + "America/Indiana/Tell_City", + "America/Indiana/Knox", + "America/Menominee", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/North_Dakota/Beulah", + "America/Denver", + "America/Boise", + "America/Phoenix", + "America/Los_Angeles", + "America/Anchorage", + "America/Juneau", + "America/Sitka", + "America/Metlakatla", + "America/Yakutat", + "America/Nome", + "America/Adak", + "Pacific/Honolulu", + "America/Montevideo", + "Asia/Samarkand", + "Asia/Tashkent", + "America/Caracas", + "Asia/Ho_Chi_Minh", + "Pacific/Efate", + "Pacific/Wallis", + "Pacific/Apia", + "Africa/Johannesburg", ]; - export function timezoneList() { let result = []; @@ -404,12 +404,14 @@ export function timezoneList() { result.sort((a, b) => { if (a.time > b.time) { return 1; - } else if (b.time > a.time) { - return -1; - } else { - return 0; } + + if (b.time > a.time) { + return -1; + } + + return 0; }) return result; -}; +}
More info about webhooks on: https://api.slack.com/messaging/webhooks
+ *Required +
More info on: https://pushover.net/api -
+
Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour. -
If you want to send notifications to different devices, fill out Device field. -
Example: twilio://AccountSid:AuthToken@FromPhoneNo
@@ -293,40 +318,46 @@
Status: - Apprise is installed - Apprise is not installed. Read more + Apprise is installed + Apprise is not installed. Read more
(24-hour)
(30-day)
- {{ monitor.url }} + {{ monitor.url }} TCP Ping {{ monitor.hostname }}:{{ monitor.port }} Ping: {{ monitor.hostname }} - + Keyword: {{ monitor.keyword }}
()
Not available, please setup.
+ Not available, please setup. +
Please assign a notification to monitor(s) to get it to work.
+ Please assign a notification to monitor(s) to get it to work. +
Create your admin account
+ Create your admin account +