Refactor modules for improved readability and consistency

Reformatted code across multiple modules, standardizing string quotes, indentation, and spacing. Improved readability by restructuring blocks and aligning object properties consistently. These changes ensure better code maintainability and follow standard conventions.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
This commit is contained in:
Toby Liddicoat 2025-02-27 19:58:07 +00:00
parent 200f179b5f
commit 9081025c4a
35 changed files with 699 additions and 369 deletions

View file

@ -3,7 +3,10 @@ const passwordHash = require("./password-hash");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { setting } = require("./util-server"); const { setting } = require("./util-server");
const { log } = require("../src/util"); const { log } = require("../src/util");
const { loginRateLimiter, apiRateLimiter } = require("./rate-limiter"); const {
loginRateLimiter,
apiRateLimiter,
} = require("./rate-limiter");
const { Settings } = require("./settings"); const { Settings } = require("./settings");
const dayjs = require("dayjs"); const dayjs = require("dayjs");

View file

@ -47,10 +47,11 @@ async function sendNotificationList(socket) {
*/ */
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
let list = await R.getAll(` let list = await R.getAll(`
SELECT * FROM heartbeat SELECT *
FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
ORDER BY time DESC ORDER BY time DESC
LIMIT 100 LIMIT 100
`, [ `, [
monitorID, monitorID,
]); ]);

View file

@ -1,7 +1,13 @@
const fs = require("fs"); const fs = require("fs");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server"); const {
const { log, sleep } = require("../src/util"); setSetting,
setting,
} = require("./util-server");
const {
log,
sleep,
} = require("../src/util");
const knex = require("knex"); const knex = require("knex");
const path = require("path"); const path = require("path");
const { EmbeddedMariaDB } = require("./embedded-mariadb"); const { EmbeddedMariaDB } = require("./embedded-mariadb");
@ -136,24 +142,24 @@ class Database {
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/"; Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
Database.sqlitePath = path.join(Database.dataDir, "kuma.db"); Database.sqlitePath = path.join(Database.dataDir, "kuma.db");
if (! fs.existsSync(Database.dataDir)) { if (!fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true }); fs.mkdirSync(Database.dataDir, { recursive: true });
} }
Database.uploadDir = path.join(Database.dataDir, "upload/"); Database.uploadDir = path.join(Database.dataDir, "upload/");
if (! fs.existsSync(Database.uploadDir)) { if (!fs.existsSync(Database.uploadDir)) {
fs.mkdirSync(Database.uploadDir, { recursive: true }); fs.mkdirSync(Database.uploadDir, { recursive: true });
} }
// Create screenshot dir // Create screenshot dir
Database.screenshotDir = path.join(Database.dataDir, "screenshots/"); Database.screenshotDir = path.join(Database.dataDir, "screenshots/");
if (! fs.existsSync(Database.screenshotDir)) { if (!fs.existsSync(Database.screenshotDir)) {
fs.mkdirSync(Database.screenshotDir, { recursive: true }); fs.mkdirSync(Database.screenshotDir, { recursive: true });
} }
Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/"); Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/");
if (! fs.existsSync(Database.dockerTLSDir)) { if (!fs.existsSync(Database.dockerTLSDir)) {
fs.mkdirSync(Database.dockerTLSDir, { recursive: true }); fs.mkdirSync(Database.dockerTLSDir, { recursive: true });
} }
@ -231,7 +237,7 @@ class Database {
if (dbConfig.type === "sqlite") { if (dbConfig.type === "sqlite") {
if (! fs.existsSync(Database.sqlitePath)) { if (!fs.existsSync(Database.sqlitePath)) {
log.info("server", "Copying Database"); log.info("server", "Copying Database");
fs.copyFileSync(Database.templatePath, Database.sqlitePath); fs.copyFileSync(Database.templatePath, Database.sqlitePath);
} }
@ -252,7 +258,7 @@ class Database {
idleTimeoutMillis: 120 * 1000, idleTimeoutMillis: 120 * 1000,
propagateCreateError: false, propagateCreateError: false,
acquireTimeoutMillis: acquireConnectionTimeout, acquireTimeoutMillis: acquireConnectionTimeout,
} },
}; };
} else if (dbConfig.type === "mariadb") { } else if (dbConfig.type === "mariadb") {
if (!/^\w+$/.test(dbConfig.dbName)) { if (!/^\w+$/.test(dbConfig.dbName)) {
@ -451,7 +457,7 @@ class Database {
static async patchSqlite() { static async patchSqlite() {
let version = parseInt(await setting("database_version")); let version = parseInt(await setting("database_version"));
if (! version) { if (!version) {
version = 0; version = 0;
} }
@ -502,7 +508,7 @@ class Database {
log.debug("db", "Database Patch 2.0 Process"); log.debug("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles"); let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) { if (!databasePatchedFiles) {
databasePatchedFiles = {}; databasePatchedFiles = {};
} }
@ -579,11 +585,11 @@ class Database {
let id = await R.store(statusPage); let id = await R.store(statusPage);
await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [ await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [
id id,
]); ]);
await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [ await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [
id id,
]); ]);
await R.exec("DELETE FROM setting WHERE type = 'statusPage'"); await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
@ -611,13 +617,13 @@ class Database {
static async patch2Recursion(sqlFilename, databasePatchedFiles) { static async patch2Recursion(sqlFilename, databasePatchedFiles) {
let value = this.patchList[sqlFilename]; let value = this.patchList[sqlFilename];
if (! value) { if (!value) {
log.info("db", sqlFilename + " skip"); log.info("db", sqlFilename + " skip");
return; return;
} }
// Check if patched // Check if patched
if (! databasePatchedFiles[sqlFilename]) { if (!databasePatchedFiles[sqlFilename]) {
log.info("db", sqlFilename + " is not patched"); log.info("db", sqlFilename + " is not patched");
if (value.parents) { if (value.parents) {
@ -652,7 +658,7 @@ class Database {
// Remove all comments (--) // Remove all comments (--)
let lines = text.split("\n"); let lines = text.split("\n");
lines = lines.filter((line) => { lines = lines.filter((line) => {
return ! line.startsWith("--"); return !line.startsWith("--");
}); });
// Split statements by semicolon // Split statements by semicolon
@ -797,7 +803,8 @@ class Database {
// Stop if stat_* tables are not empty // Stop if stat_* tables are not empty
for (let table of [ "stat_minutely", "stat_hourly", "stat_daily" ]) { for (let table of [ "stat_minutely", "stat_hourly", "stat_daily" ]) {
let countResult = await R.getRow(`SELECT COUNT(*) AS count FROM ${table}`); let countResult = await R.getRow(`SELECT COUNT(*) AS count
FROM ${table}`);
let count = countResult.count; let count = countResult.count;
if (count > 0) { if (count > 0) {
log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`); log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`);
@ -814,12 +821,12 @@ class Database {
for (let monitor of monitors) { for (let monitor of monitors) {
// Get a list of unique dates from the heartbeat table, using raw sql // Get a list of unique dates from the heartbeat table, using raw sql
let dates = await R.getAll(` let dates = await R.getAll(`
SELECT DISTINCT DATE(time) AS date SELECT DISTINCT DATE (time) AS date
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
ORDER BY date ASC ORDER BY date ASC
`, [ `, [
monitor.monitor_id monitor.monitor_id,
]); ]);
for (let date of dates) { for (let date of dates) {
@ -833,7 +840,7 @@ class Database {
SELECT status, ping, time SELECT status, ping, time
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
AND DATE(time) = ? AND DATE (time) = ?
ORDER BY time ASC ORDER BY time ASC
`, [ monitor.monitor_id, date.date ]); `, [ monitor.monitor_id, date.date ]);
@ -887,19 +894,21 @@ class Database {
log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id); log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id);
} }
await R.exec(` await R.exec(`
DELETE FROM heartbeat DELETE
FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
AND important = 0 AND important = 0
AND time < ${sqlHourOffset} AND time
AND id NOT IN ( < ${sqlHourOffset}
AND id NOT IN (
SELECT id FROM ( -- written this way for Maria's support SELECT id FROM ( -- written this way for Maria's support
SELECT id SELECT id
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
ORDER BY time DESC ORDER BY time DESC
LIMIT ? LIMIT ?
) AS limited_ids ) AS limited_ids
) )
`, [ `, [
monitor.id, monitor.id,
-24, -24,

View file

@ -146,7 +146,7 @@ class DockerHost {
static getHttpsAgentOptions(dockerType, url) { static getHttpsAgentOptions(dockerType, url) {
let baseOptions = { let baseOptions = {
maxCachedSessions: 0, maxCachedSessions: 0,
rejectUnauthorized: true rejectUnauthorized: true,
}; };
let certOptions = {}; let certOptions = {};
@ -163,13 +163,13 @@ class DockerHost {
certOptions = { certOptions = {
ca, ca,
key, key,
cert cert,
}; };
} }
return { return {
...baseOptions, ...baseOptions,
...certOptions ...certOptions,
}; };
} }
} }

View file

@ -15,7 +15,7 @@ const jobs = [
interval: "*/5 * * * *", interval: "*/5 * * * *",
jobFunc: incrementalVacuum, jobFunc: incrementalVacuum,
croner: null, croner: null,
} },
]; ];
/** /**
@ -54,5 +54,5 @@ const stopBackgroundJobs = function () {
module.exports = { module.exports = {
initBackgroundJobs, initBackgroundJobs,
stopBackgroundJobs stopBackgroundJobs,
}; };

View file

@ -1,5 +1,9 @@
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
const { parseTimeObject, parseTimeFromTimeObject, log } = require("../../src/util"); const {
parseTimeObject,
parseTimeFromTimeObject,
log,
} = require("../../src/util");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const Cron = require("croner"); const Cron = require("croner");
@ -192,7 +196,8 @@ class Maintenance extends BeanModel {
* @returns {void} * @returns {void}
*/ */
static validateCron(cron) { static validateCron(cron) {
let job = new Cron(cron, () => {}); let job = new Cron(cron, () => {
});
job.stop(); job.stop();
} }

View file

@ -1,11 +1,37 @@
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const axios = require("axios"); const axios = require("axios");
const { Prometheus } = require("../prometheus"); const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, const {
SQL_DATETIME_FORMAT, evaluateJsonQuery log,
UP,
DOWN,
PENDING,
MAINTENANCE,
flipStatus,
MAX_INTERVAL_SECOND,
MIN_INTERVAL_SECOND,
SQL_DATETIME_FORMAT,
evaluateJsonQuery,
} = require("../../src/util"); } = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, const {
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal tcping,
ping,
checkCertificate,
checkStatusCode,
getTotalClientInRoom,
setting,
mssqlQuery,
postgresQuery,
mysqlQuery,
setSetting,
httpNtlm,
radius,
grpcQuery,
redisPingAsync,
kafkaProducerAsync,
getOidcTokenClientCredentials,
rootCertificatesFingerprints,
axiosAbortSignal,
} = require("../util-server"); } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
@ -61,7 +87,10 @@ class Monitor extends BeanModel {
} }
if (certExpiry && (this.type === "http" || this.type === "keyword" || this.type === "json-query") && this.getURLProtocol() === "https:") { if (certExpiry && (this.type === "http" || this.type === "keyword" || this.type === "json-query") && this.getURLProtocol() === "https:") {
const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id); const {
certExpiryDaysRemaining,
validCert,
} = await this.getCertExpiry(this.id);
obj.certExpiryDaysRemaining = certExpiryDaysRemaining; obj.certExpiryDaysRemaining = certExpiryDaysRemaining;
obj.validCert = validCert; obj.validCert = validCert;
} }
@ -218,13 +247,13 @@ class Monitor extends BeanModel {
if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) { if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) {
return { return {
certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining, certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining,
validCert: true validCert: true,
}; };
} }
} }
return { return {
certExpiryDaysRemaining: "", certExpiryDaysRemaining: "",
validCert: false validCert: false,
}; };
} }
@ -334,7 +363,7 @@ class Monitor extends BeanModel {
let beatInterval = this.interval; let beatInterval = this.interval;
if (! beatInterval) { if (!beatInterval) {
beatInterval = 1; beatInterval = 1;
} }
@ -479,7 +508,7 @@ class Monitor extends BeanModel {
...(contentType ? { "Content-Type": contentType } : {}), ...(contentType ? { "Content-Type": contentType } : {}),
...(basicAuthHeader), ...(basicAuthHeader),
...(oauth2AuthHeader), ...(oauth2AuthHeader),
...(this.headers ? JSON.parse(this.headers) : {}) ...(this.headers ? JSON.parse(this.headers) : {}),
}, },
maxRedirects: this.maxredirects, maxRedirects: this.maxredirects,
validateStatus: (status) => { validateStatus: (status) => {
@ -504,7 +533,10 @@ class Monitor extends BeanModel {
const proxy = await R.load("proxy", this.proxy_id); const proxy = await R.load("proxy", this.proxy_id);
if (proxy && proxy.active) { if (proxy && proxy.active) {
const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, { const {
httpAgent,
httpsAgent,
} = Proxy.createAgents(proxy, {
httpsAgentOptions: httpsAgentOptions, httpsAgentOptions: httpsAgentOptions,
}); });
@ -518,7 +550,7 @@ class Monitor extends BeanModel {
let jar = new CookieJar(); let jar = new CookieJar();
let httpsCookieAgentOptions = { let httpsCookieAgentOptions = {
...httpsAgentOptions, ...httpsAgentOptions,
cookies: { jar } cookies: { jar },
}; };
options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions); options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions);
} }
@ -600,7 +632,10 @@ class Monitor extends BeanModel {
} else if (this.type === "json-query") { } else if (this.type === "json-query") {
let data = res.data; let data = res.data;
const { status, response } = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue); const {
status,
response,
} = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue);
if (status) { if (status) {
bean.status = UP; bean.status = UP;
@ -681,7 +716,7 @@ class Monitor extends BeanModel {
params: { params: {
filter: filter, filter: filter,
key: steamAPIKey, key: steamAPIKey,
} },
}); });
if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) { if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) {
@ -690,7 +725,8 @@ class Monitor extends BeanModel {
try { try {
bean.ping = await ping(this.hostname, this.packetSize); bean.ping = await ping(this.hostname, this.packetSize);
} catch (_) { } } catch (_) {
}
} else { } else {
throw new Error("Server not found on Steam"); throw new Error("Server not found on Steam");
} }
@ -739,7 +775,7 @@ class Monitor extends BeanModel {
} else if (dockerHost._dockerType === "tcp") { } else if (dockerHost._dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon); options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon);
options.httpsAgent = new https.Agent( options.httpsAgent = new https.Agent(
DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL) DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL),
); );
} }
@ -984,12 +1020,12 @@ class Monitor extends BeanModel {
previousBeat = bean; previousBeat = bean;
if (! this.isStop) { if (!this.isStop) {
log.debug("monitor", `[${this.name}] SetTimeout for next check.`); log.debug("monitor", `[${this.name}] SetTimeout for next check.`);
let intervalRemainingMs = Math.max( let intervalRemainingMs = Math.max(
1, 1,
beatInterval * 1000 - dayjs().diff(dayjs.utc(bean.time)) beatInterval * 1000 - dayjs().diff(dayjs.utc(bean.time)),
); );
log.debug("monitor", `[${this.name}] Next heartbeat in: ${intervalRemainingMs}ms`); log.debug("monitor", `[${this.name}] Next heartbeat in: ${intervalRemainingMs}ms`);
@ -1013,7 +1049,7 @@ class Monitor extends BeanModel {
UptimeKumaServer.errorLog(e, false); UptimeKumaServer.errorLog(e, false);
log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues"); log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues");
if (! this.isStop) { if (!this.isStop) {
log.info("monitor", "Try to restart the monitor"); log.info("monitor", "Try to restart the monitor");
this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
} }
@ -1047,7 +1083,7 @@ class Monitor extends BeanModel {
username: this.basic_auth_user, username: this.basic_auth_user,
password: this.basic_auth_pass, password: this.basic_auth_pass,
domain: this.authDomain, domain: this.authDomain,
workstation: this.authWorkstation ? this.authWorkstation : undefined workstation: this.authWorkstation ? this.authWorkstation : undefined,
}); });
} else { } else {
res = await axios.request(options); res = await axios.request(options);
@ -1065,8 +1101,9 @@ class Monitor extends BeanModel {
let oauth2AuthHeader = { let oauth2AuthHeader = {
"Authorization": this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token, "Authorization": this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token,
}; };
options.headers = { ...(options.headers), options.headers = {
...(oauth2AuthHeader) ...(options.headers),
...(oauth2AuthHeader),
}; };
return this.makeAxiosRequest(options, true); return this.makeAxiosRequest(options, true);
@ -1158,7 +1195,7 @@ class Monitor extends BeanModel {
if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) { if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
log.debug("monitor", "Resetting sent_history"); log.debug("monitor", "Resetting sent_history");
await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [ await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
this.id this.id,
]); ]);
} else { } else {
log.debug("monitor", "No need to reset sent_history"); log.debug("monitor", "No need to reset sent_history");
@ -1168,7 +1205,8 @@ class Monitor extends BeanModel {
} else { } else {
log.debug("monitor", "Not valid object"); log.debug("monitor", "Not valid object");
} }
} catch (e) { } } catch (e) {
}
} }
@ -1326,8 +1364,9 @@ class Monitor extends BeanModel {
for (let notification of notificationList) { for (let notification of notificationList) {
try { try {
const heartbeatJSON = bean.toJSON(); const heartbeatJSON = bean.toJSON();
const monitorData = [{ id: monitor.id, const monitorData = [ {
active: monitor.active id: monitor.id,
active: monitor.active,
}]; }];
const preloadData = await Monitor.preparePreloadData(monitorData); const preloadData = await Monitor.preparePreloadData(monitorData);
// Prevent if the msg is undefined, notifications such as Discord cannot send out. // Prevent if the msg is undefined, notifications such as Discord cannot send out.
@ -1370,7 +1409,7 @@ class Monitor extends BeanModel {
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
const notificationList = await Monitor.getNotificationList(this); const notificationList = await Monitor.getNotificationList(this);
if (! notificationList.length > 0) { if (!notificationList.length > 0) {
// fail fast. If no notification is set, all the following checks can be skipped. // fail fast. If no notification is set, all the following checks can be skipped.
log.debug("monitor", "No notification, no need to send cert notification"); log.debug("monitor", "No notification, no need to send cert notification");
return; return;
@ -1458,7 +1497,7 @@ class Monitor extends BeanModel {
*/ */
static async getPreviousHeartbeat(monitorID) { static async getPreviousHeartbeat(monitorID) {
return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [ return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [
monitorID monitorID,
]); ]);
} }
@ -1570,7 +1609,7 @@ class Monitor extends BeanModel {
monitor_id: row.monitor_id, monitor_id: row.monitor_id,
value: row.value, value: row.value,
name: row.name, name: row.name,
color: row.color color: row.color,
}); });
}); });
@ -1687,7 +1726,7 @@ class Monitor extends BeanModel {
*/ */
static async unlinkAllChildren(groupID) { static async unlinkAllChildren(groupID) {
return await R.exec("UPDATE `monitor` SET parent = ? WHERE parent = ? ", [ return await R.exec("UPDATE `monitor` SET parent = ? WHERE parent = ? ", [
null, groupID null, groupID,
]); ]);
} }

View file

@ -8,7 +8,15 @@ const { marked } = require("marked");
const { Feed } = require("feed"); const { Feed } = require("feed");
const config = require("../config"); const config = require("../config");
const { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE, DOWN } = require("../../src/util"); const {
STATUS_PAGE_ALL_DOWN,
STATUS_PAGE_ALL_UP,
STATUS_PAGE_MAINTENANCE,
STATUS_PAGE_PARTIAL_DOWN,
UP,
MAINTENANCE,
DOWN,
} = require("../../src/util");
class StatusPage extends BeanModel { class StatusPage extends BeanModel {
@ -16,7 +24,7 @@ class StatusPage extends BeanModel {
* Like this: { "test-uptime.kuma.pet": "default" } * Like this: { "test-uptime.kuma.pet": "default" }
* @type {{}} * @type {{}}
*/ */
static domainMappingList = { }; static domainMappingList = {};
/** /**
* Handle responses to RSS pages * Handle responses to RSS pages
@ -26,7 +34,7 @@ class StatusPage extends BeanModel {
*/ */
static async handleStatusPageRSSResponse(response, slug) { static async handleStatusPageRSSResponse(response, slug) {
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [
slug slug,
]); ]);
if (statusPage) { if (statusPage) {
@ -51,7 +59,7 @@ class StatusPage extends BeanModel {
} }
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [
slug slug,
]); ]);
if (statusPage) { if (statusPage) {
@ -68,7 +76,10 @@ class StatusPage extends BeanModel {
* @returns {Promise<string>} the rendered html * @returns {Promise<string>} the rendered html
*/ */
static async renderRSS(statusPage, slug) { static async renderRSS(statusPage, slug) {
const { heartbeats, statusDescription } = await StatusPage.getRSSPageData(statusPage); const {
heartbeats,
statusDescription,
} = await StatusPage.getRSSPageData(statusPage);
let proto = config.isSSL ? "https" : "http"; let proto = config.isSSL ? "https" : "http";
let host = `${proto}://${config.hostname || "localhost"}:${config.port}/status/${slug}`; let host = `${proto}://${config.hostname || "localhost"}:${config.port}/status/${slug}`;
@ -135,7 +146,7 @@ class StatusPage extends BeanModel {
// Preload data // Preload data
// Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186 // Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), { const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), {
"isScriptContext": true "isScriptContext": true,
}); });
const script = $(` const script = $(`
@ -174,7 +185,7 @@ class StatusPage extends BeanModel {
} }
} }
if (! hasUp) { if (!hasUp) {
status = STATUS_PAGE_ALL_DOWN; status = STATUS_PAGE_ALL_DOWN;
} }
@ -223,7 +234,7 @@ class StatusPage extends BeanModel {
const showTags = !!statusPage.show_tags; const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id statusPage.id,
]); ]);
let heartbeats = []; let heartbeats = [];
@ -236,7 +247,7 @@ class StatusPage extends BeanModel {
heartbeats.push({ heartbeats.push({
...monitor, ...monitor,
status: heartbeat.status, status: heartbeat.status,
time: heartbeat.time time: heartbeat.time,
}); });
} }
} }
@ -251,7 +262,7 @@ class StatusPage extends BeanModel {
return { return {
heartbeats, heartbeats,
statusDescription statusDescription,
}; };
} }
@ -279,7 +290,7 @@ class StatusPage extends BeanModel {
const showTags = !!statusPage.show_tags; const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id statusPage.id,
]); ]);
for (let groupBean of list) { for (let groupBean of list) {
@ -442,7 +453,7 @@ class StatusPage extends BeanModel {
*/ */
static async slugToID(slug) { static async slugToID(slug) {
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [ return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
slug slug,
]); ]);
} }

View file

@ -2,7 +2,10 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
const passwordHash = require("../password-hash"); const passwordHash = require("../password-hash");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
const { shake256, SHAKE256_LENGTH } = require("../util-server"); const {
shake256,
SHAKE256_LENGTH,
} = require("../util-server");
class User extends BeanModel { class User extends BeanModel {
/** /**
@ -15,7 +18,7 @@ class User extends BeanModel {
static async resetPassword(userID, newPassword) { static async resetPassword(userID, newPassword) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
passwordHash.generate(newPassword), passwordHash.generate(newPassword),
userID userID,
]); ]);
} }
@ -29,7 +32,7 @@ class User extends BeanModel {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
hashedPassword, hashedPassword,
this.id this.id,
]); ]);
this.password = hashedPassword; this.password = hashedPassword;

View file

@ -100,7 +100,7 @@ function ApiCache() {
* Generated by Trelent * Generated by Trelent
*/ */
function debug(a, b, c, d) { function debug(a, b, c, d) {
let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) { let arr = [ "\x1b[36m[apicache]\x1b[0m", a, b, c, d ].filter(function (arg) {
return arg !== undefined; return arg !== undefined;
}); });
let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1; let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1;
@ -210,7 +210,8 @@ function ApiCache() {
try { try {
redis.hset(key, "response", JSON.stringify(value)); redis.hset(key, "response", JSON.stringify(value));
redis.hset(key, "duration", duration); redis.hset(key, "duration", duration);
redis.expire(key, duration / 1000, expireCallback || function () {}); redis.expire(key, duration / 1000, expireCallback || function () {
});
} catch (err) { } catch (err) {
debug("[apicache] error in redis.hset()"); debug("[apicache] error in redis.hset()");
} }
@ -247,8 +248,8 @@ function ApiCache() {
} }
res._apicache.content = Buffer.concat( res._apicache.content = Buffer.concat(
[oldContent, content], [ oldContent, content ],
oldContent.length + content.length oldContent.length + content.length,
); );
} else { } else {
res._apicache.content = content; res._apicache.content = content;
@ -268,7 +269,7 @@ function ApiCache() {
* @param {function(Object, Object):boolean} toggle * @param {function(Object, Object):boolean} toggle
*/ */
function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) {
// monkeypatch res.end to create cache object // monkeypatch res.end to create cache object
res._apicache = { res._apicache = {
write: res.write, write: res.write,
writeHead: res.writeHead, writeHead: res.writeHead,
@ -314,7 +315,7 @@ function ApiCache() {
res.statusCode, res.statusCode,
headers, headers,
res._apicache.content, res._apicache.content,
encoding encoding,
); );
cacheResponse(key, cacheObject, duration); cacheResponse(key, cacheObject, duration);
@ -367,7 +368,7 @@ function ApiCache() {
let data = cacheObject.data; let data = cacheObject.data;
if (data && data.type === "Buffer") { if (data && data.type === "Buffer") {
data = data =
typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data); typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
} }
// test Etag against If-None-Match for 304 // test Etag against If-None-Match for 304
@ -511,15 +512,15 @@ function ApiCache() {
}; };
/** /**
* Return cache performance statistics (hit rate). Suitable for * Return cache performance statistics (hit rate). Suitable for
* putting into a route: * putting into a route:
* <code> * <code>
* app.get('/api/cache/performance', (req, res) => { * app.get('/api/cache/performance', (req, res) => {
* res.json(apicache.getPerformance()) * res.json(apicache.getPerformance())
* }) * })
* </code> * </code>
* @returns {any[]} * @returns {any[]}
*/ */
this.getPerformance = function () { this.getPerformance = function () {
return performanceArray.map(function (p) { return performanceArray.map(function (p) {
return p.report(); return p.report();
@ -528,7 +529,7 @@ function ApiCache() {
/** /**
* Get index of a group * Get index of a group
* @param {string} group * @param {string} group
* @returns {number} * @returns {number}
*/ */
this.getIndex = function (group) { this.getIndex = function (group) {
@ -543,9 +544,9 @@ function ApiCache() {
* Express middleware * Express middleware
* @param {(string|number)} strDuration Duration to cache responses * @param {(string|number)} strDuration Duration to cache responses
* for. * for.
* @param {function(Object, Object):boolean} middlewareToggle * @param {function(Object, Object):boolean} middlewareToggle
* @param {Object} localOptions Options for APICache * @param {Object} localOptions Options for APICache
* @returns * @returns
*/ */
this.middleware = function cache(strDuration, middlewareToggle, localOptions) { this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
let duration = instance.getDuration(strDuration); let duration = instance.getDuration(strDuration);
@ -573,7 +574,8 @@ function ApiCache() {
* A Function for non tracking performance * A Function for non tracking performance
*/ */
function NOOPCachePerformance() { function NOOPCachePerformance() {
this.report = this.hit = this.miss = function () {}; // noop; this.report = this.hit = this.miss = function () {
}; // noop;
} }
/** /**
@ -762,8 +764,8 @@ function ApiCache() {
} }
if ( if (
req.headers["x-apicache-bypass"] || req.headers["x-apicache-bypass"] ||
req.headers["x-apicache-force-fetch"] || req.headers["x-apicache-force-fetch"] ||
(opt.respectCacheControl && req.headers["cache-control"] == "no-cache") (opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
) { ) {
return bypass(); return bypass();
} }
@ -826,7 +828,7 @@ function ApiCache() {
JSON.parse(obj.response), JSON.parse(obj.response),
middlewareToggle, middlewareToggle,
next, next,
duration duration,
); );
} else { } else {
perf.miss(key); perf.miss(key);
@ -837,7 +839,7 @@ function ApiCache() {
key, key,
duration, duration,
strDuration, strDuration,
middlewareToggle middlewareToggle,
); );
} }
}); });
@ -859,7 +861,7 @@ function ApiCache() {
/** /**
* Process options * Process options
* @param {Object} options * @param {Object} options
* @returns {Object} * @returns {Object}
*/ */
this.options = function (options) { this.options = function (options) {

View file

@ -2,7 +2,7 @@ const apicache = require("./apicache");
apicache.options({ apicache.options({
headerBlacklist: [ headerBlacklist: [
"cache-control" "cache-control",
], ],
headers: { headers: {
// Disable client side cache, only server side cache. // Disable client side cache, only server side cache.

View file

@ -4,7 +4,7 @@ function MemoryCache() {
} }
/** /**
* *
* @param {string} key Key to store cache as * @param {string} key Key to store cache as
* @param {any} value Value to store * @param {any} value Value to store
* @param {number} time Time to store for * @param {number} time Time to store for
@ -22,7 +22,7 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
timeout: setTimeout(function () { timeout: setTimeout(function () {
instance.delete(key); instance.delete(key);
return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key); return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
}, time) }, time),
}; };
this.cache[key] = entry; this.cache[key] = entry;
@ -52,7 +52,7 @@ MemoryCache.prototype.delete = function (key) {
/** /**
* Get value of key * Get value of key
* @param {string} key * @param {string} key
* @returns {Object} * @returns {Object}
*/ */
MemoryCache.prototype.get = function (key) { MemoryCache.prototype.get = function (key) {
@ -63,7 +63,7 @@ MemoryCache.prototype.get = function (key) {
/** /**
* Get value of cache entry * Get value of cache entry
* @param {string} key * @param {string} key
* @returns {any} * @returns {any}
*/ */
MemoryCache.prototype.getValue = function (key) { MemoryCache.prototype.getValue = function (key) {

View file

@ -1,4 +1,4 @@
'use strict'; "use strict";
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0; module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
/* Indicates that Unicode strings are supported for use in security buffer /* Indicates that Unicode strings are supported for use in security buffer
@ -74,4 +74,4 @@ module.exports.NTLMFLAG_NEGOTIATE_KEY_EXCHANGE = 1 << 30;
/* Indicates that the client will provide an encrypted master key in /* Indicates that the client will provide an encrypted master key in
the "Session Key" field of the Type 3 message. */ the "Session Key" field of the Type 3 message. */
module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31; module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31;
//# sourceMappingURL=flags.js.map //# sourceMappingURL=flags.js.map

View file

@ -1,6 +1,7 @@
'use strict'; "use strict";
// Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js // Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js
var crypto = require('crypto'); var crypto = require("crypto");
function createLMResponse(challenge, lmhash) { function createLMResponse(challenge, lmhash) {
var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0); var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0);
lmhash.copy(pwBuffer); lmhash.copy(pwBuffer);
@ -9,19 +10,21 @@ function createLMResponse(challenge, lmhash) {
calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16); calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16);
return buf; return buf;
} }
function createLMHash(password) { function createLMHash(password) {
var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from('KGS!@#$%', 'ascii'); var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from("KGS!@#$%", "ascii");
if (password.length > 14) { if (password.length > 14) {
buf.fill(0); buf.fill(0);
return buf; return buf;
} }
pwBuffer.fill(0); pwBuffer.fill(0);
pwBuffer.write(password.toUpperCase(), 0, 'ascii'); pwBuffer.write(password.toUpperCase(), 0, "ascii");
return Buffer.concat([ return Buffer.concat([
calculateDES(pwBuffer.slice(0, 7), magicKey), calculateDES(pwBuffer.slice(0, 7), magicKey),
calculateDES(pwBuffer.slice(7), magicKey) calculateDES(pwBuffer.slice(7), magicKey),
]); ]);
} }
function calculateDES(key, message) { function calculateDES(key, message) {
var desKey = new Buffer.alloc(8); var desKey = new Buffer.alloc(8);
desKey[0] = key[0] & 0xFE; desKey[0] = key[0] & 0xFE;
@ -39,9 +42,10 @@ function calculateDES(key, message) {
} }
desKey[i] |= (parity % 2) === 0 ? 1 : 0; desKey[i] |= (parity % 2) === 0 ? 1 : 0;
} }
var des = crypto.createCipheriv('DES-ECB', desKey, ''); var des = crypto.createCipheriv("DES-ECB", desKey, "");
return des.update(message); return des.update(message);
} }
function createNTLMResponse(challenge, ntlmhash) { function createNTLMResponse(challenge, ntlmhash) {
var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0); var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0);
ntlmhash.copy(ntlmBuffer); ntlmhash.copy(ntlmBuffer);
@ -50,30 +54,36 @@ function createNTLMResponse(challenge, ntlmhash) {
calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16); calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16);
return buf; return buf;
} }
function createNTLMHash(password) { function createNTLMHash(password) {
var md4sum = crypto.createHash('md4'); var md4sum = crypto.createHash("md4");
md4sum.update(new Buffer.from(password, 'ucs2')); md4sum.update(new Buffer.from(password, "ucs2"));
return md4sum.digest(); return md4sum.digest();
} }
function createNTLMv2Hash(ntlmhash, username, authTargetName) { function createNTLMv2Hash(ntlmhash, username, authTargetName) {
var hmac = crypto.createHmac('md5', ntlmhash); var hmac = crypto.createHmac("md5", ntlmhash);
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2')); hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, "ucs2"));
return hmac.digest(); return hmac.digest();
} }
function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) { function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName),
hmac = crypto.createHmac("md5", ntlm2hash);
//server challenge //server challenge
type2message.challenge.copy(buf, 8); type2message.challenge.copy(buf, 8);
//client nonce //client nonce
buf.write(nonce || createPseudoRandomValue(16), 16, 'hex'); buf.write(nonce || createPseudoRandomValue(16), 16, "hex");
//create hash //create hash
hmac.update(buf.slice(8)); hmac.update(buf.slice(8));
var hashedBuffer = hmac.digest(); var hashedBuffer = hmac.digest();
hashedBuffer.copy(buf); hashedBuffer.copy(buf);
return buf; return buf;
} }
function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) { function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length),
ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac("md5", ntlm2hash);
//the first 8 bytes are spare to store the hashed value before the blob //the first 8 bytes are spare to store the hashed value before the blob
//server challenge //server challenge
type2message.challenge.copy(buf, 8); type2message.challenge.copy(buf, 8);
@ -86,12 +96,12 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
// maybe think about a different solution here // maybe think about a different solution here
// 11644473600000 = diff between 1970 and 1601 // 11644473600000 = diff between 1970 and 1601
var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16); var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
var timestampLow = Number('0x' + timestamp.substring(Math.max(0, timestamp.length - 8))); var timestampLow = Number("0x" + timestamp.substring(Math.max(0, timestamp.length - 8)));
var timestampHigh = Number('0x' + timestamp.substring(0, Math.max(0, timestamp.length - 8))); var timestampHigh = Number("0x" + timestamp.substring(0, Math.max(0, timestamp.length - 8)));
buf.writeUInt32LE(timestampLow, 24, false); buf.writeUInt32LE(timestampLow, 24, false);
buf.writeUInt32LE(timestampHigh, 28, false); buf.writeUInt32LE(timestampHigh, 28, false);
//random client nonce //random client nonce
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex'); buf.write(nonce || createPseudoRandomValue(16), 32, "hex");
//zero //zero
buf.writeUInt32LE(0, 40); buf.writeUInt32LE(0, 40);
//complete target information block from type 2 message //complete target information block from type 2 message
@ -103,13 +113,15 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
hashedBuffer.copy(buf); hashedBuffer.copy(buf);
return buf; return buf;
} }
function createPseudoRandomValue(length) { function createPseudoRandomValue(length) {
var str = ''; var str = "";
while (str.length < length) { while (str.length < length) {
str += Math.floor(Math.random() * 16).toString(16); str += Math.floor(Math.random() * 16).toString(16);
} }
return str; return str;
} }
module.exports = { module.exports = {
createLMHash: createLMHash, createLMHash: createLMHash,
createNTLMHash: createNTLMHash, createNTLMHash: createNTLMHash,
@ -117,6 +129,6 @@ module.exports = {
createNTLMResponse: createNTLMResponse, createNTLMResponse: createNTLMResponse,
createLMv2Response: createLMv2Response, createLMv2Response: createLMv2Response,
createNTLMv2Response: createNTLMv2Response, createNTLMv2Response: createNTLMv2Response,
createPseudoRandomValue: createPseudoRandomValue createPseudoRandomValue: createPseudoRandomValue,
}; };
//# sourceMappingURL=hash.js.map //# sourceMappingURL=hash.js.map

View file

@ -1,13 +1,14 @@
'use strict'; "use strict";
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js
var os = require('os'), flags = require('./flags'), hash = require('./hash'); var os = require("os"), flags = require("./flags"), hash = require("./hash");
var NTLMSIGNATURE = "NTLMSSP\0"; var NTLMSIGNATURE = "NTLMSSP\0";
function createType1Message(workstation, target) { function createType1Message(workstation, target) {
var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024); var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024);
workstation = workstation === undefined ? os.hostname() : workstation; workstation = workstation === undefined ? os.hostname() : workstation;
target = target === undefined ? '' : target; target = target === undefined ? "" : target;
//signature //signature
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii'); buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, "ascii");
pos += NTLMSIGNATURE.length; pos += NTLMSIGNATURE.length;
//message type //message type
buf.writeUInt32LE(1, pos); buf.writeUInt32LE(1, pos);
@ -27,7 +28,7 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos); buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
pos += 4; pos += 4;
if (target.length > 0) { if (target.length > 0) {
dataPos += buf.write(target, dataPos, 'ascii'); dataPos += buf.write(target, dataPos, "ascii");
} }
//workstation security buffer //workstation security buffer
buf.writeUInt16LE(workstation.length, pos); buf.writeUInt16LE(workstation.length, pos);
@ -37,39 +38,39 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos); buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
pos += 4; pos += 4;
if (workstation.length > 0) { if (workstation.length > 0) {
dataPos += buf.write(workstation, dataPos, 'ascii'); dataPos += buf.write(workstation, dataPos, "ascii");
} }
return 'NTLM ' + buf.toString('base64', 0, dataPos); return "NTLM " + buf.toString("base64", 0, dataPos);
} }
function decodeType2Message(str) { function decodeType2Message(str) {
if (str === undefined) { if (str === undefined) {
throw new Error('Invalid argument'); throw new Error("Invalid argument");
} }
//convenience //convenience
if (Object.prototype.toString.call(str) !== '[object String]') { if (Object.prototype.toString.call(str) !== "[object String]") {
if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) { if (str.hasOwnProperty("headers") && str.headers.hasOwnProperty("www-authenticate")) {
str = str.headers['www-authenticate']; str = str.headers["www-authenticate"];
} } else {
else { throw new Error("Invalid argument");
throw new Error('Invalid argument');
} }
} }
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str); var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
if (ntlmMatch) { if (ntlmMatch) {
str = ntlmMatch[1]; str = ntlmMatch[1];
} }
var buf = new Buffer.from(str, 'base64'), obj = {}; var buf = new Buffer.from(str, "base64"), obj = {};
//check signature //check signature
if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) { if (buf.toString("ascii", 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
throw new Error('Invalid message signature: ' + str); throw new Error("Invalid message signature: " + str);
} }
//check message type //check message type
if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) { if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) {
throw new Error('Invalid message type (no type 2)'); throw new Error("Invalid message type (no type 2)");
} }
//read flags //read flags
obj.flags = buf.readUInt32LE(20); obj.flags = buf.readUInt32LE(20);
obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? 'ascii' : 'ucs2'; obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? "ascii" : "ucs2";
obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1; obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1;
obj.challenge = buf.slice(24, 32); obj.challenge = buf.slice(24, 32);
//read target name //read target name
@ -78,10 +79,10 @@ function decodeType2Message(str) {
//skipping allocated space //skipping allocated space
var offset = buf.readUInt32LE(16); var offset = buf.readUInt32LE(16);
if (length === 0) { if (length === 0) {
return ''; return "";
} }
if ((offset + length) > buf.length || offset < 32) { if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message'); throw new Error("Bad type 2 message");
} }
return buf.toString(obj.encoding, offset, offset + length); return buf.toString(obj.encoding, offset, offset + length);
})(); })();
@ -98,7 +99,7 @@ function decodeType2Message(str) {
return info; return info;
} }
if ((offset + length) > buf.length || offset < 32) { if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message'); throw new Error("Bad type 2 message");
} }
var pos = offset; var pos = offset;
while (pos < (offset + length)) { while (pos < (offset + length)) {
@ -113,37 +114,38 @@ function decodeType2Message(str) {
var blockTypeStr = void 0; var blockTypeStr = void 0;
switch (blockType) { switch (blockType) {
case 1: case 1:
blockTypeStr = 'SERVER'; blockTypeStr = "SERVER";
break; break;
case 2: case 2:
blockTypeStr = 'DOMAIN'; blockTypeStr = "DOMAIN";
break; break;
case 3: case 3:
blockTypeStr = 'FQDN'; blockTypeStr = "FQDN";
break; break;
case 4: case 4:
blockTypeStr = 'DNS'; blockTypeStr = "DNS";
break; break;
case 5: case 5:
blockTypeStr = 'PARENT_DNS'; blockTypeStr = "PARENT_DNS";
break; break;
default: default:
blockTypeStr = ''; blockTypeStr = "";
break; break;
} }
if (blockTypeStr) { if (blockTypeStr) {
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength); info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength);
} }
pos += blockLength; pos += blockLength;
} }
return { return {
parsed: info, parsed: info,
buffer: targetInfoBuffer buffer: targetInfoBuffer,
}; };
})(); })();
} }
return obj; return obj;
} }
function createType3Message(type2Message, username, password, workstation, target) { function createType3Message(type2Message, username, password, workstation, target) {
var dataPos = 52, buf = new Buffer.alloc(1024); var dataPos = 52, buf = new Buffer.alloc(1024);
if (workstation === undefined) { if (workstation === undefined) {
@ -153,12 +155,14 @@ function createType3Message(type2Message, username, password, workstation, targe
target = type2Message.targetName; target = type2Message.targetName;
} }
//signature //signature
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii'); buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, "ascii");
//message type //message type
buf.writeUInt32LE(3, 8); buf.writeUInt32LE(3, 8);
if (type2Message.version === 2) { if (type2Message.version === 2) {
dataPos = 64; dataPos = 64;
var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16), lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target), ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target); var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16),
lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target),
ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target);
//lmv2 security buffer //lmv2 security buffer
buf.writeUInt16LE(lmv2.length, 12); buf.writeUInt16LE(lmv2.length, 12);
buf.writeUInt16LE(lmv2.length, 14); buf.writeUInt16LE(lmv2.length, 14);
@ -171,9 +175,10 @@ function createType3Message(type2Message, username, password, workstation, targe
buf.writeUInt32LE(dataPos, 24); buf.writeUInt32LE(dataPos, 24);
ntlmv2.copy(buf, dataPos); ntlmv2.copy(buf, dataPos);
dataPos += ntlmv2.length; dataPos += ntlmv2.length;
} } else {
else { var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password),
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash); lm = hash.createLMResponse(type2Message.challenge, lmHash),
ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
//lm security buffer //lm security buffer
buf.writeUInt16LE(lm.length, 12); buf.writeUInt16LE(lm.length, 12);
buf.writeUInt16LE(lm.length, 14); buf.writeUInt16LE(lm.length, 14);
@ -188,18 +193,18 @@ function createType3Message(type2Message, username, password, workstation, targe
dataPos += ntlm.length; dataPos += ntlm.length;
} }
//target name security buffer //target name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 28); buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 28);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 30); buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 30);
buf.writeUInt32LE(dataPos, 32); buf.writeUInt32LE(dataPos, 32);
dataPos += buf.write(target, dataPos, type2Message.encoding); dataPos += buf.write(target, dataPos, type2Message.encoding);
//user name security buffer //user name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 36); buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 36);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 38); buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 38);
buf.writeUInt32LE(dataPos, 40); buf.writeUInt32LE(dataPos, 40);
dataPos += buf.write(username, dataPos, type2Message.encoding); dataPos += buf.write(username, dataPos, type2Message.encoding);
//workstation name security buffer //workstation name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 44); buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 44);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 46); buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 46);
buf.writeUInt32LE(dataPos, 48); buf.writeUInt32LE(dataPos, 48);
dataPos += buf.write(workstation, dataPos, type2Message.encoding); dataPos += buf.write(workstation, dataPos, type2Message.encoding);
if (type2Message.version === 2) { if (type2Message.version === 2) {
@ -210,11 +215,12 @@ function createType3Message(type2Message, username, password, workstation, targe
//flags //flags
buf.writeUInt32LE(type2Message.flags, 60); buf.writeUInt32LE(type2Message.flags, 60);
} }
return 'NTLM ' + buf.toString('base64', 0, dataPos); return "NTLM " + buf.toString("base64", 0, dataPos);
} }
module.exports = { module.exports = {
createType1Message: createType1Message, createType1Message: createType1Message,
decodeType2Message: decodeType2Message, decodeType2Message: decodeType2Message,
createType3Message: createType3Message createType3Message: createType3Message,
}; };
//# sourceMappingURL=ntlm.js.map //# sourceMappingURL=ntlm.js.map

View file

@ -1,57 +1,172 @@
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) {
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); k2 = k;
}) : (function(o, m, k, k2) { }
if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, {
enumerable: true,
get: function () {
return m[k];
},
});
}) : (function (o, m, k, k2) {
if (k2 === undefined) {
k2 = k;
}
o[k2] = m[k]; o[k2] = m[k];
})); }));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v }); Object.defineProperty(o, "default", {
}) : function(o, v) { enumerable: true,
value: v,
});
}) : function (o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) {
return mod;
}
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); if (mod != null) {
for (var k in mod) {
if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) {
__createBinding(result, mod, k);
}
}
}
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } function adopt(value) {
return value instanceof P ? value : new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function fulfilled(value) {
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } try {
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
var __generator = (this && this.__generator) || function (thisArg, body) { var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; var _ = {
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; label: 0,
function verb(n) { return function (v) { return step([n, v]); }; } sent: function () {
function step(op) { if (t[0] & 1) {
if (f) throw new TypeError("Generator is already executing."); throw t[1];
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
} }
op = body.call(thisArg, _); return t[1];
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } },
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; trys: [],
ops: [],
}, f, y, t, g;
return g = {
next: verb(0),
"throw": verb(1),
"return": verb(2),
}, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
return this;
}), g;
function verb(n) {
return function (v) {
return step([ n, v ]);
};
}
function step(op) {
if (f) {
throw new TypeError("Generator is already executing.");
}
while (_) {
try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) {
return t;
}
if (y = 0, t) {
op = [ op[0] & 2, t.value ];
}
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return {
value: op[1],
done: false,
};
case 5:
_.label++;
y = op[1];
op = [ 0 ];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2]) {
_.ops.pop();
}
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
} catch (e) {
op = [ 6, e ];
y = 0;
} finally {
f = t = 0;
}
}
if (op[0] & 5) {
throw op[1];
}
return {
value: op[0] ? op[1] : void 0,
done: true,
};
} }
}; };
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
@ -64,12 +179,13 @@ var ntlm = __importStar(require("./ntlm"));
var https = __importStar(require("https")); var https = __importStar(require("https"));
var http = __importStar(require("http")); var http = __importStar(require("http"));
var dev_null_1 = __importDefault(require("dev-null")); var dev_null_1 = __importDefault(require("dev-null"));
/** /**
* @param credentials An NtlmCredentials object containing the username and password * @param credentials An NtlmCredentials object containing the username and password
* @param AxiosConfig The Axios config for the instance you wish to create * @param AxiosConfig The Axios config for the instance you wish to create
* *
* @returns This function returns an axios instance configured to use the provided credentials * @returns This function returns an axios instance configured to use the provided credentials
*/ */
function NtlmClient(credentials, AxiosConfig) { function NtlmClient(credentials, AxiosConfig) {
var _this = this; var _this = this;
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {}; var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
@ -82,46 +198,56 @@ function NtlmClient(credentials, AxiosConfig) {
var client = axios_1.default.create(config); var client = axios_1.default.create(config);
client.interceptors.response.use(function (response) { client.interceptors.response.use(function (response) {
return response; return response;
}, function (err) { return __awaiter(_this, void 0, void 0, function () { }, function (err) {
var error, t1Msg, t2Msg, t3Msg, stream_1; return __awaiter(_this, void 0, void 0, function () {
var _a; var error, t1Msg, t2Msg, t3Msg, stream_1;
return __generator(this, function (_b) { var _a;
switch (_b.label) { return __generator(this, function (_b) {
case 0: switch (_b.label) {
error = err.response; case 0:
if (!(error && error.status === 401 error = err.response;
&& error.headers['www-authenticate'] if (!(error && error.status === 401
&& error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3]; && error.headers["www-authenticate"]
// This length check is a hack because SharePoint is awkward and will && error.headers["www-authenticate"].includes("NTLM"))) {
// include the Negotiate option when responding with the T2 message return [ 3 /*break*/, 3 ];
// There is nore we could do to ensure we are processing correctly, }
// but this is the easiest option for now // This length check is a hack because SharePoint is awkward and will
if (error.headers['www-authenticate'].length < 50) { // include the Negotiate option when responding with the T2 message
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain); // There is nore we could do to ensure we are processing correctly,
error.config.headers["Authorization"] = t1Msg; // but this is the easiest option for now
} if (error.headers["www-authenticate"].length < 50) {
else { t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
t2Msg = ntlm.decodeType2Message((error.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]); error.config.headers["Authorization"] = t1Msg;
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain); } else {
error.config.headers["X-retry"] = "false"; t2Msg = ntlm.decodeType2Message((error.headers["www-authenticate"].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
error.config.headers["Authorization"] = t3Msg; t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
} error.config.headers["X-retry"] = "false";
if (!(error.config.responseType === "stream")) return [3 /*break*/, 2]; error.config.headers["Authorization"] = t3Msg;
stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data; }
if (!(stream_1 && !stream_1.readableEnded)) return [3 /*break*/, 2]; if (!(error.config.responseType === "stream")) {
return [4 /*yield*/, new Promise(function (resolve) { return [ 3 /*break*/, 2 ];
}
stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
if (!(stream_1 && !stream_1.readableEnded)) {
return [ 3 /*break*/, 2 ];
}
return [ 4 /*yield*/, new Promise(function (resolve) {
stream_1.pipe((0, dev_null_1.default)()); stream_1.pipe((0, dev_null_1.default)());
stream_1.once('close', resolve); stream_1.once('close', resolve);
})]; }) ];
case 1: case 1:
_b.sent(); _b.sent();
_b.label = 2; _b.label = 2;
case 2: return [2 /*return*/, client(error.config)]; case 2:
case 3: throw err; return [ 2 /*return*/, client(error.config) ];
} case 3:
throw err;
}
});
}); });
}); }); });
return client; return client;
} }
exports.NtlmClient = NtlmClient; exports.NtlmClient = NtlmClient;
//# sourceMappingURL=ntlmClient.js.map //# sourceMappingURL=ntlmClient.js.map

View file

@ -1,20 +1,24 @@
import { PluginFunc, ConfigType } from 'dayjs' import { PluginFunc, ConfigType } from "dayjs";
declare const plugin: PluginFunc declare const plugin: PluginFunc;
export = plugin export = plugin
declare module 'dayjs' { declare module "dayjs" {
interface Dayjs { interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs tz(timezone?: string, keepLocalTime?: boolean): Dayjs;
offsetName(type?: 'short' | 'long'): string | undefined
}
interface DayjsTimezone { offsetName(type?: "short" | "long"): string | undefined;
(date: ConfigType, timezone?: string): Dayjs }
(date: ConfigType, format: string, timezone?: string): Dayjs
guess(): string
setDefault(timezone?: string): void
}
const tz: DayjsTimezone interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs;
(date: ConfigType, format: string, timezone?: string): Dayjs;
guess(): string;
setDefault(timezone?: string): void;
}
const tz: DayjsTimezone;
} }

View file

@ -15,7 +15,7 @@
day: 2, day: 2,
hour: 3, hour: 3,
minute: 4, minute: 4,
second: 5 second: 5,
}; };
let e = {}; let e = {};
return function (n, i, o) { return function (n, i, o) {
@ -37,7 +37,7 @@
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
timeZoneName: i timeZoneName: i,
}), e[o] = r), r; }), e[o] = r), r;
}(n, i); }(n, i);
return r.formatToParts(o); return r.formatToParts(o);

View file

@ -1,5 +1,8 @@
const { MonitorType } = require("./monitor-type"); const { MonitorType } = require("./monitor-type");
const { UP, DOWN } = require("../../src/util"); const {
UP,
DOWN,
} = require("../../src/util");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { dnsResolve } = require("../util-server"); const { dnsResolve } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
@ -14,7 +17,7 @@ class DnsMonitorType extends MonitorType {
supportsConditions = true; supportsConditions = true;
conditionVariables = [ conditionVariables = [
new ConditionVariable("record", defaultStringOperators ), new ConditionVariable("record", defaultStringOperators),
]; ];
/** /**

View file

@ -1,5 +1,8 @@
const { MonitorType } = require("./monitor-type"); const { MonitorType } = require("./monitor-type");
const { log, UP } = require("../../src/util"); const {
log,
UP,
} = require("../../src/util");
const mqtt = require("mqtt"); const mqtt = require("mqtt");
const jsonata = require("jsonata"); const jsonata = require("jsonata");
@ -57,7 +60,12 @@ class MqttMonitorType extends MonitorType {
*/ */
mqttAsync(hostname, topic, options = {}) { mqttAsync(hostname, topic, options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { port, username, password, interval = 20 } = options; const {
port,
username,
password,
interval = 20,
} = options;
// Adds MQTT protocol to the hostname if not already present // Adds MQTT protocol to the hostname if not already present
if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) { if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
@ -77,7 +85,7 @@ class MqttMonitorType extends MonitorType {
let client = mqtt.connect(mqttUrl, { let client = mqtt.connect(mqttUrl, {
username, username,
password, password,
clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8) clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8),
}); });
client.on("connect", () => { client.on("connect", () => {

View file

@ -1,5 +1,9 @@
const { MonitorType } = require("./monitor-type"); const { MonitorType } = require("./monitor-type");
const { log, UP, DOWN } = require("../../src/util"); const {
log,
UP,
DOWN,
} = require("../../src/util");
const { axiosAbortSignal } = require("../util-server"); const { axiosAbortSignal } = require("../util-server");
const axios = require("axios"); const axios = require("axios");
@ -21,7 +25,7 @@ class RabbitMqMonitorType extends MonitorType {
for (let baseUrl of baseUrls) { for (let baseUrl of baseUrls) {
try { try {
// Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com // Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com
if ( !baseUrl.endsWith("/") ) { if (!baseUrl.endsWith("/")) {
baseUrl += "/"; baseUrl += "/";
} }
const options = { const options = {

View file

@ -1,6 +1,9 @@
const { MonitorType } = require("./monitor-type"); const { MonitorType } = require("./monitor-type");
const { chromium } = require("playwright-core"); const { chromium } = require("playwright-core");
const { UP, log } = require("../../src/util"); const {
UP,
log,
} = require("../../src/util");
const { Settings } = require("../settings"); const { Settings } = require("../settings");
const commandExistsSync = require("command-exists").sync; const commandExistsSync = require("command-exists").sync;
const childProcess = require("child_process"); const childProcess = require("child_process");
@ -122,7 +125,7 @@ async function prepareChromeExecutable(executablePath) {
executablePath = "/usr/bin/chromium"; executablePath = "/usr/bin/chromium";
// Install chromium in container via apt install // Install chromium in container via apt install
if ( !commandExistsSync(executablePath)) { if (!commandExistsSync(executablePath)) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
log.info("Chromium", "Installing Chromium..."); log.info("Chromium", "Installing Chromium...");
let child = childProcess.exec("apt update && apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk"); let child = childProcess.exec("apt update && apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk");
@ -213,6 +216,7 @@ async function testChrome(executablePath) {
throw new Error(e.message); throw new Error(e.message);
} }
} }
// test remote browser // test remote browser
/** /**
* @param {string} remoteBrowserURL Remote Browser URL * @param {string} remoteBrowserURL Remote Browser URL
@ -228,6 +232,7 @@ async function testRemoteBrowser(remoteBrowserURL) {
throw new Error(e.message); throw new Error(e.message);
} }
} }
class RealBrowserMonitorType extends MonitorType { class RealBrowserMonitorType extends MonitorType {
name = "real-browser"; name = "real-browser";

View file

@ -1,5 +1,9 @@
const { MonitorType } = require("./monitor-type"); const { MonitorType } = require("./monitor-type");
const { UP, log, evaluateJsonQuery } = require("../../src/util"); const {
UP,
log,
evaluateJsonQuery,
} = require("../../src/util");
const snmp = require("net-snmp"); const snmp = require("net-snmp");
class SNMPMonitorType extends MonitorType { class SNMPMonitorType extends MonitorType {
@ -42,7 +46,10 @@ class SNMPMonitorType extends MonitorType {
// We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in. // We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in.
const value = varbinds[0].value; const value = varbinds[0].value;
const { status, response } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue); const {
status,
response,
} = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
if (status) { if (status) {
heartbeat.status = UP; heartbeat.status = UP;

View file

@ -5,6 +5,15 @@ const NotifyClient = require("notifications-node-client").NotifyClient;
class GovNotify extends NotificationProvider { class GovNotify extends NotificationProvider {
name = "GovNotify"; name = "GovNotify";
/**
* Sends notifications via email and SMS using the GOV.UK Notify service.
* @param {object} notification The notification object containing configuration such as API key, email recipients, SMS recipients, message template, and template IDs for email and SMS.
* @param {string} msg The message content to send if no message template is provided in the notification object.
* @param {object | null} monitorJSON Optional parameter containing monitoring-related data.
* @param {object | null} heartbeatJSON Optional parameter containing heartbeat-related data, used to determine notification subject (e.g., status up or down).
* @returns {Promise<string>} A promise that resolves to a success message after sending notifications or rejects with an error if the sending fails.
* @throws {Error} Throws an error if notification sending fails.
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
try { try {
const apiKey = notification.apiKey; const apiKey = notification.apiKey;

View file

@ -20,19 +20,21 @@ class HomeAssistant extends NotificationProvider {
{ {
title: "Uptime Kuma", title: "Uptime Kuma",
message: msg, message: msg,
...(notificationService !== "persistent_notification" && { data: { ...(notificationService !== "persistent_notification" && {
name: monitorJSON?.name, data: {
status: heartbeatJSON?.status, name: monitorJSON?.name,
channel: "Uptime Kuma", status: heartbeatJSON?.status,
icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true", channel: "Uptime Kuma",
} }), icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
},
}),
}, },
{ {
headers: { headers: {
Authorization: `Bearer ${notification.longLivedAccessToken}`, Authorization: `Bearer ${notification.longLivedAccessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
} },
); );
return okMsg; return okMsg;

View file

@ -159,7 +159,7 @@ class Notification {
new GovNotify(), new GovNotify(),
]; ];
for (let item of list) { for (let item of list) {
if (! item.name) { if (!item.name) {
throw new Error("Notification provider without name"); throw new Error("Notification provider without name");
} }
@ -203,7 +203,7 @@ class Notification {
userID, userID,
]); ]);
if (! bean) { if (!bean) {
throw new Error("notification not found"); throw new Error("notification not found");
} }
@ -236,7 +236,7 @@ class Notification {
userID, userID,
]); ]);
if (! bean) { if (!bean) {
throw new Error("notification not found"); throw new Error("notification not found");
} }
@ -263,7 +263,7 @@ class Notification {
*/ */
async function applyNotificationEveryMonitor(notificationID, userID) { async function applyNotificationEveryMonitor(notificationID, userID) {
let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
userID userID,
]); ]);
for (let i = 0; i < monitors.length; i++) { for (let i = 0; i < monitors.length; i++) {
@ -272,7 +272,7 @@ async function applyNotificationEveryMonitor(notificationID, userID) {
notificationID, notificationID,
]); ]);
if (! checkNotification) { if (!checkNotification) {
let relation = R.dispense("monitor_notification"); let relation = R.dispense("monitor_notification");
relation.monitor_id = monitors[i].id; relation.monitor_id = monitors[i].id;
relation.notification_id = notificationID; relation.notification_id = notificationID;

View file

@ -12,24 +12,24 @@ const commonLabels = [
const monitorCertDaysRemaining = new PrometheusClient.Gauge({ const monitorCertDaysRemaining = new PrometheusClient.Gauge({
name: "monitor_cert_days_remaining", name: "monitor_cert_days_remaining",
help: "The number of days remaining until the certificate expires", help: "The number of days remaining until the certificate expires",
labelNames: commonLabels labelNames: commonLabels,
}); });
const monitorCertIsValid = new PrometheusClient.Gauge({ const monitorCertIsValid = new PrometheusClient.Gauge({
name: "monitor_cert_is_valid", name: "monitor_cert_is_valid",
help: "Is the certificate still valid? (1 = Yes, 0= No)", help: "Is the certificate still valid? (1 = Yes, 0= No)",
labelNames: commonLabels labelNames: commonLabels,
}); });
const monitorResponseTime = new PrometheusClient.Gauge({ const monitorResponseTime = new PrometheusClient.Gauge({
name: "monitor_response_time", name: "monitor_response_time",
help: "Monitor Response Time (ms)", help: "Monitor Response Time (ms)",
labelNames: commonLabels labelNames: commonLabels,
}); });
const monitorStatus = new PrometheusClient.Gauge({ const monitorStatus = new PrometheusClient.Gauge({
name: "monitor_status", name: "monitor_status",
help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)", help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)",
labelNames: commonLabels labelNames: commonLabels,
}); });
class Prometheus { class Prometheus {
@ -44,7 +44,7 @@ class Prometheus {
monitor_type: monitor.type, monitor_type: monitor.type,
monitor_url: monitor.url, monitor_url: monitor.url,
monitor_hostname: monitor.hostname, monitor_hostname: monitor.hostname,
monitor_port: monitor.port monitor_port: monitor.port,
}; };
} }
@ -119,5 +119,5 @@ class Prometheus {
} }
module.exports = { module.exports = {
Prometheus Prometheus,
}; };

View file

@ -36,7 +36,7 @@ class Proxy {
if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) { if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) {
throw new Error(` throw new Error(`
Unsupported proxy protocol "${proxy.protocol}. Unsupported proxy protocol "${proxy.protocol}.
Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."` Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`,
); );
} }
@ -92,7 +92,10 @@ class Proxy {
* @throws Proxy protocol is unsupported * @throws Proxy protocol is unsupported
*/ */
static createAgents(proxy, options) { static createAgents(proxy, options) {
const { httpAgentOptions, httpsAgentOptions } = options || {}; const {
httpAgentOptions,
httpsAgentOptions,
} = options || {};
let agent; let agent;
let httpAgent; let httpAgent;
let httpsAgent; let httpsAgent;
@ -150,12 +153,13 @@ class Proxy {
httpsAgent = agent; httpsAgent = agent;
break; break;
default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`); default:
throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
} }
return { return {
httpAgent, httpAgent,
httpsAgent httpsAgent,
}; };
} }

View file

@ -11,7 +11,15 @@ const { R } = require("redbean-node");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor"); const Monitor = require("../model/monitor");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util"); const {
UP,
MAINTENANCE,
DOWN,
PENDING,
flipStatus,
log,
badgeConstants,
} = require("../../src/util");
const StatusPage = require("../model/status_page"); const StatusPage = require("../model/status_page");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const { makeBadge } = require("badge-maker"); const { makeBadge } = require("badge-maker");
@ -28,7 +36,7 @@ let io = server.io;
router.get("/api/entry-page", async (request, response) => { router.get("/api/entry-page", async (request, response) => {
allowDevAllOrigin(response); allowDevAllOrigin(response);
let result = { }; let result = {};
let hostname = request.hostname; let hostname = request.hostname;
if ((await setting("trustProxy")) && request.headers["x-forwarded-host"]) { if ((await setting("trustProxy")) && request.headers["x-forwarded-host"]) {
hostname = request.headers["x-forwarded-host"]; hostname = request.headers["x-forwarded-host"];
@ -53,10 +61,10 @@ router.all("/api/push/:pushToken", async (request, response) => {
let status = (statusString === "up") ? UP : DOWN; let status = (statusString === "up") ? UP : DOWN;
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
pushToken pushToken,
]); ]);
if (! monitor) { if (!monitor) {
throw new Error("Monitor not found or not active."); throw new Error("Monitor not found or not active.");
} }
@ -127,7 +135,7 @@ router.all("/api/push/:pushToken", async (request, response) => {
} catch (e) { } catch (e) {
response.status(404).json({ response.status(404).json({
ok: false, ok: false,
msg: e.message msg: e.message,
}); });
} }
}); });
@ -159,7 +167,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
AND monitor_group.monitor_id = ? AND monitor_group.monitor_id = ?
AND public = 1 AND public = 1
`, `,
[ requestedMonitorId ] [ requestedMonitorId ],
); );
const badgeValues = { style }; const badgeValues = { style };
@ -242,7 +250,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
AND monitor_group.monitor_id = ? AND monitor_group.monitor_id = ?
AND public = 1 AND public = 1
`, `,
[ requestedMonitorId ] [ requestedMonitorId ],
); );
const badgeValues = { style }; const badgeValues = { style };
@ -362,7 +370,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
request.params.duration request.params.duration
? parseInt(request.params.duration, 10) ? parseInt(request.params.duration, 10)
: 24, : 24,
720 720,
); );
const overrideValue = value && parseFloat(value); const overrideValue = value && parseFloat(value);
@ -376,7 +384,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
AND public = 1 AND public = 1
AND heartbeat.monitor_id = ? AND heartbeat.monitor_id = ?
`, `,
[ -requestedDuration, requestedMonitorId ] [ -requestedDuration, requestedMonitorId ],
)); ));
const badgeValues = { style }; const badgeValues = { style };
@ -443,7 +451,7 @@ router.get("/api/badge/:id/cert-exp", cache("5 minutes"), async (request, respon
AND monitor_group.monitor_id = ? AND monitor_group.monitor_id = ?
AND public = 1 AND public = 1
`, `,
[ requestedMonitorId ] [ requestedMonitorId ],
); );
const badgeValues = { style }; const badgeValues = { style };
@ -528,7 +536,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
AND monitor_group.monitor_id = ? AND monitor_group.monitor_id = ?
AND public = 1 AND public = 1
`, `,
[ requestedMonitorId ] [ requestedMonitorId ],
); );
const badgeValues = { style }; const badgeValues = { style };
@ -540,7 +548,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
badgeValues.color = badgeConstants.naColor; badgeValues.color = badgeConstants.naColor;
} else { } else {
const heartbeat = await Monitor.getPreviousHeartbeat( const heartbeat = await Monitor.getPreviousHeartbeat(
requestedMonitorId requestedMonitorId,
); );
if (!heartbeat.ping) { if (!heartbeat.ping) {

View file

@ -2,7 +2,10 @@ let express = require("express");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const StatusPage = require("../model/status_page"); const StatusPage = require("../model/status_page");
const { allowDevAllOrigin, sendHttpError } = require("../util-server"); const {
allowDevAllOrigin,
sendHttpError,
} = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { badgeConstants } = require("../../src/util"); const { badgeConstants } = require("../../src/util");
const { makeBadge } = require("badge-maker"); const { makeBadge } = require("badge-maker");
@ -44,7 +47,7 @@ router.get("/api/status-page/:slug", cache("5 minutes"), async (request, respons
try { try {
// Get Status Page // Get Status Page
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [
slug slug,
]); ]);
if (!statusPage) { if (!statusPage) {
@ -81,7 +84,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
AND public = 1 AND public = 1
AND \`group\`.status_page_id = ? AND \`group\`.status_page_id = ?
`, [ `, [
statusPageID statusPageID,
]); ]);
for (let monitorID of monitorIDList) { for (let monitorID of monitorIDList) {
@ -103,7 +106,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
response.json({ response.json({
heartbeatList, heartbeatList,
uptimeList uptimeList,
}); });
} catch (error) { } catch (error) {
@ -120,7 +123,7 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
try { try {
// Get Status Page // Get Status Page
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [
slug slug,
]); ]);
if (!statusPage) { if (!statusPage) {
@ -137,9 +140,9 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
{ {
"src": statusPage.icon, "src": statusPage.icon,
"sizes": "128x128", "sizes": "128x128",
"type": "image/png" "type": "image/png",
} },
] ],
}); });
} catch (error) { } catch (error) {
@ -159,7 +162,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
downColor = badgeConstants.defaultDownColor, downColor = badgeConstants.defaultDownColor,
partialColor = "#F6BE00", partialColor = "#F6BE00",
maintenanceColor = "#808080", maintenanceColor = "#808080",
style = badgeConstants.defaultStyle style = badgeConstants.defaultStyle,
} = request.query; } = request.query;
try { try {
@ -169,7 +172,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
AND public = 1 AND public = 1
AND \`group\`.status_page_id = ? AND \`group\`.status_page_id = ?
`, [ `, [
statusPageID statusPageID,
]); ]);
let hasUp = false; let hasUp = false;

View file

@ -37,13 +37,19 @@ if (!semver.satisfies(nodeVersion, requiredNodeVersions)) {
} }
const args = require("args-parser")(process.argv); const args = require("args-parser")(process.argv);
const { sleep, log, getRandomInt, genSecret, isDev } = require("../src/util"); const {
sleep,
log,
getRandomInt,
genSecret,
isDev,
} = require("../src/util");
const config = require("./config"); const config = require("./config");
log.debug("server", "Arguments"); log.debug("server", "Arguments");
log.debug("server", args); log.debug("server", args);
if (! process.env.NODE_ENV) { if (!process.env.NODE_ENV) {
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
} }
@ -90,7 +96,16 @@ const Monitor = require("./model/monitor");
const User = require("./model/user"); const User = require("./model/user");
log.debug("server", "Importing Settings"); log.debug("server", "Importing Settings");
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, doubleCheckPassword, shake256, SHAKE256_LENGTH, allowDevAllOrigin, const {
getSettings,
setSettings,
setting,
initJWTSecret,
checkLogin,
doubleCheckPassword,
shake256,
SHAKE256_LENGTH,
allowDevAllOrigin,
} = require("./util-server"); } = require("./util-server");
log.debug("server", "Importing Notification"); log.debug("server", "Importing Notification");
@ -101,8 +116,14 @@ log.debug("server", "Importing Database");
const Database = require("./database"); const Database = require("./database");
log.debug("server", "Importing Background Jobs"); log.debug("server", "Importing Background Jobs");
const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs"); const {
const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter"); initBackgroundJobs,
stopBackgroundJobs,
} = require("./jobs");
const {
loginRateLimiter,
twoFaRateLimiter,
} = require("./rate-limiter");
const { apiAuth } = require("./auth"); const { apiAuth } = require("./auth");
const { login } = require("./auth"); const { login } = require("./auth");
@ -122,7 +143,7 @@ const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CL
// 2FA / notp verification defaults // 2FA / notp verification defaults
const twoFAVerifyOptions = { const twoFAVerifyOptions = {
"window": 1, "window": 1,
"time": 30 "time": 30,
}; };
/** /**
@ -132,13 +153,26 @@ const twoFAVerifyOptions = {
const testMode = !!args["test"] || false; const testMode = !!args["test"] || false;
// Must be after io instantiation // Must be after io instantiation
const { sendNotificationList, sendHeartbeatList, sendInfo, sendProxyList, sendDockerHostList, sendAPIKeyList, sendRemoteBrowserList, sendMonitorTypeList } = require("./client"); const {
sendNotificationList,
sendHeartbeatList,
sendInfo,
sendProxyList,
sendDockerHostList,
sendAPIKeyList,
sendRemoteBrowserList,
sendMonitorTypeList,
} = require("./client");
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
const { databaseSocketHandler } = require("./socket-handlers/database-socket-handler"); const { databaseSocketHandler } = require("./socket-handlers/database-socket-handler");
const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler"); const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler");
const TwoFA = require("./2fa"); const TwoFA = require("./2fa");
const StatusPage = require("./model/status_page"); const StatusPage = require("./model/status_page");
const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler"); const {
cloudflaredSocketHandler,
autoStart: cloudflaredAutoStart,
stop: cloudflaredStop,
} = require("./socket-handlers/cloudflared-socket-handler");
const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler"); const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler");
const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler"); const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler");
const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler"); const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler");
@ -933,8 +967,9 @@ let needSetup = false;
monitorID, monitorID,
socket.userID, socket.userID,
]); ]);
const monitorData = [{ id: monitor.id, const monitorData = [ {
active: monitor.active id: monitor.id,
active: monitor.active,
}]; }];
const preloadData = await Monitor.preparePreloadData(monitorData); const preloadData = await Monitor.preparePreloadData(monitorData);
callback({ callback({
@ -966,7 +1001,8 @@ let needSetup = false;
SELECT * SELECT *
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
AND time > ${sqlHourOffset} AND time
> ${sqlHourOffset}
ORDER BY time ASC ORDER BY time ASC
`, [ `, [
monitorID, monitorID,
@ -1519,7 +1555,7 @@ let needSetup = false;
log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`);
await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [ await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [
monitorID monitorID,
]); ]);
await sendHeartbeatList(socket, monitorID, true, true); await sendHeartbeatList(socket, monitorID, true, true);
@ -1658,7 +1694,7 @@ async function checkOwner(userID, monitorID) {
userID, userID,
]); ]);
if (! row) { if (!row) {
throw new Error("You do not own this monitor."); throw new Error("You do not own this monitor.");
} }
} }
@ -1698,7 +1734,7 @@ async function afterLogin(socket, user) {
// Set server timezone from client browser if not set // Set server timezone from client browser if not set
// It should be run once only // It should be run once only
if (! await Settings.get("initServerTimezone")) { if (!await Settings.get("initServerTimezone")) {
log.debug("server", "emit initServerTimezone"); log.debug("server", "emit initServerTimezone");
socket.emit("initServerTimezone"); socket.emit("initServerTimezone");
} }
@ -1722,7 +1758,7 @@ async function initDatabase(testMode = false) {
"jwtSecret", "jwtSecret",
]); ]);
if (! jwtSecretBean) { if (!jwtSecretBean) {
log.info("server", "JWT secret is not found, generate one."); log.info("server", "JWT secret is not found, generate one.");
jwtSecretBean = await initJWTSecret(); jwtSecretBean = await initJWTSecret();
log.info("server", "Stored JWT secret into database"); log.info("server", "Stored JWT secret into database");

View file

@ -17,9 +17,7 @@ class Settings {
* } * }
* @type {{}} * @type {{}}
*/ */
static cacheList = { static cacheList = {};
};
static cacheCleaner = null; static cacheCleaner = null;
@ -61,7 +59,7 @@ class Settings {
Settings.cacheList[key] = { Settings.cacheList[key] = {
value: v, value: v,
timestamp: Date.now() timestamp: Date.now(),
}; };
return v; return v;
@ -129,7 +127,7 @@ class Settings {
for (let key of keyList) { for (let key of keyList) {
let bean = await R.findOne("setting", " `key` = ? ", [ let bean = await R.findOne("setting", " `key` = ? ", [
key key,
]); ]);
if (bean == null) { if (bean == null) {

View file

@ -1,7 +1,11 @@
const tcpp = require("tcp-ping"); const tcpp = require("tcp-ping");
const ping = require("@louislam/ping"); const ping = require("@louislam/ping");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { log, genSecret, badgeConstants } = require("../src/util"); const {
log,
genSecret,
badgeConstants,
} = require("../src/util");
const passwordHash = require("./password-hash"); const passwordHash = require("./password-hash");
const { Resolver } = require("dns"); const { Resolver } = require("dns");
const iconv = require("iconv-lite"); const iconv = require("iconv-lite");
@ -22,14 +26,20 @@ const tls = require("tls");
const { const {
dictionaries: { dictionaries: {
rfc2865: { file, attributes }, rfc2865: {
file,
attributes,
},
}, },
} = require("node-radius-utils"); } = require("node-radius-utils");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
// SASLOptions used in JSDoc // SASLOptions used in JSDoc
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const { Kafka, SASLOptions } = require("kafkajs"); const {
Kafka,
SASLOptions,
} = require("kafkajs");
const crypto = require("crypto"); const crypto = require("crypto");
const isWindows = process.platform === /^win/.test(process.platform); const isWindows = process.platform === /^win/.test(process.platform);
@ -75,7 +85,7 @@ exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSe
let client = new oauthProvider.Client({ let client = new oauthProvider.Client({
client_id: clientId, client_id: clientId,
client_secret: clientSecret, client_secret: clientSecret,
token_endpoint_auth_method: authMethod token_endpoint_auth_method: authMethod,
}); });
// Increase default timeout and clock tolerance // Increase default timeout and clock tolerance
@ -185,7 +195,12 @@ exports.pingAsync = function (hostname, ipv6 = false, size = 56) {
*/ */
exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, saslOptions = {}) { exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, saslOptions = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { interval = 20, allowAutoTopicCreation = false, ssl = false, clientId = "Uptime-Kuma" } = options; const {
interval = 20,
allowAutoTopicCreation = false,
ssl = false,
clientId = "Uptime-Kuma",
} = options;
let connectedToKafka = false; let connectedToKafka = false;
@ -213,7 +228,7 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
allowAutoTopicCreation: allowAutoTopicCreation, allowAutoTopicCreation: allowAutoTopicCreation,
retry: { retry: {
retries: 0, retries: 0,
} },
}); });
producer.connect().then( producer.connect().then(
@ -234,14 +249,14 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
connectedToKafka = true; connectedToKafka = true;
clearTimeout(timeoutID); clearTimeout(timeoutID);
}); });
} },
).catch( ).catch(
(e) => { (e) => {
connectedToKafka = true; connectedToKafka = true;
producer.disconnect(); producer.disconnect();
clearTimeout(timeoutID); clearTimeout(timeoutID);
reject(new Error("Error in producer connection: " + e.message)); reject(new Error("Error in producer connection: " + e.message));
} },
); );
producer.on("producer.network.request_timeout", (_) => { producer.on("producer.network.request_timeout", (_) => {
@ -409,7 +424,7 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const connection = mysql.createConnection({ const connection = mysql.createConnection({
uri: connectionString, uri: connectionString,
password password,
}); });
connection.on("error", (err) => { connection.on("error", (err) => {
@ -494,8 +509,8 @@ exports.redisPingAsync = function (dsn, rejectUnauthorized) {
const client = redis.createClient({ const client = redis.createClient({
url: dsn, url: dsn,
socket: { socket: {
rejectUnauthorized rejectUnauthorized,
} },
}); });
client.on("error", (err) => { client.on("error", (err) => {
if (client.isOpen) { if (client.isOpen) {
@ -661,7 +676,7 @@ exports.checkCertificate = function (socket) {
return { return {
valid: valid, valid: valid,
certInfo: parsedInfo certInfo: parsedInfo,
}; };
}; };
@ -693,7 +708,7 @@ exports.checkStatusCode = function (status, acceptedCodes) {
} }
} else { } else {
log.error("monitor", `${codeRange} is not a valid status code range`); log.error("monitor", `${codeRange} is not a valid status code range`);
continue;
} }
} }
@ -925,14 +940,21 @@ module.exports.timeObjectToLocal = (obj, timezone = undefined) => {
* @returns {Promise<object>} Result of gRPC query * @returns {Promise<object>} Result of gRPC query
*/ */
module.exports.grpcQuery = async (options) => { module.exports.grpcQuery = async (options) => {
const { grpcUrl, grpcProtobufData, grpcServiceName, grpcEnableTls, grpcMethod, grpcBody } = options; const {
grpcUrl,
grpcProtobufData,
grpcServiceName,
grpcEnableTls,
grpcMethod,
grpcBody,
} = options;
const protocObject = protojs.parse(grpcProtobufData); const protocObject = protojs.parse(grpcProtobufData);
const protoServiceObject = protocObject.root.lookupService(grpcServiceName); const protoServiceObject = protocObject.root.lookupService(grpcServiceName);
const Client = grpc.makeGenericClientConstructor({}); const Client = grpc.makeGenericClientConstructor({});
const credentials = grpcEnableTls ? grpc.credentials.createSsl() : grpc.credentials.createInsecure(); const credentials = grpcEnableTls ? grpc.credentials.createSsl() : grpc.credentials.createInsecure();
const client = new Client( const client = new Client(
grpcUrl, grpcUrl,
credentials credentials,
); );
const grpcService = protoServiceObject.create(function (method, requestData, cb) { const grpcService = protoServiceObject.create(function (method, requestData, cb) {
const fullServiceName = method.fullName; const fullServiceName = method.fullName;
@ -955,14 +977,14 @@ module.exports.grpcQuery = async (options) => {
return resolve({ return resolve({
code: err.code, code: err.code,
errorMessage: err.details, errorMessage: err.details,
data: "" data: "",
}); });
} else { } else {
log.debug("monitor:", `gRPC response: ${JSON.stringify(response)}`); log.debug("monitor:", `gRPC response: ${JSON.stringify(response)}`);
return resolve({ return resolve({
code: 1, code: 1,
errorMessage: "", errorMessage: "",
data: responseData data: responseData,
}); });
} }
}); });
@ -970,7 +992,7 @@ module.exports.grpcQuery = async (options) => {
return resolve({ return resolve({
code: -1, code: -1,
errorMessage: `Error ${err}. Please review your gRPC configuration option. The service name must not include package name value, and the method name must follow camelCase format`, errorMessage: `Error ${err}. Please review your gRPC configuration option. The service name must not include package name value, and the method name must follow camelCase format`,
data: "" data: "",
}); });
} }

View file

@ -81,5 +81,5 @@ class ArrayWithKey {
} }
module.exports = { module.exports = {
ArrayWithKey ArrayWithKey,
}; };

View file

@ -44,5 +44,5 @@ class LimitQueue extends ArrayWithKey {
} }
module.exports = { module.exports = {
LimitQueue LimitQueue,
}; };