From 03b2d8d5210b354562e73136934eeb849c90f35b Mon Sep 17 00:00:00 2001 From: Matthew Nickson Date: Wed, 20 Apr 2022 19:56:40 +0100 Subject: [PATCH] Add JSDoc to server/* Signed-off-by: Matthew Nickson --- server/2fa.js | 6 +- server/auth.js | 21 +++++-- server/check-version.js | 6 ++ server/client.js | 26 +++++++-- server/database.js | 37 ++++++++++--- server/image-data-uri.js | 17 ++++++ server/jobs.js | 5 ++ server/notification.js | 58 ++++++++++++++------ server/password-hash.js | 28 ++++++++-- server/ping-lite.js | 29 +++++++--- server/prometheus.js | 8 +++ server/rate-limiter.js | 20 +++++++ server/server.js | 64 +++++++++++++++++++++- server/util-server.js | 115 +++++++++++++++++++++++++++++++++++---- 14 files changed, 380 insertions(+), 60 deletions(-) diff --git a/server/2fa.js b/server/2fa.js index bc8145cf..c7076da2 100644 --- a/server/2fa.js +++ b/server/2fa.js @@ -1,8 +1,12 @@ -const { checkLogin } = require("./util-server"); const { R } = require("redbean-node"); class TwoFA { + /** + * Disable 2FA for specified user + * @param {number} userID ID of user to disable + * @returns {Promise} + */ static async disable2FA(userID) { return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [ userID, diff --git a/server/auth.js b/server/auth.js index c476ea1e..422dd666 100644 --- a/server/auth.js +++ b/server/auth.js @@ -6,10 +6,10 @@ const { debug } = require("../src/util"); const { loginRateLimiter } = require("./rate-limiter"); /** - * - * @param username : string - * @param password : string - * @returns {Promise} + * Login to web app + * @param {string} username + * @param {string} password + * @returns {Promise<(Bean|null)>} */ exports.login = async function (username, password) { let user = await R.findOne("user", " username = ? AND active = 1 ", [ @@ -30,6 +30,19 @@ exports.login = async function (username, password) { return null; }; +/** + * Callback for myAuthorizer + * @callback myAuthorizerCB + * @param {any} err Any error encountered + * @param {boolean} authorized Is the client authorized? + */ + +/** + * Custom authorizer for express-basic-auth + * @param {string} username + * @param {string} password + * @param {myAuthorizerCB} callback + */ function myAuthorizer(username, password, callback) { setting("disableAuth").then((result) => { if (result) { diff --git a/server/check-version.js b/server/check-version.js index a3465ddf..daec4975 100644 --- a/server/check-version.js +++ b/server/check-version.js @@ -6,6 +6,7 @@ exports.latestVersion = null; let interval; +/** Start 48 hour check interval */ exports.startInterval = () => { let check = async () => { try { @@ -28,6 +29,11 @@ exports.startInterval = () => { interval = setInterval(check, 3600 * 1000 * 48); }; +/** + * Enable the check update feature + * @param {boolean} value Should the check update feature be enabled? + * @returns {Promise} + */ exports.enableCheckUpdate = async (value) => { await setSetting("checkUpdate", value); diff --git a/server/client.js b/server/client.js index c7b3bc16..8b0f7520 100644 --- a/server/client.js +++ b/server/client.js @@ -7,6 +7,11 @@ const { io } = require("./server"); const { setting } = require("./util-server"); const checkVersion = require("./check-version"); +/** + * Send list of notification providers to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} + */ async function sendNotificationList(socket) { const timeLogger = new TimeLogger(); @@ -28,8 +33,11 @@ async function sendNotificationList(socket) { /** * Send Heartbeat History list to socket - * @param toUser True = send to all browsers with the same user id, False = send to the current browser only - * @param overwrite Overwrite client-side's heartbeat 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 + * @returns {Promise} */ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { const timeLogger = new TimeLogger(); @@ -56,10 +64,11 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = /** * Important Heart beat list (aka event list) - * @param socket - * @param monitorID - * @param toUser True = send to all browsers with the same user id, False = send to the current browser only - * @param overwrite Overwrite client-side's heartbeat 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 + * @returns {Promise} */ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { const timeLogger = new TimeLogger(); @@ -83,6 +92,11 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove } +/** + * Send application info + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} + */ async function sendInfo(socket) { socket.emit("info", { version: checkVersion.version, diff --git a/server/database.js b/server/database.js index afcace70..04c51c21 100644 --- a/server/database.js +++ b/server/database.js @@ -63,6 +63,10 @@ class Database { static noReject = true; + /** + * Initialize the database + * @param {Object} args Arguments to initialize DB with + */ static init(args) { // Data Directory (must be end with "/") Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/"; @@ -80,6 +84,12 @@ class Database { console.log(`Data Dir: ${Database.dataDir}`); } + /** + * Connect to the database + * @param {boolean} [testMode=false] Should the connection be + * started in test mode? + * @returns {Promise} + */ static async connect(testMode = false) { const acquireConnectionTimeout = 120 * 1000; @@ -129,6 +139,7 @@ class Database { console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()")); } + /** Patch the database */ static async patch() { let version = parseInt(await setting("database_version")); @@ -173,7 +184,9 @@ class Database { } /** + * Patch DB using new process * Call it from patch() only + * @private * @returns {Promise} */ static async patch2() { @@ -212,9 +225,12 @@ class Database { } /** + * Patch database using new patching process * Used it patch2() only + * @private * @param sqlFilename * @param databasePatchedFiles + * @returns {Promise} */ static async patch2Recursion(sqlFilename, databasePatchedFiles) { let value = this.patchList[sqlFilename]; @@ -249,12 +265,12 @@ class Database { } /** - * Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself - * @param filename + * Load an SQL file and execute it + * @param filename Filename of SQL file to import * @returns {Promise} */ static async importSQLFile(filename) { - + // Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself await R.getCell("SELECT 1"); let text = fs.readFileSync(filename).toString(); @@ -282,6 +298,10 @@ class Database { } } + /** + * Aquire a direct connection to database + * @returns {any} + */ static getBetterSQLite3Database() { return R.knex.client.acquireConnection(); } @@ -317,7 +337,7 @@ class Database { /** * One backup one time in this process. * Reset this.backupPath if you want to backup again - * @param version + * @param {string} version Version code of backup */ static backup(version) { if (! this.backupPath) { @@ -339,9 +359,7 @@ class Database { } } - /** - * - */ + /** Restore from most recent backup */ static restore() { if (this.backupPath) { console.error("Patching the database failed!!! Restoring the backup"); @@ -383,6 +401,7 @@ class Database { } } + /** Get the size of the database */ static getSize() { debug("Database.getSize()"); let stats = fs.statSync(Database.path); @@ -390,6 +409,10 @@ class Database { return stats.size; } + /** + * Shrink the database + * @returns {Promise} + */ static async shrink() { await R.exec("VACUUM"); } diff --git a/server/image-data-uri.js b/server/image-data-uri.js index 3ccaab7d..b5b6f2d6 100644 --- a/server/image-data-uri.js +++ b/server/image-data-uri.js @@ -6,6 +6,11 @@ let fs = require("fs"); let ImageDataURI = (() => { + /** + * Decode the data:image/ URI + * @param {string} dataURI data:image/ URI to decode + * @returns {Object} + */ function decode(dataURI) { if (!/data:image\//.test(dataURI)) { console.log("ImageDataURI :: Error :: It seems that it is not an Image Data URI. Couldn't match \"data:image/\""); @@ -20,6 +25,12 @@ let ImageDataURI = (() => { }; } + /** + * Endcode an image into data:image/ URI + * @param {(Buffer|string)} data Data to encode + * @param {string} mediaType Media type of data + * @returns {(string|null)} + */ function encode(data, mediaType) { if (!data || !mediaType) { console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType "); @@ -33,6 +44,12 @@ let ImageDataURI = (() => { return dataImgBase64; } + /** + * Write data URI to file + * @param {string} dataURI data:image/ URI + * @param {string} filePath Path to write file to + * @returns {Promise} + */ function outputFile(dataURI, filePath) { filePath = filePath || "./"; return new Promise((resolve, reject) => { diff --git a/server/jobs.js b/server/jobs.js index 0469d5ca..e4e7514b 100644 --- a/server/jobs.js +++ b/server/jobs.js @@ -9,6 +9,11 @@ const jobs = [ }, ]; +/** + * Initialize background jobs + * @param {Object} args Arguments to pass to workers + * @returns {Bree} + */ const initBackgroundJobs = function (args) { const bree = new Bree({ root: path.resolve("server", "jobs"), diff --git a/server/notification.js b/server/notification.js index 4d72c81c..a95af022 100644 --- a/server/notification.js +++ b/server/notification.js @@ -32,6 +32,7 @@ class Notification { providerList = {}; + /** Initialize the notification providers */ static init() { console.log("Prepare Notification Providers"); @@ -81,13 +82,13 @@ class Notification { } /** - * - * @param notification : BeanModel - * @param msg : string General Message - * @param monitorJSON : object Monitor details (For Up/Down only) - * @param heartbeatJSON : object Heartbeat details (For Up/Down only) + * Send a notification + * @param {BeanModel} notification + * @param {string} msg General Message + * @param {Object} monitorJSON Monitor details (For Up/Down only) + * @param {Object} heartbeatJSON Heartbeat details (For Up/Down only) * @returns {Promise} Successful msg - * Throw Error with fail msg + * @throws Error with fail msg */ static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { if (this.providerList[notification.type]) { @@ -97,28 +98,35 @@ class Notification { } } + /** + * Save a notification + * @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} + */ static async save(notification, notificationID, userID) { - let bean + let bean; if (notificationID) { bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, - ]) + ]); if (! bean) { - throw new Error("notification not found") + throw new Error("notification not found"); } } else { - bean = R.dispense("notification") + bean = R.dispense("notification"); } bean.name = notification.name; bean.user_id = userID; bean.config = JSON.stringify(notification); bean.is_default = notification.isDefault || false; - await R.store(bean) + await R.store(bean); if (notification.applyExisting) { await applyNotificationEveryMonitor(bean.id, userID); @@ -127,19 +135,29 @@ class Notification { return bean; } + /** + * Delete a notification + * @param {number} notificationID ID of notification to delete + * @param {number} userID ID of user who created notification + * @returns {Promise} + */ static async delete(notificationID, userID) { let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, - ]) + ]); if (! bean) { - throw new Error("notification not found") + throw new Error("notification not found"); } - await R.trash(bean) + await R.trash(bean); } + /** + * Check if apprise exists + * @returns {boolean} Does the command apprise exist? + */ static checkApprise() { let commandExistsSync = require("command-exists").sync; let exists = commandExistsSync("apprise"); @@ -148,6 +166,12 @@ class Notification { } +/** + * Apply the notification to every monitor + * @param {number} notificationID ID of notification to apply + * @param {number} userID ID of user who created notification + * @returns {Promise} + */ async function applyNotificationEveryMonitor(notificationID, userID) { let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ userID @@ -157,17 +181,17 @@ async function applyNotificationEveryMonitor(notificationID, userID) { let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ monitors[i].id, notificationID, - ]) + ]); if (! checkNotification) { let relation = R.dispense("monitor_notification"); relation.monitor_id = monitors[i].id; relation.notification_id = notificationID; - await R.store(relation) + await R.store(relation); } } } module.exports = { Notification, -} +}; diff --git a/server/password-hash.js b/server/password-hash.js index 91e5e1ad..482aa475 100644 --- a/server/password-hash.js +++ b/server/password-hash.js @@ -2,22 +2,42 @@ const passwordHashOld = require("password-hash"); const bcrypt = require("bcryptjs"); const saltRounds = 10; +/** + * Hash a password + * @param {string} password + * @returns {string} + */ exports.generate = function (password) { return bcrypt.hashSync(password, saltRounds); -} +}; +/** + * Verify a password against a hash + * @param {string} password + * @param {string} hash + * @returns {boolean} Does the password match the hash? + */ exports.verify = function (password, hash) { if (isSHA1(hash)) { - return passwordHashOld.verify(password, hash) + return passwordHashOld.verify(password, hash); } return bcrypt.compareSync(password, hash); -} +}; +/** + * Is the hash a SHA1 hash + * @param {string} hash + * @returns {boolean} + */ function isSHA1(hash) { return (typeof hash === "string" && hash.startsWith("sha1")) } +/** + * Does the hash need to be rehashed? + * @returns {boolean} + */ exports.needRehash = function (hash) { return isSHA1(hash); -} +}; diff --git a/server/ping-lite.js b/server/ping-lite.js index 0075e80b..2c84a473 100644 --- a/server/ping-lite.js +++ b/server/ping-lite.js @@ -8,6 +8,11 @@ const util = require("./util-server"); module.exports = Ping; +/** + * Constructor for ping class + * @param {string} host Host to ping + * @param {string} options Command line options for ping + */ function Ping(host, options) { if (!host) { throw new Error("You must specify a host to ping!"); @@ -75,8 +80,17 @@ function Ping(host, options) { Ping.prototype.__proto__ = events.EventEmitter.prototype; -// SEND A PING -// =========== +/** + * 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) { @@ -145,8 +159,10 @@ Ping.prototype.send = function (callback) { } }; -// CALL Ping#send(callback) ON A TIMER -// =================================== +/** + * Ping every interval + * @param {pingCB} callback Callback to call with results + */ Ping.prototype.start = function (callback) { let self = this; this._i = setInterval(function () { @@ -155,8 +171,7 @@ Ping.prototype.start = function (callback) { self.send(callback); }; -// STOP SENDING PINGS -// ================== +/** Stop sending pings */ Ping.prototype.stop = function () { clearInterval(this._i); }; @@ -165,7 +180,7 @@ Ping.prototype.stop = function () { * 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 data + * @param {any} data * @returns {string} */ function convertOutput(data) { diff --git a/server/prometheus.js b/server/prometheus.js index f91b0a13..acf2302b 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -34,6 +34,9 @@ const monitor_status = new PrometheusClient.Gauge({ class Prometheus { monitorLabelValues = {} + /** + * @param {Object} monitor Monitor object to monitor + */ constructor(monitor) { this.monitorLabelValues = { monitor_name: monitor.name, @@ -44,6 +47,11 @@ class Prometheus { }; } + /** + * Update the metrics page + * @param {Object} heartbeat Heartbeat details + * @param {Object} tlsInfo TLS details + */ update(heartbeat, tlsInfo) { if (typeof tlsInfo !== "undefined") { diff --git a/server/rate-limiter.js b/server/rate-limiter.js index 0bacc14c..2df956c0 100644 --- a/server/rate-limiter.js +++ b/server/rate-limiter.js @@ -2,11 +2,26 @@ const { RateLimiter } = require("limiter"); const { debug } = require("../src/util"); class KumaRateLimiter { + /** + * @param {Object} config Rate limiter configuration object + */ constructor(config) { this.errorMessage = config.errorMessage; this.rateLimiter = new RateLimiter(config); } + /** + * Callback for pass + * @callback passCB + * @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} + */ async pass(callback, num = 1) { const remainingRequests = await this.removeTokens(num); debug("Rate Limit (remainingRequests):" + remainingRequests); @@ -22,6 +37,11 @@ class KumaRateLimiter { return true; } + /** + * Remove a given number of tokens + * @param {number} [num=1] Number of tokens to remove + * @returns {Promise} + */ async removeTokens(num = 1) { return await this.rateLimiter.removeTokens(num); } diff --git a/server/server.js b/server/server.js index 153cac4f..88394472 100644 --- a/server/server.js +++ b/server/server.js @@ -1362,6 +1362,13 @@ exports.entryPage = "dashboard"; })(); +/** + * Update notifications for a given monitor + * @param {number} monitorID ID of monitor to update + * @param {Array} notificationIDList List of new notification + * providers to add + * @returns {Promise} + */ async function updateMonitorNotification(monitorID, notificationIDList) { await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ monitorID, @@ -1377,6 +1384,13 @@ async function updateMonitorNotification(monitorID, notificationIDList) { } } +/** + * Check if a given user owns a specific monitor + * @param {number} userID + * @param {number} monitorID + * @returns {Promise} + * @throws {Error} The specified user does not own the monitor + */ async function checkOwner(userID, monitorID) { let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [ monitorID, @@ -1388,12 +1402,23 @@ async function checkOwner(userID, monitorID) { } } +/** + * Send the monitor list to clients + * @param {Socket} socket Socket.io instance + * @returns {Object} + */ async function sendMonitorList(socket) { let list = await getMonitorJSONList(socket.userID); io.to(socket.userID).emit("monitorList", list); return list; } +/** + * Function called after user login + * @param {Socket} socket Socket.io instance + * @param {Object} user User object + * @returns {Promise} + */ async function afterLogin(socket, user) { socket.userID = user.id; socket.join(user.id); @@ -1416,6 +1441,11 @@ async function afterLogin(socket, user) { } } +/** + * Get a JSON representation of monitor list + * @param {number} userID + * @returns {Promise} + */ async function getMonitorJSONList(userID) { let result = {}; @@ -1430,6 +1460,12 @@ async function getMonitorJSONList(userID) { return result; } +/** + * Initialize the database + * @param {boolean} [testMode=false] Should the connection be + * started in test mode? + * @returns {Promise} + */ async function initDatabase(testMode = false) { if (! fs.existsSync(Database.path)) { console.log("Copying Database"); @@ -1464,6 +1500,12 @@ async function initDatabase(testMode = false) { jwtSecret = jwtSecretBean.value; } +/** + * Start the specified monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ async function startMonitor(userID, monitorID) { await checkOwner(userID, monitorID); @@ -1486,10 +1528,22 @@ async function startMonitor(userID, monitorID) { monitor.start(io); } +/** + * Restart a given monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ async function restartMonitor(userID, monitorID) { return await startMonitor(userID, monitorID); } +/** + * Pause a given monitor +* @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ async function pauseMonitor(userID, monitorID) { await checkOwner(userID, monitorID); @@ -1505,9 +1559,7 @@ async function pauseMonitor(userID, monitorID) { } } -/** - * Resume active monitors - */ +/** Resume active monitors */ async function startMonitors() { let list = await R.find("monitor", " active = 1 "); @@ -1522,6 +1574,11 @@ async function startMonitors() { } } +/** + * Shutdown the application + * @param {string} signal Shutdown signal + * @returns {Promise} + */ async function shutdownFunction(signal) { console.log("Shutdown requested"); console.log("Called signal: " + signal); @@ -1535,6 +1592,7 @@ async function shutdownFunction(signal) { await Database.close(); } +/** Final function called before application exits */ function finalFunction() { console.log("Graceful shutdown successful!"); } diff --git a/server/util-server.js b/server/util-server.js index 2264ebea..8f48505d 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -37,6 +37,12 @@ exports.initJWTSecret = async () => { return jwtSecretBean; }; +/** + * Send TCP request to specified hostname and port + * @param {string} hostname Hostname / address of machine + * @param {number} port TCP port to test + * @returns {Promise} Maximum time in ms rounded to nearest integer + */ exports.tcping = function (hostname, port) { return new Promise((resolve, reject) => { tcpp.ping({ @@ -58,6 +64,11 @@ exports.tcping = function (hostname, port) { }); }; +/** + * Ping the specified machine + * @param {string} hostname Hostname / address of machine + * @returns {Promise} Time for ping in ms rounded to nearest integer + */ exports.ping = async (hostname) => { try { return await exports.pingAsync(hostname); @@ -71,6 +82,12 @@ exports.ping = async (hostname) => { } }; +/** + * Ping the specified machine + * @param {string} hostname Hostname / address of machine to ping + * @param {boolean} ipv6 Should IPv6 be used? + * @returns {Promise} Time for ping in ms rounded to nearest integer + */ exports.pingAsync = function (hostname, ipv6 = false) { return new Promise((resolve, reject) => { const ping = new Ping(hostname, { @@ -89,6 +106,14 @@ exports.pingAsync = function (hostname, ipv6 = false) { }); }; +// `string[]`, `Object[]` and `Object`. +/** + * Resolves a given record using the specified DNS server + * @param {string} hostname The hostname of the record to lookup + * @param {string} resolver_server The DNS server to use + * @param {string} rrtype The type of record to request + * @returns {Promise<(string[]|Object[]|Object)>} + */ exports.dnsResolve = function (hostname, resolver_server, rrtype) { const resolver = new Resolver(); resolver.setServers([resolver_server]); @@ -113,6 +138,11 @@ exports.dnsResolve = function (hostname, resolver_server, rrtype) { }); }; +/** + * Retrieve value of setting based on key + * @param {string} key Key of setting to retrieve + * @returns {Promise} Object representation of setting + */ exports.setting = async function (key) { let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ key, @@ -127,6 +157,13 @@ exports.setting = async function (key) { } }; +/** + * Sets the specified setting to specifed value + * @param {string} key Key of setting to set + * @param {any} value Value to set to + * @param {?string} type Type of setting + * @returns {Promise} + */ exports.setSetting = async function (key, value, type = null) { let bean = await R.findOne("setting", " `key` = ? ", [ key, @@ -140,6 +177,11 @@ exports.setSetting = async function (key, value, type = null) { await R.store(bean); }; +/** + * Get settings based on type + * @param {?string} type The type of setting + * @returns {Promise} + */ exports.getSettings = async function (type) { let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [ type, @@ -158,6 +200,12 @@ exports.getSettings = async function (type) { return result; }; +/** + * Set settings based on type + * @param {?string} type Type of settings to set + * @param {Object} data Values of settings + * @returns {Promise} + */ exports.setSettings = async function (type, data) { let keyList = Object.keys(data); @@ -184,12 +232,23 @@ exports.setSettings = async function (type, data) { }; // ssl-checker by @dyaa -// param: res - response object from axios -// return an object containing the certificate information +//https://github.com/dyaa/ssl-checker/blob/master/src/index.ts +/** + * Get number of days between two dates + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} + */ const getDaysBetween = (validFrom, validTo) => Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); +/** + * Get days remaining from a time range + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} + */ const getDaysRemaining = (validFrom, validTo) => { const daysRemaining = getDaysBetween(validFrom, validTo); if (new Date(validTo).getTime() < new Date().getTime()) { @@ -198,8 +257,11 @@ const getDaysRemaining = (validFrom, validTo) => { return daysRemaining; }; -// Fix certificate Info for display -// param: info - the chain obtained from getPeerCertificate() +/** + * Fix certificate info for display + * @param {Object} info The chain obtained from getPeerCertificate() + * @returns {Object} An object representing certificate information + */ const parseCertificateInfo = function (info) { let link = info; let i = 0; @@ -239,6 +301,11 @@ const parseCertificateInfo = function (info) { return info; }; +/** + * Check if certificate is valid + * @param {Object} res Response object from axios + * @returns {Object} Object containing certificate information + */ exports.checkCertificate = function (res) { const info = res.request.res.socket.getPeerCertificate(true); const valid = res.request.res.socket.authorized || false; @@ -252,12 +319,13 @@ exports.checkCertificate = function (res) { }; }; -// Check if the provided status code is within the accepted ranges -// Param: status - the status code to check -// Param: accepted_codes - an array of accepted status codes -// Return: true if the status code is within the accepted ranges, false otherwise -// Will throw an error if the provided status code is not a valid range string or code string - +/** + * Check if the provided status code is within the accepted ranges + * @param {string} status The status code to check + * @param {Array} accepted_codes An array of accepted status codes + * @returns {boolean} True if status code within range, false otherwise + * @throws {Error} Will throw an error if the provided status code is not a valid range string or code string + */ exports.checkStatusCode = function (status, accepted_codes) { if (accepted_codes == null || accepted_codes.length === 0) { return false; @@ -281,6 +349,12 @@ exports.checkStatusCode = function (status, accepted_codes) { return false; }; +/** + * Get total number of clients in room + * @param {Server} io Socket server instance + * @param {string} roomName Name of room to check + * @returns {number} + */ exports.getTotalClientInRoom = (io, roomName) => { const sockets = io.sockets; @@ -304,23 +378,36 @@ exports.getTotalClientInRoom = (io, roomName) => { } }; +/** + * Allow CORS all origins if development + * @param {Object} res Response object from axios + */ exports.allowDevAllOrigin = (res) => { if (process.env.NODE_ENV === "development") { exports.allowAllOrigin(res); } }; +/** + * Allow CORS all origins + * @param {Object} res Response object from axios + */ exports.allowAllOrigin = (res) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); }; +/** + * Check if a user is logged in + * @param {Socket} socket Socket instance + */ exports.checkLogin = (socket) => { if (! socket.userID) { throw new Error("You are not logged in."); } }; +/** Start Unit tests */ exports.startUnitTest = async () => { console.log("Starting unit test..."); const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; @@ -341,7 +428,8 @@ exports.startUnitTest = async () => { }; /** - * @param body : Buffer + * Convert unknown string to UTF8 + * @param {Uint8Array} body Buffer * @returns {string} */ exports.convertToUTF8 = (body) => { @@ -359,6 +447,11 @@ try { }); } catch (_) { } +/** + * Write error to log file + * @param {any} error The error to write + * @param {boolean} outputToConsole Should the error also be output to console? + */ exports.errorLog = (error, outputToConsole = true) => { try { if (logFile) {