Merge remote-tracking branch 'origin_kuma/master'

# Conflicts:
#	package-lock.json
#	server/database.js
#	server/model/monitor.js
#	server/routers/api-router.js
#	server/server.js
#	src/components/MonitorList.vue
#	src/components/PingChart.vue
#	src/icon.js
#	src/pages/DashboardHome.vue
#	src/pages/StatusPage.vue
#	src/router.js
#	src/util.js
This commit is contained in:
Karel Krýda
2022-04-30 13:40:34 +02:00
196 changed files with 17960 additions and 6036 deletions

View File

@@ -1,16 +1,21 @@
import { io } from "socket.io-client";
import { useToast } from "vue-toastification";
import jwt_decode from "jwt-decode";
import jwtDecode from "jwt-decode";
import Favico from "favico.js";
const toast = useToast();
let socket;
const noSocketIOPages = [
"/status-page",
"/status",
"/"
/^\/status-page$/, // /status-page
/^\/status/, // /status**
/^\/$/ // /
];
const favicon = new Favico({
animation: "none"
});
export default {
data() {
@@ -23,6 +28,7 @@ export default {
connectCount: 0,
initedSocketIO: false,
},
username: null,
remember: (localStorage.remember !== "0"),
allowLoginDialog: false, // Allowed to show login dialog, but "loggedIn" have to be true too. This exists because prevent the login dialog show 0.1s in first before the socket server auth-ed.
loggedIn: false,
@@ -34,7 +40,19 @@ export default {
uptimeList: { },
tlsInfoList: {},
notificationList: [],
statusPageListLoaded: false,
statusPageList: [],
proxyList: [],
connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...",
showReverseProxyGuide: true,
cloudflared: {
cloudflareTunnelToken: "",
installed: null,
running: false,
message: "",
errorMessage: "",
currentPassword: "",
}
};
},
@@ -52,8 +70,12 @@ export default {
}
// No need to connect to the socket.io for status page
if (! bypass && noSocketIOPages.includes(location.pathname)) {
return;
if (! bypass && location.pathname) {
for (let page of noSocketIOPages) {
if (location.pathname.match(page)) {
return;
}
}
}
this.socket.initedSocketIO = true;
@@ -69,7 +91,7 @@ export default {
}
socket = io(wsHost, {
transports: ["websocket"],
transports: [ "websocket" ],
});
socket.on("info", (info) => {
@@ -83,12 +105,13 @@ export default {
socket.on("autoLogin", (monitorID, data) => {
this.loggedIn = true;
this.storage().token = "autoLogin";
this.socket.token = "autoLogin";
this.allowLoginDialog = false;
});
socket.on("monitorList", (data) => {
// Add Helper function
Object.entries(data).forEach(([monitorID, monitor]) => {
Object.entries(data).forEach(([ monitorID, monitor ]) => {
monitor.getUrl = () => {
try {
return new URL(monitor.url);
@@ -108,6 +131,21 @@ export default {
this.notificationList = data;
});
socket.on("statusPageList", (data) => {
this.statusPageListLoaded = true;
this.statusPageList = data;
});
socket.on("proxyList", (data) => {
this.proxyList = data.map(item => {
item.auth = !!item.auth;
item.active = !!item.active;
item.default = !!item.default;
return item;
});
});
socket.on("heartbeat", (data) => {
if (! (data.monitorID in this.heartbeatList)) {
this.heartbeatList[data.monitorID] = [];
@@ -174,6 +212,7 @@ export default {
socket.on("connect_error", (err) => {
console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`);
this.connectionErrorMsg = `Cannot connect to the socket server. [${err}] Reconnecting...`;
this.showReverseProxyGuide = true;
this.socket.connected = false;
this.socket.firstConnect = false;
});
@@ -188,6 +227,7 @@ export default {
console.log("Connected to the socket server");
this.socket.connectCount++;
this.socket.connected = true;
this.showReverseProxyGuide = false;
// Reset Heartbeat list if it is re-connect
if (this.socket.connectCount >= 2) {
@@ -200,7 +240,6 @@ export default {
if (token !== "autoLogin") {
this.loginByToken(token);
} else {
// Timeout if it is not actually auto login
setTimeout(() => {
if (! this.loggedIn) {
@@ -208,7 +247,6 @@ export default {
this.$root.storage().removeItem("token");
}
}, 5000);
}
} else {
this.allowLoginDialog = true;
@@ -217,6 +255,12 @@ export default {
this.socket.firstConnect = false;
});
// cloudflared
socket.on("cloudflared_installed", (res) => this.cloudflared.installed = res);
socket.on("cloudflared_running", (res) => this.cloudflared.running = res);
socket.on("cloudflared_message", (res) => this.cloudflared.message = res);
socket.on("cloudflared_errorMessage", (res) => this.cloudflared.errorMessage = res);
socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
},
storage() {
@@ -227,7 +271,7 @@ export default {
const jwtToken = this.$root.storage().token;
if (jwtToken && jwtToken !== "autoLogin") {
return jwt_decode(jwtToken);
return jwtDecode(jwtToken);
}
return undefined;
},
@@ -244,6 +288,14 @@ export default {
}
},
toastSuccess(msg) {
toast.success(msg);
},
toastError(msg) {
toast.error(msg);
},
login(username, password, token, callback) {
socket.emit("login", {
username,
@@ -258,6 +310,7 @@ export default {
this.storage().token = res.token;
this.socket.token = res.token;
this.loggedIn = true;
this.username = this.getJWTPayload()?.username;
// Trigger Chrome Save Password
history.pushState({}, "");
@@ -275,6 +328,7 @@ export default {
this.logout();
} else {
this.loggedIn = true;
this.username = this.getJWTPayload()?.username;
}
});
},
@@ -284,6 +338,7 @@ export default {
this.storage().removeItem("token");
this.socket.token = null;
this.loggedIn = false;
this.username = null;
this.clearData();
},
@@ -374,6 +429,14 @@ export default {
computed: {
usernameFirstChar() {
if (typeof this.username == "string" && this.username.length >= 1) {
return this.username.charAt(0).toUpperCase();
} else {
return "🐻";
}
},
lastHeartbeatList() {
let result = {};
@@ -426,10 +489,49 @@ export default {
return result;
},
stats() {
let result = {
up: 0,
down: 0,
unknown: 0,
pause: 0,
};
for (let monitorID in this.$root.monitorList) {
let beat = this.$root.lastHeartbeatList[monitorID];
let monitor = this.$root.monitorList[monitorID];
if (monitor && ! monitor.active) {
result.pause++;
} else if (beat) {
if (beat.status === 1) {
result.up++;
} else if (beat.status === 0) {
result.down++;
} else if (beat.status === 2) {
result.up++;
} else {
result.unknown++;
}
} else {
result.unknown++;
}
}
return result;
},
},
watch: {
// Update Badge
"stats.down"(to, from) {
if (to !== from) {
favicon.badge(to);
}
},
// Reload the SPA if the server version is changed.
"info.version"(to, from) {
if (from && from !== to) {
@@ -443,9 +545,15 @@ export default {
// Reconnect the socket io, if status-page to dashboard
"$route.fullPath"(newValue, oldValue) {
if (noSocketIOPages.includes(newValue)) {
return;
if (newValue) {
for (let page of noSocketIOPages) {
if (newValue.match(page)) {
return;
}
}
}
this.initSocketIO();
},

View File

@@ -6,6 +6,7 @@ export default {
userTheme: localStorage.theme,
userHeartbeatBar: localStorage.heartbeatBarTheme,
statusPageTheme: "light",
forceStatusPageTheme: false,
path: "",
};
},
@@ -27,13 +28,17 @@ export default {
computed: {
theme() {
// As entry can be status page now, set forceStatusPageTheme to true to use status page theme
if (this.forceStatusPageTheme) {
return this.statusPageTheme;
}
// Entry no need dark
if (this.path === "") {
return "light";
}
if (this.path === "/status-page" || this.path === "/status") {
if (this.path.startsWith("/status-page") || this.path.startsWith("/status")) {
return this.statusPageTheme;
} else {
if (this.userTheme === "auto") {