upload logo and expose ./data/upload to url

pull/124/head
LouisLam 3 years ago
parent 61c737c53c
commit 15c00d9158

@ -5,10 +5,23 @@ const { debug, sleep } = require("../src/util");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const knex = require("knex"); const knex = require("knex");
/**
* Database & App Data Folder
*/
class Database { class Database {
static templatePath = "./db/kuma.db"; static templatePath = "./db/kuma.db";
/**
* Data Dir (Default: ./data)
*/
static dataDir; static dataDir;
/**
* User Upload Dir (Default: ./data/upload)
*/
static uploadDir;
static path; static path;
/** /**
@ -52,6 +65,13 @@ class Database {
if (! fs.existsSync(Database.dataDir)) { if (! fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true }); fs.mkdirSync(Database.dataDir, { recursive: true });
} }
Database.uploadDir = Database.dataDir + "upload/";
if (! fs.existsSync(Database.uploadDir)) {
fs.mkdirSync(Database.uploadDir, { recursive: true });
}
console.log(`Data Dir: ${Database.dataDir}`); console.log(`Data Dir: ${Database.dataDir}`);
} }

@ -0,0 +1,57 @@
/*
From https://github.com/DiegoZoracKy/image-data-uri/blob/master/lib/image-data-uri.js
Modified with 0 dependencies
*/
let fs = require("fs");
let ImageDataURI = (() => {
function decode(dataURI) {
if (!/data:image\//.test(dataURI)) {
console.log("ImageDataURI :: Error :: It seems that it is not an Image Data URI. Couldn't match \"data:image/\"");
return null;
}
let regExMatches = dataURI.match("data:(image/.*);base64,(.*)");
return {
imageType: regExMatches[1],
dataBase64: regExMatches[2],
dataBuffer: new Buffer(regExMatches[2], "base64")
};
}
function encode(data, mediaType) {
if (!data || !mediaType) {
console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType ");
return null;
}
mediaType = (/\//.test(mediaType)) ? mediaType : "image/" + mediaType;
let dataBase64 = (Buffer.isBuffer(data)) ? data.toString("base64") : new Buffer(data).toString("base64");
let dataImgBase64 = "data:" + mediaType + ";base64," + dataBase64;
return dataImgBase64;
}
function outputFile(dataURI, filePath) {
filePath = filePath || "./";
return new Promise((resolve, reject) => {
let imageDecoded = decode(dataURI);
fs.writeFile(filePath, imageDecoded.dataBuffer, err => {
if (err) {
return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4));
}
resolve(filePath);
});
});
}
return {
decode: decode,
encode: encode,
outputFile: outputFile,
};
})();
module.exports = ImageDataURI;

@ -8,12 +8,12 @@ console.log("Node Env: " + process.env.NODE_ENV);
const { sleep, debug, TimeLogger, getRandomInt } = require("../src/util"); const { sleep, debug, TimeLogger, getRandomInt } = require("../src/util");
console.log("Importing Node libraries") console.log("Importing Node libraries");
const fs = require("fs"); const fs = require("fs");
const http = require("http"); const http = require("http");
const https = require("https"); const https = require("https");
console.log("Importing 3rd-party libraries") console.log("Importing 3rd-party libraries");
debug("Importing express"); debug("Importing express");
const express = require("express"); const express = require("express");
debug("Importing socket.io"); debug("Importing socket.io");
@ -70,7 +70,7 @@ if (! fs.existsSync(Database.dataDir)) {
} }
console.log(`Data Dir: ${Database.dataDir}`); console.log(`Data Dir: ${Database.dataDir}`);
console.log("Creating express and socket.io instance") console.log("Creating express and socket.io instance");
const app = express(); const app = express();
let server; let server;
@ -157,6 +157,9 @@ exports.entryPage = "dashboard";
app.use("/", express.static("dist")); app.use("/", express.static("dist"));
// ./data/upload
app.use("/upload", express.static(Database.uploadDir));
app.get("/.well-known/change-password", async (_, response) => { app.get("/.well-known/change-password", async (_, response) => {
response.redirect("https://github.com/louislam/uptime-kuma/wiki/Reset-Password-via-CLI"); response.redirect("https://github.com/louislam/uptime-kuma/wiki/Reset-Password-via-CLI");
}); });
@ -167,22 +170,26 @@ exports.entryPage = "dashboard";
// Universal Route Handler, must be at the end of all express route. // Universal Route Handler, must be at the end of all express route.
app.get("*", async (_request, response) => { app.get("*", async (_request, response) => {
response.send(indexHTML); if (_request.originalUrl.startsWith("/upload/")) {
response.status(404).send("File not found.");
} else {
response.send(indexHTML);
}
}); });
console.log("Adding socket handler") console.log("Adding socket handler");
io.on("connection", async (socket) => { io.on("connection", async (socket) => {
socket.emit("info", { socket.emit("info", {
version: checkVersion.version, version: checkVersion.version,
latestVersion: checkVersion.latestVersion, latestVersion: checkVersion.latestVersion,
}) });
totalClient++; totalClient++;
if (needSetup) { if (needSetup) {
console.log("Redirect to setup page") console.log("Redirect to setup page");
socket.emit("setup") socket.emit("setup");
} }
socket.on("disconnect", () => { socket.on("disconnect", () => {
@ -198,44 +205,44 @@ exports.entryPage = "dashboard";
try { try {
let decoded = jwt.verify(token, jwtSecret); let decoded = jwt.verify(token, jwtSecret);
console.log("Username from JWT: " + decoded.username) console.log("Username from JWT: " + decoded.username);
let user = await R.findOne("user", " username = ? AND active = 1 ", [ let user = await R.findOne("user", " username = ? AND active = 1 ", [
decoded.username, decoded.username,
]) ]);
if (user) { if (user) {
debug("afterLogin") debug("afterLogin");
afterLogin(socket, user) afterLogin(socket, user);
debug("afterLogin ok") debug("afterLogin ok");
callback({ callback({
ok: true, ok: true,
}) });
} else { } else {
callback({ callback({
ok: false, ok: false,
msg: "The user is inactive or deleted.", msg: "The user is inactive or deleted.",
}) });
} }
} catch (error) { } catch (error) {
callback({ callback({
ok: false, ok: false,
msg: "Invalid token.", msg: "Invalid token.",
}) });
} }
}); });
socket.on("login", async (data, callback) => { socket.on("login", async (data, callback) => {
console.log("Login") console.log("Login");
let user = await login(data.username, data.password) let user = await login(data.username, data.password);
if (user) { if (user) {
afterLogin(socket, user) afterLogin(socket, user);
if (user.twofaStatus == 0) { if (user.twofaStatus == 0) {
callback({ callback({
@ -243,13 +250,13 @@ exports.entryPage = "dashboard";
token: jwt.sign({ token: jwt.sign({
username: data.username, username: data.username,
}, jwtSecret), }, jwtSecret),
}) });
} }
if (user.twofaStatus == 1 && !data.token) { if (user.twofaStatus == 1 && !data.token) {
callback({ callback({
tokenRequired: true, tokenRequired: true,
}) });
} }
if (data.token) { if (data.token) {
@ -261,39 +268,39 @@ exports.entryPage = "dashboard";
token: jwt.sign({ token: jwt.sign({
username: data.username, username: data.username,
}, jwtSecret), }, jwtSecret),
}) });
} else { } else {
callback({ callback({
ok: false, ok: false,
msg: "Invalid Token!", msg: "Invalid Token!",
}) });
} }
} }
} else { } else {
callback({ callback({
ok: false, ok: false,
msg: "Incorrect username or password.", msg: "Incorrect username or password.",
}) });
} }
}); });
socket.on("logout", async (callback) => { socket.on("logout", async (callback) => {
socket.leave(socket.userID) socket.leave(socket.userID);
socket.userID = null; socket.userID = null;
callback(); callback();
}); });
socket.on("prepare2FA", async (callback) => { socket.on("prepare2FA", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let user = await R.findOne("user", " id = ? AND active = 1 ", [ let user = await R.findOne("user", " id = ? AND active = 1 ", [
socket.userID, socket.userID,
]) ]);
if (user.twofa_status == 0) { if (user.twofa_status == 0) {
let newSecret = await genSecret() let newSecret = await genSecret();
let encodedSecret = base32.encode(newSecret); let encodedSecret = base32.encode(newSecret);
let uri = `otpauth://totp/Uptime%20Kuma:${user.username}?secret=${encodedSecret}`; let uri = `otpauth://totp/Uptime%20Kuma:${user.username}?secret=${encodedSecret}`;
@ -305,24 +312,24 @@ exports.entryPage = "dashboard";
callback({ callback({
ok: true, ok: true,
uri: uri, uri: uri,
}) });
} else { } else {
callback({ callback({
ok: false, ok: false,
msg: "2FA is already enabled.", msg: "2FA is already enabled.",
}) });
} }
} catch (error) { } catch (error) {
callback({ callback({
ok: false, ok: false,
msg: "Error while trying to prepare 2FA.", msg: "Error while trying to prepare 2FA.",
}) });
} }
}); });
socket.on("save2FA", async (callback) => { socket.on("save2FA", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("UPDATE `user` SET twofa_status = 1 WHERE id = ? ", [ await R.exec("UPDATE `user` SET twofa_status = 1 WHERE id = ? ", [
socket.userID, socket.userID,
@ -331,18 +338,18 @@ exports.entryPage = "dashboard";
callback({ callback({
ok: true, ok: true,
msg: "2FA Enabled.", msg: "2FA Enabled.",
}) });
} catch (error) { } catch (error) {
callback({ callback({
ok: false, ok: false,
msg: "Error while trying to change 2FA.", msg: "Error while trying to change 2FA.",
}) });
} }
}); });
socket.on("disable2FA", async (callback) => { socket.on("disable2FA", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [ await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
socket.userID, socket.userID,
@ -351,19 +358,19 @@ exports.entryPage = "dashboard";
callback({ callback({
ok: true, ok: true,
msg: "2FA Disabled.", msg: "2FA Disabled.",
}) });
} catch (error) { } catch (error) {
callback({ callback({
ok: false, ok: false,
msg: "Error while trying to change 2FA.", msg: "Error while trying to change 2FA.",
}) });
} }
}); });
socket.on("verifyToken", async (token, callback) => { socket.on("verifyToken", async (token, callback) => {
let user = await R.findOne("user", " id = ? AND active = 1 ", [ let user = await R.findOne("user", " id = ? AND active = 1 ", [
socket.userID, socket.userID,
]) ]);
let verify = notp.totp.verify(token, user.twofa_secret); let verify = notp.totp.verify(token, user.twofa_secret);
@ -371,40 +378,40 @@ exports.entryPage = "dashboard";
callback({ callback({
ok: true, ok: true,
valid: true, valid: true,
}) });
} else { } else {
callback({ callback({
ok: false, ok: false,
msg: "Invalid Token.", msg: "Invalid Token.",
valid: false, valid: false,
}) });
} }
}); });
socket.on("twoFAStatus", async (callback) => { socket.on("twoFAStatus", async (callback) => {
checkLogin(socket) checkLogin(socket);
try { try {
let user = await R.findOne("user", " id = ? AND active = 1 ", [ let user = await R.findOne("user", " id = ? AND active = 1 ", [
socket.userID, socket.userID,
]) ]);
if (user.twofa_status == 1) { if (user.twofa_status == 1) {
callback({ callback({
ok: true, ok: true,
status: true, status: true,
}) });
} else { } else {
callback({ callback({
ok: true, ok: true,
status: false, status: false,
}) });
} }
} catch (error) { } catch (error) {
callback({ callback({
ok: false, ok: false,
msg: "Error while trying to get 2FA status.", msg: "Error while trying to get 2FA status.",
}) });
} }
}); });
@ -415,13 +422,13 @@ exports.entryPage = "dashboard";
socket.on("setup", async (username, password, callback) => { socket.on("setup", async (username, password, callback) => {
try { try {
if ((await R.count("user")) !== 0) { if ((await R.count("user")) !== 0) {
throw new Error("Uptime Kuma has been setup. If you want to setup again, please delete the database.") throw new Error("Uptime Kuma has been setup. If you want to setup again, please delete the database.");
} }
let user = R.dispense("user") let user = R.dispense("user");
user.username = username; user.username = username;
user.password = passwordHash.generate(password) user.password = passwordHash.generate(password);
await R.store(user) await R.store(user);
needSetup = false; needSetup = false;
@ -445,8 +452,8 @@ exports.entryPage = "dashboard";
// Add a new monitor // Add a new monitor
socket.on("add", async (monitor, callback) => { socket.on("add", async (monitor, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let bean = R.dispense("monitor") let bean = R.dispense("monitor");
let notificationIDList = monitor.notificationIDList; let notificationIDList = monitor.notificationIDList;
delete monitor.notificationIDList; delete monitor.notificationIDList;
@ -454,11 +461,11 @@ exports.entryPage = "dashboard";
monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes); monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
delete monitor.accepted_statuscodes; delete monitor.accepted_statuscodes;
bean.import(monitor) bean.import(monitor);
bean.user_id = socket.userID bean.user_id = socket.userID;
await R.store(bean) await R.store(bean);
await updateMonitorNotification(bean.id, notificationIDList) await updateMonitorNotification(bean.id, notificationIDList);
await startMonitor(socket.userID, bean.id); await startMonitor(socket.userID, bean.id);
await sendMonitorList(socket); await sendMonitorList(socket);
@ -480,18 +487,18 @@ exports.entryPage = "dashboard";
// Edit a monitor // Edit a monitor
socket.on("editMonitor", async (monitor, callback) => { socket.on("editMonitor", async (monitor, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let bean = await R.findOne("monitor", " id = ? ", [ monitor.id ]) let bean = await R.findOne("monitor", " id = ? ", [ monitor.id ]);
if (bean.user_id !== socket.userID) { if (bean.user_id !== socket.userID) {
throw new Error("Permission denied.") throw new Error("Permission denied.");
} }
bean.name = monitor.name bean.name = monitor.name;
bean.type = monitor.type bean.type = monitor.type;
bean.url = monitor.url bean.url = monitor.url;
bean.interval = monitor.interval bean.interval = monitor.interval;
bean.retryInterval = monitor.retryInterval; bean.retryInterval = monitor.retryInterval;
bean.hostname = monitor.hostname; bean.hostname = monitor.hostname;
bean.maxretries = monitor.maxretries; bean.maxretries = monitor.maxretries;
@ -504,12 +511,12 @@ exports.entryPage = "dashboard";
bean.dns_resolve_type = monitor.dns_resolve_type; bean.dns_resolve_type = monitor.dns_resolve_type;
bean.dns_resolve_server = monitor.dns_resolve_server; bean.dns_resolve_server = monitor.dns_resolve_server;
await R.store(bean) await R.store(bean);
await updateMonitorNotification(bean.id, monitor.notificationIDList) await updateMonitorNotification(bean.id, monitor.notificationIDList);
if (bean.active) { if (bean.active) {
await restartMonitor(socket.userID, bean.id) await restartMonitor(socket.userID, bean.id);
} }
await sendMonitorList(socket); await sendMonitorList(socket);
@ -521,7 +528,7 @@ exports.entryPage = "dashboard";
}); });
} catch (e) { } catch (e) {
console.error(e) console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,
@ -531,13 +538,13 @@ exports.entryPage = "dashboard";
socket.on("getMonitorList", async (callback) => { socket.on("getMonitorList", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await sendMonitorList(socket); await sendMonitorList(socket);
callback({ callback({
ok: true, ok: true,
}); });
} catch (e) { } catch (e) {
console.error(e) console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,
@ -547,14 +554,14 @@ exports.entryPage = "dashboard";
socket.on("getMonitor", async (monitorID, callback) => { socket.on("getMonitor", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
console.log(`Get Monitor: ${monitorID} User ID: ${socket.userID}`) console.log(`Get Monitor: ${monitorID} User ID: ${socket.userID}`);
let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [ let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [
monitorID, monitorID,
socket.userID, socket.userID,
]) ]);
callback({ callback({
ok: true, ok: true,
@ -572,7 +579,7 @@ exports.entryPage = "dashboard";
// Start or Resume the monitor // Start or Resume the monitor
socket.on("resumeMonitor", async (monitorID, callback) => { socket.on("resumeMonitor", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await startMonitor(socket.userID, monitorID); await startMonitor(socket.userID, monitorID);
await sendMonitorList(socket); await sendMonitorList(socket);
@ -591,8 +598,8 @@ exports.entryPage = "dashboard";
socket.on("pauseMonitor", async (monitorID, callback) => { socket.on("pauseMonitor", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await pauseMonitor(socket.userID, monitorID) await pauseMonitor(socket.userID, monitorID);
await sendMonitorList(socket); await sendMonitorList(socket);
callback({ callback({
@ -610,13 +617,13 @@ exports.entryPage = "dashboard";
socket.on("deleteMonitor", async (monitorID, callback) => { socket.on("deleteMonitor", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`) console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
if (monitorID in monitorList) { if (monitorID in monitorList) {
monitorList[monitorID].stop(); monitorList[monitorID].stop();
delete monitorList[monitorID] delete monitorList[monitorID];
} }
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [ await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
@ -641,9 +648,9 @@ exports.entryPage = "dashboard";
socket.on("getTags", async (callback) => { socket.on("getTags", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
const list = await R.findAll("tag") const list = await R.findAll("tag");
callback({ callback({
ok: true, ok: true,
@ -660,12 +667,12 @@ exports.entryPage = "dashboard";
socket.on("addTag", async (tag, callback) => { socket.on("addTag", async (tag, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let bean = R.dispense("tag") let bean = R.dispense("tag");
bean.name = tag.name bean.name = tag.name;
bean.color = tag.color bean.color = tag.color;
await R.store(bean) await R.store(bean);
callback({ callback({
ok: true, ok: true,
@ -682,12 +689,12 @@ exports.entryPage = "dashboard";
socket.on("editTag", async (tag, callback) => { socket.on("editTag", async (tag, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let bean = await R.findOne("monitor", " id = ? ", [ tag.id ]) let bean = await R.findOne("monitor", " id = ? ", [ tag.id ]);
bean.name = tag.name bean.name = tag.name;
bean.color = tag.color bean.color = tag.color;
await R.store(bean) await R.store(bean);
callback({ callback({
ok: true, ok: true,
@ -704,9 +711,9 @@ exports.entryPage = "dashboard";
socket.on("deleteTag", async (tagID, callback) => { socket.on("deleteTag", async (tagID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("DELETE FROM tag WHERE id = ? ", [ tagID ]) await R.exec("DELETE FROM tag WHERE id = ? ", [ tagID ]);
callback({ callback({
ok: true, ok: true,
@ -723,13 +730,13 @@ exports.entryPage = "dashboard";
socket.on("addMonitorTag", async (tagID, monitorID, value, callback) => { socket.on("addMonitorTag", async (tagID, monitorID, value, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("INSERT INTO monitor_tag (tag_id, monitor_id, value) VALUES (?, ?, ?)", [ await R.exec("INSERT INTO monitor_tag (tag_id, monitor_id, value) VALUES (?, ?, ?)", [
tagID, tagID,
monitorID, monitorID,
value, value,
]) ]);
callback({ callback({
ok: true, ok: true,
@ -746,13 +753,13 @@ exports.entryPage = "dashboard";
socket.on("editMonitorTag", async (tagID, monitorID, value, callback) => { socket.on("editMonitorTag", async (tagID, monitorID, value, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("UPDATE monitor_tag SET value = ? WHERE tag_id = ? AND monitor_id = ?", [ await R.exec("UPDATE monitor_tag SET value = ? WHERE tag_id = ? AND monitor_id = ?", [
value, value,
tagID, tagID,
monitorID, monitorID,
]) ]);
callback({ callback({
ok: true, ok: true,
@ -769,13 +776,13 @@ exports.entryPage = "dashboard";
socket.on("deleteMonitorTag", async (tagID, monitorID, value, callback) => { socket.on("deleteMonitorTag", async (tagID, monitorID, value, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await R.exec("DELETE FROM monitor_tag WHERE tag_id = ? AND monitor_id = ? AND value = ?", [ await R.exec("DELETE FROM monitor_tag WHERE tag_id = ? AND monitor_id = ? AND value = ?", [
tagID, tagID,
monitorID, monitorID,
value, value,
]) ]);
// Cleanup unused Tags // Cleanup unused Tags
await R.exec("delete from tag where ( select count(*) from monitor_tag mt where tag.id = mt.tag_id ) = 0"); await R.exec("delete from tag where ( select count(*) from monitor_tag mt where tag.id = mt.tag_id ) = 0");
@ -795,15 +802,15 @@ exports.entryPage = "dashboard";
socket.on("changePassword", async (password, callback) => { socket.on("changePassword", async (password, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
if (! password.currentPassword) { if (! password.currentPassword) {
throw new Error("Invalid new password") throw new Error("Invalid new password");
} }
let user = await R.findOne("user", " id = ? AND active = 1 ", [ let user = await R.findOne("user", " id = ? AND active = 1 ", [
socket.userID, socket.userID,
]) ]);
if (user && passwordHash.verify(password.currentPassword, user.password)) { if (user && passwordHash.verify(password.currentPassword, user.password)) {
@ -812,9 +819,9 @@ exports.entryPage = "dashboard";
callback({ callback({
ok: true, ok: true,
msg: "Password has been updated successfully.", msg: "Password has been updated successfully.",
}) });
} else { } else {
throw new Error("Incorrect current password") throw new Error("Incorrect current password");
} }
} catch (e) { } catch (e) {
@ -827,7 +834,7 @@ exports.entryPage = "dashboard";
socket.on("getSettings", async (callback) => { socket.on("getSettings", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
callback({ callback({
ok: true, ok: true,
@ -844,7 +851,7 @@ exports.entryPage = "dashboard";
socket.on("setSettings", async (data, callback) => { socket.on("setSettings", async (data, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await setSettings("general", data); await setSettings("general", data);
exports.entryPage = data.entryPage; exports.entryPage = data.entryPage;
@ -865,10 +872,10 @@ exports.entryPage = "dashboard";
// Add or Edit // Add or Edit
socket.on("addNotification", async (notification, notificationID, callback) => { socket.on("addNotification", async (notification, notificationID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let notificationBean = await Notification.save(notification, notificationID, socket.userID) let notificationBean = await Notification.save(notification, notificationID, socket.userID);
await sendNotificationList(socket) await sendNotificationList(socket);
callback({ callback({
ok: true, ok: true,
@ -886,10 +893,10 @@ exports.entryPage = "dashboard";
socket.on("deleteNotification", async (notificationID, callback) => { socket.on("deleteNotification", async (notificationID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
await Notification.delete(notificationID, socket.userID) await Notification.delete(notificationID, socket.userID);
await sendNotificationList(socket) await sendNotificationList(socket);
callback({ callback({
ok: true, ok: true,
@ -906,9 +913,9 @@ exports.entryPage = "dashboard";
socket.on("testNotification", async (notification, callback) => { socket.on("testNotification", async (notification, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let msg = await Notification.send(notification, notification.name + " Testing") let msg = await Notification.send(notification, notification.name + " Testing");
callback({ callback({
ok: true, ok: true,
@ -916,7 +923,7 @@ exports.entryPage = "dashboard";
}); });
} catch (e) { } catch (e) {
console.error(e) console.error(e);
callback({ callback({
ok: false, ok: false,
@ -927,7 +934,7 @@ exports.entryPage = "dashboard";
socket.on("checkApprise", async (callback) => { socket.on("checkApprise", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
callback(Notification.checkApprise()); callback(Notification.checkApprise());
} catch (e) { } catch (e) {
callback(false); callback(false);
@ -936,19 +943,19 @@ exports.entryPage = "dashboard";
socket.on("uploadBackup", async (uploadedJSON, importHandle, callback) => { socket.on("uploadBackup", async (uploadedJSON, importHandle, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
let backupData = JSON.parse(uploadedJSON); let backupData = JSON.parse(uploadedJSON);
console.log(`Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`) console.log(`Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`);
let notificationListData = backupData.notificationList; let notificationListData = backupData.notificationList;
let monitorListData = backupData.monitorList; let monitorListData = backupData.monitorList;
if (importHandle == "overwrite") { if (importHandle == "overwrite") {
for (let id in monitorList) { for (let id in monitorList) {
let monitor = monitorList[id] let monitor = monitorList[id];
await monitor.stop() await monitor.stop();
} }
await R.exec("DELETE FROM heartbeat"); await R.exec("DELETE FROM heartbeat");
await R.exec("DELETE FROM monitor_notification"); await R.exec("DELETE FROM monitor_notification");
@ -965,7 +972,7 @@ exports.entryPage = "dashboard";
if ((importHandle == "skip" && notificationNameListString.includes(notificationListData[i].name) == false) || importHandle == "keep" || importHandle == "overwrite") { if ((importHandle == "skip" && notificationNameListString.includes(notificationListData[i].name) == false) || importHandle == "keep" || importHandle == "overwrite") {
let notification = JSON.parse(notificationListData[i].config); let notification = JSON.parse(notificationListData[i].config);
await Notification.save(notification, null, socket.userID) await Notification.save(notification, null, socket.userID);
} }
} }
@ -994,9 +1001,9 @@ exports.entryPage = "dashboard";
dns_resolve_type: monitorListData[i].dns_resolve_type, dns_resolve_type: monitorListData[i].dns_resolve_type,
dns_resolve_server: monitorListData[i].dns_resolve_server, dns_resolve_server: monitorListData[i].dns_resolve_server,
notificationIDList: {}, notificationIDList: {},
} };
let bean = R.dispense("monitor") let bean = R.dispense("monitor");
let notificationIDList = monitor.notificationIDList; let notificationIDList = monitor.notificationIDList;
delete monitor.notificationIDList; delete monitor.notificationIDList;
@ -1004,11 +1011,11 @@ exports.entryPage = "dashboard";
monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes); monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
delete monitor.accepted_statuscodes; delete monitor.accepted_statuscodes;
bean.import(monitor) bean.import(monitor);
bean.user_id = socket.userID bean.user_id = socket.userID;
await R.store(bean) await R.store(bean);
await updateMonitorNotification(bean.id, notificationIDList) await updateMonitorNotification(bean.id, notificationIDList);
if (monitorListData[i].active == 1) { if (monitorListData[i].active == 1) {
await startMonitor(socket.userID, bean.id); await startMonitor(socket.userID, bean.id);
@ -1019,7 +1026,7 @@ exports.entryPage = "dashboard";
} }
} }
await sendNotificationList(socket) await sendNotificationList(socket);
await sendMonitorList(socket); await sendMonitorList(socket);
} }
@ -1038,9 +1045,9 @@ exports.entryPage = "dashboard";
socket.on("clearEvents", async (monitorID, callback) => { socket.on("clearEvents", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
console.log(`Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`) console.log(`Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`);
await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", [ await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", [
"", "",
@ -1064,9 +1071,9 @@ exports.entryPage = "dashboard";
socket.on("clearHeartbeats", async (monitorID, callback) => { socket.on("clearHeartbeats", async (monitorID, callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
console.log(`Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`) console.log(`Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`);
await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [ await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [
monitorID monitorID
@ -1088,9 +1095,9 @@ exports.entryPage = "dashboard";
socket.on("clearStatistics", async (callback) => { socket.on("clearStatistics", async (callback) => {
try { try {
checkLogin(socket) checkLogin(socket);
console.log(`Clear Statistics User ID: ${socket.userID}`) console.log(`Clear Statistics User ID: ${socket.userID}`);
await R.exec("DELETE FROM heartbeat"); await R.exec("DELETE FROM heartbeat");
@ -1115,18 +1122,18 @@ exports.entryPage = "dashboard";
// Better do anything after added all socket handlers here // Better do anything after added all socket handlers here
// *************************** // ***************************
debug("check auto login") debug("check auto login");
if (await setting("disableAuth")) { if (await setting("disableAuth")) {
console.log("Disabled Auth: auto login to admin") console.log("Disabled Auth: auto login to admin");
afterLogin(socket, await R.findOne("user")) afterLogin(socket, await R.findOne("user"));
socket.emit("autoLogin") socket.emit("autoLogin");
} else { } else {
debug("need auth") debug("need auth");
} }
}); });
console.log("Init the server") console.log("Init the server");
server.once("error", async (err) => { server.once("error", async (err) => {
console.error("Cannot listen: " + err.message); console.error("Cannot listen: " + err.message);
@ -1148,14 +1155,14 @@ exports.entryPage = "dashboard";
async function updateMonitorNotification(monitorID, notificationIDList) { async function updateMonitorNotification(monitorID, notificationIDList) {
await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [
monitorID, monitorID,
]) ]);
for (let notificationID in notificationIDList) { for (let notificationID in notificationIDList) {
if (notificationIDList[notificationID]) { if (notificationIDList[notificationID]) {
let relation = R.dispense("monitor_notification"); let relation = R.dispense("monitor_notification");
relation.monitor_id = monitorID; relation.monitor_id = monitorID;
relation.notification_id = notificationID; relation.notification_id = notificationID;
await R.store(relation) await R.store(relation);
} }
} }
} }
@ -1164,7 +1171,7 @@ async function checkOwner(userID, monitorID) {
let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [ let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [
monitorID, monitorID,
userID, userID,
]) ]);
if (! row) { if (! row) {
throw new Error("You do not own this monitor."); throw new Error("You do not own this monitor.");
@ -1173,16 +1180,16 @@ async function checkOwner(userID, monitorID) {
async function sendMonitorList(socket) { async function sendMonitorList(socket) {
let list = await getMonitorJSONList(socket.userID); let list = await getMonitorJSONList(socket.userID);
io.to(socket.userID).emit("monitorList", list) io.to(socket.userID).emit("monitorList", list);
return list; return list;
} }
async function afterLogin(socket, user) { async function afterLogin(socket, user) {
socket.userID = user.id; socket.userID = user.id;
socket.join(user.id) socket.join(user.id);
let monitorList = await sendMonitorList(socket) let monitorList = await sendMonitorList(socket);
sendNotificationList(socket) sendNotificationList(socket);
await sleep(500); await sleep(500);
@ -1195,7 +1202,7 @@ async function afterLogin(socket, user) {
} }
for (let monitorID in monitorList) { for (let monitorID in monitorList) {
await Monitor.sendStats(io, monitorID, user.id) await Monitor.sendStats(io, monitorID, user.id);
} }
} }
@ -1204,7 +1211,7 @@ async function getMonitorJSONList(userID) {
let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [ let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [
userID, userID,
]) ]);
for (let monitor of monitorList) { for (let monitor of monitorList) {
result[monitor.id] = await monitor.toJSON(); result[monitor.id] = await monitor.toJSON();
@ -1215,16 +1222,16 @@ async function getMonitorJSONList(userID) {
async function initDatabase() { async function initDatabase() {
if (! fs.existsSync(Database.path)) { if (! fs.existsSync(Database.path)) {
console.log("Copying Database") console.log("Copying Database");
fs.copyFileSync(Database.templatePath, Database.path); fs.copyFileSync(Database.templatePath, Database.path);
} }
console.log("Connecting to Database") console.log("Connecting to Database");
await Database.connect(); await Database.connect();
console.log("Connected") console.log("Connected");
// Patch the database // Patch the database
await Database.patch() await Database.patch();
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [ let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [
"jwtSecret", "jwtSecret",
@ -1240,7 +1247,7 @@ async function initDatabase() {
// If there is no record in user table, it is a new Uptime Kuma instance, need to setup // If there is no record in user table, it is a new Uptime Kuma instance, need to setup
if ((await R.count("user")) === 0) { if ((await R.count("user")) === 0) {
console.log("No user, need setup") console.log("No user, need setup");
needSetup = true; needSetup = true;
} }
@ -1248,9 +1255,9 @@ async function initDatabase() {
} }
async function startMonitor(userID, monitorID) { async function startMonitor(userID, monitorID) {
await checkOwner(userID, monitorID) await checkOwner(userID, monitorID);
console.log(`Resume Monitor: ${monitorID} User ID: ${userID}`) console.log(`Resume Monitor: ${monitorID} User ID: ${userID}`);
await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [ await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [
monitorID, monitorID,
@ -1259,24 +1266,24 @@ async function startMonitor(userID, monitorID) {
let monitor = await R.findOne("monitor", " id = ? ", [ let monitor = await R.findOne("monitor", " id = ? ", [
monitorID, monitorID,
]) ]);
if (monitor.id in monitorList) { if (monitor.id in monitorList) {
monitorList[monitor.id].stop(); monitorList[monitor.id].stop();
} }
monitorList[monitor.id] = monitor; monitorList[monitor.id] = monitor;
monitor.start(io) monitor.start(io);
} }
async function restartMonitor(userID, monitorID) { async function restartMonitor(userID, monitorID) {
return await startMonitor(userID, monitorID) return await startMonitor(userID, monitorID);
} }
async function pauseMonitor(userID, monitorID) { async function pauseMonitor(userID, monitorID) {
await checkOwner(userID, monitorID) await checkOwner(userID, monitorID);
console.log(`Pause Monitor: ${monitorID} User ID: ${userID}`) console.log(`Pause Monitor: ${monitorID} User ID: ${userID}`);
await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [ await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [
monitorID, monitorID,
@ -1292,7 +1299,7 @@ async function pauseMonitor(userID, monitorID) {
* Resume active monitors * Resume active monitors
*/ */
async function startMonitors() { async function startMonitors() {
let list = await R.find("monitor", " active = 1 ") let list = await R.find("monitor", " active = 1 ");
for (let monitor of list) { for (let monitor of list) {
monitorList[monitor.id] = monitor; monitorList[monitor.id] = monitor;
@ -1309,10 +1316,10 @@ async function shutdownFunction(signal) {
console.log("Shutdown requested"); console.log("Shutdown requested");
console.log("Called signal: " + signal); console.log("Called signal: " + signal);
console.log("Stopping all monitors") console.log("Stopping all monitors");
for (let id in monitorList) { for (let id in monitorList) {
let monitor = monitorList[id] let monitor = monitorList[id];
monitor.stop() monitor.stop();
} }
await sleep(2000); await sleep(2000);
await Database.close(); await Database.close();

@ -2,6 +2,9 @@ const { R } = require("redbean-node");
const { checkLogin, setSettings } = require("../util-server"); const { checkLogin, setSettings } = require("../util-server");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { debug } = require("../../src/util"); const { debug } = require("../../src/util");
const ImageDataURI = require("../image-data-uri");
const Database = require("../database");
const fs = require("fs");
module.exports.statusPageSocketHandler = (socket) => { module.exports.statusPageSocketHandler = (socket) => {
@ -67,18 +70,35 @@ module.exports.statusPageSocketHandler = (socket) => {
}); });
// Save Status Page // Save Status Page
// imgDataUrl Only Accept PNG!
socket.on("saveStatusPage", async (config, imgDataUrl, publicGroupList, callback) => { socket.on("saveStatusPage", async (config, imgDataUrl, publicGroupList, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
const header = "data:image/png;base64,";
// Check logo format
// If is image data url, convert to png file
// Else assume it is a url, nothing to do
if (imgDataUrl.startsWith("data:")) {
if (! imgDataUrl.startsWith(header)) {
throw new Error("Only allowed PNG logo.");
}
// Convert to file
await ImageDataURI.outputFile(imgDataUrl, Database.uploadDir + "logo.png");
config.logo = "/upload/logo.png?t=" + Date.now();
} else {
config.icon = imgDataUrl;
}
// Save Config // Save Config
await setSettings("statusPage", config); await setSettings("statusPage", config);
await R.transaction(async (trx) => { await R.transaction(async (trx) => {
// Save Icon
// Save Public Group List // Save Public Group List
const groupIDList = []; const groupIDList = [];
let groupOrder = 1; let groupOrder = 1;

@ -26,6 +26,7 @@ import {
faArrowsAltV, faArrowsAltV,
faUnlink, faUnlink,
faQuestionCircle, faQuestionCircle,
faImages, faUpload,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
library.add( library.add(
@ -51,6 +52,8 @@ library.add(
faArrowsAltV, faArrowsAltV,
faUnlink, faUnlink,
faQuestionCircle, faQuestionCircle,
faImages,
faUpload,
); );
export { FontAwesomeIcon }; export { FontAwesomeIcon };

@ -1,7 +1,6 @@
import { io } from "socket.io-client"; import { io } from "socket.io-client";
import { useToast } from "vue-toastification"; import { useToast } from "vue-toastification";
import axios from "axios"; const toast = useToast();
const toast = useToast()
let socket; let socket;
@ -32,7 +31,7 @@ export default {
certInfoList: {}, certInfoList: {},
notificationList: [], notificationList: [],
connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...",
} };
}, },
created() { created() {
@ -74,7 +73,7 @@ export default {
}); });
socket.on("setup", (monitorID, data) => { socket.on("setup", (monitorID, data) => {
this.$router.push("/setup") this.$router.push("/setup");
}); });
socket.on("autoLogin", (monitorID, data) => { socket.on("autoLogin", (monitorID, data) => {
@ -132,7 +131,7 @@ export default {
this.importantHeartbeatList[data.monitorID] = []; this.importantHeartbeatList[data.monitorID] = [];
} }
this.importantHeartbeatList[data.monitorID].unshift(data) this.importantHeartbeatList[data.monitorID].unshift(data);
} }
}); });
@ -140,27 +139,27 @@ export default {
if (! (monitorID in this.heartbeatList) || overwrite) { if (! (monitorID in this.heartbeatList) || overwrite) {
this.heartbeatList[monitorID] = data; this.heartbeatList[monitorID] = data;
} else { } else {
this.heartbeatList[monitorID] = data.concat(this.heartbeatList[monitorID]) this.heartbeatList[monitorID] = data.concat(this.heartbeatList[monitorID]);
} }
}); });
socket.on("avgPing", (monitorID, data) => { socket.on("avgPing", (monitorID, data) => {
this.avgPingList[monitorID] = data this.avgPingList[monitorID] = data;
}); });
socket.on("uptime", (monitorID, type, data) => { socket.on("uptime", (monitorID, type, data) => {
this.uptimeList[`${monitorID}_${type}`] = data this.uptimeList[`${monitorID}_${type}`] = data;
}); });
socket.on("certInfo", (monitorID, data) => { socket.on("certInfo", (monitorID, data) => {
this.certInfoList[monitorID] = JSON.parse(data) this.certInfoList[monitorID] = JSON.parse(data);
}); });
socket.on("importantHeartbeatList", (monitorID, data, overwrite) => { socket.on("importantHeartbeatList", (monitorID, data, overwrite) => {
if (! (monitorID in this.importantHeartbeatList) || overwrite) { if (! (monitorID in this.importantHeartbeatList) || overwrite) {
this.importantHeartbeatList[monitorID] = data; this.importantHeartbeatList[monitorID] = data;
} else { } else {
this.importantHeartbeatList[monitorID] = data.concat(this.importantHeartbeatList[monitorID]) this.importantHeartbeatList[monitorID] = data.concat(this.importantHeartbeatList[monitorID]);
} }
}); });
@ -172,26 +171,26 @@ export default {
}); });
socket.on("disconnect", () => { socket.on("disconnect", () => {
console.log("disconnect") console.log("disconnect");
this.connectionErrorMsg = "Lost connection to the socket server. Reconnecting..."; this.connectionErrorMsg = "Lost connection to the socket server. Reconnecting...";
this.socket.connected = false; this.socket.connected = false;
}); });
socket.on("connect", () => { socket.on("connect", () => {
console.log("connect") console.log("connect");
this.socket.connectCount++; this.socket.connectCount++;
this.socket.connected = true; this.socket.connected = true;
// Reset Heartbeat list if it is re-connect // Reset Heartbeat list if it is re-connect
if (this.socket.connectCount >= 2) { if (this.socket.connectCount >= 2) {
this.clearData() this.clearData();
} }
let token = this.storage().token; let token = this.storage().token;
if (token) { if (token) {
if (token !== "autoLogin") { if (token !== "autoLogin") {
this.loginByToken(token) this.loginByToken(token);
} else { } else {
// Timeout if it is not actually auto login // Timeout if it is not actually auto login
@ -235,7 +234,7 @@ export default {
token, token,
}, (res) => { }, (res) => {
if (res.tokenRequired) { if (res.tokenRequired) {
callback(res) callback(res);
} }
if (res.ok) { if (res.ok) {
@ -244,11 +243,11 @@ export default {
this.loggedIn = true; this.loggedIn = true;
// Trigger Chrome Save Password // Trigger Chrome Save Password
history.pushState({}, "") history.pushState({}, "");
} }
callback(res) callback(res);
}) });
}, },
loginByToken(token) { loginByToken(token) {
@ -256,11 +255,11 @@ export default {
this.allowLoginDialog = true; this.allowLoginDialog = true;
if (! res.ok) { if (! res.ok) {
this.logout() this.logout();
} else { } else {
this.loggedIn = true; this.loggedIn = true;
} }
}) });
}, },
logout() { logout() {
@ -268,68 +267,68 @@ export default {
this.socket.token = null; this.socket.token = null;
this.loggedIn = false; this.loggedIn = false;
this.clearData() this.clearData();
}, },
prepare2FA(callback) { prepare2FA(callback) {
socket.emit("prepare2FA", callback) socket.emit("prepare2FA", callback);
}, },
save2FA(secret, callback) { save2FA(secret, callback) {
socket.emit("save2FA", callback) socket.emit("save2FA", callback);
}, },
disable2FA(callback) { disable2FA(callback) {
socket.emit("disable2FA", callback) socket.emit("disable2FA", callback);
}, },
verifyToken(token, callback) { verifyToken(token, callback) {
socket.emit("verifyToken", token, callback) socket.emit("verifyToken", token, callback);
}, },
twoFAStatus(callback) { twoFAStatus(callback) {
socket.emit("twoFAStatus", callback) socket.emit("twoFAStatus", callback);
}, },
getMonitorList(callback) { getMonitorList(callback) {
socket.emit("getMonitorList", callback) socket.emit("getMonitorList", callback);
}, },
add(monitor, callback) { add(monitor, callback) {
socket.emit("add", monitor, callback) socket.emit("add", monitor, callback);
}, },
deleteMonitor(monitorID, callback) { deleteMonitor(monitorID, callback) {
socket.emit("deleteMonitor", monitorID, callback) socket.emit("deleteMonitor", monitorID, callback);
}, },
clearData() { clearData() {
console.log("reset heartbeat list") console.log("reset heartbeat list");
this.heartbeatList = {} this.heartbeatList = {};
this.importantHeartbeatList = {} this.importantHeartbeatList = {};
}, },
uploadBackup(uploadedJSON, importHandle, callback) { uploadBackup(uploadedJSON, importHandle, callback) {
socket.emit("uploadBackup", uploadedJSON, importHandle, callback) socket.emit("uploadBackup", uploadedJSON, importHandle, callback);
}, },
clearEvents(monitorID, callback) { clearEvents(monitorID, callback) {
socket.emit("clearEvents", monitorID, callback) socket.emit("clearEvents", monitorID, callback);
}, },
clearHeartbeats(monitorID, callback) { clearHeartbeats(monitorID, callback) {
socket.emit("clearHeartbeats", monitorID, callback) socket.emit("clearHeartbeats", monitorID, callback);
}, },
clearStatistics(callback) { clearStatistics(callback) {
socket.emit("clearStatistics", callback) socket.emit("clearStatistics", callback);
}, },
}, },
computed: { computed: {
lastHeartbeatList() { lastHeartbeatList() {
let result = {} let result = {};
for (let monitorID in this.heartbeatList) { for (let monitorID in this.heartbeatList) {
let index = this.heartbeatList[monitorID].length - 1; let index = this.heartbeatList[monitorID].length - 1;
@ -340,15 +339,15 @@ export default {
}, },
statusList() { statusList() {
let result = {} let result = {};
let unknown = { let unknown = {
text: "Unknown", text: "Unknown",
color: "secondary", color: "secondary",
} };
for (let monitorID in this.lastHeartbeatList) { for (let monitorID in this.lastHeartbeatList) {
let lastHeartBeat = this.lastHeartbeatList[monitorID] let lastHeartBeat = this.lastHeartbeatList[monitorID];
if (! lastHeartBeat) { if (! lastHeartBeat) {
result[monitorID] = unknown; result[monitorID] = unknown;
@ -381,12 +380,12 @@ export default {
// Reload the SPA if the server version is changed. // Reload the SPA if the server version is changed.
"info.version"(to, from) { "info.version"(to, from) {
if (from && from !== to) { if (from && from !== to) {
window.location.reload() window.location.reload();
} }
}, },
remember() { remember() {
localStorage.remember = (this.remember) ? "1" : "0" localStorage.remember = (this.remember) ? "1" : "0";
}, },
// Reconnect the socket io, if status-page to dashboard // Reconnect the socket io, if status-page to dashboard
@ -399,4 +398,4 @@ export default {
}, },
} };

@ -3,7 +3,10 @@
<!-- Logo & Title --> <!-- Logo & Title -->
<h1> <h1>
<!-- Logo --> <!-- Logo -->
<img :src="imgDataUrl" alt class="logo me-2" :class="logoClass" @click="showImageCropUploadMethod" /> <div class="logo-wrapper" @click="showImageCropUploadMethod">
<img :src="imgDataUrl" alt class="logo me-2" :class="logoClass" />
<font-awesome-icon v-if="enableEditMode" class="icon-upload" icon="upload" />
</div>
<!-- Uploader --> <!-- Uploader -->
<!-- url="/api/status-page/upload-logo" --> <!-- url="/api/status-page/upload-logo" -->
@ -376,6 +379,16 @@ export default {
axios.get("/api/status-page/config").then((res) => { axios.get("/api/status-page/config").then((res) => {
this.config = res.data; this.config = res.data;
if (this.config.logo) {
this.imgDataUrl = this.config.logo;
// Special handle for dev
const env = process.env.NODE_ENV;
if (env === "development" || localStorage.dev === "dev") {
let baseURL = location.protocol + "//" + location.hostname + ":3001";
this.imgDataUrl = baseURL + this.imgDataUrl;
}
}
}); });
axios.get("/api/status-page/incident").then((res) => { axios.get("/api/status-page/incident").then((res) => {
@ -557,6 +570,30 @@ footer {
min-width: 50px; min-width: 50px;
} }
.logo-wrapper {
display: inline-block;
position: relative;
&:hover {
.icon-upload {
transform: scale(1.2);
}
}
.icon-upload {
transition: all $easing-in 0.2s;
position: absolute;
bottom: 6px;
font-size: 20px;
left: -14px;
background-color: white;
padding: 5px;
border-radius: 10px;
cursor: pointer;
box-shadow: 0 15px 70px rgba(0, 0, 0, 0.9);
}
}
.logo { .logo {
transition: all $easing-in 0.2s; transition: all $easing-in 0.2s;
@ -577,7 +614,7 @@ footer {
} }
.date { .date {
font-size: 14px; font-size: 12px;
} }
} }

Loading…
Cancel
Save