diff --git a/extra/reformat-changelog.js b/extra/reformat-changelog.js
new file mode 100644
index 000000000..80a1b725a
--- /dev/null
+++ b/extra/reformat-changelog.js
@@ -0,0 +1,44 @@
+// Generate on GitHub
+const input = `
+* Add Korean translation by @Alanimdeo in https://github.com/louislam/dockge/pull/86
+`;
+
+const template = `
+### 🆕 New Features
+
+### 💇♀️ Improvements
+
+### 🐞 Bug Fixes
+
+### ⬆️ Security Fixes
+
+### 🦎 Translation Contributions
+
+### Others
+- Other small changes, code refactoring and comment/doc updates in this repo:
+`;
+
+const lines = input.split("\n").filter((line) => line.trim() !== "");
+
+for (const line of lines) {
+ // Split the last " by "
+ const usernamePullRequesURL = line.split(" by ").pop();
+
+ if (!usernamePullRequesURL) {
+ console.log("Unable to parse", line);
+ continue;
+ }
+
+ const [ username, pullRequestURL ] = usernamePullRequesURL.split(" in ");
+ const pullRequestID = "#" + pullRequestURL.split("/").pop();
+ let message = line.split(" by ").shift();
+
+ if (!message) {
+ console.log("Unable to parse", line);
+ continue;
+ }
+
+ message = message.split("* ").pop();
+ console.log("-", pullRequestID, message, `(Thanks ${username})`);
+}
+console.log(template);
diff --git a/package.json b/package.json
index dd312fba9..3d4ac3fb0 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"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.23.6 && npm ci --production && npm run download-dist",
+ "setup": "git checkout 1.23.7 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@@ -97,6 +97,7 @@
"express-static-gzip": "~2.1.7",
"form-data": "~4.0.0",
"gamedig": "~4.1.0",
+ "html-escaper": "^3.0.3",
"http-graceful-shutdown": "~3.1.7",
"http-proxy-agent": "~5.0.0",
"https-proxy-agent": "~5.0.1",
diff --git a/server/google-analytics.js b/server/google-analytics.js
index ceae7d2eb..57ae7b754 100644
--- a/server/google-analytics.js
+++ b/server/google-analytics.js
@@ -1,4 +1,5 @@
const jsesc = require("jsesc");
+const { escape } = require("html-escaper");
/**
* Returns a string that represents the javascript that is required to insert the Google Analytics scripts
@@ -7,15 +8,18 @@ const jsesc = require("jsesc");
* @returns {string} HTML script tags to inject into page
*/
function getGoogleAnalyticsScript(tagId) {
- let escapedTagId = jsesc(tagId, { isScriptContext: true });
+ let escapedTagIdJS = jsesc(tagId, { isScriptContext: true });
- if (escapedTagId) {
- escapedTagId = escapedTagId.trim();
+ if (escapedTagIdJS) {
+ escapedTagIdJS = escapedTagIdJS.trim();
}
+ // Escape the tag ID for use in an HTML attribute.
+ let escapedTagIdHTMLAttribute = escape(tagId);
+
return `
-
-
+
+
`;
}
diff --git a/server/model/monitor.js b/server/model/monitor.js
index f2e14269c..4c1dbc453 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -3,7 +3,7 @@ const dayjs = require("dayjs");
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
- SQL_DATETIME_FORMAT, isDev, sleep, getRandomInt
+ SQL_DATETIME_FORMAT
} = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery,
redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@@ -343,16 +343,6 @@ class Monitor extends BeanModel {
}
}
- // Evil
- if (isDev) {
- if (process.env.EVIL_RANDOM_MONITOR_SLEEP === "SURE") {
- if (getRandomInt(0, 100) === 0) {
- log.debug("evil", `[${this.name}] Evil mode: Random sleep: ` + beatInterval * 10000);
- await sleep(beatInterval * 10000);
- }
- }
- }
-
// Expose here for prometheus update
// undefined if not https
let tlsInfo = undefined;
@@ -492,7 +482,7 @@ class Monitor extends BeanModel {
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
- signal: axiosAbortSignal(this.timeout * 1000),
+ signal: axiosAbortSignal((this.timeout + 10) * 1000),
};
if (bodyValue) {
@@ -905,7 +895,11 @@ class Monitor extends BeanModel {
} catch (error) {
- bean.msg = error.message;
+ if (error?.name === "CanceledError") {
+ bean.msg = `timeout by AbortSignal (${this.timeout}s)`;
+ } else {
+ bean.msg = error.message;
+ }
// If UP come in here, it must be upside down mode
// Just reset the retries
@@ -1001,7 +995,6 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] Next heartbeat in: ${intervalRemainingMs}ms`);
this.heartbeatInterval = setTimeout(safeBeat, intervalRemainingMs);
- this.lastScheduleBeatTime = dayjs();
} else {
log.info("monitor", `[${this.name}] isStop = true, no next check.`);
}
@@ -1014,9 +1007,7 @@ class Monitor extends BeanModel {
*/
const safeBeat = async () => {
try {
- this.lastStartBeatTime = dayjs();
await beat();
- this.lastEndBeatTime = dayjs();
} catch (e) {
console.trace(e);
UptimeKumaServer.errorLog(e, false);
@@ -1025,9 +1016,6 @@ class Monitor extends BeanModel {
if (! this.isStop) {
log.info("monitor", "Try to restart the monitor");
this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
- this.lastScheduleBeatTime = dayjs();
- } else {
- log.info("monitor", "isStop = true, no next check.");
}
}
};
diff --git a/server/monitor-types/tailscale-ping.js b/server/monitor-types/tailscale-ping.js
index e5275391a..2f26894fc 100644
--- a/server/monitor-types/tailscale-ping.js
+++ b/server/monitor-types/tailscale-ping.js
@@ -1,6 +1,6 @@
const { MonitorType } = require("./monitor-type");
-const { UP, log } = require("../../src/util");
-const exec = require("child_process").exec;
+const { UP } = require("../../src/util");
+const childProcess = require("child_process");
/**
* A TailscalePing class extends the MonitorType.
@@ -23,7 +23,6 @@ class TailscalePing extends MonitorType {
let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval);
this.parseTailscaleOutput(tailscaleOutput, heartbeat);
} catch (err) {
- log.debug("Tailscale", err);
// trigger log function somewhere to display a notification or alert to the user (but how?)
throw new Error(`Error checking Tailscale ping: ${err}`);
}
@@ -37,26 +36,21 @@ class TailscalePing extends MonitorType {
* @throws Will throw an error if the command execution encounters any error.
*/
async runTailscalePing(hostname, interval) {
- let cmd = `tailscale ping ${hostname}`;
-
- log.debug("Tailscale", cmd);
-
- return new Promise((resolve, reject) => {
- let timeout = interval * 1000 * 0.8;
- exec(cmd, { timeout: timeout }, (error, stdout, stderr) => {
- // we may need to handle more cases if tailscale reports an error that isn't necessarily an error (such as not-logged in or DERP health-related issues)
- if (error) {
- reject(`Execution error: ${error.message}`);
- return;
- }
- if (stderr) {
- reject(`Error in output: ${stderr}`);
- return;
- }
-
- resolve(stdout);
- });
+ let timeout = interval * 1000 * 0.8;
+ let res = childProcess.spawnSync("tailscale", [ "ping", hostname ], {
+ timeout: timeout
});
+ if (res.error) {
+ throw new Error(`Execution error: ${res.error.message}`);
+ }
+ if (res.stderr && res.stderr.toString()) {
+ throw new Error(`Error in output: ${res.stderr.toString()}`);
+ }
+ if (res.stdout && res.stdout.toString()) {
+ return res.stdout.toString();
+ } else {
+ throw new Error("No output from Tailscale ping");
+ }
}
/**
@@ -74,7 +68,7 @@ class TailscalePing extends MonitorType {
heartbeat.status = UP;
let time = line.split(" in ")[1].split(" ")[0];
heartbeat.ping = parseInt(time);
- heartbeat.msg = line;
+ heartbeat.msg = "OK";
break;
} else if (line.includes("timed out")) {
throw new Error(`Ping timed out: "${line}"`);
diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js
index 2ef375dcd..1269bc25e 100644
--- a/server/socket-handlers/general-socket-handler.js
+++ b/server/socket-handlers/general-socket-handler.js
@@ -44,29 +44,45 @@ module.exports.generalSocketHandler = (socket, server) => {
});
socket.on("getGameList", async (callback) => {
- callback({
- ok: true,
- gameList: getGameList(),
- });
- });
-
- socket.on("testChrome", (executable, callback) => {
- // Just noticed that await call could block the whole socket.io server!!! Use pure promise instead.
- testChrome(executable).then((version) => {
+ try {
+ checkLogin(socket);
callback({
ok: true,
- msg: {
- key: "foundChromiumVersion",
- values: [ version ],
- },
- msgi18n: true,
+ gameList: getGameList(),
});
- }).catch((e) => {
+ } catch (e) {
callback({
ok: false,
msg: e.message,
});
- });
+ }
+ });
+
+ socket.on("testChrome", (executable, callback) => {
+ try {
+ checkLogin(socket);
+ // Just noticed that await call could block the whole socket.io server!!! Use pure promise instead.
+ testChrome(executable).then((version) => {
+ callback({
+ ok: true,
+ msg: {
+ key: "foundChromiumVersion",
+ values: [ version ],
+ },
+ msgi18n: true,
+ });
+ }).catch((e) => {
+ callback({
+ ok: false,
+ msg: e.message,
+ });
+ });
+ } catch (e) {
+ callback({
+ ok: false,
+ msg: e.message,
+ });
+ }
});
socket.on("getPushExample", (language, callback) => {
diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js
index 1673a6012..3944a7e69 100644
--- a/server/uptime-kuma-server.js
+++ b/server/uptime-kuma-server.js
@@ -12,7 +12,6 @@ const { Settings } = require("./settings");
const dayjs = require("dayjs");
const childProcess = require("child_process");
const path = require("path");
-const axios = require("axios");
// DO NOT IMPORT HERE IF THE MODULES USED `UptimeKumaServer.getInstance()`, put at the bottom of this file instead.
/**
@@ -62,8 +61,6 @@ class UptimeKumaServer {
*/
jwtSecret = null;
- checkMonitorsInterval = null;
-
/**
* Get the current instance of the server if it exists, otherwise
* create a new instance.
@@ -376,10 +373,6 @@ class UptimeKumaServer {
if (enable || enable === null) {
this.startNSCDServices();
}
-
- this.checkMonitorsInterval = setInterval(() => {
- this.checkMonitors();
- }, 60 * 1000);
}
/**
@@ -392,8 +385,6 @@ class UptimeKumaServer {
if (enable || enable === null) {
this.stopNSCDServices();
}
-
- clearInterval(this.checkMonitorsInterval);
}
/**
@@ -427,83 +418,6 @@ class UptimeKumaServer {
}
}
- /**
- * Start the specified monitor
- * @param {number} monitorID ID of monitor to start
- * @returns {Promise}
- */
- async startMonitor(monitorID) {
- log.info("manage", `Resume Monitor: ${monitorID} by server`);
-
- await R.exec("UPDATE monitor SET active = 1 WHERE id = ?", [
- monitorID,
- ]);
-
- let monitor = await R.findOne("monitor", " id = ? ", [
- monitorID,
- ]);
-
- if (monitor.id in this.monitorList) {
- this.monitorList[monitor.id].stop();
- }
-
- this.monitorList[monitor.id] = monitor;
- monitor.start(this.io);
- }
-
- /**
- * Restart a given monitor
- * @param {number} monitorID ID of monitor to start
- * @returns {Promise}
- */
- async restartMonitor(monitorID) {
- return await this.startMonitor(monitorID);
- }
-
- /**
- * Check if monitors are running properly
- */
- async checkMonitors() {
- log.debug("monitor_checker", "Checking monitors");
-
- for (let monitorID in this.monitorList) {
- let monitor = this.monitorList[monitorID];
-
- // Not for push monitor
- if (monitor.type === "push") {
- continue;
- }
-
- if (!monitor.active) {
- continue;
- }
-
- // Check the lastStartBeatTime, if it is too long, then restart
- if (monitor.lastScheduleBeatTime ) {
- let diff = dayjs().diff(monitor.lastStartBeatTime, "second");
-
- if (diff > monitor.interval * 1.5) {
- log.error("monitor_checker", `Monitor Interval: ${monitor.interval} Monitor ` + monitorID + " lastStartBeatTime diff: " + diff);
- log.error("monitor_checker", "Unexpected error: Monitor " + monitorID + " is struck for unknown reason");
- log.error("monitor_checker", "Last start beat time: " + R.isoDateTime(monitor.lastStartBeatTime));
- log.error("monitor_checker", "Last end beat time: " + R.isoDateTime(monitor.lastEndBeatTime));
- log.error("monitor_checker", "Last ScheduleBeatTime: " + R.isoDateTime(monitor.lastScheduleBeatTime));
-
- // Restart
- log.error("monitor_checker", `Restarting monitor ${monitorID} automatically now`);
- this.restartMonitor(monitorID);
- } else {
- //log.debug("monitor_checker", "Monitor " + monitorID + " is running normally");
- }
- } else {
- //log.debug("monitor_checker", "Monitor " + monitorID + " is not started yet, skipp");
- }
-
- }
-
- log.debug("monitor_checker", "Checking monitors end");
- }
-
/**
* Default User-Agent when making HTTP requests
* @returns {string} User-Agent
diff --git a/server/util-server.js b/server/util-server.js
index a155588f8..3e95e70aa 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -461,6 +461,7 @@ exports.postgresQuery = function (connectionString, query) {
});
} catch (e) {
reject(e);
+ client.end();
}
}
});
@@ -1154,7 +1155,6 @@ module.exports.axiosAbortSignal = (timeoutMs) => {
// v16-: AbortSignal.timeout is not supported
try {
const abortController = new AbortController();
-
setTimeout(() => abortController.abort(), timeoutMs);
return abortController.signal;
diff --git a/src/util.js b/src/util.js
index f6ed5cd99..c0710a204 100644
--- a/src/util.js
+++ b/src/util.js
@@ -123,6 +123,9 @@ class Logger {
}
}
log(module, msg, level) {
+ if (level === "DEBUG" && !exports.isDev) {
+ return;
+ }
if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) {
return;
}
diff --git a/src/util.ts b/src/util.ts
index 0f8981102..f1f812938 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -182,6 +182,10 @@ class Logger {
* @param level Log level. One of INFO, WARN, ERROR, DEBUG or can be customized.
*/
log(module: string, msg: any, level: string) {
+ if (level === "DEBUG" && !isDev) {
+ return;
+ }
+
if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) {
return;
}