Added JSDoc to ESLint (#3529)

* Added JSDoc to eslint rules

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>

* Fixed JSDoc eslint errors

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>

* Update the check-linters workflow to Node.js 20

---------

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
npm-publish
Matthew Nickson 9 months ago committed by GitHub
parent da4f4e3d76
commit 8a92054c2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,7 @@ module.exports = {
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"plugin:jsdoc/recommended-error",
],
parser: "vue-eslint-parser",
parserOptions: {
@ -21,6 +22,9 @@ module.exports = {
sourceType: "module",
requireConfigFile: false,
},
plugins: [
"jsdoc"
],
rules: {
"yoda": "error",
eqeqeq: [ "warn", "smart" ],
@ -97,7 +101,42 @@ module.exports = {
}],
"no-control-regex": "off",
"one-var": [ "error", "never" ],
"max-statements-per-line": [ "error", { "max": 1 }]
"max-statements-per-line": [ "error", { "max": 1 }],
"jsdoc/check-tag-names": [
"error",
{
"definedTags": [ "link" ]
}
],
"jsdoc/no-undefined-types": "off",
"jsdoc/no-defaults": [
"error",
{ "noOptionalParamNames": true }
],
"jsdoc/require-throws": "error",
"jsdoc/require-jsdoc": [
"error",
{
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
}
}
],
"jsdoc/no-blank-block-descriptions": "error",
"jsdoc/require-returns-check": [
"error",
{ "reportMissingReturnForUndefinedTypes": false }
],
"jsdoc/require-returns": [
"error",
{
"forceRequireReturn": true,
"forceReturnsWithAsync": true
}
],
"jsdoc/require-param-type": "error",
"jsdoc/require-param-description": "error"
},
"overrides": [
{

@ -71,10 +71,10 @@ jobs:
- run: git config --global core.autocrlf false # Mainly for Windows
- uses: actions/checkout@v3
- name: Use Node.js 14
- name: Use Node.js 20
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 20
- run: npm install
- run: npm run lint

@ -36,6 +36,8 @@ if (! exists) {
/**
* Commit updated files
* @param {string} version Version to update to
* @returns {void}
* @throws Error committing files
*/
function commit(version) {
let msg = "Update to " + version;
@ -55,6 +57,7 @@ function commit(version) {
/**
* Create a tag with the specified version
* @param {string} version Tag to create
* @returns {void}
*/
function tag(version) {
let res = childProcess.spawnSync("git", [ "tag", version ]);
@ -68,6 +71,7 @@ function tag(version) {
* Check if a tag exists for the specified version
* @param {string} version Version to check
* @returns {boolean} Does the tag already exist
* @throws Version is not valid
*/
function tagExists(version) {
if (! version) {

@ -15,6 +15,7 @@ download(url);
/**
* Downloads the latest version of the dist from a GitHub release.
* @param {string} url The URL to download from.
* @returns {void}
*
* Generated by Trelent
*/

@ -4,12 +4,12 @@ const fs = require("fs");
* to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16,
* or the `recursive` property removing completely in the future Node.js version.
* See the link below.
*
* @todo Once we drop the support for Node.js v14 (or at least versions before v14.14.0), we can safely replace this function with `fs.rmSync`, since `fs.rmSync` was add in Node.js v14.14.0 and currently we supports all the Node.js v14 versions that include the versions before the v14.14.0, and this function have almost the same signature with `fs.rmSync`.
* @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation infomation of `fs.rmdirSync`
* @link https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options the document of `fs.rmSync`
* @param {fs.PathLike} path Valid types for path values in "fs".
* @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`.
* @param {fs.RmDirOptions} options options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`.
* @returns {void}
*/
const rmSync = (path, options) => {
if (typeof fs.rmSync === "function") {

@ -138,7 +138,7 @@ server.listen({
/**
* Get human readable request type from request code
* @param {number} code Request code to translate
* @returns {string} Human readable request type
* @returns {string|void} Human readable request type
*/
function type(code) {
for (let name in Packet.TYPE) {

@ -7,11 +7,17 @@ class SimpleMqttServer {
aedes = require("aedes")();
server = require("net").createServer(this.aedes.handle);
/**
* @param {number} port Port to listen on
*/
constructor(port) {
this.port = port;
}
/** Start the MQTT server */
/**
* Start the MQTT server
* @returns {void}
*/
start() {
this.server.listen(this.port, () => {
console.log("server started and listening on port ", this.port);

@ -12,6 +12,7 @@ import rmSync from "../fs-rmSync.js";
* created with this code if one does not already exist
* @param {string} baseLang The second base language file to copy. This
* will be ignored if set to "en" as en.js is copied by default
* @returns {void}
*/
function copyFiles(langCode, baseLang) {
if (fs.existsSync("./languages")) {
@ -33,7 +34,8 @@ function copyFiles(langCode, baseLang) {
/**
* Update the specified language file
* @param {string} langCode Language code to update
* @param {string} baseLang Second language to copy keys from
* @param {string} baseLangCode Second language to copy keys from
* @returns {void}
*/
async function updateLanguage(langCode, baseLangCode) {
const en = (await import("./languages/en.js")).default;

@ -39,6 +39,8 @@ if (! exists) {
/**
* Commit updated files
* @param {string} version Version to update to
* @returns {void}
* @throws Error when committing files
*/
function commit(version) {
let msg = "Update to " + version;
@ -55,6 +57,7 @@ function commit(version) {
/**
* Create a tag with the specified version
* @param {string} version Tag to create
* @returns {void}
*/
function tag(version) {
let res = childProcess.spawnSync("git", [ "tag", version ]);
@ -65,6 +68,7 @@ function tag(version) {
* Check if a tag exists for the specified version
* @param {string} version Version to check
* @returns {boolean} Does the tag already exist
* @throws Version is not valid
*/
function tagExists(version) {
if (! version) {

@ -13,6 +13,7 @@ updateWiki(newVersion);
/**
* Update the wiki with new version number
* @param {string} newVersion Version to update to
* @returns {void}
*/
function updateWiki(newVersion) {
const wikiDir = "./tmp/wiki";
@ -46,6 +47,7 @@ function updateWiki(newVersion) {
/**
* Check if a directory exists and then delete it
* @param {string} dir Directory to delete
* @returns {void}
*/
function safeDelete(dir) {
if (fs.existsSync(dir)) {

104
package-lock.json generated

@ -103,6 +103,7 @@
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"eslint": "~8.14.0",
"eslint-plugin-jsdoc": "^46.4.6",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10",
"jest": "~29.6.1",
@ -3456,6 +3457,20 @@
"ms": "^2.1.1"
}
},
"node_modules/@es-joy/jsdoccomment": {
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz",
"integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==",
"dev": true,
"dependencies": {
"comment-parser": "1.4.0",
"esquery": "^1.5.0",
"jsdoc-type-pratt-parser": "~4.0.0"
},
"engines": {
"node": ">=16"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@ -6227,6 +6242,15 @@
}
]
},
"node_modules/are-docs-informative": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
"integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
"dev": true,
"engines": {
"node": ">=14"
}
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
@ -6992,6 +7016,18 @@
"node": ">=10.0.0"
}
},
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/bulk-write-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bulk-write-stream/-/bulk-write-stream-2.0.1.tgz",
@ -7533,6 +7569,15 @@
"node": ">= 6"
}
},
"node_modules/comment-parser": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz",
"integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==",
"dev": true,
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/commist": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
@ -8967,6 +9012,41 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "46.4.6",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz",
"integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==",
"dev": true,
"dependencies": {
"@es-joy/jsdoccomment": "~0.40.1",
"are-docs-informative": "^0.0.2",
"comment-parser": "1.4.0",
"debug": "^4.3.4",
"escape-string-regexp": "^4.0.0",
"esquery": "^1.5.0",
"is-builtin-module": "^3.2.1",
"semver": "^7.5.4",
"spdx-expression-parse": "^3.0.1"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-plugin-vue": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz",
@ -10845,6 +10925,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-builtin-module": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
"dev": true,
"dependencies": {
"builtin-modules": "^3.3.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@ -13025,6 +13120,15 @@
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
"node_modules/jsdoc-type-pratt-parser": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz",
"integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==",
"dev": true,
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/jsesc": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",

@ -165,6 +165,7 @@
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"eslint": "~8.14.0",
"eslint-plugin-jsdoc": "^46.4.6",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10",
"jest": "~29.6.1",

@ -9,9 +9,9 @@ const dayjs = require("dayjs");
/**
* Login to web app
* @param {string} username
* @param {string} password
* @returns {Promise<(Bean|null)>}
* @param {string} username Username to login with
* @param {string} password Password to login with
* @returns {Promise<(Bean|null)>} User or null if login failed
*/
exports.login = async function (username, password) {
if (typeof username !== "string" || typeof password !== "string") {
@ -39,6 +39,7 @@ exports.login = async function (username, password) {
/**
* Validate a provided API key
* @param {string} key API key to verify
* @returns {boolean} API is ok?
*/
async function verifyAPIKey(key) {
if (typeof key !== "string") {
@ -73,9 +74,10 @@ async function verifyAPIKey(key) {
/**
* Custom authorizer for express-basic-auth
* @param {string} username
* @param {string} password
* @param {authCallback} callback
* @param {string} username Username to login with
* @param {string} password Password to login with
* @param {authCallback} callback Callback to handle login result
* @returns {void}
*/
function apiAuthorizer(username, password, callback) {
// API Rate Limit
@ -99,9 +101,10 @@ function apiAuthorizer(username, password, callback) {
/**
* Custom authorizer for express-basic-auth
* @param {string} username
* @param {string} password
* @param {authCallback} callback
* @param {string} username Username to login with
* @param {string} password Password to login with
* @param {authCallback} callback Callback to handle login result
* @returns {void}
*/
function userAuthorizer(username, password, callback) {
// Login Rate Limit
@ -126,7 +129,8 @@ function userAuthorizer(username, password, callback) {
* Use basic auth if auth is not disabled
* @param {express.Request} req Express request object
* @param {express.Response} res Express response object
* @param {express.NextFunction} next
* @param {express.NextFunction} next Next handler in chain
* @returns {void}
*/
exports.basicAuth = async function (req, res, next) {
const middleware = basicAuth({
@ -148,7 +152,8 @@ exports.basicAuth = async function (req, res, next) {
* Use use API Key if API keys enabled, else use basic auth
* @param {express.Request} req Express request object
* @param {express.Response} res Express response object
* @param {express.NextFunction} next
* @param {express.NextFunction} next Next handler in chain
* @returns {void}
*/
exports.apiAuth = async function (req, res, next) {
if (!await Settings.get("disableAuth")) {

@ -15,6 +15,7 @@ class CacheableDnsHttpAgent {
/**
* Register/Disable cacheable to global agents
* @returns {void}
*/
static async update() {
log.debug("CacheableDnsHttpAgent", "update");
@ -40,14 +41,15 @@ class CacheableDnsHttpAgent {
/**
* Attach cacheable to HTTP agent
* @param {http.Agent} agent Agent to install
* @returns {void}
*/
static install(agent) {
this.cacheable.install(agent);
}
/**
* @var {https.AgentOptions} agentOptions
* @return {https.Agent}
* @param {https.AgentOptions} agentOptions Options to pass to HTTPS agent
* @returns {https.Agent} The new HTTPS agent
*/
static getHttpsAgent(agentOptions) {
if (!this.enable) {
@ -63,8 +65,8 @@ class CacheableDnsHttpAgent {
}
/**
* @var {http.AgentOptions} agentOptions
* @return {https.Agents}
* @param {http.AgentOptions} agentOptions Options to pass to the HTTP agent
* @returns {https.Agents} The new HTTP agent
*/
static getHttpAgent(agentOptions) {
if (!this.enable) {

@ -12,7 +12,7 @@ const checkVersion = require("./check-version");
/**
* Send list of notification providers to client
* @param {Socket} socket Socket.io socket instance
* @returns {Promise<Bean[]>}
* @returns {Promise<Bean[]>} List of notifications
*/
async function sendNotificationList(socket) {
const timeLogger = new TimeLogger();
@ -40,8 +40,8 @@ async function sendNotificationList(socket) {
* Send Heartbeat History list to socket
* @param {Socket} socket Socket.io instance
* @param {number} monitorID ID of monitor to send heartbeat history
* @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list
* @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} overwrite Overwrite client-side's heartbeat list
* @returns {Promise<void>}
*/
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
@ -71,8 +71,8 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite =
* Important Heart beat list (aka event list)
* @param {Socket} socket Socket.io instance
* @param {number} monitorID ID of monitor to send heartbeat history
* @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list
* @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} overwrite Overwrite client-side's heartbeat list
* @returns {Promise<void>}
*/
async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
@ -100,7 +100,7 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
/**
* Emit proxy list to client
* @param {Socket} socket Socket.io socket instance
* @return {Promise<Bean[]>}
* @returns {Promise<Bean[]>} List of proxies
*/
async function sendProxyList(socket) {
const timeLogger = new TimeLogger();
@ -141,7 +141,7 @@ async function sendAPIKeyList(socket) {
/**
* Emits the version information to the client.
* @param {Socket} socket Socket.io socket instance
* @param {boolean} hideVersion
* @param {boolean} hideVersion Should we hide the version information in the response?
* @returns {Promise<void>}
*/
async function sendInfo(socket, hideVersion = false) {
@ -165,7 +165,7 @@ async function sendInfo(socket, hideVersion = false) {
/**
* Send list of docker hosts to client
* @param {Socket} socket Socket.io socket instance
* @returns {Promise<Bean[]>}
* @returns {Promise<Bean[]>} List of docker hosts
*/
async function sendDockerHostList(socket) {
const timeLogger = new TimeLogger();

@ -101,7 +101,8 @@ class Database {
/**
* Initialize the data directory
* @param {Object} args Arguments to initialize DB with
* @param {object} args Arguments to initialize DB with
* @returns {void}
*/
static initDataDir(args) {
// Data Directory (must be end with "/")
@ -154,11 +155,11 @@ class Database {
/**
* Connect to the database
* @param {boolean} [testMode=false] Should the connection be
* @param {boolean} testMode Should the connection be
* started in test mode?
* @param {boolean} [autoloadModels=true] Should models be
* @param {boolean} autoloadModels Should models be
* automatically loaded?
* @param {boolean} [noLog=false] Should logs not be output?
* @param {boolean} noLog Should logs not be output?
* @returns {Promise<void>}
*/
static async connect(testMode = false, autoloadModels = true, noLog = false) {
@ -312,6 +313,10 @@ class Database {
}
}
/**
* Patch the database
* @returns {void}
*/
static async patch() {
// Still need to keep this for old versions of Uptime Kuma
if (Database.dbConfig.type === "sqlite") {
@ -497,8 +502,8 @@ class Database {
* Patch database using new patching process
* Used it patch2() only
* @private
* @param sqlFilename
* @param databasePatchedFiles
* @param {string} sqlFilename Name of SQL file to load
* @param {object} databasePatchedFiles Patch status of database files
* @returns {Promise<void>}
*/
static async patch2Recursion(sqlFilename, databasePatchedFiles) {
@ -533,7 +538,7 @@ class Database {
/**
* Load an SQL file and execute it
* @param filename Filename of SQL file to import
* @param {string} filename Filename of SQL file to import
* @returns {Promise<void>}
*/
static async importSQLFile(filename) {
@ -567,7 +572,7 @@ class Database {
/**
* Aquire a direct connection to database
* @returns {any}
* @returns {any} Database connection
*/
static getBetterSQLite3Database() {
return R.knex.client.acquireConnection();
@ -604,7 +609,10 @@ class Database {
process.removeListener("unhandledRejection", listener);
}
/** Get the size of the database */
/**
* Get the size of the database
* @returns {number} Size of database
*/
static getSize() {
log.debug("db", "Database.getSize()");
let stats = fs.statSync(Database.sqlitePath);

@ -14,10 +14,10 @@ class DockerHost {
/**
* Save a docker host
* @param {Object} dockerHost Docker host to save
* @param {object} dockerHost Docker host to save
* @param {?number} dockerHostID ID of the docker host to update
* @param {number} userID ID of the user who adds the docker host
* @returns {Promise<Bean>}
* @returns {Promise<Bean>} Updated docker host
*/
static async save(dockerHost, dockerHostID, userID) {
let bean;
@ -64,7 +64,7 @@ class DockerHost {
/**
* Fetches the amount of containers on the Docker host
* @param {Object} dockerHost Docker host to check for
* @param {object} dockerHost Docker host to check for
* @returns {number} Total amount of containers on the host
*/
static async testDockerHost(dockerHost) {
@ -108,6 +108,8 @@ class DockerHost {
/**
* Since axios 0.27.X, it does not accept `tcp://` protocol.
* Change it to `http://` on the fly in order to fix it. (https://github.com/louislam/uptime-kuma/issues/2165)
* @param {any} url URL to fix
* @returns {any} URL with tcp:// replaced by http://
*/
static patchDockerURL(url) {
if (typeof url === "string") {
@ -129,11 +131,10 @@ class DockerHost {
* 'data/docker-tls/example.com/' would be searched for certificate files),
* then 'ca.pem', 'key.pem' and 'cert.pem' files are included in the agent options.
* File names can also be overridden via 'DOCKER_TLS_FILE_NAME_(CA|KEY|CERT)'.
*
* @param {String} dockerType i.e. "tcp" or "socket"
* @param {String} url The docker host URL rewritten to https://
* @return {Object}
* */
* @param {string} dockerType i.e. "tcp" or "socket"
* @param {string} url The docker host URL rewritten to https://
* @returns {object} HTTP agent options
*/
static getHttpsAgentOptions(dockerType, url) {
let baseOptions = {
maxCachedSessions: 0,

@ -3,8 +3,8 @@ const jsesc = require("jsesc");
/**
* Returns a string that represents the javascript that is required to insert the Google Analytics scripts
* into a webpage.
* @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script.
* @returns {string}
* @param {string} tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script.
* @returns {string} HTML script tags to inject into page
*/
function getGoogleAnalyticsScript(tagId) {
let escapedTagId = jsesc(tagId, { isScriptContext: true });

@ -10,7 +10,7 @@ let ImageDataURI = (() => {
/**
* Decode the data:image/ URI
* @param {string} dataURI data:image/ URI to decode
* @returns {?Object} An object with properties "imageType" and "dataBase64".
* @returns {?object} An object with properties "imageType" and "dataBase64".
* The former is the image type, e.g., "png", and the latter is a base64
* encoded string of the image's binary data. If it fails to parse, returns
* null instead of an object.
@ -52,8 +52,8 @@ let ImageDataURI = (() => {
/**
* Write data URI to file
* @param {string} dataURI data:image/ URI
* @param {string} [filePath] Path to write file to
* @returns {Promise<string>}
* @param {string} filePath Path to write file to
* @returns {Promise<string|void>} Write file error
*/
function outputFile(dataURI, filePath) {
filePath = filePath || "./";

@ -39,7 +39,10 @@ const initBackgroundJobs = async function () {
};
/** Stop all background jobs if running */
/**
* Stop all background jobs if running
* @returns {void}
*/
const stopBackgroundJobs = function () {
for (const job of jobs) {
if (job.croner) {

@ -7,7 +7,7 @@ const DEFAULT_KEEP_PERIOD = 180;
/**
* Clears old data from the heartbeat table of the database.
* @return {Promise<void>} A promise that resolves when the data has been cleared.
* @returns {Promise<void>} A promise that resolves when the data has been cleared.
*/
const clearOldData = async () => {

@ -4,7 +4,7 @@ const Database = require("../database");
/**
* Run incremental_vacuum and checkpoint the WAL.
* @return {Promise<void>} A promise that resolves when the process is finished.
* @returns {Promise<void>} A promise that resolves when the process is finished.
*/
const incrementalVacuum = async () => {

@ -19,7 +19,7 @@ class APIKey extends BeanModel {
/**
* Returns an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
toJSON() {
return {
@ -37,7 +37,7 @@ class APIKey extends BeanModel {
/**
* Returns an object that ready to parse to JSON with sensitive fields
* removed
* @returns {Object}
* @returns {object} Object ready to parse
*/
toPublicJSON() {
return {
@ -53,9 +53,9 @@ class APIKey extends BeanModel {
/**
* Create a new API Key and store it in the database
* @param {Object} key Object sent by client
* @param {object} key Object sent by client
* @param {int} userID ID of socket user
* @returns {Promise<bean>}
* @returns {Promise<bean>} API key
*/
static async save(key, userID) {
let bean;

@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
class DockerHost extends BeanModel {
/**
* Returns an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
toJSON() {
return {

@ -4,10 +4,12 @@ const { R } = require("redbean-node");
class Group extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @param {boolean} [showTags=false] Should the JSON include monitor tags
* @returns {Object}
* Return an object that ready to parse to JSON for public Only show
* necessary data to public
* @param {boolean} showTags Should the JSON include monitor tags
* @param {boolean} certExpiry Should JSON include info about
* certificate expiry?
* @returns {object} Object ready to parse
*/
async toPublicJSON(showTags = false, certExpiry = false) {
let monitorBeanList = await this.getMonitorList();
@ -27,7 +29,7 @@ class Group extends BeanModel {
/**
* Get all monitors
* @returns {Bean[]}
* @returns {Bean[]} List of monitors
*/
async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(`

@ -12,7 +12,7 @@ class Heartbeat extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
* @returns {object} Object ready to parse
*/
toPublicJSON() {
return {
@ -25,7 +25,7 @@ class Heartbeat extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
toJSON() {
return {

@ -5,7 +5,7 @@ class Incident extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
* @returns {object} Object ready to parse
*/
toPublicJSON() {
return {

@ -11,7 +11,7 @@ class Maintenance extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
* @returns {object} Object ready to parse
*/
async toPublicJSON() {
@ -98,7 +98,7 @@ class Maintenance extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @param {string} timezone If not specified, the timeRange will be in UTC
* @returns {Object}
* @returns {object} Object ready to parse
*/
async toJSON(timezone = null) {
return this.toPublicJSON(timezone);
@ -142,7 +142,7 @@ class Maintenance extends BeanModel {
/**
* Convert data from socket to bean
* @param {Bean} bean Bean to fill in
* @param {Object} obj Data to fill bean with
* @param {object} obj Data to fill bean with
* @returns {Bean} Filled bean
*/
static async jsonToBean(bean, obj) {
@ -188,7 +188,7 @@ class Maintenance extends BeanModel {
/**
* Throw error if cron is invalid
* @param cron
* @param {string|Date} cron Pattern or date
* @returns {Promise<void>}
*/
static async validateCron(cron) {
@ -198,6 +198,8 @@ class Maintenance extends BeanModel {
/**
* Run the cron
* @param {boolean} throwError Should an error be thrown on failure
* @returns {Promise<void>}
*/
async run(throwError = false) {
if (this.beanMeta.job) {
@ -290,6 +292,10 @@ class Maintenance extends BeanModel {
}
}
/**
* Get timeslots where maintenance is running
* @returns {object|null} Maintenance time slot
*/
getRunningTimeslot() {
let start = dayjs(this.beanMeta.job.nextRun(dayjs().add(-this.duration, "second").toDate()));
let end = start.add(this.duration, "second");
@ -305,6 +311,10 @@ class Maintenance extends BeanModel {
}
}
/**
* Stop the maintenance
* @returns {void}
*/
stop() {
if (this.beanMeta.job) {
this.beanMeta.job.stop();
@ -312,10 +322,18 @@ class Maintenance extends BeanModel {
}
}
/**
* Is this maintenance currently active
* @returns {boolean} The maintenance is active?
*/
async isUnderMaintenance() {
return (await this.getStatus()) === "under-maintenance";
}
/**
* Get the timezone of the maintenance
* @returns {string} timezone
*/
async getTimezone() {
if (!this.timezone || this.timezone === "SAME_AS_SERVER") {
return await UptimeKumaServer.getInstance().getTimezone();
@ -323,10 +341,18 @@ class Maintenance extends BeanModel {
return this.timezone;
}
/**
* Get offset for timezone
* @returns {string} offset
*/
async getTimezoneOffset() {
return dayjs.tz(dayjs(), await this.getTimezone()).format("Z");
}
/**
* Get the current status of the maintenance
* @returns {string} Current status
*/
async getStatus() {
if (!this.active) {
return "inactive";

@ -34,9 +34,12 @@ const Database = require("../database");
class Monitor extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
* Return an object that ready to parse to JSON for public Only show
* necessary data to public
* @param {boolean} showTags Include tags in JSON
* @param {boolean} certExpiry Include certificate expiry info in
* JSON
* @returns {object} Object ready to parse
*/
async toPublicJSON(showTags = false, certExpiry = false) {
let obj = {
@ -65,7 +68,9 @@ class Monitor extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
* @param {boolean} includeSensitiveData Include sensitive data in
* JSON
* @returns {object} Object ready to parse
*/
async toJSON(includeSensitiveData = true) {
@ -183,9 +188,9 @@ class Monitor extends BeanModel {
}
/**
* Checks if the monitor is active based on itself and its parents
* @returns {Promise<Boolean>}
*/
* Checks if the monitor is active based on itself and its parents
* @returns {Promise<boolean>} Is the monitor active?
*/
async isActive() {
const parentActive = await Monitor.isParentActive(this.id);
@ -194,7 +199,8 @@ class Monitor extends BeanModel {
/**
* Get all tags applied to this monitor
* @returns {Promise<LooseObject<any>[]>}
* @returns {Promise<LooseObject<any>[]>} List of tags on the
* monitor
*/
async getTags() {
return await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ? ORDER BY tag.name", [ this.id ]);
@ -203,7 +209,8 @@ class Monitor extends BeanModel {
/**
* Gets certificate expiry for this monitor
* @param {number} monitorID ID of monitor to send
* @returns {Promise<LooseObject<any>>}
* @returns {Promise<LooseObject<any>>} Certificate expiry info for
* monitor
*/
async getCertExpiry(monitorID) {
let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
@ -228,7 +235,9 @@ class Monitor extends BeanModel {
/**
* Encode user and password to Base64 encoding
* for HTTP "basic" auth, as per RFC-7617
* @returns {string}
* @param {string} user Username to encode
* @param {string} pass Password to encode
* @returns {string} Encoded username:password
*/
encodeBase64(user, pass) {
return Buffer.from(user + ":" + pass).toString("base64");
@ -236,7 +245,7 @@ class Monitor extends BeanModel {
/**
* Is the TLS expiry notification enabled?
* @returns {boolean}
* @returns {boolean} Enabled?
*/
isEnabledExpiryNotification() {
return Boolean(this.expiryNotification);
@ -244,7 +253,7 @@ class Monitor extends BeanModel {
/**
* Parse to boolean
* @returns {boolean}
* @returns {boolean} Should TLS errors be ignored?
*/
getIgnoreTls() {
return Boolean(this.ignoreTls);
@ -252,7 +261,7 @@ class Monitor extends BeanModel {
/**
* Parse to boolean
* @returns {boolean}
* @returns {boolean} Is the monitor in upside down mode?
*/
isUpsideDown() {
return Boolean(this.upsideDown);
@ -260,7 +269,7 @@ class Monitor extends BeanModel {
/**
* Parse to boolean
* @returns {boolean}
* @returns {boolean} Invert keyword match?
*/
isInvertKeyword() {
return Boolean(this.invertKeyword);
@ -268,7 +277,7 @@ class Monitor extends BeanModel {
/**
* Parse to boolean
* @returns {boolean}
* @returns {boolean} Enable TLS for gRPC?
*/
getGrpcEnableTls() {
return Boolean(this.grpcEnableTls);
@ -276,7 +285,7 @@ class Monitor extends BeanModel {
/**
* Get accepted status codes
* @returns {Object}
* @returns {object} Accepted status codes
*/
getAcceptedStatuscodes() {
return JSON.parse(this.accepted_statuscodes_json);
@ -289,6 +298,7 @@ class Monitor extends BeanModel {
/**
* Start monitor
* @param {Server} io Socket server instance
* @returns {void}
*/
start(io) {
let previousBeat = null;
@ -980,7 +990,10 @@ class Monitor extends BeanModel {
};
/** Get a heartbeat and handle errors */
/**
* Get a heartbeat and handle errors7
* @returns {void}
*/
const safeBeat = async () => {
try {
await beat();
@ -1008,10 +1021,10 @@ class Monitor extends BeanModel {
/**
* Make a request using axios
* @param {Object} options Options for Axios
* @param {object} options Options for Axios
* @param {boolean} finalCall Should this be the final call i.e
* don't retry on faliure
* @returns {Object} Axios response
* don't retry on failure
* @returns {object} Axios response
*/
async makeAxiosRequest(options, finalCall = false) {
try {
@ -1046,7 +1059,10 @@ class Monitor extends BeanModel {
}
}
/** Stop monitor */
/**
* Stop monitor
* @returns {void}
*/
stop() {
clearTimeout(this.heartbeatInterval);
this.isStop = true;
@ -1056,7 +1072,7 @@ class Monitor extends BeanModel {
/**
* Get prometheus instance
* @returns {Prometheus|undefined}
* @returns {Prometheus|undefined} Current prometheus instance
*/
getPrometheus() {
return this.prometheus;
@ -1066,7 +1082,7 @@ class Monitor extends BeanModel {
* Helper Method:
* returns URL object for further usage
* returns null if url is invalid
* @returns {(null|URL)}
* @returns {(null|URL)} Monitor URL
*/
getUrl() {
try {
@ -1078,8 +1094,8 @@ class Monitor extends BeanModel {
/**
* Store TLS info to database
* @param checkCertificateResult
* @returns {Promise<Object>}
* @param {object} checkCertificateResult Certificate to update
* @returns {Promise<object>} Updated certificate
*/
async updateTlsInfo(checkCertificateResult) {
let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
@ -1126,6 +1142,7 @@ class Monitor extends BeanModel {
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
* @returns {void}
*/
static async sendStats(io, monitorID, userID) {
const hasClients = getTotalClientInRoom(io, userID) > 0;
@ -1143,6 +1160,10 @@ class Monitor extends BeanModel {
/**
* Send the average ping to user
* @param {number} duration Hours
* @param {Server} io Socket instance to send data to
* @param {number} monitorID ID of monitor to read
* @param {number} userID ID of user to send data to
* @returns {void}
*/
static async sendAvgPing(duration, io, monitorID, userID) {
const timeLogger = new TimeLogger();
@ -1168,6 +1189,7 @@ class Monitor extends BeanModel {
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
* @returns {void}
*/
static async sendCertInfo(io, monitorID, userID) {
let tlsInfo = await R.findOne("monitor_tls_info", "monitor_id = ?", [
@ -1184,6 +1206,8 @@ class Monitor extends BeanModel {
* https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime
* @param {number} duration Hours
* @param {number} monitorID ID of monitor to calculate
* @param {boolean} forceNoCache Should the uptime be recalculated?
* @returns {number} Uptime of monitor
*/
static async calcUptime(duration, monitorID, forceNoCache = false) {
@ -1264,6 +1288,7 @@ class Monitor extends BeanModel {
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
* @returns {void}
*/
static async sendUptime(duration, io, monitorID, userID) {
const uptime = await this.calcUptime(duration, monitorID);
@ -1338,6 +1363,7 @@ class Monitor extends BeanModel {
* @param {boolean} isFirstBeat Is this beat the first of this monitor?
* @param {Monitor} monitor The monitor to send a notificaton about
* @param {Bean} bean Status information about monitor
* @returns {void}
*/
static async sendNotification(isFirstBeat, monitor, bean) {
if (!isFirstBeat || bean.status === DOWN) {
@ -1378,7 +1404,7 @@ class Monitor extends BeanModel {
/**
* Get list of notification providers for a given monitor
* @param {Monitor} monitor Monitor to get notification providers for
* @returns {Promise<LooseObject<any>[]>}
* @returns {Promise<LooseObject<any>[]>} List of notifications
*/
static async getNotificationList(monitor) {
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
@ -1389,7 +1415,8 @@ class Monitor extends BeanModel {
/**
* checks certificate chain for expiring certificates
* @param {Object} tlsInfoObject Information about certificate
* @param {object} tlsInfoObject Information about certificate
* @returns {void}
*/
async checkCertExpiryNotifications(tlsInfoObject) {
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
@ -1476,7 +1503,7 @@ class Monitor extends BeanModel {
/**
* Get the status of the previous heartbeat
* @param {number} monitorID ID of monitor to check
* @returns {Promise<LooseObject<any>>}
* @returns {Promise<LooseObject<any>>} Previous heartbeat
*/
static async getPreviousHeartbeat(monitorID) {
return await R.getRow(`
@ -1490,7 +1517,7 @@ class Monitor extends BeanModel {
/**
* Check if monitor is under maintenance
* @param {number} monitorID ID of monitor to check
* @returns {Promise<boolean>}
* @returns {Promise<boolean>} Is the monitor under maintenance
*/
static async isUnderMaintenance(monitorID) {
const maintenanceIDList = await R.getCol(`
@ -1513,7 +1540,11 @@ class Monitor extends BeanModel {
return false;
}
/** Make sure monitor interval is between bounds */
/**
* Make sure monitor interval is between bounds
* @returns {void}
* @throws Interval is outside of range
*/
validate() {
if (this.interval > MAX_INTERVAL_SECOND) {
throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
@ -1526,7 +1557,7 @@ class Monitor extends BeanModel {
/**
* Gets Parent of the monitor
* @param {number} monitorID ID of monitor to get
* @returns {Promise<LooseObject<any>>}
* @returns {Promise<LooseObject<any>>} Parent
*/
static async getParent(monitorID) {
return await R.getRow(`
@ -1542,7 +1573,7 @@ class Monitor extends BeanModel {
/**
* Gets all Children of the monitor
* @param {number} monitorID ID of monitor to get
* @returns {Promise<LooseObject<any>>}
* @returns {Promise<LooseObject<any>>} Children
*/
static async getChildren(monitorID) {
return await R.getAll(`
@ -1555,7 +1586,7 @@ class Monitor extends BeanModel {
/**
* Gets Full Path-Name (Groups and Name)
* @returns {Promise<String>}
* @returns {Promise<string>} Full path name of this monitor
*/
async getPathName() {
let path = this.name;
@ -1575,8 +1606,8 @@ class Monitor extends BeanModel {
/**
* Gets recursive all child ids
* @param {number} monitorID ID of the monitor to get
* @returns {Promise<Array>}
* @param {number} monitorID ID of the monitor to get
* @returns {Promise<Array>} IDs of all children
*/
static async getAllChildrenIDs(monitorID) {
const childs = await Monitor.getChildren(monitorID);
@ -1607,10 +1638,10 @@ class Monitor extends BeanModel {
}
/**
* Checks recursive if parent (ancestors) are active
* @param {number} monitorID ID of the monitor to get
* @returns {Promise<Boolean>}
*/
* Checks recursive if parent (ancestors) are active
* @param {number} monitorID ID of the monitor to get
* @returns {Promise<boolean>} Is the parent monitor active?
*/
static async isParentActive(monitorID) {
const parent = await Monitor.getParent(monitorID);

@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
class Proxy extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
toJSON() {
return {

@ -14,10 +14,11 @@ class StatusPage extends BeanModel {
static domainMappingList = { };
/**
*
* @param {Response} response
* @param {string} indexHTML
* @param {string} slug
* Handle responses to status page
* @param {Response} response Response object
* @param {string} indexHTML HTML to render
* @param {string} slug Status page slug
* @returns {void}
*/
static async handleStatusPageResponse(response, indexHTML, slug) {
let statusPage = await R.findOne("status_page", " slug = ? ", [
@ -33,8 +34,9 @@ class StatusPage extends BeanModel {
/**
* SSR for status pages
* @param {string} indexHTML
* @param {StatusPage} statusPage
* @param {string} indexHTML HTML page to render
* @param {StatusPage} statusPage Status page populate HTML with
* @returns {void}
*/
static async renderHTML(indexHTML, statusPage) {
const $ = cheerio.load(indexHTML);
@ -87,7 +89,8 @@ class StatusPage extends BeanModel {
/**
* Get all status page data in one call
* @param {StatusPage} statusPage
* @param {StatusPage} statusPage Status page to get data for
* @returns {object} Status page data
*/
static async getStatusPageData(statusPage) {
const config = await statusPage.toPublicJSON();
@ -142,7 +145,7 @@ class StatusPage extends BeanModel {
* Send status page list to client
* @param {Server} io io Socket server instance
* @param {Socket} socket Socket.io instance
* @returns {Promise<Bean[]>}
* @returns {Promise<Bean[]>} Status page list
*/
static async sendStatusPageList(io, socket) {
let result = {};
@ -159,7 +162,7 @@ class StatusPage extends BeanModel {
/**
* Update list of domain names
* @param {string[]} domainNameList
* @param {string[]} domainNameList List of status page domains
* @returns {Promise<void>}
*/
async updateDomainNameList(domainNameList) {
@ -203,7 +206,7 @@ class StatusPage extends BeanModel {
/**
* Get list of domain names
* @returns {Object[]}
* @returns {object[]} List of status page domains
*/
getDomainNameList() {
let domainList = [];
@ -219,7 +222,7 @@ class StatusPage extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
async toJSON() {
return {
@ -243,7 +246,7 @@ class StatusPage extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
* @returns {object} Object ready to parse
*/
async toPublicJSON() {
return {
@ -264,7 +267,8 @@ class StatusPage extends BeanModel {
/**
* Convert slug to status page ID
* @param {string} slug
* @param {string} slug Status page slug
* @returns {Promise<number>} ID of status page
*/
static async slugToID(slug) {
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
@ -274,7 +278,7 @@ class StatusPage extends BeanModel {
/**
* Get path to the icon for the page
* @returns {string}
* @returns {string} Path
*/
getIcon() {
if (!this.icon) {
@ -287,7 +291,7 @@ class StatusPage extends BeanModel {
/**
* Get list of maintenances
* @param {number} statusPageId ID of status page to get maintenance for
* @returns {Object} Object representing maintenances sanitized for public
* @returns {object} Object representing maintenances sanitized for public
*/
static async getMaintenanceList(statusPageId) {
try {

@ -4,7 +4,7 @@ class Tag extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
* @returns {object} Object ready to parse
*/
toJSON() {
return {

@ -7,7 +7,7 @@ class User extends BeanModel {
* Reset user password
* Fix #1510, as in the context reset-password.js, there is no auto model mapping. Call this static function instead.
* @param {number} userID ID of user to update
* @param {string} newPassword
* @param {string} newPassword Users new password
* @returns {Promise<void>}
*/
static async resetPassword(userID, newPassword) {
@ -19,7 +19,7 @@ class User extends BeanModel {
/**
* Reset this users password
* @param {string} newPassword
* @param {string} newPassword Users new password
* @returns {Promise<void>}
*/
async resetPassword(newPassword) {

@ -3,10 +3,10 @@ class MonitorType {
name = undefined;
/**
*
* @param {Monitor} monitor
* @param {Heartbeat} heartbeat
* @param {UptimeKumaServer} server
* Run the monitoring check on the given monitor
* @param {Monitor} monitor Monitor to check
* @param {Heartbeat} heartbeat Monitor heartbeat to update
* @param {UptimeKumaServer} server Uptime Kuma server
* @returns {Promise<void>}
*/
async check(monitor, heartbeat, server) {

@ -51,6 +51,11 @@ if (process.platform === "win32") {
log.debug("chrome", allowedList);
/**
* Is the executable path allowed?
* @param {string} executablePath Path to executable
* @returns {Promise<boolean>} The executable is allowed?
*/
async function isAllowedChromeExecutable(executablePath) {
console.log(config.args);
if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") {
@ -61,6 +66,11 @@ async function isAllowedChromeExecutable(executablePath) {
return allowedList.includes(executablePath);
}
/**
* Get the current instance of the browser. If there isn't one, create
* it.
* @returns {Promise<Browser>} The browser
*/
async function getBrowser() {
if (!browser) {
let executablePath = await Settings.get("chromeExecutable");
@ -75,6 +85,11 @@ async function getBrowser() {
return browser;
}
/**
* Prepare the chrome executable path
* @param {string} executablePath Path to chrome executable
* @returns {Promise<string>} Executable path
*/
async function prepareChromeExecutable(executablePath) {
// Special code for using the playwright_chromium
if (typeof executablePath === "string" && executablePath.toLocaleLowerCase() === "#playwright_chromium") {
@ -121,6 +136,12 @@ async function prepareChromeExecutable(executablePath) {
return executablePath;
}
/**
* Find the chrome executable
* @param {any[]} executables Executables to search through
* @returns {any} Executable
* @throws Could not find executable
*/
function findChrome(executables) {
// Use the last working executable, so we don't have to search for it again
if (lastAutoDetectChromeExecutable) {
@ -138,6 +159,10 @@ function findChrome(executables) {
throw new Error("Chromium not found, please specify Chromium executable path in the settings page.");
}
/**
* Reset chrome
* @returns {Promise<void>}
*/
async function resetChrome() {
if (browser) {
await browser.close();
@ -147,8 +172,8 @@ async function resetChrome() {
/**
* Test if the chrome executable is valid and return the version
* @param executablePath
* @returns {Promise<string>}
* @param {string} executablePath Path to executable
* @returns {Promise<string>} Chrome version
*/
async function testChrome(executablePath) {
try {
@ -175,6 +200,9 @@ class RealBrowserMonitorType extends MonitorType {
name = "real-browser";
/**
* @inheritdoc
*/
async check(monitor, heartbeat, server) {
const browser = await getBrowser();
const context = await browser.newContext();

@ -13,9 +13,9 @@ class TailscalePing extends MonitorType {
/**
* Checks the ping status of the URL associated with the monitor.
* It then parses the Tailscale ping command output to update the heatrbeat.
*
* @param {Object} monitor - The monitor object associated with the check.
* @param {Object} heartbeat - The heartbeat object to update.
* @param {object} monitor The monitor object associated with the check.
* @param {object} heartbeat The heartbeat object to update.
* @returns {Promise<void>}
* @throws Will throw an error if checking Tailscale ping encounters any error
*/
async check(monitor, heartbeat) {
@ -31,9 +31,9 @@ class TailscalePing extends MonitorType {
/**
* Runs the Tailscale ping command to the given URL.
*
* @param {string} hostname - The hostname to ping.
* @returns {Promise<string>} - A Promise that resolves to the output of the Tailscale ping command
* @param {string} hostname The hostname to ping.
* @param {number} interval Interval to send ping
* @returns {Promise<string>} A Promise that resolves to the output of the Tailscale ping command
* @throws Will throw an error if the command execution encounters any error.
*/
async runTailscalePing(hostname, interval) {
@ -61,9 +61,9 @@ class TailscalePing extends MonitorType {
/**
* Parses the output of the Tailscale ping command to update the heartbeat.
*
* @param {string} tailscaleOutput - The output of the Tailscale ping command.
* @param {Object} heartbeat - The heartbeat object to update.
* @param {string} tailscaleOutput The output of the Tailscale ping command.
* @param {object} heartbeat The heartbeat object to update.
* @returns {void}
* @throws Will throw an eror if the output contains any unexpected string.
*/
parseTailscaleOutput(tailscaleOutput, heartbeat) {

@ -6,6 +6,9 @@ class Alerta extends NotificationProvider {
name = "alerta";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -7,6 +7,9 @@ class AlertNow extends NotificationProvider {
name = "AlertNow";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -7,6 +7,9 @@ const qs = require("qs");
class AliyunSMS extends NotificationProvider {
name = "AliyunSMS";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
@ -78,9 +81,9 @@ class AliyunSMS extends NotificationProvider {
/**
* Aliyun request sign
* @param {Object} param Parameters object to sign
* @param {object} param Parameters object to sign
* @param {string} AccessKeySecret Secret key to sign parameters with
* @returns {string}
* @returns {string} Base64 encoded request
*/
sign(param, AccessKeySecret) {
let param2 = {};
@ -122,7 +125,7 @@ class AliyunSMS extends NotificationProvider {
/**
* Convert status constant to string
* @param {const} status The status constant
* @returns {string}
* @returns {string} Status
*/
statusToString(status) {
switch (status) {

@ -5,6 +5,9 @@ class Apprise extends NotificationProvider {
name = "apprise";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const args = [ "-vv", "-b", msg, notification.appriseURL ];
if (notification.title) {

@ -18,6 +18,9 @@ const successMessage = "Successes!";
class Bark extends NotificationProvider {
name = "Bark";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let barkEndpoint = notification.barkEndpoint;
@ -45,8 +48,9 @@ class Bark extends NotificationProvider {
/**
* Add additional parameter for better on device styles (iOS 15
* optimized)
* @param {BeanModel} notification Notification to send
* @param {string} postUrl URL to append parameters to
* @returns {string}
* @returns {string} Additional URL parameters
*/
appendAdditionalParameters(notification, postUrl) {
// set icon to uptime kuma icon, 11kb should be fine
@ -70,7 +74,8 @@ class Bark extends NotificationProvider {
/**
* Check if result is successful
* @param {Object} result Axios response object
* @param {object} result Axios response object
* @returns {void}
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
@ -84,10 +89,11 @@ class Bark extends NotificationProvider {
/**
* Send the message
* @param {BeanModel} notification Notification to send
* @param {string} title Message title
* @param {string} subtitle Message
* @param {string} endpoint Endpoint to send request to
* @returns {string}
* @returns {string} Success message
*/
async postNotification(notification, title, subtitle, endpoint) {
// url encode title and subtitle

@ -5,6 +5,9 @@ class ClickSendSMS extends NotificationProvider {
name = "clicksendsms";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -6,6 +6,9 @@ const Crypto = require("crypto");
class DingDing extends NotificationProvider {
name = "DingDing";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
@ -39,8 +42,8 @@ class DingDing extends NotificationProvider {
/**
* Send message to DingDing
* @param {BeanModel} notification
* @param {Object} params Parameters of message
* @param {BeanModel} notification Notification to send
* @param {object} params Parameters of message
* @returns {boolean} True if successful else false
*/
async sendToDingDing(notification, params) {
@ -66,7 +69,7 @@ class DingDing extends NotificationProvider {
* DingDing sign
* @param {Date} timestamp Timestamp of message
* @param {string} secretKey Secret key to sign data with
* @returns {string}
* @returns {string} Base64 encoded signature
*/
sign(timestamp, secretKey) {
return Crypto
@ -78,7 +81,7 @@ class DingDing extends NotificationProvider {
/**
* Convert status constant to string
* @param {const} status The status constant
* @returns {string}
* @returns {string} Status
*/
statusToString(status) {
// TODO: Move to notification-provider.js to avoid repetition in classes

@ -6,6 +6,9 @@ class Discord extends NotificationProvider {
name = "discord";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ const { DOWN, UP } = require("../../src/util");
class Feishu extends NotificationProvider {
name = "Feishu";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let feishuWebHookUrl = notification.feishuWebHookUrl;

@ -7,6 +7,9 @@ const successMessage = "Sent Successfully.";
class FlashDuty extends NotificationProvider {
name = "FlashDuty";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
try {
if (heartbeatJSON == null) {
@ -33,12 +36,12 @@ class FlashDuty extends NotificationProvider {
this.throwGeneralAxiosError(error);
}
}
/**
* Generate a monitor url from the monitors infomation
* @param {Object} monitorInfo Monitor details
* @returns {string|undefined}
* @param {object} monitorInfo Monitor details
* @returns {string|undefined} Monitor URL
*/
genMonitorUrl(monitorInfo) {
if (monitorInfo.type === "port" && monitorInfo.port) {
return monitorInfo.hostname + ":" + monitorInfo.port;
@ -54,9 +57,9 @@ class FlashDuty extends NotificationProvider {
* @param {BeanModel} notification Message title
* @param {string} title Message
* @param {string} body Message
* @param {Object} monitorInfo Monitor details
* @param {object} monitorInfo Monitor details
* @param {string} eventStatus Monitor status (Info, Warning, Critical, Ok)
* @returns {string}
* @returns {string} Success message
*/
async postNotification(notification, title, body, monitorInfo, eventStatus) {
const options = {

@ -5,6 +5,9 @@ class FreeMobile extends NotificationProvider {
name = "FreeMobile";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -6,6 +6,9 @@ class GoAlert extends NotificationProvider {
name = "GoAlert";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -8,6 +8,9 @@ class GoogleChat extends NotificationProvider {
name = "GoogleChat";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -5,6 +5,9 @@ class Gorush extends NotificationProvider {
name = "gorush";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class Gotify extends NotificationProvider {
name = "gotify";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -6,6 +6,9 @@ const defaultNotificationService = "notify";
class HomeAssistant extends NotificationProvider {
name = "HomeAssistant";
/**
* @inheritdoc
*/
async send(notification, message, monitor = null, heartbeat = null) {
const notificationService = notification?.notificationService || defaultNotificationService;

@ -5,6 +5,9 @@ class Kook extends NotificationProvider {
name = "Kook";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let url = "https://www.kookapp.cn/api/v3/message/create";

@ -6,6 +6,9 @@ class Line extends NotificationProvider {
name = "line";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -7,6 +7,9 @@ class LineNotify extends NotificationProvider {
name = "LineNotify";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -6,6 +6,9 @@ class LunaSea extends NotificationProvider {
name = "lunasea";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let lunaseaurl = "";

@ -6,6 +6,9 @@ const { log } = require("../../src/util");
class Matrix extends NotificationProvider {
name = "matrix";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -6,6 +6,9 @@ class Mattermost extends NotificationProvider {
name = "mattermost";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -33,6 +33,9 @@ if (semver.lt(nodeVersion, "16.0.0")) {
class Nostr extends NotificationProvider {
name = "nostr";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
// All DMs should have same timestamp
const createdAt = Math.floor(Date.now() / 1000);
@ -86,6 +89,11 @@ class Nostr extends NotificationProvider {
return `${successfulRelays}/${relays.length} relays connected.`;
}
/**
* Get the private key for the sender
* @param {string} sender Sender to retrieve key for
* @returns {nip19.DecodeResult} Private key
*/
async getPrivateKey(sender) {
try {
const senderDecodeResult = await nip19.decode(sender);
@ -96,6 +104,11 @@ class Nostr extends NotificationProvider {
}
}
/**
* Get public keys for recipients
* @param {string} recipients Newline delimited list of recipients
* @returns {nip19.DecodeResult[]} Public keys
*/
async getPublicKeys(recipients) {
const recipientsList = recipients.split("\n");
const publicKeys = [];

@ -2,16 +2,16 @@ class NotificationProvider {
/**
* Notification Provider Name
* @type string
* @type {string}
*/
name = undefined;
/**
* Send a notification
* @param {BeanModel} notification
* @param {BeanModel} notification Notification to send
* @param {string} msg General Message
* @param {?Object} monitorJSON Monitor details (For Up/Down only)
* @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {?object} monitorJSON Monitor details (For Up/Down only)
* @param {?object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Return Successful Message
* @throws Error with fail msg
*/
@ -22,6 +22,7 @@ class NotificationProvider {
/**
* Throws an error
* @param {any} error The error to throw
* @returns {void}
* @throws {any} The error specified
*/
throwGeneralAxiosError(error) {

@ -6,6 +6,9 @@ class Ntfy extends NotificationProvider {
name = "ntfy";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -5,6 +5,9 @@ class Octopush extends NotificationProvider {
name = "octopush";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class OneBot extends NotificationProvider {
name = "OneBot";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -68,11 +68,11 @@ class Opsgenie extends NotificationProvider {
}
/**
*
* @param {BeanModel} notification
* Make POST request to Opsgenie
* @param {BeanModel} notification Notification to send
* @param {string} url Request url
* @param {Object} data Request body
* @returns {Promise<string>}
* @param {object} data Request body
* @returns {Promise<string>} Success message
*/
async post(notification, url, data) {
let config = {

@ -39,7 +39,8 @@ class PagerDuty extends NotificationProvider {
/**
* Check if result is successful, result code should be in range 2xx
* @param {Object} result Axios response object
* @param {object} result Axios response object
* @returns {void}
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
@ -56,9 +57,9 @@ class PagerDuty extends NotificationProvider {
* @param {BeanModel} notification Message title
* @param {string} title Message title
* @param {string} body Message
* @param {Object} monitorInfo Monitor details (For Up/Down only)
* @param {object} monitorInfo Monitor details (For Up/Down only)
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
* @returns {string}
* @returns {Promise<string>} Success message
*/
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {

@ -32,7 +32,8 @@ class PagerTree extends NotificationProvider {
/**
* Check if result is successful, result code should be in range 2xx
* @param {Object} result Axios response object
* @param {object} result Axios response object
* @returns {void}
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
@ -48,9 +49,10 @@ class PagerTree extends NotificationProvider {
* Send the message
* @param {BeanModel} notification Message title
* @param {string} title Message title
* @param {Object} monitorJSON Monitor details (For Up/Down only)
* @param {object} monitorJSON Monitor details (For Up/Down only)
* @param {object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {?string} eventAction Action event for PagerTree (create, resolve)
* @returns {string}
* @returns {Promise<string>} Success state
*/
async postNotification(notification, title, monitorJSON, heartbeatJSON, eventAction = "create") {

@ -5,6 +5,9 @@ class PromoSMS extends NotificationProvider {
name = "promosms";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -7,6 +7,9 @@ class Pushbullet extends NotificationProvider {
name = "pushbullet";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -6,6 +6,9 @@ class PushDeer extends NotificationProvider {
name = "PushDeer";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let endpoint = "/message/push";

@ -5,6 +5,9 @@ class Pushover extends NotificationProvider {
name = "pushover";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let pushoverlink = "https://api.pushover.net/1/messages.json";

@ -5,6 +5,9 @@ class Pushy extends NotificationProvider {
name = "pushy";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -8,6 +8,9 @@ class RocketChat extends NotificationProvider {
name = "rocket.chat";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -6,6 +6,9 @@ class ServerChan extends NotificationProvider {
name = "ServerChan";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
@ -23,8 +26,8 @@ class ServerChan extends NotificationProvider {
/**
* Get the formatted title for message
* @param {?Object} monitorJSON Monitor details (For Up/Down only)
* @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {?object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {?object} monitorJSON Monitor details (For Up/Down only)
* @returns {string} Formatted title
*/
checkStatus(heartbeatJSON, monitorJSON) {

@ -5,6 +5,9 @@ class SerwerSMS extends NotificationProvider {
name = "serwersms";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class Signal extends NotificationProvider {
name = "signal";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -10,7 +10,9 @@ class Slack extends NotificationProvider {
/**
* Deprecated property notification.slackbutton
* Set it as primary base url if this is not yet set.
* @deprecated
* @param {string} url The primary base URL to use
* @returns {Promise<void>}
*/
static async deprecateURL(url) {
let currentPrimaryBaseURL = await setting("primaryBaseURL");
@ -25,6 +27,9 @@ class Slack extends NotificationProvider {
}
}
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -4,6 +4,9 @@ const axios = require("axios");
class SMSC extends NotificationProvider {
name = "smsc";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -5,6 +5,9 @@ class SMSEagle extends NotificationProvider {
name = "SMSEagle";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class SMSManager extends NotificationProvider {
name = "SMSManager";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
try {
let data = {

@ -6,6 +6,9 @@ class SMTP extends NotificationProvider {
name = "smtp";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const config = {

@ -37,7 +37,8 @@ class Splunk extends NotificationProvider {
/**
* Check if result is successful, result code should be in range 2xx
* @param {Object} result Axios response object
* @param {object} result Axios response object
* @returns {void}
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
@ -54,9 +55,9 @@ class Splunk extends NotificationProvider {
* @param {BeanModel} notification Message title
* @param {string} title Message title
* @param {string} body Message
* @param {Object} monitorInfo Monitor details (For Up/Down only)
* @param {object} monitorInfo Monitor details (For Up/Down only)
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
* @returns {string}
* @returns {Promise<string>} Success state
*/
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {

@ -6,6 +6,9 @@ class Squadcast extends NotificationProvider {
name = "squadcast";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -7,6 +7,9 @@ class Stackfield extends NotificationProvider {
name = "stackfield";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null) {
let okMsg = "Sent Successfully.";
try {

@ -9,7 +9,7 @@ class Teams extends NotificationProvider {
* Generate the message to send
* @param {const} status The status constant
* @param {string} monitorName Name of monitor
* @returns {string}
* @returns {string} Status message
*/
_statusMessageFactory = (status, monitorName) => {
if (status === DOWN) {
@ -37,11 +37,12 @@ class Teams extends NotificationProvider {
/**
* Generate payload for notification
* @param {const} status The status of the monitor
* @param {string} monitorMessage Message to send
* @param {string} monitorName Name of monitor affected
* @param {string} monitorUrl URL of monitor affected
* @returns {Object}
* @param {object} args Method arguments
* @param {const} args.status The status of the monitor
* @param {string} args.monitorMessage Message to send
* @param {string} args.monitorName Name of monitor affected
* @param {string} args.monitorUrl URL of monitor affected
* @returns {object} Notification payload
*/
_notificationPayloadFactory = ({
status,
@ -96,7 +97,8 @@ class Teams extends NotificationProvider {
/**
* Send the notification
* @param {string} webhookUrl URL to send the request to
* @param {Object} payload Payload generated by _notificationPayloadFactory
* @param {object} payload Payload generated by _notificationPayloadFactory
* @returns {Promise<void>}
*/
_sendNotification = async (webhookUrl, payload) => {
await axios.post(webhookUrl, payload);
@ -116,6 +118,9 @@ class Teams extends NotificationProvider {
return this._sendNotification(webhookUrl, payload);
};
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class TechulusPush extends NotificationProvider {
name = "PushByTechulus";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class Telegram extends NotificationProvider {
name = "telegram";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -5,6 +5,9 @@ class Twilio extends NotificationProvider {
name = "twilio";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -7,6 +7,9 @@ class Webhook extends NotificationProvider {
name = "webhook";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -6,6 +6,9 @@ class WeCom extends NotificationProvider {
name = "WeCom";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
@ -26,9 +29,9 @@ class WeCom extends NotificationProvider {
/**
* Generate the message to send
* @param {Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {string} msg General message
* @returns {Object}
* @returns {object} Message
*/
composeMessage(heartbeatJSON, msg) {
let title;

@ -10,7 +10,7 @@ class ZohoCliq extends NotificationProvider {
* Generate the message to send
* @param {const} status The status constant
* @param {string} monitorName Name of monitor
* @returns {string}
* @returns {string} Status message
*/
_statusMessageFactory = (status, monitorName) => {
if (status === DOWN) {
@ -25,6 +25,7 @@ class ZohoCliq extends NotificationProvider {
* Send the notification
* @param {string} webhookUrl URL to send the request to
* @param {Array} payload Payload generated by _notificationPayloadFactory
* @returns {Promise<void>}
*/
_sendNotification = async (webhookUrl, payload) => {
await axios.post(webhookUrl, { text: payload.join("\n") });
@ -32,11 +33,12 @@ class ZohoCliq extends NotificationProvider {
/**
* Generate payload for notification
* @param {const} status The status of the monitor
* @param {string} monitorMessage Message to send
* @param {string} monitorName Name of monitor affected
* @param {string} monitorUrl URL of monitor affected
* @returns {Array}
* @param {object} args Method arguments
* @param {const} args.status The status of the monitor
* @param {string} args.monitorMessage Message to send
* @param {string} args.monitorName Name of monitor affected
* @param {string} args.monitorUrl URL of monitor affected
* @returns {Array} Notification payload
*/
_notificationPayloadFactory = ({
status,
@ -74,6 +76,9 @@ class ZohoCliq extends NotificationProvider {
return this._sendNotification(webhookUrl, payload);
};
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";

@ -58,7 +58,12 @@ class Notification {
providerList = {};
/** Initialize the notification providers */
/**
* Initialize the notification providers
* @returns {void}
* @throws Notification provider does not have a name
* @throws Duplicate notification providers in list
*/
static init() {
log.info("notification", "Prepare Notification Providers");
@ -133,10 +138,10 @@ class Notification {
/**
* Send a notification
* @param {BeanModel} notification
* @param {BeanModel} notification Notification to send
* @param {string} msg General Message
* @param {Object} monitorJSON Monitor details (For Up/Down only)
* @param {Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {object} monitorJSON Monitor details (For Up/Down only)
* @param {object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Successful msg
* @throws Error with fail msg
*/
@ -150,10 +155,10 @@ class Notification {
/**
* Save a notification
* @param {Object} notification Notification to save
* @param {object} notification Notification to save
* @param {?number} notificationID ID of notification to update
* @param {number} userID ID of user who adds notification
* @returns {Promise<Bean>}
* @returns {Promise<Bean>} Notification that was saved
*/
static async save(notification, notificationID, userID) {
let bean;

@ -4,8 +4,8 @@ const saltRounds = 10;
/**
* Hash a password
* @param {string} password
* @returns {string}
* @param {string} password Password to hash
* @returns {string} Hash
*/
exports.generate = function (password) {
return bcrypt.hashSync(password, saltRounds);
@ -13,8 +13,8 @@ exports.generate = function (password) {
/**
* Verify a password against a hash
* @param {string} password
* @param {string} hash
* @param {string} password Password to verify
* @param {string} hash Hash to verify against
* @returns {boolean} Does the password match the hash?
*/
exports.verify = function (password, hash) {
@ -27,8 +27,8 @@ exports.verify = function (password, hash) {
/**
* Is the hash a SHA1 hash
* @param {string} hash
* @returns {boolean}
* @param {string} hash Hash to check
* @returns {boolean} Is SHA1 hash?
*/
function isSHA1(hash) {
return (typeof hash === "string" && hash.startsWith("sha1"));
@ -36,7 +36,8 @@ function isSHA1(hash) {
/**
* Does the hash need to be rehashed?
* @returns {boolean}
* @param {string} hash Hash to check
* @returns {boolean} Needs to be rehashed?
*/
exports.needRehash = function (hash) {
return isSHA1(hash);

@ -36,7 +36,7 @@ class Prometheus {
monitorLabelValues = {};
/**
* @param {Object} monitor Monitor object to monitor
* @param {object} monitor Monitor object to monitor
*/
constructor(monitor) {
this.monitorLabelValues = {
@ -50,8 +50,9 @@ class Prometheus {
/**
* Update the metrics page
* @param {Object} heartbeat Heartbeat details
* @param {Object} tlsInfo TLS details
* @param {object} heartbeat Heartbeat details
* @param {object} tlsInfo TLS details
* @returns {void}
*/
update(heartbeat, tlsInfo) {
@ -99,7 +100,10 @@ class Prometheus {
}
}
/** Remove monitor from prometheus */
/**
* Remove monitor from prometheus
* @returns {void}
*/
remove() {
try {
monitorCertDaysRemaining.remove(this.monitorLabelValues);

@ -11,11 +11,10 @@ class Proxy {
/**
* Saves and updates given proxy entity
*
* @param proxy
* @param proxyID
* @param userID
* @return {Promise<Bean>}
* @param {object} proxy Proxy to store
* @param {number} proxyID ID of proxy to update
* @param {number} userID ID of user the proxy belongs to
* @returns {Promise<Bean>} Updated proxy
*/
static async save(proxy, proxyID, userID) {
let bean;
@ -65,10 +64,9 @@ class Proxy {
/**
* Deletes proxy with given id and removes it from monitors
*
* @param proxyID
* @param userID
* @return {Promise<void>}
* @param {number} proxyID ID of proxy to delete
* @param {number} userID ID of proxy owner
* @returns {Promise<void>}
*/
static async delete(proxyID, userID) {
const bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]);
@ -86,10 +84,10 @@ class Proxy {
/**
* Create HTTP and HTTPS agents related with given proxy bean object
*
* @param proxy proxy bean object
* @param options http and https agent options
* @return {{httpAgent: Agent, httpsAgent: Agent}}
* @param {object} proxy proxy bean object
* @param {object} options http and https agent options
* @returns {{httpAgent: Agent, httpsAgent: Agent}} New HTTP and HTTPS agents
* @throws Proxy protocol is unsupported
*/
static createAgents(proxy, options) {
const { httpAgentOptions, httpsAgentOptions } = options || {};
@ -171,10 +169,9 @@ class Proxy {
/**
* Applies given proxy id to monitors
*
* @param proxyID
* @param userID
* @return {Promise<void>}
* @param {number} proxyID ID of proxy to apply
* @param {number} userID ID of proxy owner
* @returns {Promise<void>}
*/
async function applyProxyEveryMonitor(proxyID, userID) {
// Find all monitors with id and proxy id

@ -3,7 +3,7 @@ const { log } = require("../src/util");
class KumaRateLimiter {
/**
* @param {Object} config Rate limiter configuration object
* @param {object} config Rate limiter configuration object
*/
constructor(config) {
this.errorMessage = config.errorMessage;
@ -13,14 +13,14 @@ class KumaRateLimiter {
/**
* Callback for pass
* @callback passCB
* @param {Object} err Too many requests
* @param {object} err Too many requests
*/
/**
* Should the request be passed through
* @param {passCB} callback
* @param {number} [num=1] Number of tokens to remove
* @returns {Promise<boolean>}
* @param {passCB} callback Callback function to call with decision
* @param {number} num Number of tokens to remove
* @returns {Promise<boolean>} Should the request be allowed?
*/
async pass(callback, num = 1) {
const remainingRequests = await this.removeTokens(num);
@ -39,8 +39,8 @@ class KumaRateLimiter {
/**
* Remove a given number of tokens
* @param {number} [num=1] Number of tokens to remove
* @returns {Promise<number>}
* @param {number} num Number of tokens to remove
* @returns {Promise<number>} Number of remaining tokens
*/
async removeTokens(num = 1) {
return await this.rateLimiter.removeTokens(num);

@ -1699,8 +1699,8 @@ async function updateMonitorNotification(monitorID, notificationIDList) {
/**
* Check if a given user owns a specific monitor
* @param {number} userID
* @param {number} monitorID
* @param {number} userID ID of user to check
* @param {number} monitorID ID of monitor to check
* @returns {Promise<void>}
* @throws {Error} The specified user does not own the monitor
*/
@ -1719,7 +1719,7 @@ async function checkOwner(userID, monitorID) {
* Function called after user login
* This function is used to send the heartbeat list of a monitor.
* @param {Socket} socket Socket.io instance
* @param {Object} user User object
* @param {object} user User object
* @returns {Promise<void>}
*/
async function afterLogin(socket, user) {
@ -1760,7 +1760,7 @@ async function afterLogin(socket, user) {
/**
* Initialize the database
* @param {boolean} [testMode=false] Should the connection be
* @param {boolean} testMode Should the connection be
* started in test mode?
* @returns {Promise<void>}
*/
@ -1852,7 +1852,10 @@ async function pauseMonitor(userID, monitorID) {
}
}
/** Resume active monitors */
/**
* Resume active monitors
* @returns {Promise<void>}
*/
async function startMonitors() {
let list = await R.find("monitor", " active = 1 ");
@ -1896,7 +1899,10 @@ async function shutdownFunction(signal) {
Settings.stopCacheCleaner();
}
/** Final function called before application exits */
/**
* Final function called before application exits
* @returns {void}
*/
function finalFunction() {
log.info("server", "Graceful shutdown successful!");
}

@ -96,7 +96,7 @@ class Settings {
/**
* Get settings based on type
* @param {string} type The type of setting
* @returns {Promise<Bean>}
* @returns {Promise<Bean>} Settings
*/
static async getSettings(type) {
let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
@ -119,7 +119,7 @@ class Settings {
/**
* Set settings based on type
* @param {string} type Type of settings to set
* @param {Object} data Values of settings
* @param {object} data Values of settings
* @returns {Promise<void>}
*/
static async setSettings(type, data) {
@ -150,8 +150,9 @@ class Settings {
}
/**
*
* @param {string[]} keyList
* Delete selected keys from settings cache
* @param {string[]} keyList Keys to remove
* @returns {void}
*/
static deleteCache(keyList) {
for (let key of keyList) {
@ -159,6 +160,10 @@ class Settings {
}
}
/**
* Stop the cache cleaner if running
* @returns {void}
*/
static stopCacheCleaner() {
if (Settings.cacheCleaner) {
clearInterval(Settings.cacheCleaner);

@ -9,8 +9,9 @@ const { Settings } = require("../settings");
const { sendAPIKeyList } = require("../client");
/**
* Handlers for Maintenance
* Handlers for API keys
* @param {Socket} socket Socket.io instance
* @returns {void}
*/
module.exports.apiKeySocketHandler = (socket) => {
// Add a new api key

@ -11,6 +11,7 @@ const cloudflared = new CloudflaredTunnel();
* Change running state
* @param {string} running Is it running?
* @param {string} message Message to pass
* @returns {void}
*/
cloudflared.change = (running, message) => {
io.to("cloudflared").emit(prefix + "running", running);
@ -19,7 +20,8 @@ cloudflared.change = (running, message) => {
/**
* Emit an error message
* @param {string} errorMessage
* @param {string} errorMessage Error message to send
* @returns {void}
*/
cloudflared.error = (errorMessage) => {
io.to("cloudflared").emit(prefix + "errorMessage", errorMessage);
@ -28,6 +30,7 @@ cloudflared.error = (errorMessage) => {
/**
* Handler for cloudflared
* @param {Socket} socket Socket.io instance
* @returns {void}
*/
module.exports.cloudflaredSocketHandler = (socket) => {
@ -89,6 +92,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
/**
* Automatically start cloudflared
* @param {string} token Cloudflared tunnel token
* @returns {Promise<void>}
*/
module.exports.autoStart = async (token) => {
if (!token) {
@ -106,7 +110,10 @@ module.exports.autoStart = async (token) => {
}
};
/** Stop cloudflared */
/**
* Stop cloudflared
* @returns {Promise<void>}
*/
module.exports.stop = async () => {
log.info("cloudflared", "Stop cloudflared");
if (cloudflared) {

@ -4,6 +4,7 @@ const Database = require("../database");
/**
* Handlers for database
* @param {Socket} socket Socket.io instance
* @returns {void}
*/
module.exports = (socket) => {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save