Compare commits

...

6 Commits

Author SHA1 Message Date
Louis Lam
b782b25e17 Update to 1.14.1 2022-04-19 16:01:08 +08:00
Louis Lam
919393cac9 Partially change the server core into a class, remove all require("./server") #1520 2022-04-19 15:38:59 +08:00
Louis Lam
1ba92d803e Update to 1.14.0 2022-04-12 14:17:13 +08:00
Louis Lam
45ca3085b2 Update CONTRIBUTING.md 2022-04-12 13:53:52 +08:00
Louis Lam
a0d1ae2cce Better alignment of monitor list item 2022-04-11 18:02:18 +08:00
Louis Lam
f030487f7d Fix theme color that do not apply to status page with a custom domain 2022-04-10 13:46:00 +08:00
13 changed files with 130 additions and 74 deletions

View File

@@ -44,6 +44,8 @@ My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/
### Recommended Pull Request Guideline ### Recommended Pull Request Guideline
Before deep into coding, disscussion first is preferred. Creating an empty pull request for disscussion would be recommended.
1. Fork the project 1. Fork the project
1. Clone your fork repo to local 1. Clone your fork repo to local
1. Create a new branch 1. Create a new branch
@@ -53,6 +55,7 @@ My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/
1. Create a pull request: https://github.com/louislam/uptime-kuma/compare 1. Create a pull request: https://github.com/louislam/uptime-kuma/compare
1. Write a proper description 1. Write a proper description
1. Click "Change to draft" 1. Click "Change to draft"
1. Discussion
#### ❌ Won't Merge #### ❌ Won't Merge

View File

@@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.14.0-beta.2", "version": "1.14.1",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -36,7 +36,7 @@
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.13.2 && npm ci --production && npm run download-dist", "setup": "git checkout 1.14.1 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",

View File

@@ -3,7 +3,8 @@
*/ */
const { TimeLogger } = require("../src/util"); const { TimeLogger } = require("../src/util");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { io } = require("./server"); const { UptimeKumaServer } = require("./uptime-kuma-server");
const io = UptimeKumaServer.getInstance().io;
const { setting } = require("./util-server"); const { setting } = require("./util-server");
const checkVersion = require("./check-version"); const checkVersion = require("./check-version");

View File

@@ -3,7 +3,7 @@ const HttpProxyAgent = require("http-proxy-agent");
const HttpsProxyAgent = require("https-proxy-agent"); const HttpsProxyAgent = require("https-proxy-agent");
const SocksProxyAgent = require("socks-proxy-agent"); const SocksProxyAgent = require("socks-proxy-agent");
const { debug } = require("../src/util"); const { debug } = require("../src/util");
const server = require("./server"); const { UptimeKumaServer } = require("./uptime-kuma-server");
class Proxy { class Proxy {
@@ -151,6 +151,8 @@ class Proxy {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async reloadProxy() { static async reloadProxy() {
const server = UptimeKumaServer.getInstance();
let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor"); let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor");
for (let monitorID in server.monitorList) { for (let monitorID in server.monitorList) {

View File

@@ -1,15 +1,16 @@
let express = require("express"); let express = require("express");
const { allowDevAllOrigin, getSettings, setting } = require("../util-server"); const { allowDevAllOrigin, getSettings, setting } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const server = require("../server");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor"); const Monitor = require("../model/monitor");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { UP, flipStatus, debug } = require("../../src/util"); const { UP, flipStatus, debug } = require("../../src/util");
const StatusPage = require("../model/status_page"); const StatusPage = require("../model/status_page");
const { UptimeKumaServer } = require("../uptime-kuma-server");
let router = express.Router(); let router = express.Router();
let cache = apicache.middleware; let cache = apicache.middleware;
const server = UptimeKumaServer.getInstance();
let io = server.io; let io = server.io;
router.get("/api/entry-page", async (request, response) => { router.get("/api/entry-page", async (request, response) => {

View File

@@ -1,3 +1,8 @@
/*
* Uptime Kuma Server
* node "server/server.js"
* DO NOT require("./server") in other modules, it likely creates circular dependency!
*/
console.log("Welcome to Uptime Kuma"); console.log("Welcome to Uptime Kuma");
// Check Node.js Version // Check Node.js Version
@@ -24,14 +29,10 @@ console.log("Node Env: " + process.env.NODE_ENV);
console.log("Importing Node libraries"); console.log("Importing Node libraries");
const fs = require("fs"); const fs = require("fs");
const http = require("http");
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");
const { Server } = require("socket.io");
debug("Importing redbean-node"); debug("Importing redbean-node");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
debug("Importing jsonwebtoken"); debug("Importing jsonwebtoken");
@@ -48,26 +49,10 @@ debug("Importing 2FA Modules");
const notp = require("notp"); const notp = require("notp");
const base32 = require("thirty-two"); const base32 = require("thirty-two");
/** const { UptimeKumaServer } = require("./uptime-kuma-server");
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. const server = UptimeKumaServer.getInstance(args);
* @type {UptimeKumaServer} const io = module.exports.io = server.io;
*/ const app = server.app;
class UptimeKumaServer {
/**
* Main monitor list
* @type {{}}
*/
monitorList = {};
entryPage = "dashboard";
async sendMonitorList(socket) {
let list = await getMonitorJSONList(socket.userID);
io.to(socket.userID).emit("monitorList", list);
return list;
}
}
const server = module.exports = new UptimeKumaServer();
console.log("Importing this project modules"); console.log("Importing this project modules");
debug("Importing Monitor"); debug("Importing Monitor");
@@ -110,10 +95,6 @@ if (hostname) {
} }
const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001); const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001);
// SSL
const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined;
const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined;
const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false; const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false;
const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined; const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined;
@@ -133,25 +114,6 @@ if (config.demoMode) {
console.log("==== Demo Mode ===="); console.log("==== Demo Mode ====");
} }
console.log("Creating express and socket.io instance");
const app = express();
let httpServer;
if (sslKey && sslCert) {
console.log("Server Type: HTTPS");
httpServer = https.createServer({
key: fs.readFileSync(sslKey),
cert: fs.readFileSync(sslCert)
}, app);
} else {
console.log("Server Type: HTTP");
httpServer = http.createServer(app);
}
const io = new Server(httpServer);
module.exports.io = io;
// Must be after io instantiation // Must be after io instantiation
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList } = require("./client"); const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList } = require("./client");
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
@@ -1433,12 +1395,12 @@ try {
console.log("Init the server"); console.log("Init the server");
httpServer.once("error", async (err) => { server.httpServer.once("error", async (err) => {
console.error("Cannot listen: " + err.message); console.error("Cannot listen: " + err.message);
await shutdownFunction(); await shutdownFunction();
}); });
httpServer.listen(port, hostname, () => { server.httpServer.listen(port, hostname, () => {
if (hostname) { if (hostname) {
console.log(`Listening on ${hostname}:${port}`); console.log(`Listening on ${hostname}:${port}`);
} else { } else {
@@ -1510,20 +1472,6 @@ async function afterLogin(socket, user) {
} }
} }
async function getMonitorJSONList(userID) {
let result = {};
let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [
userID,
]);
for (let monitor of monitorList) {
result[monitor.id] = await monitor.toJSON();
}
return result;
}
async function initDatabase(testMode = false) { async function initDatabase(testMode = false) {
if (! fs.existsSync(Database.path)) { if (! fs.existsSync(Database.path)) {
console.log("Copying Database"); console.log("Copying Database");
@@ -1636,7 +1584,7 @@ function finalFunction() {
console.log("Graceful shutdown successful!"); console.log("Graceful shutdown successful!");
} }
gracefulShutdown(httpServer, { gracefulShutdown(server.httpServer, {
signals: "SIGINT SIGTERM", signals: "SIGINT SIGTERM",
timeout: 30000, // timeout: 30 secs timeout: 30000, // timeout: 30 secs
development: false, // not in dev mode development: false, // not in dev mode

View File

@@ -1,6 +1,7 @@
const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server"); const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server");
const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); const { CloudflaredTunnel } = require("node-cloudflared-tunnel");
const { io } = require("../server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const io = UptimeKumaServer.getInstance().io;
const prefix = "cloudflared_"; const prefix = "cloudflared_";
const cloudflared = new CloudflaredTunnel(); const cloudflared = new CloudflaredTunnel();

View File

@@ -1,7 +1,8 @@
const { checkLogin } = require("../util-server"); const { checkLogin } = require("../util-server");
const { Proxy } = require("../proxy"); const { Proxy } = require("../proxy");
const { sendProxyList } = require("../client"); const { sendProxyList } = require("../client");
const server = require("../server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const server = UptimeKumaServer.getInstance();
module.exports.proxySocketHandler = (socket) => { module.exports.proxySocketHandler = (socket) => {
socket.on("addProxy", async (proxy, proxyID, callback) => { socket.on("addProxy", async (proxy, proxyID, callback) => {

View File

@@ -6,7 +6,7 @@ const ImageDataURI = require("../image-data-uri");
const Database = require("../database"); const Database = require("../database");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const StatusPage = require("../model/status_page"); const StatusPage = require("../model/status_page");
const server = require("../server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
module.exports.statusPageSocketHandler = (socket) => { module.exports.statusPageSocketHandler = (socket) => {
@@ -212,6 +212,8 @@ module.exports.statusPageSocketHandler = (socket) => {
]; ];
await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data); await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data);
const server = UptimeKumaServer.getInstance();
// Also change entry page to new slug if it is the default one, and slug is changed. // Also change entry page to new slug if it is the default one, and slug is changed.
if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) { if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) {
server.entryPage = "statusPage-" + statusPage.slug; server.entryPage = "statusPage-" + statusPage.slug;
@@ -281,6 +283,8 @@ module.exports.statusPageSocketHandler = (socket) => {
// Delete a status page // Delete a status page
socket.on("deleteStatusPage", async (slug, callback) => { socket.on("deleteStatusPage", async (slug, callback) => {
const server = UptimeKumaServer.getInstance();
try { try {
checkLogin(socket); checkLogin(socket);

View File

@@ -0,0 +1,82 @@
const express = require("express");
const https = require("https");
const fs = require("fs");
const http = require("http");
const { Server } = require("socket.io");
const { R } = require("redbean-node");
/**
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
* @type {UptimeKumaServer}
*/
class UptimeKumaServer {
/**
*
* @type {UptimeKumaServer}
*/
static instance = null;
/**
* Main monitor list
* @type {{}}
*/
monitorList = {};
entryPage = "dashboard";
app = undefined;
httpServer = undefined;
io = undefined;
static getInstance(args) {
if (UptimeKumaServer.instance == null) {
UptimeKumaServer.instance = new UptimeKumaServer(args);
}
return UptimeKumaServer.instance;
}
constructor(args) {
// SSL
const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined;
const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined;
console.log("Creating express and socket.io instance");
this.app = express();
if (sslKey && sslCert) {
console.log("Server Type: HTTPS");
this.httpServer = https.createServer({
key: fs.readFileSync(sslKey),
cert: fs.readFileSync(sslCert)
}, this.app);
} else {
console.log("Server Type: HTTP");
this.httpServer = http.createServer(this.app);
}
this.io = new Server(this.httpServer);
}
async sendMonitorList(socket) {
let list = await this.getMonitorJSONList(socket.userID);
this.io.to(socket.userID).emit("monitorList", list);
return list;
}
async getMonitorJSONList(userID) {
let result = {};
let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [
userID,
]);
for (let monitor of monitorList) {
result[monitor.id] = await monitor.toJSON();
}
return result;
}
}
module.exports = {
UptimeKumaServer
};

View File

@@ -36,7 +36,7 @@
</div> </div>
<div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> <div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
<div class="col-12"> <div class="col-12 bottom-style">
<HeartbeatBar size="small" :monitor-id="item.id" /> <HeartbeatBar size="small" :monitor-id="item.id" />
</div> </div>
</div> </div>
@@ -203,9 +203,16 @@ export default {
} }
.tags { .tags {
padding-left: 62px; margin-top: 4px;
padding-left: 67px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0; gap: 0;
} }
.bottom-style {
padding-left: 67px;
margin-top: 5px;
}
</style> </style>

View File

@@ -6,6 +6,7 @@ export default {
userTheme: localStorage.theme, userTheme: localStorage.theme,
userHeartbeatBar: localStorage.heartbeatBarTheme, userHeartbeatBar: localStorage.heartbeatBarTheme,
statusPageTheme: "light", statusPageTheme: "light",
forceStatusPageTheme: false,
path: "", path: "",
}; };
}, },
@@ -27,6 +28,10 @@ export default {
computed: { computed: {
theme() { 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 // Entry no need dark
if (this.path === "") { if (this.path === "") {

View File

@@ -26,6 +26,7 @@ export default {
if (res.type === "statusPageMatchedDomain") { if (res.type === "statusPageMatchedDomain") {
this.statusPageSlug = res.statusPageSlug; this.statusPageSlug = res.statusPageSlug;
this.$root.forceStatusPageTheme = true;
} else if (res.type === "entryPage") { // Dev only. For production, the logic is in the server side } else if (res.type === "entryPage") { // Dev only. For production, the logic is in the server side
const entryPage = res.entryPage; const entryPage = res.entryPage;