mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-17 17:06:56 +08:00
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:
@@ -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();
|
||||
},
|
||||
|
||||
|
@@ -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") {
|
||||
|
Reference in New Issue
Block a user