mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-10-25 07:39:22 +08:00 
			
		
		
		
	introduce consistent logging
This commit is contained in:
		| @@ -2,7 +2,6 @@ const basicAuth = require("express-basic-auth"); | ||||
| const passwordHash = require("./password-hash"); | ||||
| const { R } = require("redbean-node"); | ||||
| const { setting } = require("./util-server"); | ||||
| const { debug } = require("../src/util"); | ||||
| const { loginRateLimiter } = require("./rate-limiter"); | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const fs = require("fs"); | ||||
| const { R } = require("redbean-node"); | ||||
| const { setSetting, setting } = require("./util-server"); | ||||
| const { debug, sleep } = require("../src/util"); | ||||
| const { log, sleep } = require("../src/util"); | ||||
| const dayjs = require("dayjs"); | ||||
| const knex = require("knex"); | ||||
|  | ||||
| @@ -76,7 +76,7 @@ class Database { | ||||
|             fs.mkdirSync(Database.uploadDir, { recursive: true }); | ||||
|         } | ||||
|  | ||||
|         console.log(`Data Dir: ${Database.dataDir}`); | ||||
|         log("db", `Data Dir: ${Database.dataDir}`); | ||||
|     } | ||||
|  | ||||
|     static async connect() { | ||||
| @@ -117,10 +117,10 @@ class Database { | ||||
|         await R.exec("PRAGMA cache_size = -12000"); | ||||
|         await R.exec("PRAGMA auto_vacuum = FULL"); | ||||
|  | ||||
|         console.log("SQLite config:"); | ||||
|         console.log(await R.getAll("PRAGMA journal_mode")); | ||||
|         console.log(await R.getAll("PRAGMA cache_size")); | ||||
|         console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()")); | ||||
|         log("db", "SQLite config:"); | ||||
|         log("db", await R.getAll("PRAGMA journal_mode")); | ||||
|         log("db", await R.getAll("PRAGMA cache_size")); | ||||
|         log("db","SQLite Version: " + await R.getCell("SELECT sqlite_version()")); | ||||
|     } | ||||
|  | ||||
|     static async patch() { | ||||
| @@ -130,15 +130,15 @@ class Database { | ||||
|             version = 0; | ||||
|         } | ||||
|  | ||||
|         console.info("Your database version: " + version); | ||||
|         console.info("Latest database version: " + this.latestVersion); | ||||
|         log("db", "Your database version: " + version); | ||||
|         log("db", "Latest database version: " + this.latestVersion); | ||||
|  | ||||
|         if (version === this.latestVersion) { | ||||
|             console.info("Database patch not needed"); | ||||
|             log("db", "Database patch not needed"); | ||||
|         } else if (version > this.latestVersion) { | ||||
|             console.info("Warning: Database version is newer than expected"); | ||||
|             log("db", "Warning: Database version is newer than expected"); | ||||
|         } else { | ||||
|             console.info("Database patch is needed"); | ||||
|             log("db", "Database patch is needed"); | ||||
|  | ||||
|             this.backup(version); | ||||
|  | ||||
| @@ -146,17 +146,17 @@ class Database { | ||||
|             try { | ||||
|                 for (let i = version + 1; i <= this.latestVersion; i++) { | ||||
|                     const sqlFile = `./db/patch${i}.sql`; | ||||
|                     console.info(`Patching ${sqlFile}`); | ||||
|                     log("db", `Patching ${sqlFile}`); | ||||
|                     await Database.importSQLFile(sqlFile); | ||||
|                     console.info(`Patched ${sqlFile}`); | ||||
|                     log("db", `Patched ${sqlFile}`); | ||||
|                     await setSetting("database_version", i); | ||||
|                 } | ||||
|             } catch (ex) { | ||||
|                 await Database.close(); | ||||
|  | ||||
|                 console.error(ex); | ||||
|                 console.error("Start Uptime-Kuma failed due to issue patching the database"); | ||||
|                 console.error("Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); | ||||
|                 log("db", ex, "error"); | ||||
|                 log("db", "Start Uptime-Kuma failed due to issue patching the database", "error"); | ||||
|                 log("db", "Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues", "error"); | ||||
|  | ||||
|                 this.restore(); | ||||
|                 process.exit(1); | ||||
| @@ -171,15 +171,15 @@ class Database { | ||||
|      * @returns {Promise<void>} | ||||
|      */ | ||||
|     static async patch2() { | ||||
|         console.log("Database Patch 2.0 Process"); | ||||
|         log("db", "Database Patch 2.0 Process"); | ||||
|         let databasePatchedFiles = await setting("databasePatchedFiles"); | ||||
|  | ||||
|         if (! databasePatchedFiles) { | ||||
|             databasePatchedFiles = {}; | ||||
|         } | ||||
|  | ||||
|         debug("Patched files:"); | ||||
|         debug(databasePatchedFiles); | ||||
|         log("db", "Patched files:", "debug"); | ||||
|         log("db", databasePatchedFiles, "debug"); | ||||
|  | ||||
|         try { | ||||
|             for (let sqlFilename in this.patchList) { | ||||
| @@ -187,15 +187,15 @@ class Database { | ||||
|             } | ||||
|  | ||||
|             if (this.patched) { | ||||
|                 console.log("Database Patched Successfully"); | ||||
|                 log("db", "Database Patched Successfully"); | ||||
|             } | ||||
|  | ||||
|         } catch (ex) { | ||||
|             await Database.close(); | ||||
|  | ||||
|             console.error(ex); | ||||
|             console.error("Start Uptime-Kuma failed due to issue patching the database"); | ||||
|             console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); | ||||
|             log("db", ex, "error"); | ||||
|             log("db", "Start Uptime-Kuma failed due to issue patching the database", "error"); | ||||
|             log("db", "Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues", "error"); | ||||
|  | ||||
|             this.restore(); | ||||
|  | ||||
| @@ -214,16 +214,16 @@ class Database { | ||||
|         let value = this.patchList[sqlFilename]; | ||||
|  | ||||
|         if (! value) { | ||||
|             console.log(sqlFilename + " skip"); | ||||
|             log("db", sqlFilename + " skip"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Check if patched | ||||
|         if (! databasePatchedFiles[sqlFilename]) { | ||||
|             console.log(sqlFilename + " is not patched"); | ||||
|             log("db", sqlFilename + " is not patched"); | ||||
|  | ||||
|             if (value.parents) { | ||||
|                 console.log(sqlFilename + " need parents"); | ||||
|                 log("db", sqlFilename + " need parents"); | ||||
|                 for (let parentSQLFilename of value.parents) { | ||||
|                     await this.patch2Recursion(parentSQLFilename, databasePatchedFiles); | ||||
|                 } | ||||
| @@ -231,14 +231,14 @@ class Database { | ||||
|  | ||||
|             this.backup(dayjs().format("YYYYMMDDHHmmss")); | ||||
|  | ||||
|             console.log(sqlFilename + " is patching"); | ||||
|             log("db", sqlFilename + " is patching"); | ||||
|             this.patched = true; | ||||
|             await this.importSQLFile("./db/" + sqlFilename); | ||||
|             databasePatchedFiles[sqlFilename] = true; | ||||
|             console.log(sqlFilename + " was patched successfully"); | ||||
|             log("db", sqlFilename + " was patched successfully"); | ||||
|  | ||||
|         } else { | ||||
|             debug(sqlFilename + " is already patched, skip"); | ||||
|             log("db", sqlFilename + " is already patched, skip", "debug"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -290,7 +290,7 @@ class Database { | ||||
|         }; | ||||
|         process.addListener("unhandledRejection", listener); | ||||
|  | ||||
|         console.log("Closing the database"); | ||||
|         log("db", "Closing the database"); | ||||
|  | ||||
|         while (true) { | ||||
|             Database.noReject = true; | ||||
| @@ -300,10 +300,10 @@ class Database { | ||||
|             if (Database.noReject) { | ||||
|                 break; | ||||
|             } else { | ||||
|                 console.log("Waiting to close the database"); | ||||
|                 log("db", "Waiting to close the database"); | ||||
|             } | ||||
|         } | ||||
|         console.log("SQLite closed"); | ||||
|         log("db", "SQLite closed"); | ||||
|  | ||||
|         process.removeListener("unhandledRejection", listener); | ||||
|     } | ||||
| @@ -315,7 +315,7 @@ class Database { | ||||
|      */ | ||||
|     static backup(version) { | ||||
|         if (! this.backupPath) { | ||||
|             console.info("Backing up the database"); | ||||
|             log("db", "Backing up the database"); | ||||
|             this.backupPath = this.dataDir + "kuma.db.bak" + version; | ||||
|             fs.copyFileSync(Database.path, this.backupPath); | ||||
|  | ||||
| @@ -338,7 +338,7 @@ class Database { | ||||
|      */ | ||||
|     static restore() { | ||||
|         if (this.backupPath) { | ||||
|             console.error("Patching the database failed!!! Restoring the backup"); | ||||
|             log("db", "Patching the database failed!!! Restoring the backup", "error"); | ||||
|  | ||||
|             const shmPath = Database.path + "-shm"; | ||||
|             const walPath = Database.path + "-wal"; | ||||
| @@ -357,7 +357,7 @@ class Database { | ||||
|                     fs.unlinkSync(walPath); | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 console.log("Restore failed; you may need to restore the backup manually"); | ||||
|                 log("db", "Restore failed; you may need to restore the backup manually", "error"); | ||||
|                 process.exit(1); | ||||
|             } | ||||
|  | ||||
| @@ -373,14 +373,14 @@ class Database { | ||||
|             } | ||||
|  | ||||
|         } else { | ||||
|             console.log("Nothing to restore"); | ||||
|             log("db", "Nothing to restore"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static getSize() { | ||||
|         debug("Database.getSize()"); | ||||
|         log("db", "Database.getSize()", "debug"); | ||||
|         let stats = fs.statSync(Database.path); | ||||
|         debug(stats); | ||||
|         log("db", stats, "debug"); | ||||
|         return stats.size; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -3,12 +3,13 @@ | ||||
|     Modified with 0 dependencies | ||||
|  */ | ||||
| let fs = require("fs"); | ||||
| const { log } = require("../src/util"); | ||||
|  | ||||
| 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/\""); | ||||
|             log("image-data-uri", "It seems that it is not an Image Data URI. Couldn't match \"data:image/\"", "error"); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
| @@ -22,7 +23,7 @@ let ImageDataURI = (() => { | ||||
|  | ||||
|     function encode(data, mediaType) { | ||||
|         if (!data || !mediaType) { | ||||
|             console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType "); | ||||
|             log("image-data-uri", "Missing some of the required params: data, mediaType", "error"); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const path = require("path"); | ||||
| const Bree = require("bree"); | ||||
| const { SHARE_ENV } = require("worker_threads"); | ||||
| const { log } = require("../src/util"); | ||||
|  | ||||
| const jobs = [ | ||||
|     { | ||||
| @@ -18,7 +19,7 @@ const initBackgroundJobs = function (args) { | ||||
|             workerData: args, | ||||
|         }, | ||||
|         workerMessageHandler: (message) => { | ||||
|             console.log("[Background Job]:", message); | ||||
|             log("jobs", message); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ dayjs.extend(utc); | ||||
| dayjs.extend(timezone); | ||||
| const axios = require("axios"); | ||||
| const { Prometheus } = require("../prometheus"); | ||||
| const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); | ||||
| const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); | ||||
| const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog } = require("../util-server"); | ||||
| const { R } = require("redbean-node"); | ||||
| const { BeanModel } = require("redbean-node/dist/bean-model"); | ||||
| @@ -141,7 +141,7 @@ class Monitor extends BeanModel { | ||||
|                     // Do not do any queries/high loading things before the "bean.ping" | ||||
|                     let startTime = dayjs().valueOf(); | ||||
|  | ||||
|                     debug(`[${this.name}] Prepare Options for axios`); | ||||
|                     log("monitor", `[${this.name}] Prepare Options for axios`, "debug"); | ||||
|                     const options = { | ||||
|                         url: this.url, | ||||
|                         method: (this.method || "get").toLowerCase(), | ||||
| @@ -162,7 +162,7 @@ class Monitor extends BeanModel { | ||||
|                         }, | ||||
|                     }; | ||||
|  | ||||
|                     debug(`[${this.name}] Axios Request`); | ||||
|                     log("monitor", `[${this.name}] Axios Request`, "debug"); | ||||
|                     let res = await axios.request(options); | ||||
|                     bean.msg = `${res.status} - ${res.statusText}`; | ||||
|                     bean.ping = dayjs().valueOf() - startTime; | ||||
| @@ -170,29 +170,30 @@ class Monitor extends BeanModel { | ||||
|                     // Check certificate if https is used | ||||
|                     let certInfoStartTime = dayjs().valueOf(); | ||||
|                     if (this.getUrl()?.protocol === "https:") { | ||||
|                         debug(`[${this.name}] Check cert`); | ||||
|                         log("monitor", `[${this.name}] Check cert`, "debug"); | ||||
|                         try { | ||||
|                             let tlsInfoObject = checkCertificate(res); | ||||
|                             tlsInfo = await this.updateTlsInfo(tlsInfoObject); | ||||
|  | ||||
|                             if (!this.getIgnoreTls()) { | ||||
|                                 debug(`[${this.name}] call sendCertNotification`); | ||||
|                                 log("monitor", `[${this.name}] call sendCertNotification`, "debug"); | ||||
|                                 await this.sendCertNotification(tlsInfoObject); | ||||
|                             } | ||||
|  | ||||
|                         } catch (e) { | ||||
|                             if (e.message !== "No TLS certificate in response") { | ||||
|                                 console.error(e.message); | ||||
|                                 log("monitor", "Caught error", "error"); | ||||
|                                 log("monitor", e.message, "error"); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (process.env.TIMELOGGER === "1") { | ||||
|                         debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); | ||||
|                         log("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms", "debug"); | ||||
|                     } | ||||
|  | ||||
|                     if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) { | ||||
|                         console.log(res.data); | ||||
|                         log("monitor", res.data); | ||||
|                     } | ||||
|  | ||||
|                     if (this.type === "http") { | ||||
| @@ -272,7 +273,7 @@ class Monitor extends BeanModel { | ||||
|                         time | ||||
|                     ]); | ||||
|  | ||||
|                     debug("heartbeatCount" + heartbeatCount + " " + time); | ||||
|                     log("monitor", "heartbeatCount" + heartbeatCount + " " + time, "debug"); | ||||
|  | ||||
|                     if (heartbeatCount <= 0) { | ||||
|                         throw new Error("No heartbeat in the time window"); | ||||
| @@ -355,7 +356,7 @@ class Monitor extends BeanModel { | ||||
|  | ||||
|             let beatInterval = this.interval; | ||||
|  | ||||
|             debug(`[${this.name}] Check isImportant`); | ||||
|             log("monitor", `[${this.name}] Check isImportant`, "debug"); | ||||
|             let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); | ||||
|  | ||||
|             // Mark as important if status changed, ignore pending pings, | ||||
| @@ -363,11 +364,11 @@ class Monitor extends BeanModel { | ||||
|             if (isImportant) { | ||||
|                 bean.important = true; | ||||
|  | ||||
|                 debug(`[${this.name}] sendNotification`); | ||||
|                 log("monitor", `[${this.name}] sendNotification`, "debug"); | ||||
|                 await Monitor.sendNotification(isFirstBeat, this, bean); | ||||
|  | ||||
|                 // Clear Status Page Cache | ||||
|                 debug(`[${this.name}] apicache clear`); | ||||
|                 log("monitor", `[${this.name}] apicache clear`, "debug"); | ||||
|                 apicache.clear(); | ||||
|  | ||||
|             } else { | ||||
| @@ -375,24 +376,24 @@ class Monitor extends BeanModel { | ||||
|             } | ||||
|  | ||||
|             if (bean.status === UP) { | ||||
|                 console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||
|                 log("monitor", `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||
|             } else if (bean.status === PENDING) { | ||||
|                 if (this.retryInterval > 0) { | ||||
|                     beatInterval = this.retryInterval; | ||||
|                 } | ||||
|                 console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||
|                 log("monitor", `Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`, "warn"); | ||||
|             } else { | ||||
|                 console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||
|                 log("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`, "warn"); | ||||
|             } | ||||
|  | ||||
|             debug(`[${this.name}] Send to socket`); | ||||
|             log("monitor", `[${this.name}] Send to socket`, "debug"); | ||||
|             io.to(this.user_id).emit("heartbeat", bean.toJSON()); | ||||
|             Monitor.sendStats(io, this.id, this.user_id); | ||||
|  | ||||
|             debug(`[${this.name}] Store`); | ||||
|             log("monitor", `[${this.name}] Store`, "debug"); | ||||
|             await R.store(bean); | ||||
|  | ||||
|             debug(`[${this.name}] prometheus.update`); | ||||
|             log("monitor", `[${this.name}] prometheus.update`, "debug"); | ||||
|             prometheus.update(bean, tlsInfo); | ||||
|  | ||||
|             previousBeat = bean; | ||||
| @@ -401,15 +402,15 @@ class Monitor extends BeanModel { | ||||
|  | ||||
|                 if (demoMode) { | ||||
|                     if (beatInterval < 20) { | ||||
|                         console.log("beat interval too low, reset to 20s"); | ||||
|                         log("monitor", "beat interval too low, reset to 20s"); | ||||
|                         beatInterval = 20; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 debug(`[${this.name}] SetTimeout for next check.`); | ||||
|                 log("monitor", `[${this.name}] SetTimeout for next check.`, "debug"); | ||||
|                 this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000); | ||||
|             } else { | ||||
|                 console.log(`[${this.name}] isStop = true, no next check.`); | ||||
|                 log("monitor", `[${this.name}] isStop = true, no next check.`); | ||||
|             } | ||||
|  | ||||
|         }; | ||||
| @@ -420,10 +421,10 @@ class Monitor extends BeanModel { | ||||
|             } catch (e) { | ||||
|                 console.trace(e); | ||||
|                 errorLog(e, false); | ||||
|                 console.error("Please report to https://github.com/louislam/uptime-kuma/issues"); | ||||
|                 log("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues", "error"); | ||||
|  | ||||
|                 if (! this.isStop) { | ||||
|                     console.log("Try to restart the monitor"); | ||||
|                     log("monitor", "Try to restart the monitor"); | ||||
|                     this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); | ||||
|                 } | ||||
|             } | ||||
| @@ -481,17 +482,17 @@ class Monitor extends BeanModel { | ||||
|  | ||||
|                 if (isValidObjects) { | ||||
|                     if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) { | ||||
|                         debug("Resetting sent_history"); | ||||
|                         log("monitor", "Resetting sent_history", "debug"); | ||||
|                         await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [ | ||||
|                             this.id | ||||
|                         ]); | ||||
|                     } else { | ||||
|                         debug("No need to reset sent_history"); | ||||
|                         debug(oldCertInfo.certInfo.fingerprint256); | ||||
|                         debug(checkCertificateResult.certInfo.fingerprint256); | ||||
|                         log("monitor", "No need to reset sent_history", "debug"); | ||||
|                         log("monitor", oldCertInfo.certInfo.fingerprint256, "debug"); | ||||
|                         log("monitor", checkCertificateResult.certInfo.fingerprint256, "debug"); | ||||
|                     } | ||||
|                 } else { | ||||
|                     debug("Not valid object"); | ||||
|                     log("monitor", "Not valid object", "debug"); | ||||
|                 } | ||||
|             } catch (e) { } | ||||
|  | ||||
| @@ -512,7 +513,7 @@ class Monitor extends BeanModel { | ||||
|             await Monitor.sendUptime(24 * 30, io, monitorID, userID); | ||||
|             await Monitor.sendCertInfo(io, monitorID, userID); | ||||
|         } else { | ||||
|             debug("No clients in the room, no need to send stats"); | ||||
|             log("monitor", "No clients in the room, no need to send stats", "debug"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -659,8 +660,8 @@ class Monitor extends BeanModel { | ||||
|                 try { | ||||
|                     await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON()); | ||||
|                 } catch (e) { | ||||
|                     console.error("Cannot send notification to " + notification.name); | ||||
|                     console.log(e); | ||||
|                     log("monitor", "Cannot send notification to " + notification.name, "error"); | ||||
|                     log("monitor", e, "error"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -677,7 +678,7 @@ class Monitor extends BeanModel { | ||||
|         if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { | ||||
|             const notificationList = await Monitor.getNotificationList(this); | ||||
|  | ||||
|             debug("call sendCertNotificationByTargetDays"); | ||||
|             log("monitor", "call sendCertNotificationByTargetDays", "debug"); | ||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList); | ||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList); | ||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList); | ||||
| @@ -687,7 +688,7 @@ class Monitor extends BeanModel { | ||||
|     async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) { | ||||
|  | ||||
|         if (daysRemaining > targetDays) { | ||||
|             debug(`No need to send cert notification. ${daysRemaining} > ${targetDays}`); | ||||
|             log("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`, "debug"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -701,21 +702,21 @@ class Monitor extends BeanModel { | ||||
|  | ||||
|             // Sent already, no need to send again | ||||
|             if (row) { | ||||
|                 debug("Sent already, no need to send again"); | ||||
|                 log("monitor", "Sent already, no need to send again", "debug"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let sent = false; | ||||
|             debug("Send certificate notification"); | ||||
|             log("monitor", "Send certificate notification", "debug"); | ||||
|  | ||||
|             for (let notification of notificationList) { | ||||
|                 try { | ||||
|                     debug("Sending to " + notification.name); | ||||
|                     log("monitor", "Sending to " + notification.name, "debug"); | ||||
|                     await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`); | ||||
|                     sent = true; | ||||
|                 } catch (e) { | ||||
|                     console.error("Cannot send cert notification to " + notification.name); | ||||
|                     console.error(e); | ||||
|                     log("monitor", "Cannot send cert notification to " + notification.name, "error"); | ||||
|                     log("monitor", e, "error"); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -727,7 +728,7 @@ class Monitor extends BeanModel { | ||||
|                 ]); | ||||
|             } | ||||
|         } else { | ||||
|             debug("No notification, no need to send cert notification"); | ||||
|             log("monitor", "No notification, no need to send cert notification", "debug"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const NotificationProvider = require("./notification-provider"); | ||||
| const axios = require("axios"); | ||||
| const Crypto = require("crypto"); | ||||
| const { debug } = require("../../src/util"); | ||||
| const { log } = require("../../src/util"); | ||||
|  | ||||
| class Matrix extends NotificationProvider { | ||||
|     name = "matrix"; | ||||
| @@ -17,11 +17,11 @@ class Matrix extends NotificationProvider { | ||||
|                 .slice(0, size) | ||||
|         ); | ||||
|  | ||||
|         debug("Random String: " + randomString); | ||||
|         log("notification", "Random String: " + randomString, "debug"); | ||||
|  | ||||
|         const roomId = encodeURIComponent(notification.internalRoomId); | ||||
|  | ||||
|         debug("Matrix Room ID: " + roomId); | ||||
|         log("notification", "Matrix Room ID: " + roomId, "debug"); | ||||
|  | ||||
|         try { | ||||
|             let config = { | ||||
|   | ||||
| @@ -23,13 +23,14 @@ const Feishu = require("./notification-providers/feishu"); | ||||
| const AliyunSms = require("./notification-providers/aliyun-sms"); | ||||
| const DingDing = require("./notification-providers/dingding"); | ||||
| const Bark = require("./notification-providers/bark"); | ||||
| const { log } = require("../src/util"); | ||||
|  | ||||
| class Notification { | ||||
|  | ||||
|     providerList = {}; | ||||
|  | ||||
|     static init() { | ||||
|         console.log("Prepare Notification Providers"); | ||||
|         log("notification", "Prepare Notification Providers"); | ||||
|  | ||||
|         this.providerList = {}; | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const PrometheusClient = require("prom-client"); | ||||
| const { log } = require("../src/util"); | ||||
|  | ||||
| const commonLabels = [ | ||||
|     "monitor_name", | ||||
| @@ -56,20 +57,23 @@ class Prometheus { | ||||
|                 } | ||||
|                 monitor_cert_is_valid.set(this.monitorLabelValues, is_valid); | ||||
|             } catch (e) { | ||||
|                 console.error(e); | ||||
|                 log("prometheus", "Caught error", "error"); | ||||
|                 log("prometheus", e, "error"); | ||||
|             } | ||||
|  | ||||
|             try { | ||||
|                 monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining); | ||||
|             } catch (e) { | ||||
|                 console.error(e); | ||||
|                 log("prometheus", "Caught error", "error"); | ||||
|                 log("prometheus", e, "error"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             monitor_status.set(this.monitorLabelValues, heartbeat.status); | ||||
|         } catch (e) { | ||||
|             console.error(e); | ||||
|             log("prometheus", "Caught error", "error"); | ||||
|             log("prometheus", e, "error"); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
| @@ -80,7 +84,8 @@ class Prometheus { | ||||
|                 monitor_response_time.set(this.monitorLabelValues, -1); | ||||
|             } | ||||
|         } catch (e) { | ||||
|             console.error(e); | ||||
|             log("prometheus", "Caught error", "error"); | ||||
|             log("prometheus", e, "error"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| const { RateLimiter } = require("limiter"); | ||||
| const { debug } = require("../src/util"); | ||||
| const { log } = require("../src/util"); | ||||
|  | ||||
| class KumaRateLimiter { | ||||
|     constructor(config) { | ||||
| @@ -9,7 +9,7 @@ class KumaRateLimiter { | ||||
|  | ||||
|     async pass(callback, num = 1) { | ||||
|         const remainingRequests = await this.removeTokens(num); | ||||
|         debug("Rate Limit (remainingRequests):" + remainingRequests); | ||||
|         log("rate-limit", "remaining requests: " + remainingRequests); | ||||
|         if (remainingRequests < 0) { | ||||
|             if (callback) { | ||||
|                 callback({ | ||||
|   | ||||
| @@ -5,7 +5,7 @@ const server = require("../server"); | ||||
| const apicache = require("../modules/apicache"); | ||||
| const Monitor = require("../model/monitor"); | ||||
| const dayjs = require("dayjs"); | ||||
| const { UP, flipStatus, debug } = require("../../src/util"); | ||||
| const { UP, flipStatus, log } = require("../../src/util"); | ||||
| let router = express.Router(); | ||||
|  | ||||
| let cache = apicache.middleware; | ||||
| @@ -56,8 +56,8 @@ router.get("/api/push/:pushToken", async (request, response) => { | ||||
|             duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); | ||||
|         } | ||||
|  | ||||
|         debug("PreviousStatus: " + previousStatus); | ||||
|         debug("Current Status: " + status); | ||||
|         log("router", "PreviousStatus: " + previousStatus, "debug"); | ||||
|         log("router", "Current Status: " + status, "debug"); | ||||
|  | ||||
|         bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); | ||||
|         bean.monitor_id = monitor.id; | ||||
|   | ||||
							
								
								
									
										168
									
								
								server/server.js
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								server/server.js
									
									
									
									
									
								
							| @@ -1,56 +1,57 @@ | ||||
| console.log("Welcome to Uptime Kuma"); | ||||
| const args = require("args-parser")(process.argv); | ||||
| const { sleep, debug, getRandomInt, genSecret } = require("../src/util"); | ||||
| const { sleep, log, getRandomInt, genSecret } = require("../src/util"); | ||||
| const config = require("./config"); | ||||
|  | ||||
| debug(args); | ||||
| log("server", "Welcome to Uptime Kuma"); | ||||
| log("server", "Arguments", "debug"); | ||||
| log("server", args, "debug"); | ||||
|  | ||||
| if (! process.env.NODE_ENV) { | ||||
|     process.env.NODE_ENV = "production"; | ||||
| } | ||||
|  | ||||
| console.log("Node Env: " + process.env.NODE_ENV); | ||||
| log("server", "Node Env: " + process.env.NODE_ENV); | ||||
|  | ||||
| console.log("Importing Node libraries"); | ||||
| log("server", "Importing Node libraries"); | ||||
| const fs = require("fs"); | ||||
| const http = require("http"); | ||||
| const https = require("https"); | ||||
|  | ||||
| console.log("Importing 3rd-party libraries"); | ||||
| debug("Importing express"); | ||||
| log("server", "Importing 3rd-party libraries"); | ||||
| log("server", "Importing express", "debug"); | ||||
| const express = require("express"); | ||||
| debug("Importing socket.io"); | ||||
| log("server", "Importing socket.io", "debug"); | ||||
| const { Server } = require("socket.io"); | ||||
| debug("Importing redbean-node"); | ||||
| log("server", "Importing redbean-node", "debug"); | ||||
| const { R } = require("redbean-node"); | ||||
| debug("Importing jsonwebtoken"); | ||||
| log("server", "Importing jsonwebtoken", "debug"); | ||||
| const jwt = require("jsonwebtoken"); | ||||
| debug("Importing http-graceful-shutdown"); | ||||
| log("server", "Importing http-graceful-shutdown", "debug"); | ||||
| const gracefulShutdown = require("http-graceful-shutdown"); | ||||
| debug("Importing prometheus-api-metrics"); | ||||
| log("server", "Importing prometheus-api-metrics", "debug"); | ||||
| const prometheusAPIMetrics = require("prometheus-api-metrics"); | ||||
| debug("Importing compare-versions"); | ||||
| log("server", "Importing compare-versions", "debug"); | ||||
| const compareVersions = require("compare-versions"); | ||||
| const { passwordStrength } = require("check-password-strength"); | ||||
|  | ||||
| debug("Importing 2FA Modules"); | ||||
| log("server", "Importing 2FA Modules", "debug"); | ||||
| const notp = require("notp"); | ||||
| const base32 = require("thirty-two"); | ||||
|  | ||||
| console.log("Importing this project modules"); | ||||
| debug("Importing Monitor"); | ||||
| log("server", "Importing this project modules"); | ||||
| log("server", "Importing Monitor", "debug"); | ||||
| const Monitor = require("./model/monitor"); | ||||
| debug("Importing Settings"); | ||||
| log("server", "Importing Settings", "debug"); | ||||
| const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog } = require("./util-server"); | ||||
|  | ||||
| debug("Importing Notification"); | ||||
| log("server", "Importing Notification", "debug"); | ||||
| const { Notification } = require("./notification"); | ||||
| Notification.init(); | ||||
|  | ||||
| debug("Importing Database"); | ||||
| log("server", "Importing Database", "debug"); | ||||
| const Database = require("./database"); | ||||
|  | ||||
| debug("Importing Background Jobs"); | ||||
| log("server", "Importing Background Jobs", "debug"); | ||||
| const { initBackgroundJobs } = require("./jobs"); | ||||
| const { loginRateLimiter } = require("./rate-limiter"); | ||||
|  | ||||
| @@ -59,7 +60,7 @@ const { login } = require("./auth"); | ||||
| const passwordHash = require("./password-hash"); | ||||
|  | ||||
| const checkVersion = require("./check-version"); | ||||
| console.info("Version: " + checkVersion.version); | ||||
| log("server", "Version: " + checkVersion.version); | ||||
|  | ||||
| // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. | ||||
| // Dual-stack support for (::) | ||||
| @@ -71,7 +72,7 @@ if (!hostname && !FBSD) { | ||||
| } | ||||
|  | ||||
| if (hostname) { | ||||
|     console.log("Custom hostname: " + hostname); | ||||
|     log("server", "Custom hostname: " + hostname); | ||||
| } | ||||
|  | ||||
| const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001); | ||||
| @@ -94,22 +95,22 @@ const twofa_verification_opts = { | ||||
| const testMode = !!args["test"] || false; | ||||
|  | ||||
| if (config.demoMode) { | ||||
|     console.log("==== Demo Mode ===="); | ||||
|     log("server", "==== Demo Mode ===="); | ||||
| } | ||||
|  | ||||
| console.log("Creating express and socket.io instance"); | ||||
| log("server", "Creating express and socket.io instance"); | ||||
| const app = express(); | ||||
|  | ||||
| let server; | ||||
|  | ||||
| if (sslKey && sslCert) { | ||||
|     console.log("Server Type: HTTPS"); | ||||
|     log("server", "Server Type: HTTPS"); | ||||
|     server = https.createServer({ | ||||
|         key: fs.readFileSync(sslKey), | ||||
|         cert: fs.readFileSync(sslCert) | ||||
|     }, app); | ||||
| } else { | ||||
|     console.log("Server Type: HTTP"); | ||||
|     log("server", "Server Type: HTTP"); | ||||
|     server = http.createServer(app); | ||||
| } | ||||
|  | ||||
| @@ -167,7 +168,7 @@ try { | ||||
| } catch (e) { | ||||
|     // "dist/index.html" is not necessary for development | ||||
|     if (process.env.NODE_ENV !== "development") { | ||||
|         console.error("Error: Cannot find 'dist/index.html', did you install correctly?"); | ||||
|         log("server", "Error: Cannot find 'dist/index.html', did you install correctly?", "error"); | ||||
|         process.exit(1); | ||||
|     } | ||||
| } | ||||
| @@ -180,7 +181,7 @@ exports.entryPage = "dashboard"; | ||||
|  | ||||
|     exports.entryPage = await setting("entryPage"); | ||||
|  | ||||
|     console.log("Adding route"); | ||||
|     log("server", "Adding route"); | ||||
|  | ||||
|     // *************************** | ||||
|     // Normal Router here | ||||
| @@ -233,7 +234,7 @@ exports.entryPage = "dashboard"; | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     console.log("Adding socket handler"); | ||||
|     log("server", "Adding socket handler"); | ||||
|     io.on("connection", async (socket) => { | ||||
|  | ||||
|         sendInfo(socket); | ||||
| @@ -241,7 +242,7 @@ exports.entryPage = "dashboard"; | ||||
|         totalClient++; | ||||
|  | ||||
|         if (needSetup) { | ||||
|             console.log("Redirect to setup page"); | ||||
|             log("server", "Redirect to setup page"); | ||||
|             socket.emit("setup"); | ||||
|         } | ||||
|  | ||||
| @@ -254,33 +255,40 @@ exports.entryPage = "dashboard"; | ||||
|         // *************************** | ||||
|  | ||||
|         socket.on("loginByToken", async (token, callback) => { | ||||
|             log("auth", `Login by token. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|             try { | ||||
|                 let decoded = jwt.verify(token, jwtSecret); | ||||
|  | ||||
|                 console.log("Username from JWT: " + decoded.username); | ||||
|                 log("auth", "Username from JWT: " + decoded.username); | ||||
|  | ||||
|                 let user = await R.findOne("user", " username = ? AND active = 1 ", [ | ||||
|                     decoded.username, | ||||
|                 ]); | ||||
|  | ||||
|                 if (user) { | ||||
|                     debug("afterLogin"); | ||||
|  | ||||
|                     log("auth", "afterLogin", "debug"); | ||||
|                     afterLogin(socket, user); | ||||
|                     log("auth", "afterLogin ok", "debug"); | ||||
|  | ||||
|                     debug("afterLogin ok"); | ||||
|                     log("auth", `Successfully logged in user ${decoded.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                     callback({ | ||||
|                         ok: true, | ||||
|                     }); | ||||
|                 } else { | ||||
|  | ||||
|                     log("auth", `Inactive or deleted user ${decoded.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                     callback({ | ||||
|                         ok: false, | ||||
|                         msg: "The user is inactive or deleted.", | ||||
|                     }); | ||||
|                 } | ||||
|             } catch (error) { | ||||
|  | ||||
|                 log("auth", `Invalid token for user ${decoded.username}. IP=${getClientIp(socket)}`, "error"); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: false, | ||||
|                     msg: "Invalid token.", | ||||
| @@ -290,10 +298,11 @@ exports.entryPage = "dashboard"; | ||||
|         }); | ||||
|  | ||||
|         socket.on("login", async (data, callback) => { | ||||
|             console.log("Login"); | ||||
|             log("auth", `Login by username + password. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|             // Login Rate Limit | ||||
|             if (! await loginRateLimiter.pass(callback)) { | ||||
|                 log("auth", `Too many failed requests for user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @@ -302,6 +311,9 @@ exports.entryPage = "dashboard"; | ||||
|             if (user) { | ||||
|                 if (user.twofa_status == 0) { | ||||
|                     afterLogin(socket, user); | ||||
|  | ||||
|                     log("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                     callback({ | ||||
|                         ok: true, | ||||
|                         token: jwt.sign({ | ||||
| @@ -311,6 +323,9 @@ exports.entryPage = "dashboard"; | ||||
|                 } | ||||
|  | ||||
|                 if (user.twofa_status == 1 && !data.token) { | ||||
|  | ||||
|                     log("auth", `2FA token required for user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                     callback({ | ||||
|                         tokenRequired: true, | ||||
|                     }); | ||||
| @@ -327,6 +342,8 @@ exports.entryPage = "dashboard"; | ||||
|                             socket.userID, | ||||
|                         ]); | ||||
|  | ||||
|                         log("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                         callback({ | ||||
|                             ok: true, | ||||
|                             token: jwt.sign({ | ||||
| @@ -334,6 +351,9 @@ exports.entryPage = "dashboard"; | ||||
|                             }, jwtSecret), | ||||
|                         }); | ||||
|                     } else { | ||||
|  | ||||
|                         log("auth", `Invalid token provided for user ${data.username}. IP=${getClientIp(socket)}`, "warn"); | ||||
|  | ||||
|                         callback({ | ||||
|                             ok: false, | ||||
|                             msg: "Invalid Token!", | ||||
| @@ -341,6 +361,9 @@ exports.entryPage = "dashboard"; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|  | ||||
|                 log("auth", `Incorrect username or password for user ${data.username}. IP=${getClientIp(socket)}`, "warn"); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: false, | ||||
|                     msg: "Incorrect username or password.", | ||||
| @@ -405,11 +428,16 @@ exports.entryPage = "dashboard"; | ||||
|                     socket.userID, | ||||
|                 ]); | ||||
|  | ||||
|                 log("auth", `Saved 2FA token for user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: true, | ||||
|                     msg: "2FA Enabled.", | ||||
|                 }); | ||||
|             } catch (error) { | ||||
|  | ||||
|                 log("auth", `Error changing 2FA token for user ${data.username}. IP=${getClientIp(socket)}`, "error"); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: false, | ||||
|                     msg: "Error while trying to change 2FA.", | ||||
| @@ -425,14 +453,19 @@ exports.entryPage = "dashboard"; | ||||
|                     socket.userID, | ||||
|                 ]); | ||||
|  | ||||
|                 log("auth", `Disabled 2FA token for user ${data.username}. IP=${getClientIp(socket)}`); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: true, | ||||
|                     msg: "2FA Disabled.", | ||||
|                 }); | ||||
|             } catch (error) { | ||||
|  | ||||
|                 log("auth", `Error disabling 2FA token for user ${data.username}. IP=${getClientIp(socket)}`, "error"); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: false, | ||||
|                     msg: "Error while trying to change 2FA.", | ||||
|                     msg: "Error while trying to disable 2FA.", | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
| @@ -544,6 +577,8 @@ exports.entryPage = "dashboard"; | ||||
|                 await startMonitor(socket.userID, bean.id); | ||||
|                 await sendMonitorList(socket); | ||||
|  | ||||
|                 log("monitor", `Added Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: true, | ||||
|                     msg: "Added Successfully.", | ||||
| @@ -551,6 +586,9 @@ exports.entryPage = "dashboard"; | ||||
|                 }); | ||||
|  | ||||
|             } catch (e) { | ||||
|  | ||||
|                 log("monitor", `Error adding Monitor: ${monitorID} User ID: ${socket.userID}`, "error"); | ||||
|  | ||||
|                 callback({ | ||||
|                     ok: false, | ||||
|                     msg: e.message, | ||||
| @@ -634,7 +672,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Get Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|                 log("monitor", `Get Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [ | ||||
|                     monitorID, | ||||
| @@ -658,7 +696,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); | ||||
|                 log("monitor", `Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 if (period == null) { | ||||
|                     throw new Error("Invalid period."); | ||||
| @@ -729,7 +767,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|                 log("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 if (monitorID in monitorList) { | ||||
|                     monitorList[monitorID].stop(); | ||||
| @@ -1065,7 +1103,7 @@ exports.entryPage = "dashboard"; | ||||
|  | ||||
|                 let backupData = JSON.parse(uploadedJSON); | ||||
|  | ||||
|                 console.log(`Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`); | ||||
|                 log("manage", `Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`); | ||||
|  | ||||
|                 let notificationListData = backupData.notificationList; | ||||
|                 let monitorListData = backupData.monitorList; | ||||
| @@ -1237,7 +1275,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|                 log("manage", `Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", [ | ||||
|                     "", | ||||
| @@ -1263,7 +1301,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|                 log("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||
|  | ||||
|                 await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [ | ||||
|                     monitorID | ||||
| @@ -1287,7 +1325,7 @@ exports.entryPage = "dashboard"; | ||||
|             try { | ||||
|                 checkLogin(socket); | ||||
|  | ||||
|                 console.log(`Clear Statistics User ID: ${socket.userID}`); | ||||
|                 log("manage", `Clear Statistics User ID: ${socket.userID}`); | ||||
|  | ||||
|                 await R.exec("DELETE FROM heartbeat"); | ||||
|  | ||||
| @@ -1307,24 +1345,24 @@ exports.entryPage = "dashboard"; | ||||
|         statusPageSocketHandler(socket); | ||||
|         databaseSocketHandler(socket); | ||||
|  | ||||
|         debug("added all socket handlers"); | ||||
|         log("server", "added all socket handlers", "debug"); | ||||
|  | ||||
|         // *************************** | ||||
|         // Better do anything after added all socket handlers here | ||||
|         // *************************** | ||||
|  | ||||
|         debug("check auto login"); | ||||
|         log("auth", "check auto login", "debug"); | ||||
|         if (await setting("disableAuth")) { | ||||
|             console.log("Disabled Auth: auto login to admin"); | ||||
|             log("auth", "Disabled Auth: auto login to admin"); | ||||
|             afterLogin(socket, await R.findOne("user")); | ||||
|             socket.emit("autoLogin"); | ||||
|         } else { | ||||
|             debug("need auth"); | ||||
|             log("auth", "need auth", "debug"); | ||||
|         } | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     console.log("Init the server"); | ||||
|     log("server", "Init the server"); | ||||
|  | ||||
|     server.once("error", async (err) => { | ||||
|         console.error("Cannot listen: " + err.message); | ||||
| @@ -1333,9 +1371,9 @@ exports.entryPage = "dashboard"; | ||||
|  | ||||
|     server.listen(port, hostname, () => { | ||||
|         if (hostname) { | ||||
|             console.log(`Listening on ${hostname}:${port}`); | ||||
|             log("server", `Listening on ${hostname}:${port}`); | ||||
|         } else { | ||||
|             console.log(`Listening on ${port}`); | ||||
|             log("server", `Listening on ${port}`); | ||||
|         } | ||||
|         startMonitors(); | ||||
|         checkVersion.startInterval(); | ||||
| @@ -1419,13 +1457,13 @@ async function getMonitorJSONList(userID) { | ||||
|  | ||||
| async function initDatabase() { | ||||
|     if (! fs.existsSync(Database.path)) { | ||||
|         console.log("Copying Database"); | ||||
|         log("server", "Copying Database"); | ||||
|         fs.copyFileSync(Database.templatePath, Database.path); | ||||
|     } | ||||
|  | ||||
|     console.log("Connecting to the Database"); | ||||
|     log("server", "Connecting to the Database"); | ||||
|     await Database.connect(); | ||||
|     console.log("Connected"); | ||||
|     log("server", "Connected"); | ||||
|  | ||||
|     // Patch the database | ||||
|     await Database.patch(); | ||||
| @@ -1435,16 +1473,16 @@ async function initDatabase() { | ||||
|     ]); | ||||
|  | ||||
|     if (! jwtSecretBean) { | ||||
|         console.log("JWT secret is not found, generate one."); | ||||
|         log("server", "JWT secret is not found, generate one."); | ||||
|         jwtSecretBean = await initJWTSecret(); | ||||
|         console.log("Stored JWT secret into database"); | ||||
|         log("server", "Stored JWT secret into database"); | ||||
|     } else { | ||||
|         console.log("Load JWT secret from database."); | ||||
|         log("server", "Load JWT secret from database."); | ||||
|     } | ||||
|  | ||||
|     // If there is no record in user table, it is a new Uptime Kuma instance, need to setup | ||||
|     if ((await R.count("user")) === 0) { | ||||
|         console.log("No user, need setup"); | ||||
|         log("server", "No user, need setup"); | ||||
|         needSetup = true; | ||||
|     } | ||||
|  | ||||
| @@ -1454,7 +1492,7 @@ async function initDatabase() { | ||||
| async function startMonitor(userID, monitorID) { | ||||
|     await checkOwner(userID, monitorID); | ||||
|  | ||||
|     console.log(`Resume Monitor: ${monitorID} User ID: ${userID}`); | ||||
|     log("manage", `Resume Monitor: ${monitorID} User ID: ${userID}`); | ||||
|  | ||||
|     await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [ | ||||
|         monitorID, | ||||
| @@ -1480,7 +1518,7 @@ async function restartMonitor(userID, monitorID) { | ||||
| async function pauseMonitor(userID, monitorID) { | ||||
|     await checkOwner(userID, monitorID); | ||||
|  | ||||
|     console.log(`Pause Monitor: ${monitorID} User ID: ${userID}`); | ||||
|     log("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`); | ||||
|  | ||||
|     await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [ | ||||
|         monitorID, | ||||
| @@ -1510,10 +1548,10 @@ async function startMonitors() { | ||||
| } | ||||
|  | ||||
| async function shutdownFunction(signal) { | ||||
|     console.log("Shutdown requested"); | ||||
|     console.log("Called signal: " + signal); | ||||
|     log("server", "Shutdown requested"); | ||||
|     log("server", "Called signal: " + signal); | ||||
|  | ||||
|     console.log("Stopping all monitors"); | ||||
|     log("server", "Stopping all monitors"); | ||||
|     for (let id in monitorList) { | ||||
|         let monitor = monitorList[id]; | ||||
|         monitor.stop(); | ||||
| @@ -1522,8 +1560,12 @@ async function shutdownFunction(signal) { | ||||
|     await Database.close(); | ||||
| } | ||||
|  | ||||
| function getClientIp(socket) { | ||||
|     return socket.client.conn.remoteAddress.replace(/^.*:/, "") | ||||
| } | ||||
|  | ||||
| function finalFunction() { | ||||
|     console.log("Graceful shutdown successful!"); | ||||
|     log("server", "Graceful shutdown successful!"); | ||||
| } | ||||
|  | ||||
| gracefulShutdown(server, { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const { R } = require("redbean-node"); | ||||
| const { checkLogin, setSettings } = require("../util-server"); | ||||
| const dayjs = require("dayjs"); | ||||
| const { debug } = require("../../src/util"); | ||||
| const { log } = require("../../src/util"); | ||||
| const ImageDataURI = require("../image-data-uri"); | ||||
| const Database = require("../database"); | ||||
| const apicache = require("../modules/apicache"); | ||||
| @@ -138,8 +138,8 @@ module.exports.statusPageSocketHandler = (socket) => { | ||||
|                 group.id = groupBean.id; | ||||
|             } | ||||
|  | ||||
|             // Delete groups that not in the list | ||||
|             debug("Delete groups that not in the list"); | ||||
|             // Delete groups that are not in the list | ||||
|             log("socket", "Delete groups that are not in the list", "debug"); | ||||
|             const slots = groupIDList.map(() => "?").join(","); | ||||
|             await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots})`, groupIDList); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const tcpp = require("tcp-ping"); | ||||
| const Ping = require("./ping-lite"); | ||||
| const { R } = require("redbean-node"); | ||||
| const { debug } = require("../src/util"); | ||||
| const { log } = require("../src/util"); | ||||
| const passwordHash = require("./password-hash"); | ||||
| const dayjs = require("dayjs"); | ||||
| const { Resolver } = require("dns"); | ||||
| @@ -119,7 +119,7 @@ exports.setting = async function (key) { | ||||
|  | ||||
|     try { | ||||
|         const v = JSON.parse(value); | ||||
|         debug(`Get Setting: ${key}: ${v}`); | ||||
|         log("util", `Get Setting: ${key}: ${v}`, "debug"); | ||||
|         return v; | ||||
|     } catch (e) { | ||||
|         return value; | ||||
| @@ -206,7 +206,7 @@ const parseCertificateInfo = function (info) { | ||||
|     const existingList = {}; | ||||
|  | ||||
|     while (link) { | ||||
|         debug(`[${i}] ${link.fingerprint}`); | ||||
|         log("util", `[${i}] ${link.fingerprint}`, "debug"); | ||||
|  | ||||
|         if (!link.valid_from || !link.valid_to) { | ||||
|             break; | ||||
| @@ -221,7 +221,7 @@ const parseCertificateInfo = function (info) { | ||||
|         if (link.issuerCertificate == null) { | ||||
|             break; | ||||
|         } else if (link.issuerCertificate.fingerprint in existingList) { | ||||
|             debug(`[Last] ${link.issuerCertificate.fingerprint}`); | ||||
|             log("util", `[Last] ${link.issuerCertificate.fingerprint}`, "debug"); | ||||
|             link.issuerCertificate = null; | ||||
|             break; | ||||
|         } else { | ||||
| @@ -242,7 +242,7 @@ exports.checkCertificate = function (res) { | ||||
|     const info = res.request.res.socket.getPeerCertificate(true); | ||||
|     const valid = res.request.res.socket.authorized || false; | ||||
|  | ||||
|     debug("Parsing Certificate Info"); | ||||
|     log("util", "Parsing Certificate Info", "debug"); | ||||
|     const parsedInfo = parseCertificateInfo(info); | ||||
|  | ||||
|     return { | ||||
| @@ -345,7 +345,7 @@ exports.startUnitTest = async () => { | ||||
|  */ | ||||
| exports.convertToUTF8 = (body) => { | ||||
|     const guessEncoding = chardet.detect(body); | ||||
|     //debug("Guess Encoding: " + guessEncoding); | ||||
|     //log("util", "Guess Encoding: " + guessEncoding, "debug"); | ||||
|     const str = iconv.decode(body, guessEncoding); | ||||
|     return str.toString(); | ||||
| }; | ||||
|   | ||||
							
								
								
									
										84
									
								
								src/util.js
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								src/util.js
									
									
									
									
									
								
							| @@ -6,10 +6,10 @@ | ||||
| // | ||||
| // Backend uses the compiled file util.js | ||||
| // Frontend uses util.ts | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; | ||||
| const _dayjs = require("dayjs"); | ||||
| const dayjs = _dayjs; | ||||
| exports.__esModule = true; | ||||
| exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; | ||||
| var _dayjs = require("dayjs"); | ||||
| var dayjs = _dayjs; | ||||
| exports.isDev = process.env.NODE_ENV === "development"; | ||||
| exports.appName = "Uptime Kuma"; | ||||
| exports.DOWN = 0; | ||||
| @@ -29,7 +29,7 @@ function flipStatus(s) { | ||||
| } | ||||
| exports.flipStatus = flipStatus; | ||||
| function sleep(ms) { | ||||
|     return new Promise(resolve => setTimeout(resolve, ms)); | ||||
|     return new Promise(function (resolve) { return setTimeout(resolve, ms); }); | ||||
| } | ||||
| exports.sleep = sleep; | ||||
| /** | ||||
| @@ -40,16 +40,36 @@ function ucfirst(str) { | ||||
|     if (!str) { | ||||
|         return str; | ||||
|     } | ||||
|     const firstLetter = str.substr(0, 1); | ||||
|     var firstLetter = str.substr(0, 1); | ||||
|     return firstLetter.toUpperCase() + str.substr(1); | ||||
| } | ||||
| exports.ucfirst = ucfirst; | ||||
| function debug(msg) { | ||||
| // log levels = info / warn / error / debug | ||||
| function log(module, msg, level) { | ||||
|     if (level === void 0) { level = "info"; } | ||||
|     module = module.toUpperCase(); | ||||
|     level = level.toUpperCase(); | ||||
|     var now = new Date().toISOString(); | ||||
|     var formattedMessage = (typeof msg === "string") ? now + " [" + module + "] " + level + ": " + msg : msg; | ||||
|     if (level === "INFO") { | ||||
|         console.log(formattedMessage); | ||||
|     } | ||||
|     else if (level === "WARN") { | ||||
|         console.warn(formattedMessage); | ||||
|     } | ||||
|     else if (level === "ERROR") { | ||||
|         console.error(formattedMessage); | ||||
|     } | ||||
|     else if (level === "DEBUG") { | ||||
|         if (exports.isDev) { | ||||
|         console.log(msg); | ||||
|             console.debug(formattedMessage); | ||||
|         } | ||||
|     } | ||||
| exports.debug = debug; | ||||
|     else { | ||||
|         console.log(formattedMessage); | ||||
|     } | ||||
| } | ||||
| exports.log = log; | ||||
| function polyfill() { | ||||
|     /** | ||||
|      * String.prototype.replaceAll() polyfill | ||||
| @@ -69,16 +89,17 @@ function polyfill() { | ||||
|     } | ||||
| } | ||||
| exports.polyfill = polyfill; | ||||
| class TimeLogger { | ||||
|     constructor() { | ||||
| var TimeLogger = /** @class */ (function () { | ||||
|     function TimeLogger() { | ||||
|         this.startTime = dayjs().valueOf(); | ||||
|     } | ||||
|     print(name) { | ||||
|     TimeLogger.prototype.print = function (name) { | ||||
|         if (exports.isDev && process.env.TIMELOGGER === "1") { | ||||
|             console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|     }; | ||||
|     return TimeLogger; | ||||
| }()); | ||||
| exports.TimeLogger = TimeLogger; | ||||
| /** | ||||
|  * Returns a random number between min (inclusive) and max (exclusive) | ||||
| @@ -106,12 +127,12 @@ exports.getRandomInt = getRandomInt; | ||||
|  * Returns either the NodeJS crypto.randomBytes() function or its | ||||
|  * browser equivalent implemented via window.crypto.getRandomValues() | ||||
|  */ | ||||
| let getRandomBytes = ((typeof window !== 'undefined' && window.crypto) | ||||
| var getRandomBytes = ((typeof window !== 'undefined' && window.crypto) | ||||
|     // Browsers | ||||
|     ? function () { | ||||
|         return (numBytes) => { | ||||
|             let randomBytes = new Uint8Array(numBytes); | ||||
|             for (let i = 0; i < numBytes; i += 65536) { | ||||
|         return function (numBytes) { | ||||
|             var randomBytes = new Uint8Array(numBytes); | ||||
|             for (var i = 0; i < numBytes; i += 65536) { | ||||
|                 window.crypto.getRandomValues(randomBytes.subarray(i, i + Math.min(numBytes - i, 65536))); | ||||
|             } | ||||
|             return randomBytes; | ||||
| @@ -123,13 +144,13 @@ let getRandomBytes = ((typeof window !== 'undefined' && window.crypto) | ||||
|     })(); | ||||
| function getCryptoRandomInt(min, max) { | ||||
|     // synchronous version of: https://github.com/joepie91/node-random-number-csprng | ||||
|     const range = max - min; | ||||
|     var range = max - min; | ||||
|     if (range >= Math.pow(2, 32)) | ||||
|         console.log("Warning! Range is too large."); | ||||
|     let tmpRange = range; | ||||
|     let bitsNeeded = 0; | ||||
|     let bytesNeeded = 0; | ||||
|     let mask = 1; | ||||
|     var tmpRange = range; | ||||
|     var bitsNeeded = 0; | ||||
|     var bytesNeeded = 0; | ||||
|     var mask = 1; | ||||
|     while (tmpRange > 0) { | ||||
|         if (bitsNeeded % 8 === 0) | ||||
|             bytesNeeded += 1; | ||||
| @@ -137,9 +158,9 @@ function getCryptoRandomInt(min, max) { | ||||
|         mask = mask << 1 | 1; | ||||
|         tmpRange = tmpRange >>> 1; | ||||
|     } | ||||
|     const randomBytes = getRandomBytes(bytesNeeded); | ||||
|     let randomValue = 0; | ||||
|     for (let i = 0; i < bytesNeeded; i++) { | ||||
|     var randomBytes = getRandomBytes(bytesNeeded); | ||||
|     var randomValue = 0; | ||||
|     for (var i = 0; i < bytesNeeded; i++) { | ||||
|         randomValue |= randomBytes[i] << 8 * i; | ||||
|     } | ||||
|     randomValue = randomValue & mask; | ||||
| @@ -151,11 +172,12 @@ function getCryptoRandomInt(min, max) { | ||||
|     } | ||||
| } | ||||
| exports.getCryptoRandomInt = getCryptoRandomInt; | ||||
| function genSecret(length = 64) { | ||||
|     let secret = ""; | ||||
|     const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
|     const charsLength = chars.length; | ||||
|     for (let i = 0; i < length; i++) { | ||||
| function genSecret(length) { | ||||
|     if (length === void 0) { length = 64; } | ||||
|     var secret = ""; | ||||
|     var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
|     var charsLength = chars.length; | ||||
|     for (var i = 0; i < length; i++) { | ||||
|         secret += chars.charAt(getCryptoRandomInt(0, charsLength - 1)); | ||||
|     } | ||||
|     return secret; | ||||
|   | ||||
							
								
								
									
										21
									
								
								src/util.ts
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/util.ts
									
									
									
									
									
								
							| @@ -49,9 +49,26 @@ export function ucfirst(str: string) { | ||||
|     return firstLetter.toUpperCase() + str.substr(1); | ||||
| } | ||||
|  | ||||
| export function debug(msg: any) { | ||||
| // log levels = info / warn / error / debug | ||||
| export function log(module: string, msg: any, level:string = "info") { | ||||
|     module = module.toUpperCase(); | ||||
|     level = level.toUpperCase(); | ||||
|  | ||||
|     const now = new Date().toISOString(); | ||||
|     const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg; | ||||
|  | ||||
|     if (level === "INFO") { | ||||
|         console.log(formattedMessage); | ||||
|     } else if (level === "WARN") { | ||||
|         console.warn(formattedMessage); | ||||
|     } else if (level === "ERROR") { | ||||
|         console.error(formattedMessage); | ||||
|     } else if (level === "DEBUG") { | ||||
|         if (isDev) { | ||||
|         console.log(msg); | ||||
|             console.debug(formattedMessage); | ||||
|         } | ||||
|     } else { | ||||
|         console.log(formattedMessage); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user