diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12fd6ed4..a1a4a982 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,7 @@ Here are some references: - UI/UX is not close to Uptime Kuma - Existing logic is completely modified or deleted for no reason - A function that is completely out of scope +- Convert existing code into other programming languages - Unnecessary large code changes (Hard to review, causes code conflicts to other pull requests) I will mark your pull request in the [milestones](https://github.com/louislam/uptime-kuma/milestones), if I am plan to review and merge it. diff --git a/README.md b/README.md index 55a6ec50..1f26da8b 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ It is a temporary live demo, all data will be deleted after 10 minutes. Use the ## ⭐ Features -* Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / Ping / DNS Record / Push / Steam Game Server / Docker Containers. -* Fancy, Reactive, Fast UI/UX. -* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [90+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications). -* 20 second intervals. +* Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / Ping / DNS Record / Push / Steam Game Server / Docker Containers +* Fancy, Reactive, Fast UI/UX +* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [90+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications) +* 20 second intervals * [Multi Languages](https://github.com/louislam/uptime-kuma/tree/master/src/languages) -* Multiple Status Pages -* Map Status Page to Domain -* Ping Chart -* Certificate Info -* Proxy Support -* 2FA available +* Multiple status pages +* Map status pages to specific domains +* Ping chart +* Certificate info +* Proxy support +* 2FA support ## 🔧 How to Install @@ -44,14 +44,14 @@ docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name upti ⚠️ Please use a **local volume** only. Other types such as NFS are not supported. -Browse to http://localhost:3001 after starting. +Uptime Kuma is now running on http://localhost:3001 ### 💪🏻 Non-Docker Required Tools: - [Node.js](https://nodejs.org/en/download/) >= 14 - [Git](https://git-scm.com/downloads) -- [pm2](https://pm2.keymetrics.io/) - For run in background +- [pm2](https://pm2.keymetrics.io/) - For running Uptime Kuma in the background ```bash # Update your npm to the latest version @@ -73,7 +73,7 @@ pm2 start server/server.js --name uptime-kuma ``` -Browse to http://localhost:3001 after starting. +Uptime Kuma is now running on http://localhost:3001 More useful PM2 Commands diff --git a/package-lock.json b/package-lock.json index 1caf5095..7e88d126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "password-hash": "~1.2.2", "pg": "~8.8.0", "pg-connection-string": "~2.5.0", + "ping": "~0.4.2", "prom-client": "~13.2.0", "prometheus-api-metrics": "~3.2.1", "protobufjs": "~7.1.1", @@ -13501,6 +13502,18 @@ "node": ">=0.10.0" } }, + "node_modules/ping": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.2.tgz", + "integrity": "sha512-1uAw0bzHtrPbPo2s6no06oZAzY6KqKclEJR1JRZKIHKXKlPdrz9N0/1MPPB+BbrvMjN3Mk0pcod3bfLNZFRo9w==", + "dependencies": { + "q": "1.x", + "underscore": "^1.12.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -13891,6 +13904,15 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, "node_modules/qlobber": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", @@ -16121,6 +16143,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -27289,6 +27316,15 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, + "ping": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/ping/-/ping-0.4.2.tgz", + "integrity": "sha512-1uAw0bzHtrPbPo2s6no06oZAzY6KqKclEJR1JRZKIHKXKlPdrz9N0/1MPPB+BbrvMjN3Mk0pcod3bfLNZFRo9w==", + "requires": { + "q": "1.x", + "underscore": "^1.12.0" + } + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -27572,6 +27608,11 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, "qlobber": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", @@ -29292,6 +29333,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 702fda53..ebe305f9 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "password-hash": "~1.2.2", "pg": "~8.8.0", "pg-connection-string": "~2.5.0", + "ping": "~0.4.2", "prom-client": "~13.2.0", "prometheus-api-metrics": "~3.2.1", "protobufjs": "~7.1.1", diff --git a/server/ping-lite.js b/server/ping-lite.js deleted file mode 100644 index 05dff31d..00000000 --- a/server/ping-lite.js +++ /dev/null @@ -1,199 +0,0 @@ -// https://github.com/ben-bradley/ping-lite/blob/master/ping-lite.js -// Fixed on Windows -const net = require("net"); -const spawn = require("child_process").spawn; -const events = require("events"); -const fs = require("fs"); -const util = require("./util-server"); - -module.exports = Ping; - -/** - * Constructor for ping class - * @param {string} host Host to ping - * @param {object} [options] Options for the ping command - * @param {array|string} [options.args] - Arguments to pass to the ping command - */ -function Ping(host, options) { - if (!host) { - throw new Error("You must specify a host to ping!"); - } - - this._host = host; - this._options = options = (options || {}); - - events.EventEmitter.call(this); - - const timeout = 10; - - if (util.WIN) { - this._bin = "c:/windows/system32/ping.exe"; - this._args = (options.args) ? options.args : [ "-n", "1", "-w", timeout * 1000, host ]; - this._regmatch = /[><=]([0-9.]+?)ms/; - - } else if (util.LIN) { - this._bin = "/bin/ping"; - - const defaultArgs = [ "-n", "-w", timeout, "-c", "1", host ]; - - if (net.isIPv6(host) || options.ipv6) { - defaultArgs.unshift("-6"); - } - - this._args = (options.args) ? options.args : defaultArgs; - this._regmatch = /=([0-9.]+?) ms/; - - } else if (util.MAC) { - - if (net.isIPv6(host) || options.ipv6) { - this._bin = "/sbin/ping6"; - } else { - this._bin = "/sbin/ping"; - } - - this._args = (options.args) ? options.args : [ "-n", "-t", timeout, "-c", "1", host ]; - this._regmatch = /=([0-9.]+?) ms/; - - } else if (util.BSD) { - this._bin = "/sbin/ping"; - - const defaultArgs = [ "-n", "-t", timeout, "-c", "1", host ]; - - if (net.isIPv6(host) || options.ipv6) { - defaultArgs.unshift("-6"); - } - - this._args = (options.args) ? options.args : defaultArgs; - this._regmatch = /=([0-9.]+?) ms/; - - } else { - throw new Error("Could not detect your ping binary."); - } - - if (!fs.existsSync(this._bin)) { - throw new Error("Could not detect " + this._bin + " on your system"); - } - - this._i = 0; - - return this; -} - -Ping.prototype.__proto__ = events.EventEmitter.prototype; - -/** - * Callback for send - * @callback pingCB - * @param {any} err Any error encountered - * @param {number} ms Ping time in ms - */ - -/** - * Send a ping - * @param {pingCB} callback Callback to call with results - */ -Ping.prototype.send = function (callback) { - let self = this; - callback = callback || function (err, ms) { - if (err) { - return self.emit("error", err); - } - return self.emit("result", ms); - }; - - let _ended; - let _exited; - let _errored; - - this._ping = spawn(this._bin, this._args, { windowsHide: true }); // spawn the binary - - this._ping.on("error", function (err) { // handle binary errors - _errored = true; - callback(err); - }); - - this._ping.stdout.on("data", function (data) { // log stdout - if (util.WIN) { - data = convertOutput(data); - } - this._stdout = (this._stdout || "") + data; - }); - - this._ping.stdout.on("end", function () { - _ended = true; - if (_exited && !_errored) { - onEnd.call(self._ping); - } - }); - - this._ping.stderr.on("data", function (data) { // log stderr - if (util.WIN) { - data = convertOutput(data); - } - this._stderr = (this._stderr || "") + data; - }); - - this._ping.on("exit", function (code) { // handle complete - _exited = true; - if (_ended && !_errored) { - onEnd.call(self._ping); - } - }); - - /** - * @param {Function} callback - * - * Generated by Trelent - */ - function onEnd() { - let stdout = this.stdout._stdout; - let stderr = this.stderr._stderr; - let ms; - - if (stderr) { - return callback(new Error(stderr)); - } - - if (!stdout) { - return callback(new Error("No stdout detected")); - } - - ms = stdout.match(self._regmatch); // parse out the ##ms response - ms = (ms && ms[1]) ? Number(ms[1]) : ms; - - callback(null, ms, stdout); - } -}; - -/** - * Ping every interval - * @param {pingCB} callback Callback to call with results - */ -Ping.prototype.start = function (callback) { - let self = this; - this._i = setInterval(function () { - self.send(callback); - }, (self._options.interval || 5000)); - self.send(callback); -}; - -/** Stop sending pings */ -Ping.prototype.stop = function () { - clearInterval(this._i); -}; - -/** - * Try to convert to UTF-8 for Windows, as the ping's output on Windows is not UTF-8 and could be in other languages - * Thank @pemassi - * https://github.com/louislam/uptime-kuma/issues/570#issuecomment-941984094 - * @param {any} data - * @returns {string} - */ -function convertOutput(data) { - if (util.WIN) { - if (data) { - return util.convertToUTF8(data); - } - } - return data; -} diff --git a/server/util-server.js b/server/util-server.js index 3a48b5ed..d40dcbdf 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -1,5 +1,5 @@ const tcpp = require("tcp-ping"); -const Ping = require("./ping-lite"); +const ping = require("ping"); const { R } = require("redbean-node"); const { log, genSecret } = require("../src/util"); const passwordHash = require("./password-hash"); @@ -26,12 +26,7 @@ const { } = require("node-radius-utils"); const dayjs = require("dayjs"); -// From ping-lite -exports.WIN = /^win/.test(process.platform); -exports.LIN = /^linux/.test(process.platform); -exports.MAC = /^darwin/.test(process.platform); -exports.FBSD = /^freebsd/.test(process.platform); -exports.BSD = /bsd$/.test(process.platform); +const isWindows = process.platform === /^win/.test(process.platform); /** * Init or reset JWT secret @@ -105,18 +100,23 @@ exports.ping = async (hostname) => { */ exports.pingAsync = function (hostname, ipv6 = false) { return new Promise((resolve, reject) => { - const ping = new Ping(hostname, { - ipv6 - }); - - ping.send(function (err, ms, stdout) { - if (err) { - reject(err); - } else if (ms === null) { - reject(new Error(stdout)); + ping.promise.probe(hostname, { + v6: ipv6, + min_reply: 1, + timeout: 10, + }).then((res) => { + // If ping failed, it will set field to unknown + if (res.alive) { + resolve(res.time); } else { - resolve(Math.round(ms)); + if (isWindows) { + reject(new Error(exports.convertToUTF8(res.output))); + } else { + reject(new Error(res.output)); + } } + }).catch((err) => { + reject(err); }); }); };