mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-15 04:30:29 +03:00
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:
parent
200f179b5f
commit
9081025c4a
35 changed files with 699 additions and 369 deletions
server
auth.jsclient.jsdatabase.jsdocker.jsjobs.js
model
modules
apicache
axios-ntlm/lib
dayjs/plugin
monitor-types
notification-providers
notification.jsprometheus.jsproxy.jsrouters
server.jssettings.jsutil-server.jsutils
|
@ -3,7 +3,10 @@ const passwordHash = require("./password-hash");
|
|||
const { R } = require("redbean-node");
|
||||
const { setting } = require("./util-server");
|
||||
const { log } = require("../src/util");
|
||||
const { loginRateLimiter, apiRateLimiter } = require("./rate-limiter");
|
||||
const {
|
||||
loginRateLimiter,
|
||||
apiRateLimiter,
|
||||
} = require("./rate-limiter");
|
||||
const { Settings } = require("./settings");
|
||||
const dayjs = require("dayjs");
|
||||
|
||||
|
|
|
@ -47,10 +47,11 @@ async function sendNotificationList(socket) {
|
|||
*/
|
||||
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
|
||||
let list = await R.getAll(`
|
||||
SELECT * FROM heartbeat
|
||||
SELECT *
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY time DESC
|
||||
LIMIT 100
|
||||
LIMIT 100
|
||||
`, [
|
||||
monitorID,
|
||||
]);
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
const fs = require("fs");
|
||||
const { R } = require("redbean-node");
|
||||
const { setSetting, setting } = require("./util-server");
|
||||
const { log, sleep } = require("../src/util");
|
||||
const {
|
||||
setSetting,
|
||||
setting,
|
||||
} = require("./util-server");
|
||||
const {
|
||||
log,
|
||||
sleep,
|
||||
} = require("../src/util");
|
||||
const knex = require("knex");
|
||||
const path = require("path");
|
||||
const { EmbeddedMariaDB } = require("./embedded-mariadb");
|
||||
|
@ -136,24 +142,24 @@ class Database {
|
|||
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
|
||||
|
||||
Database.sqlitePath = path.join(Database.dataDir, "kuma.db");
|
||||
if (! fs.existsSync(Database.dataDir)) {
|
||||
if (!fs.existsSync(Database.dataDir)) {
|
||||
fs.mkdirSync(Database.dataDir, { recursive: true });
|
||||
}
|
||||
|
||||
Database.uploadDir = path.join(Database.dataDir, "upload/");
|
||||
|
||||
if (! fs.existsSync(Database.uploadDir)) {
|
||||
if (!fs.existsSync(Database.uploadDir)) {
|
||||
fs.mkdirSync(Database.uploadDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create screenshot dir
|
||||
Database.screenshotDir = path.join(Database.dataDir, "screenshots/");
|
||||
if (! fs.existsSync(Database.screenshotDir)) {
|
||||
if (!fs.existsSync(Database.screenshotDir)) {
|
||||
fs.mkdirSync(Database.screenshotDir, { recursive: true });
|
||||
}
|
||||
|
||||
Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/");
|
||||
if (! fs.existsSync(Database.dockerTLSDir)) {
|
||||
if (!fs.existsSync(Database.dockerTLSDir)) {
|
||||
fs.mkdirSync(Database.dockerTLSDir, { recursive: true });
|
||||
}
|
||||
|
||||
|
@ -231,7 +237,7 @@ class Database {
|
|||
|
||||
if (dbConfig.type === "sqlite") {
|
||||
|
||||
if (! fs.existsSync(Database.sqlitePath)) {
|
||||
if (!fs.existsSync(Database.sqlitePath)) {
|
||||
log.info("server", "Copying Database");
|
||||
fs.copyFileSync(Database.templatePath, Database.sqlitePath);
|
||||
}
|
||||
|
@ -252,7 +258,7 @@ class Database {
|
|||
idleTimeoutMillis: 120 * 1000,
|
||||
propagateCreateError: false,
|
||||
acquireTimeoutMillis: acquireConnectionTimeout,
|
||||
}
|
||||
},
|
||||
};
|
||||
} else if (dbConfig.type === "mariadb") {
|
||||
if (!/^\w+$/.test(dbConfig.dbName)) {
|
||||
|
@ -451,7 +457,7 @@ class Database {
|
|||
static async patchSqlite() {
|
||||
let version = parseInt(await setting("database_version"));
|
||||
|
||||
if (! version) {
|
||||
if (!version) {
|
||||
version = 0;
|
||||
}
|
||||
|
||||
|
@ -502,7 +508,7 @@ class Database {
|
|||
log.debug("db", "Database Patch 2.0 Process");
|
||||
let databasePatchedFiles = await setting("databasePatchedFiles");
|
||||
|
||||
if (! databasePatchedFiles) {
|
||||
if (!databasePatchedFiles) {
|
||||
databasePatchedFiles = {};
|
||||
}
|
||||
|
||||
|
@ -579,11 +585,11 @@ class Database {
|
|||
let id = await R.store(statusPage);
|
||||
|
||||
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", [
|
||||
id
|
||||
id,
|
||||
]);
|
||||
|
||||
await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
|
||||
|
@ -611,13 +617,13 @@ class Database {
|
|||
static async patch2Recursion(sqlFilename, databasePatchedFiles) {
|
||||
let value = this.patchList[sqlFilename];
|
||||
|
||||
if (! value) {
|
||||
if (!value) {
|
||||
log.info("db", sqlFilename + " skip");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if patched
|
||||
if (! databasePatchedFiles[sqlFilename]) {
|
||||
if (!databasePatchedFiles[sqlFilename]) {
|
||||
log.info("db", sqlFilename + " is not patched");
|
||||
|
||||
if (value.parents) {
|
||||
|
@ -652,7 +658,7 @@ class Database {
|
|||
// Remove all comments (--)
|
||||
let lines = text.split("\n");
|
||||
lines = lines.filter((line) => {
|
||||
return ! line.startsWith("--");
|
||||
return !line.startsWith("--");
|
||||
});
|
||||
|
||||
// Split statements by semicolon
|
||||
|
@ -797,7 +803,8 @@ class Database {
|
|||
|
||||
// Stop if stat_* tables are not empty
|
||||
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;
|
||||
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?)`);
|
||||
|
@ -814,12 +821,12 @@ class Database {
|
|||
for (let monitor of monitors) {
|
||||
// Get a list of unique dates from the heartbeat table, using raw sql
|
||||
let dates = await R.getAll(`
|
||||
SELECT DISTINCT DATE(time) AS date
|
||||
SELECT DISTINCT DATE (time) AS date
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY date ASC
|
||||
`, [
|
||||
monitor.monitor_id
|
||||
monitor.monitor_id,
|
||||
]);
|
||||
|
||||
for (let date of dates) {
|
||||
|
@ -833,7 +840,7 @@ class Database {
|
|||
SELECT status, ping, time
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
AND DATE(time) = ?
|
||||
AND DATE (time) = ?
|
||||
ORDER BY time ASC
|
||||
`, [ monitor.monitor_id, date.date ]);
|
||||
|
||||
|
@ -887,19 +894,21 @@ class Database {
|
|||
log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id);
|
||||
}
|
||||
await R.exec(`
|
||||
DELETE FROM heartbeat
|
||||
DELETE
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
AND important = 0
|
||||
AND time < ${sqlHourOffset}
|
||||
AND id NOT IN (
|
||||
AND important = 0
|
||||
AND time
|
||||
< ${sqlHourOffset}
|
||||
AND id NOT IN (
|
||||
SELECT id FROM ( -- written this way for Maria's support
|
||||
SELECT id
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY time DESC
|
||||
LIMIT ?
|
||||
) AS limited_ids
|
||||
)
|
||||
SELECT id
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY time DESC
|
||||
LIMIT ?
|
||||
) AS limited_ids
|
||||
)
|
||||
`, [
|
||||
monitor.id,
|
||||
-24,
|
||||
|
|
|
@ -146,7 +146,7 @@ class DockerHost {
|
|||
static getHttpsAgentOptions(dockerType, url) {
|
||||
let baseOptions = {
|
||||
maxCachedSessions: 0,
|
||||
rejectUnauthorized: true
|
||||
rejectUnauthorized: true,
|
||||
};
|
||||
let certOptions = {};
|
||||
|
||||
|
@ -163,13 +163,13 @@ class DockerHost {
|
|||
certOptions = {
|
||||
ca,
|
||||
key,
|
||||
cert
|
||||
cert,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...baseOptions,
|
||||
...certOptions
|
||||
...certOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ const jobs = [
|
|||
interval: "*/5 * * * *",
|
||||
jobFunc: incrementalVacuum,
|
||||
croner: null,
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -54,5 +54,5 @@ const stopBackgroundJobs = function () {
|
|||
|
||||
module.exports = {
|
||||
initBackgroundJobs,
|
||||
stopBackgroundJobs
|
||||
stopBackgroundJobs,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
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 dayjs = require("dayjs");
|
||||
const Cron = require("croner");
|
||||
|
@ -192,7 +196,8 @@ class Maintenance extends BeanModel {
|
|||
* @returns {void}
|
||||
*/
|
||||
static validateCron(cron) {
|
||||
let job = new Cron(cron, () => {});
|
||||
let job = new Cron(cron, () => {
|
||||
});
|
||||
job.stop();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,37 @@
|
|||
const dayjs = require("dayjs");
|
||||
const axios = require("axios");
|
||||
const { Prometheus } = require("../prometheus");
|
||||
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
|
||||
SQL_DATETIME_FORMAT, evaluateJsonQuery
|
||||
const {
|
||||
log,
|
||||
UP,
|
||||
DOWN,
|
||||
PENDING,
|
||||
MAINTENANCE,
|
||||
flipStatus,
|
||||
MAX_INTERVAL_SECOND,
|
||||
MIN_INTERVAL_SECOND,
|
||||
SQL_DATETIME_FORMAT,
|
||||
evaluateJsonQuery,
|
||||
} = require("../../src/util");
|
||||
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
|
||||
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
|
||||
const {
|
||||
tcping,
|
||||
ping,
|
||||
checkCertificate,
|
||||
checkStatusCode,
|
||||
getTotalClientInRoom,
|
||||
setting,
|
||||
mssqlQuery,
|
||||
postgresQuery,
|
||||
mysqlQuery,
|
||||
setSetting,
|
||||
httpNtlm,
|
||||
radius,
|
||||
grpcQuery,
|
||||
redisPingAsync,
|
||||
kafkaProducerAsync,
|
||||
getOidcTokenClientCredentials,
|
||||
rootCertificatesFingerprints,
|
||||
axiosAbortSignal,
|
||||
} = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
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:") {
|
||||
const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id);
|
||||
const {
|
||||
certExpiryDaysRemaining,
|
||||
validCert,
|
||||
} = await this.getCertExpiry(this.id);
|
||||
obj.certExpiryDaysRemaining = certExpiryDaysRemaining;
|
||||
obj.validCert = validCert;
|
||||
}
|
||||
|
@ -218,13 +247,13 @@ class Monitor extends BeanModel {
|
|||
if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) {
|
||||
return {
|
||||
certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining,
|
||||
validCert: true
|
||||
validCert: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
certExpiryDaysRemaining: "",
|
||||
validCert: false
|
||||
validCert: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -334,7 +363,7 @@ class Monitor extends BeanModel {
|
|||
|
||||
let beatInterval = this.interval;
|
||||
|
||||
if (! beatInterval) {
|
||||
if (!beatInterval) {
|
||||
beatInterval = 1;
|
||||
}
|
||||
|
||||
|
@ -479,7 +508,7 @@ class Monitor extends BeanModel {
|
|||
...(contentType ? { "Content-Type": contentType } : {}),
|
||||
...(basicAuthHeader),
|
||||
...(oauth2AuthHeader),
|
||||
...(this.headers ? JSON.parse(this.headers) : {})
|
||||
...(this.headers ? JSON.parse(this.headers) : {}),
|
||||
},
|
||||
maxRedirects: this.maxredirects,
|
||||
validateStatus: (status) => {
|
||||
|
@ -504,7 +533,10 @@ class Monitor extends BeanModel {
|
|||
const proxy = await R.load("proxy", this.proxy_id);
|
||||
|
||||
if (proxy && proxy.active) {
|
||||
const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, {
|
||||
const {
|
||||
httpAgent,
|
||||
httpsAgent,
|
||||
} = Proxy.createAgents(proxy, {
|
||||
httpsAgentOptions: httpsAgentOptions,
|
||||
});
|
||||
|
||||
|
@ -518,7 +550,7 @@ class Monitor extends BeanModel {
|
|||
let jar = new CookieJar();
|
||||
let httpsCookieAgentOptions = {
|
||||
...httpsAgentOptions,
|
||||
cookies: { jar }
|
||||
cookies: { jar },
|
||||
};
|
||||
options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions);
|
||||
}
|
||||
|
@ -600,7 +632,10 @@ class Monitor extends BeanModel {
|
|||
} else if (this.type === "json-query") {
|
||||
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) {
|
||||
bean.status = UP;
|
||||
|
@ -681,7 +716,7 @@ class Monitor extends BeanModel {
|
|||
params: {
|
||||
filter: filter,
|
||||
key: steamAPIKey,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) {
|
||||
|
@ -690,7 +725,8 @@ class Monitor extends BeanModel {
|
|||
|
||||
try {
|
||||
bean.ping = await ping(this.hostname, this.packetSize);
|
||||
} catch (_) { }
|
||||
} catch (_) {
|
||||
}
|
||||
} else {
|
||||
throw new Error("Server not found on Steam");
|
||||
}
|
||||
|
@ -739,7 +775,7 @@ class Monitor extends BeanModel {
|
|||
} else if (dockerHost._dockerType === "tcp") {
|
||||
options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon);
|
||||
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;
|
||||
|
||||
if (! this.isStop) {
|
||||
if (!this.isStop) {
|
||||
log.debug("monitor", `[${this.name}] SetTimeout for next check.`);
|
||||
|
||||
let intervalRemainingMs = Math.max(
|
||||
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`);
|
||||
|
@ -1013,7 +1049,7 @@ class Monitor extends BeanModel {
|
|||
UptimeKumaServer.errorLog(e, false);
|
||||
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");
|
||||
this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
|
||||
}
|
||||
|
@ -1047,7 +1083,7 @@ class Monitor extends BeanModel {
|
|||
username: this.basic_auth_user,
|
||||
password: this.basic_auth_pass,
|
||||
domain: this.authDomain,
|
||||
workstation: this.authWorkstation ? this.authWorkstation : undefined
|
||||
workstation: this.authWorkstation ? this.authWorkstation : undefined,
|
||||
});
|
||||
} else {
|
||||
res = await axios.request(options);
|
||||
|
@ -1065,8 +1101,9 @@ class Monitor extends BeanModel {
|
|||
let oauth2AuthHeader = {
|
||||
"Authorization": this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token,
|
||||
};
|
||||
options.headers = { ...(options.headers),
|
||||
...(oauth2AuthHeader)
|
||||
options.headers = {
|
||||
...(options.headers),
|
||||
...(oauth2AuthHeader),
|
||||
};
|
||||
|
||||
return this.makeAxiosRequest(options, true);
|
||||
|
@ -1158,7 +1195,7 @@ class Monitor extends BeanModel {
|
|||
if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
|
||||
log.debug("monitor", "Resetting sent_history");
|
||||
await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
|
||||
this.id
|
||||
this.id,
|
||||
]);
|
||||
} else {
|
||||
log.debug("monitor", "No need to reset sent_history");
|
||||
|
@ -1168,7 +1205,8 @@ class Monitor extends BeanModel {
|
|||
} else {
|
||||
log.debug("monitor", "Not valid object");
|
||||
}
|
||||
} catch (e) { }
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1326,8 +1364,9 @@ class Monitor extends BeanModel {
|
|||
for (let notification of notificationList) {
|
||||
try {
|
||||
const heartbeatJSON = bean.toJSON();
|
||||
const monitorData = [{ id: monitor.id,
|
||||
active: monitor.active
|
||||
const monitorData = [ {
|
||||
id: monitor.id,
|
||||
active: monitor.active,
|
||||
}];
|
||||
const preloadData = await Monitor.preparePreloadData(monitorData);
|
||||
// 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) {
|
||||
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.
|
||||
log.debug("monitor", "No notification, no need to send cert notification");
|
||||
return;
|
||||
|
@ -1458,7 +1497,7 @@ class Monitor extends BeanModel {
|
|||
*/
|
||||
static async getPreviousHeartbeat(monitorID) {
|
||||
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,
|
||||
value: row.value,
|
||||
name: row.name,
|
||||
color: row.color
|
||||
color: row.color,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1687,7 +1726,7 @@ class Monitor extends BeanModel {
|
|||
*/
|
||||
static async unlinkAllChildren(groupID) {
|
||||
return await R.exec("UPDATE `monitor` SET parent = ? WHERE parent = ? ", [
|
||||
null, groupID
|
||||
null, groupID,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,15 @@ const { marked } = require("marked");
|
|||
const { Feed } = require("feed");
|
||||
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 {
|
||||
|
||||
|
@ -16,7 +24,7 @@ class StatusPage extends BeanModel {
|
|||
* Like this: { "test-uptime.kuma.pet": "default" }
|
||||
* @type {{}}
|
||||
*/
|
||||
static domainMappingList = { };
|
||||
static domainMappingList = {};
|
||||
|
||||
/**
|
||||
* Handle responses to RSS pages
|
||||
|
@ -26,7 +34,7 @@ class StatusPage extends BeanModel {
|
|||
*/
|
||||
static async handleStatusPageRSSResponse(response, slug) {
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
slug,
|
||||
]);
|
||||
|
||||
if (statusPage) {
|
||||
|
@ -51,7 +59,7 @@ class StatusPage extends BeanModel {
|
|||
}
|
||||
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
slug,
|
||||
]);
|
||||
|
||||
if (statusPage) {
|
||||
|
@ -68,7 +76,10 @@ class StatusPage extends BeanModel {
|
|||
* @returns {Promise<string>} the rendered html
|
||||
*/
|
||||
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 host = `${proto}://${config.hostname || "localhost"}:${config.port}/status/${slug}`;
|
||||
|
@ -135,7 +146,7 @@ class StatusPage extends BeanModel {
|
|||
// Preload data
|
||||
// Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
|
||||
const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), {
|
||||
"isScriptContext": true
|
||||
"isScriptContext": true,
|
||||
});
|
||||
|
||||
const script = $(`
|
||||
|
@ -174,7 +185,7 @@ class StatusPage extends BeanModel {
|
|||
}
|
||||
}
|
||||
|
||||
if (! hasUp) {
|
||||
if (!hasUp) {
|
||||
status = STATUS_PAGE_ALL_DOWN;
|
||||
}
|
||||
|
||||
|
@ -223,7 +234,7 @@ class StatusPage extends BeanModel {
|
|||
const showTags = !!statusPage.show_tags;
|
||||
|
||||
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
|
||||
statusPage.id
|
||||
statusPage.id,
|
||||
]);
|
||||
|
||||
let heartbeats = [];
|
||||
|
@ -236,7 +247,7 @@ class StatusPage extends BeanModel {
|
|||
heartbeats.push({
|
||||
...monitor,
|
||||
status: heartbeat.status,
|
||||
time: heartbeat.time
|
||||
time: heartbeat.time,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +262,7 @@ class StatusPage extends BeanModel {
|
|||
|
||||
return {
|
||||
heartbeats,
|
||||
statusDescription
|
||||
statusDescription,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -279,7 +290,7 @@ class StatusPage extends BeanModel {
|
|||
const showTags = !!statusPage.show_tags;
|
||||
|
||||
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
|
||||
statusPage.id
|
||||
statusPage.id,
|
||||
]);
|
||||
|
||||
for (let groupBean of list) {
|
||||
|
@ -442,7 +453,7 @@ class StatusPage extends BeanModel {
|
|||
*/
|
||||
static async slugToID(slug) {
|
||||
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
|
||||
slug
|
||||
slug,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
|
|||
const passwordHash = require("../password-hash");
|
||||
const { R } = require("redbean-node");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { shake256, SHAKE256_LENGTH } = require("../util-server");
|
||||
const {
|
||||
shake256,
|
||||
SHAKE256_LENGTH,
|
||||
} = require("../util-server");
|
||||
|
||||
class User extends BeanModel {
|
||||
/**
|
||||
|
@ -15,7 +18,7 @@ class User extends BeanModel {
|
|||
static async resetPassword(userID, newPassword) {
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
passwordHash.generate(newPassword),
|
||||
userID
|
||||
userID,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -29,7 +32,7 @@ class User extends BeanModel {
|
|||
|
||||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
|
||||
hashedPassword,
|
||||
this.id
|
||||
this.id,
|
||||
]);
|
||||
|
||||
this.password = hashedPassword;
|
||||
|
|
|
@ -100,7 +100,7 @@ function ApiCache() {
|
|||
* Generated by Trelent
|
||||
*/
|
||||
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;
|
||||
});
|
||||
let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1;
|
||||
|
@ -210,7 +210,8 @@ function ApiCache() {
|
|||
try {
|
||||
redis.hset(key, "response", JSON.stringify(value));
|
||||
redis.hset(key, "duration", duration);
|
||||
redis.expire(key, duration / 1000, expireCallback || function () {});
|
||||
redis.expire(key, duration / 1000, expireCallback || function () {
|
||||
});
|
||||
} catch (err) {
|
||||
debug("[apicache] error in redis.hset()");
|
||||
}
|
||||
|
@ -247,8 +248,8 @@ function ApiCache() {
|
|||
}
|
||||
|
||||
res._apicache.content = Buffer.concat(
|
||||
[oldContent, content],
|
||||
oldContent.length + content.length
|
||||
[ oldContent, content ],
|
||||
oldContent.length + content.length,
|
||||
);
|
||||
} else {
|
||||
res._apicache.content = content;
|
||||
|
@ -268,7 +269,7 @@ function ApiCache() {
|
|||
* @param {function(Object, Object):boolean} 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 = {
|
||||
write: res.write,
|
||||
writeHead: res.writeHead,
|
||||
|
@ -314,7 +315,7 @@ function ApiCache() {
|
|||
res.statusCode,
|
||||
headers,
|
||||
res._apicache.content,
|
||||
encoding
|
||||
encoding,
|
||||
);
|
||||
cacheResponse(key, cacheObject, duration);
|
||||
|
||||
|
@ -367,7 +368,7 @@ function ApiCache() {
|
|||
let data = cacheObject.data;
|
||||
if (data && data.type === "Buffer") {
|
||||
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
|
||||
|
@ -511,15 +512,15 @@ function ApiCache() {
|
|||
};
|
||||
|
||||
/**
|
||||
* Return cache performance statistics (hit rate). Suitable for
|
||||
* putting into a route:
|
||||
* <code>
|
||||
* app.get('/api/cache/performance', (req, res) => {
|
||||
* res.json(apicache.getPerformance())
|
||||
* })
|
||||
* </code>
|
||||
* @returns {any[]}
|
||||
*/
|
||||
* Return cache performance statistics (hit rate). Suitable for
|
||||
* putting into a route:
|
||||
* <code>
|
||||
* app.get('/api/cache/performance', (req, res) => {
|
||||
* res.json(apicache.getPerformance())
|
||||
* })
|
||||
* </code>
|
||||
* @returns {any[]}
|
||||
*/
|
||||
this.getPerformance = function () {
|
||||
return performanceArray.map(function (p) {
|
||||
return p.report();
|
||||
|
@ -528,7 +529,7 @@ function ApiCache() {
|
|||
|
||||
/**
|
||||
* Get index of a group
|
||||
* @param {string} group
|
||||
* @param {string} group
|
||||
* @returns {number}
|
||||
*/
|
||||
this.getIndex = function (group) {
|
||||
|
@ -543,9 +544,9 @@ function ApiCache() {
|
|||
* Express middleware
|
||||
* @param {(string|number)} strDuration Duration to cache responses
|
||||
* for.
|
||||
* @param {function(Object, Object):boolean} middlewareToggle
|
||||
* @param {function(Object, Object):boolean} middlewareToggle
|
||||
* @param {Object} localOptions Options for APICache
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
|
||||
let duration = instance.getDuration(strDuration);
|
||||
|
@ -573,7 +574,8 @@ function ApiCache() {
|
|||
* A Function for non tracking performance
|
||||
*/
|
||||
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 (
|
||||
req.headers["x-apicache-bypass"] ||
|
||||
req.headers["x-apicache-force-fetch"] ||
|
||||
(opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
|
||||
req.headers["x-apicache-force-fetch"] ||
|
||||
(opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
|
||||
) {
|
||||
return bypass();
|
||||
}
|
||||
|
@ -826,7 +828,7 @@ function ApiCache() {
|
|||
JSON.parse(obj.response),
|
||||
middlewareToggle,
|
||||
next,
|
||||
duration
|
||||
duration,
|
||||
);
|
||||
} else {
|
||||
perf.miss(key);
|
||||
|
@ -837,7 +839,7 @@ function ApiCache() {
|
|||
key,
|
||||
duration,
|
||||
strDuration,
|
||||
middlewareToggle
|
||||
middlewareToggle,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -859,7 +861,7 @@ function ApiCache() {
|
|||
|
||||
/**
|
||||
* Process options
|
||||
* @param {Object} options
|
||||
* @param {Object} options
|
||||
* @returns {Object}
|
||||
*/
|
||||
this.options = function (options) {
|
||||
|
|
|
@ -2,7 +2,7 @@ const apicache = require("./apicache");
|
|||
|
||||
apicache.options({
|
||||
headerBlacklist: [
|
||||
"cache-control"
|
||||
"cache-control",
|
||||
],
|
||||
headers: {
|
||||
// Disable client side cache, only server side cache.
|
||||
|
|
|
@ -4,7 +4,7 @@ function MemoryCache() {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {string} key Key to store cache as
|
||||
* @param {any} value Value to store
|
||||
* @param {number} time Time to store for
|
||||
|
@ -22,7 +22,7 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
|
|||
timeout: setTimeout(function () {
|
||||
instance.delete(key);
|
||||
return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
|
||||
}, time)
|
||||
}, time),
|
||||
};
|
||||
|
||||
this.cache[key] = entry;
|
||||
|
@ -52,7 +52,7 @@ MemoryCache.prototype.delete = function (key) {
|
|||
|
||||
/**
|
||||
* Get value of key
|
||||
* @param {string} key
|
||||
* @param {string} key
|
||||
* @returns {Object}
|
||||
*/
|
||||
MemoryCache.prototype.get = function (key) {
|
||||
|
@ -63,7 +63,7 @@ MemoryCache.prototype.get = function (key) {
|
|||
|
||||
/**
|
||||
* Get value of cache entry
|
||||
* @param {string} key
|
||||
* @param {string} key
|
||||
* @returns {any}
|
||||
*/
|
||||
MemoryCache.prototype.getValue = function (key) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
|
||||
module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
|
||||
/* 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
|
||||
the "Session Key" field of the Type 3 message. */
|
||||
module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31;
|
||||
//# sourceMappingURL=flags.js.map
|
||||
//# sourceMappingURL=flags.js.map
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
// 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) {
|
||||
var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0);
|
||||
lmhash.copy(pwBuffer);
|
||||
|
@ -9,19 +10,21 @@ function createLMResponse(challenge, lmhash) {
|
|||
calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16);
|
||||
return buf;
|
||||
}
|
||||
|
||||
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) {
|
||||
buf.fill(0);
|
||||
return buf;
|
||||
}
|
||||
pwBuffer.fill(0);
|
||||
pwBuffer.write(password.toUpperCase(), 0, 'ascii');
|
||||
pwBuffer.write(password.toUpperCase(), 0, "ascii");
|
||||
return Buffer.concat([
|
||||
calculateDES(pwBuffer.slice(0, 7), magicKey),
|
||||
calculateDES(pwBuffer.slice(7), magicKey)
|
||||
calculateDES(pwBuffer.slice(7), magicKey),
|
||||
]);
|
||||
}
|
||||
|
||||
function calculateDES(key, message) {
|
||||
var desKey = new Buffer.alloc(8);
|
||||
desKey[0] = key[0] & 0xFE;
|
||||
|
@ -39,9 +42,10 @@ function calculateDES(key, message) {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
||||
function createNTLMResponse(challenge, ntlmhash) {
|
||||
var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0);
|
||||
ntlmhash.copy(ntlmBuffer);
|
||||
|
@ -50,30 +54,36 @@ function createNTLMResponse(challenge, ntlmhash) {
|
|||
calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16);
|
||||
return buf;
|
||||
}
|
||||
|
||||
function createNTLMHash(password) {
|
||||
var md4sum = crypto.createHash('md4');
|
||||
md4sum.update(new Buffer.from(password, 'ucs2'));
|
||||
var md4sum = crypto.createHash("md4");
|
||||
md4sum.update(new Buffer.from(password, "ucs2"));
|
||||
return md4sum.digest();
|
||||
}
|
||||
|
||||
function createNTLMv2Hash(ntlmhash, username, authTargetName) {
|
||||
var hmac = crypto.createHmac('md5', ntlmhash);
|
||||
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2'));
|
||||
var hmac = crypto.createHmac("md5", ntlmhash);
|
||||
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, "ucs2"));
|
||||
return hmac.digest();
|
||||
}
|
||||
|
||||
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
|
||||
type2message.challenge.copy(buf, 8);
|
||||
//client nonce
|
||||
buf.write(nonce || createPseudoRandomValue(16), 16, 'hex');
|
||||
buf.write(nonce || createPseudoRandomValue(16), 16, "hex");
|
||||
//create hash
|
||||
hmac.update(buf.slice(8));
|
||||
var hashedBuffer = hmac.digest();
|
||||
hashedBuffer.copy(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
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
|
||||
//server challenge
|
||||
type2message.challenge.copy(buf, 8);
|
||||
|
@ -86,12 +96,12 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
|
|||
// maybe think about a different solution here
|
||||
// 11644473600000 = diff between 1970 and 1601
|
||||
var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
|
||||
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 timestampLow = Number("0x" + timestamp.substring(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(timestampHigh, 28, false);
|
||||
//random client nonce
|
||||
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex');
|
||||
buf.write(nonce || createPseudoRandomValue(16), 32, "hex");
|
||||
//zero
|
||||
buf.writeUInt32LE(0, 40);
|
||||
//complete target information block from type 2 message
|
||||
|
@ -103,13 +113,15 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
|
|||
hashedBuffer.copy(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
function createPseudoRandomValue(length) {
|
||||
var str = '';
|
||||
var str = "";
|
||||
while (str.length < length) {
|
||||
str += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createLMHash: createLMHash,
|
||||
createNTLMHash: createNTLMHash,
|
||||
|
@ -117,6 +129,6 @@ module.exports = {
|
|||
createNTLMResponse: createNTLMResponse,
|
||||
createLMv2Response: createLMv2Response,
|
||||
createNTLMv2Response: createNTLMv2Response,
|
||||
createPseudoRandomValue: createPseudoRandomValue
|
||||
createPseudoRandomValue: createPseudoRandomValue,
|
||||
};
|
||||
//# sourceMappingURL=hash.js.map
|
||||
//# sourceMappingURL=hash.js.map
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
// 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";
|
||||
|
||||
function createType1Message(workstation, target) {
|
||||
var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024);
|
||||
workstation = workstation === undefined ? os.hostname() : workstation;
|
||||
target = target === undefined ? '' : target;
|
||||
target = target === undefined ? "" : target;
|
||||
//signature
|
||||
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii');
|
||||
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, "ascii");
|
||||
pos += NTLMSIGNATURE.length;
|
||||
//message type
|
||||
buf.writeUInt32LE(1, pos);
|
||||
|
@ -27,7 +28,7 @@ function createType1Message(workstation, target) {
|
|||
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
|
||||
pos += 4;
|
||||
if (target.length > 0) {
|
||||
dataPos += buf.write(target, dataPos, 'ascii');
|
||||
dataPos += buf.write(target, dataPos, "ascii");
|
||||
}
|
||||
//workstation security buffer
|
||||
buf.writeUInt16LE(workstation.length, pos);
|
||||
|
@ -37,39 +38,39 @@ function createType1Message(workstation, target) {
|
|||
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
|
||||
pos += 4;
|
||||
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) {
|
||||
if (str === undefined) {
|
||||
throw new Error('Invalid argument');
|
||||
throw new Error("Invalid argument");
|
||||
}
|
||||
//convenience
|
||||
if (Object.prototype.toString.call(str) !== '[object String]') {
|
||||
if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) {
|
||||
str = str.headers['www-authenticate'];
|
||||
}
|
||||
else {
|
||||
throw new Error('Invalid argument');
|
||||
if (Object.prototype.toString.call(str) !== "[object String]") {
|
||||
if (str.hasOwnProperty("headers") && str.headers.hasOwnProperty("www-authenticate")) {
|
||||
str = str.headers["www-authenticate"];
|
||||
} else {
|
||||
throw new Error("Invalid argument");
|
||||
}
|
||||
}
|
||||
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
|
||||
if (ntlmMatch) {
|
||||
str = ntlmMatch[1];
|
||||
}
|
||||
var buf = new Buffer.from(str, 'base64'), obj = {};
|
||||
var buf = new Buffer.from(str, "base64"), obj = {};
|
||||
//check signature
|
||||
if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
|
||||
throw new Error('Invalid message signature: ' + str);
|
||||
if (buf.toString("ascii", 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
|
||||
throw new Error("Invalid message signature: " + str);
|
||||
}
|
||||
//check message type
|
||||
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
|
||||
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.challenge = buf.slice(24, 32);
|
||||
//read target name
|
||||
|
@ -78,10 +79,10 @@ function decodeType2Message(str) {
|
|||
//skipping allocated space
|
||||
var offset = buf.readUInt32LE(16);
|
||||
if (length === 0) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
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);
|
||||
})();
|
||||
|
@ -98,7 +99,7 @@ function decodeType2Message(str) {
|
|||
return info;
|
||||
}
|
||||
if ((offset + length) > buf.length || offset < 32) {
|
||||
throw new Error('Bad type 2 message');
|
||||
throw new Error("Bad type 2 message");
|
||||
}
|
||||
var pos = offset;
|
||||
while (pos < (offset + length)) {
|
||||
|
@ -113,37 +114,38 @@ function decodeType2Message(str) {
|
|||
var blockTypeStr = void 0;
|
||||
switch (blockType) {
|
||||
case 1:
|
||||
blockTypeStr = 'SERVER';
|
||||
blockTypeStr = "SERVER";
|
||||
break;
|
||||
case 2:
|
||||
blockTypeStr = 'DOMAIN';
|
||||
blockTypeStr = "DOMAIN";
|
||||
break;
|
||||
case 3:
|
||||
blockTypeStr = 'FQDN';
|
||||
blockTypeStr = "FQDN";
|
||||
break;
|
||||
case 4:
|
||||
blockTypeStr = 'DNS';
|
||||
blockTypeStr = "DNS";
|
||||
break;
|
||||
case 5:
|
||||
blockTypeStr = 'PARENT_DNS';
|
||||
blockTypeStr = "PARENT_DNS";
|
||||
break;
|
||||
default:
|
||||
blockTypeStr = '';
|
||||
blockTypeStr = "";
|
||||
break;
|
||||
}
|
||||
if (blockTypeStr) {
|
||||
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength);
|
||||
info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength);
|
||||
}
|
||||
pos += blockLength;
|
||||
}
|
||||
return {
|
||||
parsed: info,
|
||||
buffer: targetInfoBuffer
|
||||
buffer: targetInfoBuffer,
|
||||
};
|
||||
})();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function createType3Message(type2Message, username, password, workstation, target) {
|
||||
var dataPos = 52, buf = new Buffer.alloc(1024);
|
||||
if (workstation === undefined) {
|
||||
|
@ -153,12 +155,14 @@ function createType3Message(type2Message, username, password, workstation, targe
|
|||
target = type2Message.targetName;
|
||||
}
|
||||
//signature
|
||||
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii');
|
||||
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, "ascii");
|
||||
//message type
|
||||
buf.writeUInt32LE(3, 8);
|
||||
if (type2Message.version === 2) {
|
||||
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
|
||||
buf.writeUInt16LE(lmv2.length, 12);
|
||||
buf.writeUInt16LE(lmv2.length, 14);
|
||||
|
@ -171,9 +175,10 @@ function createType3Message(type2Message, username, password, workstation, targe
|
|||
buf.writeUInt32LE(dataPos, 24);
|
||||
ntlmv2.copy(buf, dataPos);
|
||||
dataPos += ntlmv2.length;
|
||||
}
|
||||
else {
|
||||
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
|
||||
} else {
|
||||
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password),
|
||||
lm = hash.createLMResponse(type2Message.challenge, lmHash),
|
||||
ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
|
||||
//lm security buffer
|
||||
buf.writeUInt16LE(lm.length, 12);
|
||||
buf.writeUInt16LE(lm.length, 14);
|
||||
|
@ -188,18 +193,18 @@ function createType3Message(type2Message, username, password, workstation, targe
|
|||
dataPos += ntlm.length;
|
||||
}
|
||||
//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, 30);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 28);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 30);
|
||||
buf.writeUInt32LE(dataPos, 32);
|
||||
dataPos += buf.write(target, dataPos, type2Message.encoding);
|
||||
//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, 38);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 36);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 38);
|
||||
buf.writeUInt32LE(dataPos, 40);
|
||||
dataPos += buf.write(username, dataPos, type2Message.encoding);
|
||||
//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, 46);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 44);
|
||||
buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 46);
|
||||
buf.writeUInt32LE(dataPos, 48);
|
||||
dataPos += buf.write(workstation, dataPos, type2Message.encoding);
|
||||
if (type2Message.version === 2) {
|
||||
|
@ -210,11 +215,12 @@ function createType3Message(type2Message, username, password, workstation, targe
|
|||
//flags
|
||||
buf.writeUInt32LE(type2Message.flags, 60);
|
||||
}
|
||||
return 'NTLM ' + buf.toString('base64', 0, dataPos);
|
||||
return "NTLM " + buf.toString("base64", 0, dataPos);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createType1Message: createType1Message,
|
||||
decodeType2Message: decodeType2Message,
|
||||
createType3Message: createType3Message
|
||||
createType3Message: createType3Message,
|
||||
};
|
||||
//# sourceMappingURL=ntlm.js.map
|
||||
//# sourceMappingURL=ntlm.js.map
|
||||
|
|
|
@ -1,57 +1,172 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (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;
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (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];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) {
|
||||
Object.defineProperty(o, "default", {
|
||||
enumerable: true,
|
||||
value: v,
|
||||
});
|
||||
}) : function (o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
if (mod && mod.__esModule) {
|
||||
return mod;
|
||||
}
|
||||
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);
|
||||
return result;
|
||||
};
|
||||
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) {
|
||||
function fulfilled(value) { try { 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); }
|
||||
function fulfilled(value) {
|
||||
try {
|
||||
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());
|
||||
});
|
||||
};
|
||||
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;
|
||||
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;
|
||||
var _ = {
|
||||
label: 0,
|
||||
sent: function () {
|
||||
if (t[0] & 1) {
|
||||
throw t[1];
|
||||
}
|
||||
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 };
|
||||
return t[1];
|
||||
},
|
||||
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) {
|
||||
|
@ -64,12 +179,13 @@ var ntlm = __importStar(require("./ntlm"));
|
|||
var https = __importStar(require("https"));
|
||||
var http = __importStar(require("http"));
|
||||
var dev_null_1 = __importDefault(require("dev-null"));
|
||||
|
||||
/**
|
||||
* @param credentials An NtlmCredentials object containing the username and password
|
||||
* @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
|
||||
*/
|
||||
* @param credentials An NtlmCredentials object containing the username and password
|
||||
* @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
|
||||
*/
|
||||
function NtlmClient(credentials, AxiosConfig) {
|
||||
var _this = this;
|
||||
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
|
||||
|
@ -82,46 +198,56 @@ function NtlmClient(credentials, AxiosConfig) {
|
|||
var client = axios_1.default.create(config);
|
||||
client.interceptors.response.use(function (response) {
|
||||
return response;
|
||||
}, function (err) { return __awaiter(_this, void 0, void 0, function () {
|
||||
var error, t1Msg, t2Msg, t3Msg, stream_1;
|
||||
var _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
error = err.response;
|
||||
if (!(error && error.status === 401
|
||||
&& error.headers['www-authenticate']
|
||||
&& error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3];
|
||||
// This length check is a hack because SharePoint is awkward and will
|
||||
// include the Negotiate option when responding with the T2 message
|
||||
// There is nore we could do to ensure we are processing correctly,
|
||||
// but this is the easiest option for now
|
||||
if (error.headers['www-authenticate'].length < 50) {
|
||||
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
|
||||
error.config.headers["Authorization"] = t1Msg;
|
||||
}
|
||||
else {
|
||||
t2Msg = ntlm.decodeType2Message((error.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
|
||||
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
|
||||
error.config.headers["X-retry"] = "false";
|
||||
error.config.headers["Authorization"] = t3Msg;
|
||||
}
|
||||
if (!(error.config.responseType === "stream")) 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) {
|
||||
}, function (err) {
|
||||
return __awaiter(_this, void 0, void 0, function () {
|
||||
var error, t1Msg, t2Msg, t3Msg, stream_1;
|
||||
var _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
error = err.response;
|
||||
if (!(error && error.status === 401
|
||||
&& error.headers["www-authenticate"]
|
||||
&& error.headers["www-authenticate"].includes("NTLM"))) {
|
||||
return [ 3 /*break*/, 3 ];
|
||||
}
|
||||
// This length check is a hack because SharePoint is awkward and will
|
||||
// include the Negotiate option when responding with the T2 message
|
||||
// There is nore we could do to ensure we are processing correctly,
|
||||
// but this is the easiest option for now
|
||||
if (error.headers["www-authenticate"].length < 50) {
|
||||
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
|
||||
error.config.headers["Authorization"] = t1Msg;
|
||||
} else {
|
||||
t2Msg = ntlm.decodeType2Message((error.headers["www-authenticate"].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
|
||||
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
|
||||
error.config.headers["X-retry"] = "false";
|
||||
error.config.headers["Authorization"] = t3Msg;
|
||||
}
|
||||
if (!(error.config.responseType === "stream")) {
|
||||
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.once('close', resolve);
|
||||
})];
|
||||
case 1:
|
||||
_b.sent();
|
||||
_b.label = 2;
|
||||
case 2: return [2 /*return*/, client(error.config)];
|
||||
case 3: throw err;
|
||||
}
|
||||
}) ];
|
||||
case 1:
|
||||
_b.sent();
|
||||
_b.label = 2;
|
||||
case 2:
|
||||
return [ 2 /*return*/, client(error.config) ];
|
||||
case 3:
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}); });
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
exports.NtlmClient = NtlmClient;
|
||||
//# sourceMappingURL=ntlmClient.js.map
|
||||
//# sourceMappingURL=ntlmClient.js.map
|
||||
|
|
32
server/modules/dayjs/plugin/timezone.d.ts
vendored
32
server/modules/dayjs/plugin/timezone.d.ts
vendored
|
@ -1,20 +1,24 @@
|
|||
import { PluginFunc, ConfigType } from 'dayjs'
|
||||
import { PluginFunc, ConfigType } from "dayjs";
|
||||
|
||||
declare const plugin: PluginFunc
|
||||
declare const plugin: PluginFunc;
|
||||
export = plugin
|
||||
|
||||
declare module 'dayjs' {
|
||||
interface Dayjs {
|
||||
tz(timezone?: string, keepLocalTime?: boolean): Dayjs
|
||||
offsetName(type?: 'short' | 'long'): string | undefined
|
||||
}
|
||||
declare module "dayjs" {
|
||||
interface Dayjs {
|
||||
tz(timezone?: string, keepLocalTime?: boolean): Dayjs;
|
||||
|
||||
interface DayjsTimezone {
|
||||
(date: ConfigType, timezone?: string): Dayjs
|
||||
(date: ConfigType, format: string, timezone?: string): Dayjs
|
||||
guess(): string
|
||||
setDefault(timezone?: string): void
|
||||
}
|
||||
offsetName(type?: "short" | "long"): string | undefined;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
day: 2,
|
||||
hour: 3,
|
||||
minute: 4,
|
||||
second: 5
|
||||
second: 5,
|
||||
};
|
||||
let e = {};
|
||||
return function (n, i, o) {
|
||||
|
@ -37,7 +37,7 @@
|
|||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
timeZoneName: i
|
||||
timeZoneName: i,
|
||||
}), e[o] = r), r;
|
||||
}(n, i);
|
||||
return r.formatToParts(o);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { UP, DOWN } = require("../../src/util");
|
||||
const {
|
||||
UP,
|
||||
DOWN,
|
||||
} = require("../../src/util");
|
||||
const dayjs = require("dayjs");
|
||||
const { dnsResolve } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
|
@ -14,7 +17,7 @@ class DnsMonitorType extends MonitorType {
|
|||
supportsConditions = true;
|
||||
|
||||
conditionVariables = [
|
||||
new ConditionVariable("record", defaultStringOperators ),
|
||||
new ConditionVariable("record", defaultStringOperators),
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { log, UP } = require("../../src/util");
|
||||
const {
|
||||
log,
|
||||
UP,
|
||||
} = require("../../src/util");
|
||||
const mqtt = require("mqtt");
|
||||
const jsonata = require("jsonata");
|
||||
|
||||
|
@ -57,7 +60,12 @@ class MqttMonitorType extends MonitorType {
|
|||
*/
|
||||
mqttAsync(hostname, topic, options = {}) {
|
||||
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
|
||||
if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
|
||||
|
@ -77,7 +85,7 @@ class MqttMonitorType extends MonitorType {
|
|||
let client = mqtt.connect(mqttUrl, {
|
||||
username,
|
||||
password,
|
||||
clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8)
|
||||
clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8),
|
||||
});
|
||||
|
||||
client.on("connect", () => {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
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 axios = require("axios");
|
||||
|
||||
|
@ -21,7 +25,7 @@ class RabbitMqMonitorType extends MonitorType {
|
|||
for (let baseUrl of baseUrls) {
|
||||
try {
|
||||
// Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com
|
||||
if ( !baseUrl.endsWith("/") ) {
|
||||
if (!baseUrl.endsWith("/")) {
|
||||
baseUrl += "/";
|
||||
}
|
||||
const options = {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { chromium } = require("playwright-core");
|
||||
const { UP, log } = require("../../src/util");
|
||||
const {
|
||||
UP,
|
||||
log,
|
||||
} = require("../../src/util");
|
||||
const { Settings } = require("../settings");
|
||||
const commandExistsSync = require("command-exists").sync;
|
||||
const childProcess = require("child_process");
|
||||
|
@ -122,7 +125,7 @@ async function prepareChromeExecutable(executablePath) {
|
|||
executablePath = "/usr/bin/chromium";
|
||||
|
||||
// Install chromium in container via apt install
|
||||
if ( !commandExistsSync(executablePath)) {
|
||||
if (!commandExistsSync(executablePath)) {
|
||||
await new Promise((resolve, reject) => {
|
||||
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");
|
||||
|
@ -213,6 +216,7 @@ async function testChrome(executablePath) {
|
|||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// test remote browser
|
||||
/**
|
||||
* @param {string} remoteBrowserURL Remote Browser URL
|
||||
|
@ -228,6 +232,7 @@ async function testRemoteBrowser(remoteBrowserURL) {
|
|||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
class RealBrowserMonitorType extends MonitorType {
|
||||
|
||||
name = "real-browser";
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { UP, log, evaluateJsonQuery } = require("../../src/util");
|
||||
const {
|
||||
UP,
|
||||
log,
|
||||
evaluateJsonQuery,
|
||||
} = require("../../src/util");
|
||||
const snmp = require("net-snmp");
|
||||
|
||||
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.
|
||||
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) {
|
||||
heartbeat.status = UP;
|
||||
|
|
|
@ -5,6 +5,15 @@ const NotifyClient = require("notifications-node-client").NotifyClient;
|
|||
class GovNotify extends NotificationProvider {
|
||||
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) {
|
||||
try {
|
||||
const apiKey = notification.apiKey;
|
||||
|
|
|
@ -20,19 +20,21 @@ class HomeAssistant extends NotificationProvider {
|
|||
{
|
||||
title: "Uptime Kuma",
|
||||
message: msg,
|
||||
...(notificationService !== "persistent_notification" && { data: {
|
||||
name: monitorJSON?.name,
|
||||
status: heartbeatJSON?.status,
|
||||
channel: "Uptime Kuma",
|
||||
icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
|
||||
} }),
|
||||
...(notificationService !== "persistent_notification" && {
|
||||
data: {
|
||||
name: monitorJSON?.name,
|
||||
status: heartbeatJSON?.status,
|
||||
channel: "Uptime Kuma",
|
||||
icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${notification.longLivedAccessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return okMsg;
|
||||
|
|
|
@ -159,7 +159,7 @@ class Notification {
|
|||
new GovNotify(),
|
||||
];
|
||||
for (let item of list) {
|
||||
if (! item.name) {
|
||||
if (!item.name) {
|
||||
throw new Error("Notification provider without name");
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ class Notification {
|
|||
userID,
|
||||
]);
|
||||
|
||||
if (! bean) {
|
||||
if (!bean) {
|
||||
throw new Error("notification not found");
|
||||
}
|
||||
|
||||
|
@ -236,7 +236,7 @@ class Notification {
|
|||
userID,
|
||||
]);
|
||||
|
||||
if (! bean) {
|
||||
if (!bean) {
|
||||
throw new Error("notification not found");
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ class Notification {
|
|||
*/
|
||||
async function applyNotificationEveryMonitor(notificationID, userID) {
|
||||
let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
|
||||
userID
|
||||
userID,
|
||||
]);
|
||||
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
|
@ -272,7 +272,7 @@ async function applyNotificationEveryMonitor(notificationID, userID) {
|
|||
notificationID,
|
||||
]);
|
||||
|
||||
if (! checkNotification) {
|
||||
if (!checkNotification) {
|
||||
let relation = R.dispense("monitor_notification");
|
||||
relation.monitor_id = monitors[i].id;
|
||||
relation.notification_id = notificationID;
|
||||
|
|
|
@ -12,24 +12,24 @@ const commonLabels = [
|
|||
const monitorCertDaysRemaining = new PrometheusClient.Gauge({
|
||||
name: "monitor_cert_days_remaining",
|
||||
help: "The number of days remaining until the certificate expires",
|
||||
labelNames: commonLabels
|
||||
labelNames: commonLabels,
|
||||
});
|
||||
|
||||
const monitorCertIsValid = new PrometheusClient.Gauge({
|
||||
name: "monitor_cert_is_valid",
|
||||
help: "Is the certificate still valid? (1 = Yes, 0= No)",
|
||||
labelNames: commonLabels
|
||||
labelNames: commonLabels,
|
||||
});
|
||||
const monitorResponseTime = new PrometheusClient.Gauge({
|
||||
name: "monitor_response_time",
|
||||
help: "Monitor Response Time (ms)",
|
||||
labelNames: commonLabels
|
||||
labelNames: commonLabels,
|
||||
});
|
||||
|
||||
const monitorStatus = new PrometheusClient.Gauge({
|
||||
name: "monitor_status",
|
||||
help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)",
|
||||
labelNames: commonLabels
|
||||
labelNames: commonLabels,
|
||||
});
|
||||
|
||||
class Prometheus {
|
||||
|
@ -44,7 +44,7 @@ class Prometheus {
|
|||
monitor_type: monitor.type,
|
||||
monitor_url: monitor.url,
|
||||
monitor_hostname: monitor.hostname,
|
||||
monitor_port: monitor.port
|
||||
monitor_port: monitor.port,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -119,5 +119,5 @@ class Prometheus {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
Prometheus
|
||||
Prometheus,
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ class Proxy {
|
|||
if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) {
|
||||
throw new Error(`
|
||||
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
|
||||
*/
|
||||
static createAgents(proxy, options) {
|
||||
const { httpAgentOptions, httpsAgentOptions } = options || {};
|
||||
const {
|
||||
httpAgentOptions,
|
||||
httpsAgentOptions,
|
||||
} = options || {};
|
||||
let agent;
|
||||
let httpAgent;
|
||||
let httpsAgent;
|
||||
|
@ -150,12 +153,13 @@ class Proxy {
|
|||
httpsAgent = agent;
|
||||
break;
|
||||
|
||||
default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
|
||||
default:
|
||||
throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
|
||||
}
|
||||
|
||||
return {
|
||||
httpAgent,
|
||||
httpsAgent
|
||||
httpsAgent,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,15 @@ const { R } = require("redbean-node");
|
|||
const apicache = require("../modules/apicache");
|
||||
const Monitor = require("../model/monitor");
|
||||
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 { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||
const { makeBadge } = require("badge-maker");
|
||||
|
@ -28,7 +36,7 @@ let io = server.io;
|
|||
router.get("/api/entry-page", async (request, response) => {
|
||||
allowDevAllOrigin(response);
|
||||
|
||||
let result = { };
|
||||
let result = {};
|
||||
let hostname = request.hostname;
|
||||
if ((await setting("trustProxy")) && 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 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.");
|
||||
}
|
||||
|
||||
|
@ -127,7 +135,7 @@ router.all("/api/push/:pushToken", async (request, response) => {
|
|||
} catch (e) {
|
||||
response.status(404).json({
|
||||
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 public = 1
|
||||
`,
|
||||
[ requestedMonitorId ]
|
||||
[ requestedMonitorId ],
|
||||
);
|
||||
|
||||
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 public = 1
|
||||
`,
|
||||
[ requestedMonitorId ]
|
||||
[ requestedMonitorId ],
|
||||
);
|
||||
|
||||
const badgeValues = { style };
|
||||
|
@ -362,7 +370,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
|
|||
request.params.duration
|
||||
? parseInt(request.params.duration, 10)
|
||||
: 24,
|
||||
720
|
||||
720,
|
||||
);
|
||||
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 heartbeat.monitor_id = ?
|
||||
`,
|
||||
[ -requestedDuration, requestedMonitorId ]
|
||||
[ -requestedDuration, requestedMonitorId ],
|
||||
));
|
||||
|
||||
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 public = 1
|
||||
`,
|
||||
[ requestedMonitorId ]
|
||||
[ requestedMonitorId ],
|
||||
);
|
||||
|
||||
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 public = 1
|
||||
`,
|
||||
[ requestedMonitorId ]
|
||||
[ requestedMonitorId ],
|
||||
);
|
||||
|
||||
const badgeValues = { style };
|
||||
|
@ -540,7 +548,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
|
|||
badgeValues.color = badgeConstants.naColor;
|
||||
} else {
|
||||
const heartbeat = await Monitor.getPreviousHeartbeat(
|
||||
requestedMonitorId
|
||||
requestedMonitorId,
|
||||
);
|
||||
|
||||
if (!heartbeat.ping) {
|
||||
|
|
|
@ -2,7 +2,10 @@ let express = require("express");
|
|||
const apicache = require("../modules/apicache");
|
||||
const { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||
const StatusPage = require("../model/status_page");
|
||||
const { allowDevAllOrigin, sendHttpError } = require("../util-server");
|
||||
const {
|
||||
allowDevAllOrigin,
|
||||
sendHttpError,
|
||||
} = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const { badgeConstants } = require("../../src/util");
|
||||
const { makeBadge } = require("badge-maker");
|
||||
|
@ -44,7 +47,7 @@ router.get("/api/status-page/:slug", cache("5 minutes"), async (request, respons
|
|||
try {
|
||||
// Get Status Page
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
slug,
|
||||
]);
|
||||
|
||||
if (!statusPage) {
|
||||
|
@ -81,7 +84,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
|
|||
AND public = 1
|
||||
AND \`group\`.status_page_id = ?
|
||||
`, [
|
||||
statusPageID
|
||||
statusPageID,
|
||||
]);
|
||||
|
||||
for (let monitorID of monitorIDList) {
|
||||
|
@ -103,7 +106,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
|
|||
|
||||
response.json({
|
||||
heartbeatList,
|
||||
uptimeList
|
||||
uptimeList,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
|
@ -120,7 +123,7 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
|
|||
try {
|
||||
// Get Status Page
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
slug,
|
||||
]);
|
||||
|
||||
if (!statusPage) {
|
||||
|
@ -137,9 +140,9 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
|
|||
{
|
||||
"src": statusPage.icon,
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
"type": "image/png",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
|
@ -159,7 +162,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
|
|||
downColor = badgeConstants.defaultDownColor,
|
||||
partialColor = "#F6BE00",
|
||||
maintenanceColor = "#808080",
|
||||
style = badgeConstants.defaultStyle
|
||||
style = badgeConstants.defaultStyle,
|
||||
} = request.query;
|
||||
|
||||
try {
|
||||
|
@ -169,7 +172,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
|
|||
AND public = 1
|
||||
AND \`group\`.status_page_id = ?
|
||||
`, [
|
||||
statusPageID
|
||||
statusPageID,
|
||||
]);
|
||||
|
||||
let hasUp = false;
|
||||
|
|
|
@ -37,13 +37,19 @@ if (!semver.satisfies(nodeVersion, requiredNodeVersions)) {
|
|||
}
|
||||
|
||||
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");
|
||||
|
||||
log.debug("server", "Arguments");
|
||||
log.debug("server", args);
|
||||
|
||||
if (! process.env.NODE_ENV) {
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = "production";
|
||||
}
|
||||
|
||||
|
@ -90,7 +96,16 @@ const Monitor = require("./model/monitor");
|
|||
const User = require("./model/user");
|
||||
|
||||
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");
|
||||
|
||||
log.debug("server", "Importing Notification");
|
||||
|
@ -101,8 +116,14 @@ log.debug("server", "Importing Database");
|
|||
const Database = require("./database");
|
||||
|
||||
log.debug("server", "Importing Background Jobs");
|
||||
const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs");
|
||||
const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter");
|
||||
const {
|
||||
initBackgroundJobs,
|
||||
stopBackgroundJobs,
|
||||
} = require("./jobs");
|
||||
const {
|
||||
loginRateLimiter,
|
||||
twoFaRateLimiter,
|
||||
} = require("./rate-limiter");
|
||||
|
||||
const { apiAuth } = require("./auth");
|
||||
const { login } = require("./auth");
|
||||
|
@ -122,7 +143,7 @@ const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CL
|
|||
// 2FA / notp verification defaults
|
||||
const twoFAVerifyOptions = {
|
||||
"window": 1,
|
||||
"time": 30
|
||||
"time": 30,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -132,13 +153,26 @@ const twoFAVerifyOptions = {
|
|||
const testMode = !!args["test"] || false;
|
||||
|
||||
// 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 { databaseSocketHandler } = require("./socket-handlers/database-socket-handler");
|
||||
const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler");
|
||||
const TwoFA = require("./2fa");
|
||||
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 { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler");
|
||||
const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler");
|
||||
|
@ -933,8 +967,9 @@ let needSetup = false;
|
|||
monitorID,
|
||||
socket.userID,
|
||||
]);
|
||||
const monitorData = [{ id: monitor.id,
|
||||
active: monitor.active
|
||||
const monitorData = [ {
|
||||
id: monitor.id,
|
||||
active: monitor.active,
|
||||
}];
|
||||
const preloadData = await Monitor.preparePreloadData(monitorData);
|
||||
callback({
|
||||
|
@ -966,7 +1001,8 @@ let needSetup = false;
|
|||
SELECT *
|
||||
FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
AND time > ${sqlHourOffset}
|
||||
AND time
|
||||
> ${sqlHourOffset}
|
||||
ORDER BY time ASC
|
||||
`, [
|
||||
monitorID,
|
||||
|
@ -1519,7 +1555,7 @@ let needSetup = false;
|
|||
log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`);
|
||||
|
||||
await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [
|
||||
monitorID
|
||||
monitorID,
|
||||
]);
|
||||
|
||||
await sendHeartbeatList(socket, monitorID, true, true);
|
||||
|
@ -1658,7 +1694,7 @@ async function checkOwner(userID, monitorID) {
|
|||
userID,
|
||||
]);
|
||||
|
||||
if (! row) {
|
||||
if (!row) {
|
||||
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
|
||||
// It should be run once only
|
||||
if (! await Settings.get("initServerTimezone")) {
|
||||
if (!await Settings.get("initServerTimezone")) {
|
||||
log.debug("server", "emit initServerTimezone");
|
||||
socket.emit("initServerTimezone");
|
||||
}
|
||||
|
@ -1722,7 +1758,7 @@ async function initDatabase(testMode = false) {
|
|||
"jwtSecret",
|
||||
]);
|
||||
|
||||
if (! jwtSecretBean) {
|
||||
if (!jwtSecretBean) {
|
||||
log.info("server", "JWT secret is not found, generate one.");
|
||||
jwtSecretBean = await initJWTSecret();
|
||||
log.info("server", "Stored JWT secret into database");
|
||||
|
|
|
@ -17,9 +17,7 @@ class Settings {
|
|||
* }
|
||||
* @type {{}}
|
||||
*/
|
||||
static cacheList = {
|
||||
|
||||
};
|
||||
static cacheList = {};
|
||||
|
||||
static cacheCleaner = null;
|
||||
|
||||
|
@ -61,7 +59,7 @@ class Settings {
|
|||
|
||||
Settings.cacheList[key] = {
|
||||
value: v,
|
||||
timestamp: Date.now()
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
return v;
|
||||
|
@ -129,7 +127,7 @@ class Settings {
|
|||
|
||||
for (let key of keyList) {
|
||||
let bean = await R.findOne("setting", " `key` = ? ", [
|
||||
key
|
||||
key,
|
||||
]);
|
||||
|
||||
if (bean == null) {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const tcpp = require("tcp-ping");
|
||||
const ping = require("@louislam/ping");
|
||||
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 { Resolver } = require("dns");
|
||||
const iconv = require("iconv-lite");
|
||||
|
@ -22,14 +26,20 @@ const tls = require("tls");
|
|||
|
||||
const {
|
||||
dictionaries: {
|
||||
rfc2865: { file, attributes },
|
||||
rfc2865: {
|
||||
file,
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
} = require("node-radius-utils");
|
||||
const dayjs = require("dayjs");
|
||||
|
||||
// SASLOptions used in JSDoc
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { Kafka, SASLOptions } = require("kafkajs");
|
||||
const {
|
||||
Kafka,
|
||||
SASLOptions,
|
||||
} = require("kafkajs");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const isWindows = process.platform === /^win/.test(process.platform);
|
||||
|
@ -75,7 +85,7 @@ exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSe
|
|||
let client = new oauthProvider.Client({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
token_endpoint_auth_method: authMethod
|
||||
token_endpoint_auth_method: authMethod,
|
||||
});
|
||||
|
||||
// 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 = {}) {
|
||||
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;
|
||||
|
||||
|
@ -213,7 +228,7 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
|
|||
allowAutoTopicCreation: allowAutoTopicCreation,
|
||||
retry: {
|
||||
retries: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
producer.connect().then(
|
||||
|
@ -234,14 +249,14 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
|
|||
connectedToKafka = true;
|
||||
clearTimeout(timeoutID);
|
||||
});
|
||||
}
|
||||
},
|
||||
).catch(
|
||||
(e) => {
|
||||
connectedToKafka = true;
|
||||
producer.disconnect();
|
||||
clearTimeout(timeoutID);
|
||||
reject(new Error("Error in producer connection: " + e.message));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
producer.on("producer.network.request_timeout", (_) => {
|
||||
|
@ -409,7 +424,7 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) {
|
|||
return new Promise((resolve, reject) => {
|
||||
const connection = mysql.createConnection({
|
||||
uri: connectionString,
|
||||
password
|
||||
password,
|
||||
});
|
||||
|
||||
connection.on("error", (err) => {
|
||||
|
@ -494,8 +509,8 @@ exports.redisPingAsync = function (dsn, rejectUnauthorized) {
|
|||
const client = redis.createClient({
|
||||
url: dsn,
|
||||
socket: {
|
||||
rejectUnauthorized
|
||||
}
|
||||
rejectUnauthorized,
|
||||
},
|
||||
});
|
||||
client.on("error", (err) => {
|
||||
if (client.isOpen) {
|
||||
|
@ -661,7 +676,7 @@ exports.checkCertificate = function (socket) {
|
|||
|
||||
return {
|
||||
valid: valid,
|
||||
certInfo: parsedInfo
|
||||
certInfo: parsedInfo,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -693,7 +708,7 @@ exports.checkStatusCode = function (status, acceptedCodes) {
|
|||
}
|
||||
} else {
|
||||
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
|
||||
*/
|
||||
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 protoServiceObject = protocObject.root.lookupService(grpcServiceName);
|
||||
const Client = grpc.makeGenericClientConstructor({});
|
||||
const credentials = grpcEnableTls ? grpc.credentials.createSsl() : grpc.credentials.createInsecure();
|
||||
const client = new Client(
|
||||
grpcUrl,
|
||||
credentials
|
||||
credentials,
|
||||
);
|
||||
const grpcService = protoServiceObject.create(function (method, requestData, cb) {
|
||||
const fullServiceName = method.fullName;
|
||||
|
@ -955,14 +977,14 @@ module.exports.grpcQuery = async (options) => {
|
|||
return resolve({
|
||||
code: err.code,
|
||||
errorMessage: err.details,
|
||||
data: ""
|
||||
data: "",
|
||||
});
|
||||
} else {
|
||||
log.debug("monitor:", `gRPC response: ${JSON.stringify(response)}`);
|
||||
return resolve({
|
||||
code: 1,
|
||||
errorMessage: "",
|
||||
data: responseData
|
||||
data: responseData,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -970,7 +992,7 @@ module.exports.grpcQuery = async (options) => {
|
|||
return resolve({
|
||||
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`,
|
||||
data: ""
|
||||
data: "",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -81,5 +81,5 @@ class ArrayWithKey {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
ArrayWithKey
|
||||
ArrayWithKey,
|
||||
};
|
||||
|
|
|
@ -44,5 +44,5 @@ class LimitQueue extends ArrayWithKey {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
LimitQueue
|
||||
LimitQueue,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue