Compare commits

...

4 Commits

30 changed files with 483 additions and 411 deletions

View File

@@ -1,6 +1,7 @@
const basicAuth = require("express-basic-auth"); const basicAuth = require("express-basic-auth");
const passwordHash = require("./password-hash"); const passwordHash = require("./password-hash");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
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");
@@ -138,7 +139,7 @@ exports.basicAuth = async function (req, res, next) {
challenge: true, challenge: true,
}); });
const disabledAuth = await Settings.get("disableAuth"); const disabledAuth = await setting("disableAuth");
if (!disabledAuth) { if (!disabledAuth) {
middleware(req, res, next); middleware(req, res, next);

View File

@@ -1,7 +1,7 @@
const { setSetting, setting } = require("./util-server");
const axios = require("axios"); const axios = require("axios");
const compareVersions = require("compare-versions"); const compareVersions = require("compare-versions");
const { log } = require("../src/util"); const { log } = require("../src/util");
const { Settings } = require("./settings");
exports.version = require("../package.json").version; exports.version = require("../package.json").version;
exports.latestVersion = null; exports.latestVersion = null;
@@ -14,7 +14,7 @@ let interval;
exports.startInterval = () => { exports.startInterval = () => {
let check = async () => { let check = async () => {
if (await Settings.get("checkUpdate") === false) { if (await setting("checkUpdate") === false) {
return; return;
} }
@@ -28,7 +28,7 @@ exports.startInterval = () => {
res.data.slow = "1000.0.0"; res.data.slow = "1000.0.0";
} }
let checkBeta = await Settings.get("checkBeta"); let checkBeta = await setting("checkBeta");
if (checkBeta && res.data.beta) { if (checkBeta && res.data.beta) {
if (compareVersions.compare(res.data.beta, res.data.slow, ">")) { if (compareVersions.compare(res.data.beta, res.data.slow, ">")) {
@@ -57,7 +57,7 @@ exports.startInterval = () => {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
exports.enableCheckUpdate = async (value) => { exports.enableCheckUpdate = async (value) => {
await Settings.set("checkUpdate", value); await setSetting("checkUpdate", value);
clearInterval(interval); clearInterval(interval);

View File

@@ -6,8 +6,8 @@ const { R } = require("redbean-node");
const { UptimeKumaServer } = require("./uptime-kuma-server"); const { UptimeKumaServer } = require("./uptime-kuma-server");
const server = UptimeKumaServer.getInstance(); const server = UptimeKumaServer.getInstance();
const io = server.io; const io = server.io;
const { setting } = require("./util-server");
const checkVersion = require("./check-version"); const checkVersion = require("./check-version");
const { Settings } = require("./settings");
const Database = require("./database"); const Database = require("./database");
/** /**
@@ -158,8 +158,8 @@ async function sendInfo(socket, hideVersion = false) {
version, version,
latestVersion, latestVersion,
isContainer, isContainer,
primaryBaseURL: await Settings.get("primaryBaseURL"),
dbType, dbType,
primaryBaseURL: await setting("primaryBaseURL"),
serverTimezone: await server.getTimezone(), serverTimezone: await server.getTimezone(),
serverTimezoneOffset: server.getTimezoneOffset(), serverTimezoneOffset: server.getTimezoneOffset(),
}); });

View File

@@ -1,11 +1,11 @@
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 { log, sleep } = require("../src/util"); 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");
const mysql = require("mysql2/promise"); const mysql = require("mysql2/promise");
const { Settings } = require("./settings");
/** /**
* Database & App Data Folder * Database & App Data Folder
@@ -420,7 +420,7 @@ class Database {
* @deprecated * @deprecated
*/ */
static async patchSqlite() { static async patchSqlite() {
let version = parseInt(await Settings.get("database_version")); let version = parseInt(await setting("database_version"));
if (! version) { if (! version) {
version = 0; version = 0;
@@ -445,7 +445,7 @@ class Database {
log.info("db", `Patching ${sqlFile}`); log.info("db", `Patching ${sqlFile}`);
await Database.importSQLFile(sqlFile); await Database.importSQLFile(sqlFile);
log.info("db", `Patched ${sqlFile}`); log.info("db", `Patched ${sqlFile}`);
await Settings.set("database_version", i); await setSetting("database_version", i);
} }
} catch (ex) { } catch (ex) {
await Database.close(); await Database.close();
@@ -471,7 +471,7 @@ class Database {
*/ */
static async patchSqlite2() { static async patchSqlite2() {
log.debug("db", "Database Patch 2.0 Process"); log.debug("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await Settings.get("databasePatchedFiles"); let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) { if (! databasePatchedFiles) {
databasePatchedFiles = {}; databasePatchedFiles = {};
@@ -499,7 +499,7 @@ class Database {
process.exit(1); process.exit(1);
} }
await Settings.set("databasePatchedFiles", databasePatchedFiles); await setSetting("databasePatchedFiles", databasePatchedFiles);
} }
/** /**
@@ -512,27 +512,27 @@ class Database {
// Fix 1.13.0 empty slug bug // Fix 1.13.0 empty slug bug
await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''"); await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''");
let title = await Settings.get("title"); let title = await setting("title");
if (title) { if (title) {
log.info("database", "Migrating Status Page"); console.log("Migrating Status Page");
let statusPageCheck = await R.findOne("status_page", " slug = 'default' "); let statusPageCheck = await R.findOne("status_page", " slug = 'default' ");
if (statusPageCheck !== null) { if (statusPageCheck !== null) {
log.info("database", "Migrating Status Page - Skip, default slug record is already existing"); console.log("Migrating Status Page - Skip, default slug record is already existing");
return; return;
} }
let statusPage = R.dispense("status_page"); let statusPage = R.dispense("status_page");
statusPage.slug = "default"; statusPage.slug = "default";
statusPage.title = title; statusPage.title = title;
statusPage.description = await Settings.get("description"); statusPage.description = await setting("description");
statusPage.icon = await Settings.get("icon"); statusPage.icon = await setting("icon");
statusPage.theme = await Settings.get("statusPageTheme"); statusPage.theme = await setting("statusPageTheme");
statusPage.published = !!await Settings.get("statusPagePublished"); statusPage.published = !!await setting("statusPagePublished");
statusPage.search_engine_index = !!await Settings.get("searchEngineIndex"); statusPage.search_engine_index = !!await setting("searchEngineIndex");
statusPage.show_tags = !!await Settings.get("statusPageTags"); statusPage.show_tags = !!await setting("statusPageTags");
statusPage.password = null; statusPage.password = null;
if (!statusPage.title) { if (!statusPage.title) {
@@ -560,13 +560,13 @@ class Database {
await R.exec("DELETE FROM setting WHERE type = 'statusPage'"); await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
// Migrate Entry Page if it is status page // Migrate Entry Page if it is status page
let entryPage = await Settings.get("entryPage"); let entryPage = await setting("entryPage");
if (entryPage === "statusPage") { if (entryPage === "statusPage") {
await Settings.set("entryPage", "statusPage-default", "general"); await setSetting("entryPage", "statusPage-default", "general");
} }
log.info("database", "Migrating Status Page - Done"); console.log("Migrating Status Page - Done");
} }
} }

View File

@@ -1,7 +1,7 @@
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { log } = require("../../src/util"); const { log } = require("../../src/util");
const { setSetting, setting } = require("../util-server");
const Database = require("../database"); const Database = require("../database");
const { Settings } = require("../settings");
const DEFAULT_KEEP_PERIOD = 180; const DEFAULT_KEEP_PERIOD = 180;
@@ -11,11 +11,11 @@ const DEFAULT_KEEP_PERIOD = 180;
*/ */
const clearOldData = async () => { const clearOldData = async () => {
let period = await Settings.get("keepDataPeriodDays"); let period = await setting("keepDataPeriodDays");
// Set Default Period // Set Default Period
if (period == null) { if (period == null) {
await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
period = DEFAULT_KEEP_PERIOD; period = DEFAULT_KEEP_PERIOD;
} }
@@ -25,7 +25,7 @@ const clearOldData = async () => {
parsedPeriod = parseInt(period); parsedPeriod = parseInt(period);
} catch (_) { } catch (_) {
log.warn("clearOldData", "Failed to parse setting, resetting to default.."); log.warn("clearOldData", "Failed to parse setting, resetting to default..");
await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
parsedPeriod = DEFAULT_KEEP_PERIOD; parsedPeriod = DEFAULT_KEEP_PERIOD;
} }

View File

@@ -4,7 +4,7 @@ const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
SQL_DATETIME_FORMAT, evaluateJsonQuery SQL_DATETIME_FORMAT, evaluateJsonQuery
} = require("../../src/util"); } = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, mssqlQuery, postgresQuery, mysqlQuery, httpNtlm, radius, grpcQuery, const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
} = require("../util-server"); } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
@@ -24,7 +24,6 @@ const { CookieJar } = require("tough-cookie");
const { HttpsCookieAgent } = require("http-cookie-agent/http"); const { HttpsCookieAgent } = require("http-cookie-agent/http");
const https = require("https"); const https = require("https");
const http = require("http"); const http = require("http");
const { Settings } = require("../settings");
const rootCertificates = rootCertificatesFingerprints(); const rootCertificates = rootCertificatesFingerprints();
@@ -326,7 +325,7 @@ class Monitor extends BeanModel {
let previousBeat = null; let previousBeat = null;
let retries = 0; let retries = 0;
this.prometheus = await Prometheus.createAndInitMetrics(this); this.prometheus = new Prometheus(this);
const beat = async () => { const beat = async () => {
@@ -652,7 +651,7 @@ class Monitor extends BeanModel {
} else if (this.type === "steam") { } else if (this.type === "steam") {
const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/"; const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/";
const steamAPIKey = await Settings.get("steamAPIKey"); const steamAPIKey = await setting("steamAPIKey");
const filter = `addr\\${this.hostname}:${this.port}`; const filter = `addr\\${this.hostname}:${this.port}`;
if (!steamAPIKey) { if (!steamAPIKey) {
@@ -978,7 +977,7 @@ class Monitor extends BeanModel {
await R.store(bean); await R.store(bean);
log.debug("monitor", `[${this.name}] prometheus.update`); log.debug("monitor", `[${this.name}] prometheus.update`);
await this.prometheus?.update(bean, tlsInfo); this.prometheus?.update(bean, tlsInfo);
previousBeat = bean; previousBeat = bean;
@@ -1374,12 +1373,11 @@ class Monitor extends BeanModel {
return; return;
} }
let notifyDays = await Settings.get("tlsExpiryNotifyDays"); let notifyDays = await setting("tlsExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) { if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default // Reset Default
await Settings.set("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general"); await setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general");
notifyDays = [ 7, 14, 21 ]; notifyDays = [ 7, 14, 21 ];
await Settings.set("tlsExpiryNotifyDays", notifyDays, "general");
} }
if (Array.isArray(notifyDays)) { if (Array.isArray(notifyDays)) {

View File

@@ -1,6 +1,5 @@
let url = require("url"); let url = require("url");
let MemoryCache = require("./memory-cache"); let MemoryCache = require("./memory-cache");
const { log } = require("../../../src/util");
let t = { let t = {
ms: 1, ms: 1,
@@ -91,6 +90,24 @@ function ApiCache() {
instances.push(this); instances.push(this);
this.id = instances.length; this.id = instances.length;
/**
* Logs a message to the console if the `DEBUG` environment variable is set.
* @param {string} a The first argument to log.
* @param {string} b The second argument to log.
* @param {string} c The third argument to log.
* @param {string} d The fourth argument to log, and so on... (optional)
*
* Generated by Trelent
*/
function debug(a, b, c, d) {
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;
return (globalOptions.debug || debugEnv) && console.log.apply(null, arr);
}
/** /**
* Returns true if the given request and response should be logged. * Returns true if the given request and response should be logged.
* @param {Object} request The HTTP request object. * @param {Object} request The HTTP request object.
@@ -129,7 +146,7 @@ function ApiCache() {
let groupName = req.apicacheGroup; let groupName = req.apicacheGroup;
if (groupName) { if (groupName) {
log.debug("apicache", `group detected "${groupName}"`); debug("group detected \"" + groupName + "\"");
let group = (index.groups[groupName] = index.groups[groupName] || []); let group = (index.groups[groupName] = index.groups[groupName] || []);
group.unshift(key); group.unshift(key);
} }
@@ -195,7 +212,7 @@ function ApiCache() {
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) {
log.debug("apicache", `error in redis.hset(): ${err}`); debug("[apicache] error in redis.hset()");
} }
} else { } else {
memCache.add(key, value, duration, expireCallback); memCache.add(key, value, duration, expireCallback);
@@ -303,10 +320,10 @@ function ApiCache() {
// display log entry // display log entry
let elapsed = new Date() - req.apicacheTimer; let elapsed = new Date() - req.apicacheTimer;
log.debug("apicache", `adding cache entry for "${key}" @ ${strDuration} ${logDuration(elapsed)}`); debug("adding cache entry for \"" + key + "\" @ " + strDuration, logDuration(elapsed));
log.debug("apicache", `_apicache.headers: ${JSON.stringify(res._apicache.headers)}`); debug("_apicache.headers: ", res._apicache.headers);
log.debug("apicache", `res.getHeaders(): ${JSON.stringify(getSafeHeaders(res))}`); debug("res.getHeaders(): ", getSafeHeaders(res));
log.debug("apicache", `cacheObject: ${JSON.stringify(cacheObject)}`); debug("cacheObject: ", cacheObject);
} }
} }
@@ -385,10 +402,10 @@ function ApiCache() {
let redis = globalOptions.redisClient; let redis = globalOptions.redisClient;
if (group) { if (group) {
log.debug("apicache", `clearing group "${target}"`); debug("clearing group \"" + target + "\"");
group.forEach(function (key) { group.forEach(function (key) {
log.debug("apicache", `clearing cached entry for "${key}"`); debug("clearing cached entry for \"" + key + "\"");
clearTimeout(timers[key]); clearTimeout(timers[key]);
delete timers[key]; delete timers[key];
if (!globalOptions.redisClient) { if (!globalOptions.redisClient) {
@@ -397,7 +414,7 @@ function ApiCache() {
try { try {
redis.del(key); redis.del(key);
} catch (err) { } catch (err) {
log.info("apicache", "error in redis.del(\"" + key + "\")"); console.log("[apicache] error in redis.del(\"" + key + "\")");
} }
} }
index.all = index.all.filter(doesntMatch(key)); index.all = index.all.filter(doesntMatch(key));
@@ -405,7 +422,7 @@ function ApiCache() {
delete index.groups[target]; delete index.groups[target];
} else if (target) { } else if (target) {
log.debug("apicache", `clearing ${isAutomatic ? "expired" : "cached"} entry for "${target}"`); debug("clearing " + (isAutomatic ? "expired" : "cached") + " entry for \"" + target + "\"");
clearTimeout(timers[target]); clearTimeout(timers[target]);
delete timers[target]; delete timers[target];
// clear actual cached entry // clear actual cached entry
@@ -415,7 +432,7 @@ function ApiCache() {
try { try {
redis.del(target); redis.del(target);
} catch (err) { } catch (err) {
log.error("apicache", "error in redis.del(\"" + target + "\")"); console.log("[apicache] error in redis.del(\"" + target + "\")");
} }
} }
@@ -432,7 +449,7 @@ function ApiCache() {
} }
}); });
} else { } else {
log.debug("apicache", "clearing entire index"); debug("clearing entire index");
if (!redis) { if (!redis) {
memCache.clear(); memCache.clear();
@@ -444,7 +461,7 @@ function ApiCache() {
try { try {
redis.del(key); redis.del(key);
} catch (err) { } catch (err) {
log.error("apicache", `error in redis.del("${key}"): ${err}`); console.log("[apicache] error in redis.del(\"" + key + "\")");
} }
}); });
} }
@@ -511,7 +528,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) {
@@ -526,9 +543,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);
@@ -735,7 +752,7 @@ function ApiCache() {
*/ */
let cache = function (req, res, next) { let cache = function (req, res, next) {
function bypass() { function bypass() {
log.debug("apicache", "bypass detected, skipping cache."); debug("bypass detected, skipping cache.");
return next(); return next();
} }
@@ -788,7 +805,7 @@ function ApiCache() {
// send if cache hit from memory-cache // send if cache hit from memory-cache
if (cached) { if (cached) {
let elapsed = new Date() - req.apicacheTimer; let elapsed = new Date() - req.apicacheTimer;
log.debug("apicache", `sending cached (memory-cache) version of ${key} ${logDuration(elapsed)}`); debug("sending cached (memory-cache) version of", key, logDuration(elapsed));
perf.hit(key); perf.hit(key);
return sendCachedResponse(req, res, cached, middlewareToggle, next, duration); return sendCachedResponse(req, res, cached, middlewareToggle, next, duration);
@@ -800,7 +817,7 @@ function ApiCache() {
redis.hgetall(key, function (err, obj) { redis.hgetall(key, function (err, obj) {
if (!err && obj && obj.response) { if (!err && obj && obj.response) {
let elapsed = new Date() - req.apicacheTimer; let elapsed = new Date() - req.apicacheTimer;
log.debug("apicache", "sending cached (redis) version of "+ key+" "+ logDuration(elapsed)); debug("sending cached (redis) version of", key, logDuration(elapsed));
perf.hit(key); perf.hit(key);
return sendCachedResponse( return sendCachedResponse(
@@ -842,7 +859,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) {
@@ -856,7 +873,7 @@ function ApiCache() {
} }
if (globalOptions.trackPerformance) { if (globalOptions.trackPerformance) {
log.debug("apicache", "WARNING: using trackPerformance flag can cause high memory usage!"); debug("WARNING: using trackPerformance flag can cause high memory usage!");
} }
return this; return this;

View File

@@ -63,7 +63,7 @@ if (process.platform === "win32") {
* @returns {Promise<boolean>} The executable is allowed? * @returns {Promise<boolean>} The executable is allowed?
*/ */
async function isAllowedChromeExecutable(executablePath) { async function isAllowedChromeExecutable(executablePath) {
log.info("Chromium", config.args); console.log(config.args);
if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") { if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") {
return true; return true;
} }
@@ -102,8 +102,7 @@ async function getBrowser() {
*/ */
async function getRemoteBrowser(remoteBrowserID, userId) { async function getRemoteBrowser(remoteBrowserID, userId) {
let remoteBrowser = await RemoteBrowser.get(remoteBrowserID, userId); let remoteBrowser = await RemoteBrowser.get(remoteBrowserID, userId);
log.debug("Chromium", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`); log.debug("MONITOR", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`);
browser = chromium.connect(remoteBrowser.url);
browser = await chromium.connect(remoteBrowser.url); browser = await chromium.connect(remoteBrowser.url);
return browser; return browser;
} }

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util"); const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
const { Settings } = require("../settings");
class AlertNow extends NotificationProvider { class AlertNow extends NotificationProvider {
name = "AlertNow"; name = "AlertNow";
@@ -29,7 +29,7 @@ class AlertNow extends NotificationProvider {
textMsg += ` - ${msg}`; textMsg += ` - ${msg}`;
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) { if (baseURL && monitorJSON) {
textMsg += ` >> ${baseURL + getMonitorRelativeURL(monitorJSON.id)}`; textMsg += ` >> ${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
} }

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { Settings } = require("../settings"); const { setting } = require("../util-server");
const successMessage = "Sent Successfully."; const successMessage = "Sent Successfully.";
class FlashDuty extends NotificationProvider { class FlashDuty extends NotificationProvider {
@@ -84,7 +84,7 @@ class FlashDuty extends NotificationProvider {
} }
}; };
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorInfo) { if (baseURL && monitorInfo) {
options.client = "Uptime Kuma"; options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP } = require("../../src/util"); const { getMonitorRelativeURL, UP } = require("../../src/util");
const { Settings } = require("../settings");
class GoogleChat extends NotificationProvider { class GoogleChat extends NotificationProvider {
name = "GoogleChat"; name = "GoogleChat";
@@ -45,7 +45,7 @@ class GoogleChat extends NotificationProvider {
} }
// add button for monitor link if available // add button for monitor link if available
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL) { if (baseURL) {
const urlPath = monitorJSON ? getMonitorRelativeURL(monitorJSON.id) : "/"; const urlPath = monitorJSON ? getMonitorRelativeURL(monitorJSON.id) : "/";
sectionWidgets.push({ sectionWidgets.push({

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { Settings } = require("../settings"); const { setting } = require("../util-server");
let successMessage = "Sent Successfully."; let successMessage = "Sent Successfully.";
class PagerDuty extends NotificationProvider { class PagerDuty extends NotificationProvider {
@@ -95,7 +95,7 @@ class PagerDuty extends NotificationProvider {
} }
}; };
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorInfo) { if (baseURL && monitorInfo) {
options.client = "Uptime Kuma"; options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { Settings } = require("../settings"); const { setting } = require("../util-server");
let successMessage = "Sent Successfully."; let successMessage = "Sent Successfully.";
class PagerTree extends NotificationProvider { class PagerTree extends NotificationProvider {
@@ -74,7 +74,7 @@ class PagerTree extends NotificationProvider {
} }
}; };
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) { if (baseURL && monitorJSON) {
options.client = "Uptime Kuma"; options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorJSON.id); options.client_url = baseURL + getMonitorRelativeURL(monitorJSON.id);

View File

@@ -1,8 +1,8 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const Slack = require("./slack"); const Slack = require("./slack");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, DOWN } = require("../../src/util"); const { getMonitorRelativeURL, DOWN } = require("../../src/util");
const { Settings } = require("../settings");
class RocketChat extends NotificationProvider { class RocketChat extends NotificationProvider {
name = "rocket.chat"; name = "rocket.chat";
@@ -49,7 +49,7 @@ class RocketChat extends NotificationProvider {
await Slack.deprecateURL(notification.rocketbutton); await Slack.deprecateURL(notification.rocketbutton);
} }
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL) { if (baseURL) {
data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id); data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id);

View File

@@ -1,8 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { setSettings, setting } = require("../util-server");
const { getMonitorRelativeURL, UP } = require("../../src/util"); const { getMonitorRelativeURL, UP } = require("../../src/util");
const { Settings } = require("../settings");
const { log } = require("../../src/util");
class Slack extends NotificationProvider { class Slack extends NotificationProvider {
name = "slack"; name = "slack";
@@ -15,13 +14,15 @@ class Slack extends NotificationProvider {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async deprecateURL(url) { static async deprecateURL(url) {
let currentPrimaryBaseURL = await Settings.get("primaryBaseURL"); let currentPrimaryBaseURL = await setting("primaryBaseURL");
if (!currentPrimaryBaseURL) { if (!currentPrimaryBaseURL) {
log.error("notification", "Move the url to be the primary base URL"); console.log("Move the url to be the primary base URL");
await Settings.set("primaryBaseURL", url, "general"); await setSettings("general", {
primaryBaseURL: url,
});
} else { } else {
log.debug("notification", "Already there, no need to move the primary base URL"); console.log("Already there, no need to move the primary base URL");
} }
} }
@@ -135,7 +136,7 @@ class Slack extends NotificationProvider {
return okMsg; return okMsg;
} }
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
const title = "Uptime Kuma Alert"; const title = "Uptime Kuma Alert";
let data = { let data = {

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { Settings } = require("../settings"); const { setting } = require("../util-server");
let successMessage = "Sent Successfully."; let successMessage = "Sent Successfully.";
class Splunk extends NotificationProvider { class Splunk extends NotificationProvider {
@@ -95,7 +95,7 @@ class Splunk extends NotificationProvider {
} }
}; };
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorInfo) { if (baseURL && monitorInfo) {
options.client = "Uptime Kuma"; options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL } = require("../../src/util"); const { getMonitorRelativeURL } = require("../../src/util");
const { Settings } = require("../settings");
class Stackfield extends NotificationProvider { class Stackfield extends NotificationProvider {
name = "stackfield"; name = "stackfield";
@@ -23,7 +23,7 @@ class Stackfield extends NotificationProvider {
textMsg += `\n${msg}`; textMsg += `\n${msg}`;
const baseURL = await Settings.get("primaryBaseURL"); const baseURL = await setting("primaryBaseURL");
if (baseURL) { if (baseURL) {
textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`; textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
} }

View File

@@ -1,4 +1,3 @@
const { R } = require("redbean-node");
const PrometheusClient = require("prom-client"); const PrometheusClient = require("prom-client");
const { log } = require("../src/util"); const { log } = require("../src/util");
@@ -10,102 +9,36 @@ const commonLabels = [
"monitor_port", "monitor_port",
]; ];
const monitorCertDaysRemaining = new PrometheusClient.Gauge({
name: "monitor_cert_days_remaining",
help: "The number of days remaining until the certificate expires",
labelNames: commonLabels
});
const monitorCertIsValid = new PrometheusClient.Gauge({
name: "monitor_cert_is_valid",
help: "Is the certificate still valid? (1 = Yes, 0= No)",
labelNames: commonLabels
});
const monitorResponseTime = new PrometheusClient.Gauge({
name: "monitor_response_time",
help: "Monitor Response Time (ms)",
labelNames: commonLabels
});
const monitorStatus = new PrometheusClient.Gauge({
name: "monitor_status",
help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)",
labelNames: commonLabels
});
class Prometheus { class Prometheus {
monitorLabelValues = {};
/** /**
* Metric: monitor_cert_days_remaining * @param {object} monitor Monitor object to monitor
* @type {PrometheusClient.Gauge<string> | null}
*/ */
static monitorCertDaysRemaining = null; constructor(monitor) {
/**
* Metric: monitor_cert_is_valid
* @type {PrometheusClient.Gauge<string> | null}
*/
static monitorCertIsValid = null;
/**
* Metric: monitor_response_time
* @type {PrometheusClient.Gauge<string> | null}
*/
static monitorResponseTime = null;
/**
* Metric: monitor_status
* @type {PrometheusClient.Gauge<string> | null}
*/
static monitorStatus = null;
/**
* All registered metric labels.
* @type {string[] | null}
*/
static monitorLabelNames = null;
/**
* Monitor labels/values combination.
* @type {{}}
*/
monitorLabelValues;
/**
* Initialize metrics and get all label names the first time called.
* @returns {void}
*/
static async initMetrics() {
if (!this.monitorLabelNames) {
let labelNames = await R.getCol("SELECT name FROM tag");
this.monitorLabelNames = [ ...commonLabels, ...labelNames ];
}
if (!this.monitorCertDaysRemaining) {
this.monitorCertDaysRemaining = new PrometheusClient.Gauge({
name: "monitor_cert_days_remaining",
help: "The number of days remaining until the certificate expires",
labelNames: this.monitorLabelNames
});
}
if (!this.monitorCertIsValid) {
this.monitorCertIsValid = new PrometheusClient.Gauge({
name: "monitor_cert_is_valid",
help: "Is the certificate still valid? (1 = Yes, 0 = No)",
labelNames: this.monitorLabelNames
});
}
if (!this.monitorResponseTime) {
this.monitorResponseTime = new PrometheusClient.Gauge({
name: "monitor_response_time",
help: "Monitor Response Time (ms)",
labelNames: this.monitorLabelNames
});
}
if (!this.monitorStatus) {
this.monitorStatus = new PrometheusClient.Gauge({
name: "monitor_status",
help: "Monitor Status (1 = UP, 0 = DOWN, 2 = PENDING, 3 = MAINTENANCE)",
labelNames: this.monitorLabelNames
});
}
}
/**
* Wrapper to create a `Prometheus` instance and ensure metrics are initialized.
* @param {Monitor} monitor Monitor object to monitor
* @returns {Promise<Prometheus>} `Prometheus` instance
*/
static async createAndInitMetrics(monitor) {
await Prometheus.initMetrics();
let tags = await monitor.getTags();
return new Prometheus(monitor, tags);
}
/**
* Creates a prometheus metric instance.
*
* Note: Make sure to call `Prometheus.initMetrics()` once prior creating Prometheus instances.
* @param {Monitor} monitor Monitor object to monitor
* @param {Promise<LooseObject<any>[]>} tags Tags of the monitor
*/
constructor(monitor, tags) {
this.monitorLabelValues = { this.monitorLabelValues = {
monitor_name: monitor.name, monitor_name: monitor.name,
monitor_type: monitor.type, monitor_type: monitor.type,
@@ -113,12 +46,6 @@ class Prometheus {
monitor_hostname: monitor.hostname, monitor_hostname: monitor.hostname,
monitor_port: monitor.port monitor_port: monitor.port
}; };
Object.values(tags)
// only label names that were known at first metric creation.
.filter(tag => Prometheus.monitorLabelNames.includes(tag.name))
.forEach(tag => {
this.monitorLabelValues[tag.name] = tag.value;
});
} }
/** /**
@@ -128,6 +55,7 @@ class Prometheus {
* @returns {void} * @returns {void}
*/ */
update(heartbeat, tlsInfo) { update(heartbeat, tlsInfo) {
if (typeof tlsInfo !== "undefined") { if (typeof tlsInfo !== "undefined") {
try { try {
let isValid; let isValid;
@@ -136,7 +64,7 @@ class Prometheus {
} else { } else {
isValid = 0; isValid = 0;
} }
Prometheus.monitorCertIsValid.set(this.monitorLabelValues, isValid); monitorCertIsValid.set(this.monitorLabelValues, isValid);
} catch (e) { } catch (e) {
log.error("prometheus", "Caught error"); log.error("prometheus", "Caught error");
log.error("prometheus", e); log.error("prometheus", e);
@@ -144,7 +72,7 @@ class Prometheus {
try { try {
if (tlsInfo.certInfo != null) { if (tlsInfo.certInfo != null) {
Prometheus.monitorCertDaysRemaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining); monitorCertDaysRemaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining);
} }
} catch (e) { } catch (e) {
log.error("prometheus", "Caught error"); log.error("prometheus", "Caught error");
@@ -154,7 +82,7 @@ class Prometheus {
if (heartbeat) { if (heartbeat) {
try { try {
Prometheus.monitorStatus.set(this.monitorLabelValues, heartbeat.status); monitorStatus.set(this.monitorLabelValues, heartbeat.status);
} catch (e) { } catch (e) {
log.error("prometheus", "Caught error"); log.error("prometheus", "Caught error");
log.error("prometheus", e); log.error("prometheus", e);
@@ -162,10 +90,10 @@ class Prometheus {
try { try {
if (typeof heartbeat.ping === "number") { if (typeof heartbeat.ping === "number") {
Prometheus.monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping); monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping);
} else { } else {
// Is it good? // Is it good?
Prometheus.monitorResponseTime.set(this.monitorLabelValues, -1); monitorResponseTime.set(this.monitorLabelValues, -1);
} }
} catch (e) { } catch (e) {
log.error("prometheus", "Caught error"); log.error("prometheus", "Caught error");
@@ -180,10 +108,10 @@ class Prometheus {
*/ */
remove() { remove() {
try { try {
Prometheus.monitorCertDaysRemaining?.remove(this.monitorLabelValues); monitorCertDaysRemaining.remove(this.monitorLabelValues);
Prometheus.monitorCertIsValid?.remove(this.monitorLabelValues); monitorCertIsValid.remove(this.monitorLabelValues);
Prometheus.monitorResponseTime?.remove(this.monitorLabelValues); monitorResponseTime.remove(this.monitorLabelValues);
Prometheus.monitorStatus?.remove(this.monitorLabelValues); monitorStatus.remove(this.monitorLabelValues);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }

View File

@@ -2,7 +2,7 @@ const { R } = require("redbean-node");
const HttpProxyAgent = require("http-proxy-agent"); const HttpProxyAgent = require("http-proxy-agent");
const HttpsProxyAgent = require("https-proxy-agent"); const HttpsProxyAgent = require("https-proxy-agent");
const SocksProxyAgent = require("socks-proxy-agent"); const SocksProxyAgent = require("socks-proxy-agent");
const { log } = require("../src/util"); const { debug } = require("../src/util");
const { UptimeKumaServer } = require("./uptime-kuma-server"); const { UptimeKumaServer } = require("./uptime-kuma-server");
const { CookieJar } = require("tough-cookie"); const { CookieJar } = require("tough-cookie");
const { createCookieAgent } = require("http-cookie-agent/http"); const { createCookieAgent } = require("http-cookie-agent/http");
@@ -110,9 +110,9 @@ class Proxy {
proxyOptions.auth = `${proxy.username}:${proxy.password}`; proxyOptions.auth = `${proxy.username}:${proxy.password}`;
} }
log.debug("update-proxy", `Proxy Options: ${JSON.stringify(proxyOptions)}`); debug(`Proxy Options: ${JSON.stringify(proxyOptions)}`);
log.debug("update-proxy", `HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`); debug(`HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`);
log.debug("update-proxy", `HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`); debug(`HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`);
switch (proxy.protocol) { switch (proxy.protocol) {
case "http": case "http":

View File

@@ -1,5 +1,6 @@
let express = require("express"); let express = require("express");
const { const {
setting,
allowDevAllOrigin, allowDevAllOrigin,
allowAllOrigin, allowAllOrigin,
percentageToColor, percentageToColor,
@@ -17,7 +18,6 @@ const { makeBadge } = require("badge-maker");
const { Prometheus } = require("../prometheus"); const { Prometheus } = require("../prometheus");
const Database = require("../database"); const Database = require("../database");
const { UptimeCalculator } = require("../uptime-calculator"); const { UptimeCalculator } = require("../uptime-calculator");
const { Settings } = require("../settings");
let router = express.Router(); let router = express.Router();
@@ -30,7 +30,7 @@ router.get("/api/entry-page", async (request, response) => {
let result = { }; let result = { };
let hostname = request.hostname; let hostname = request.hostname;
if ((await Settings.get("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"];
} }

View File

@@ -90,7 +90,8 @@ 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 { initJWTSecret, checkLogin, doubleCheckPassword, shake256, SHAKE256_LENGTH, allowDevAllOrigin } = require("./util-server"); const { getSettings, setSettings, setting, initJWTSecret, checkLogin, doubleCheckPassword, shake256, SHAKE256_LENGTH, allowDevAllOrigin,
} = require("./util-server");
log.debug("server", "Importing Notification"); log.debug("server", "Importing Notification");
const { Notification } = require("./notification"); const { Notification } = require("./notification");
@@ -200,7 +201,7 @@ let needSetup = false;
// Entry Page // Entry Page
app.get("/", async (request, response) => { app.get("/", async (request, response) => {
let hostname = request.hostname; let hostname = request.hostname;
if (await Settings.get("trustProxy")) { if (await setting("trustProxy")) {
const proxy = request.headers["x-forwarded-host"]; const proxy = request.headers["x-forwarded-host"];
if (proxy) { if (proxy) {
hostname = proxy; hostname = proxy;
@@ -280,7 +281,7 @@ let needSetup = false;
// Robots.txt // Robots.txt
app.get("/robots.txt", async (_request, response) => { app.get("/robots.txt", async (_request, response) => {
let txt = "User-agent: *\nDisallow:"; let txt = "User-agent: *\nDisallow:";
if (!await Settings.get("searchEngineIndex")) { if (!await setting("searchEngineIndex")) {
txt += " /"; txt += " /";
} }
response.setHeader("Content-Type", "text/plain"); response.setHeader("Content-Type", "text/plain");
@@ -1326,7 +1327,7 @@ let needSetup = false;
socket.on("getSettings", async (callback) => { socket.on("getSettings", async (callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
const data = await Settings.getSettings("general"); const data = await getSettings("general");
if (!data.serverTimezone) { if (!data.serverTimezone) {
data.serverTimezone = await server.getTimezone(); data.serverTimezone = await server.getTimezone();
@@ -1354,7 +1355,7 @@ let needSetup = false;
// Disabled Auth + Want to Enable Auth => No Check // Disabled Auth + Want to Enable Auth => No Check
// Enabled Auth + Want to Disable Auth => Check!! // Enabled Auth + Want to Disable Auth => Check!!
// Enabled Auth + Want to Enable Auth => No Check // Enabled Auth + Want to Enable Auth => No Check
const currentDisabledAuth = await Settings.get("disableAuth"); const currentDisabledAuth = await setting("disableAuth");
if (!currentDisabledAuth && data.disableAuth) { if (!currentDisabledAuth && data.disableAuth) {
await doubleCheckPassword(socket, currentPassword); await doubleCheckPassword(socket, currentPassword);
} }
@@ -1368,7 +1369,7 @@ let needSetup = false;
const previousChromeExecutable = await Settings.get("chromeExecutable"); const previousChromeExecutable = await Settings.get("chromeExecutable");
const previousNSCDStatus = await Settings.get("nscd"); const previousNSCDStatus = await Settings.get("nscd");
await Settings.setSettings("general", data); await setSettings("general", data);
server.entryPage = data.entryPage; server.entryPage = data.entryPage;
// Also need to apply timezone globally // Also need to apply timezone globally
@@ -1464,7 +1465,7 @@ let needSetup = false;
}); });
} catch (e) { } catch (e) {
log.error("server", e); console.error(e);
callback({ callback({
ok: false, ok: false,
@@ -1577,7 +1578,7 @@ let needSetup = false;
// *************************** // ***************************
log.debug("auth", "check auto login"); log.debug("auth", "check auto login");
if (await Settings.get("disableAuth")) { if (await setting("disableAuth")) {
log.info("auth", "Disabled Auth: auto login to admin"); log.info("auth", "Disabled Auth: auto login to admin");
await afterLogin(socket, await R.findOne("user")); await afterLogin(socket, await R.findOne("user"));
socket.emit("autoLogin"); socket.emit("autoLogin");

View File

@@ -60,7 +60,7 @@ module.exports.apiKeySocketHandler = (socket) => {
ok: true, ok: true,
}); });
} catch (e) { } catch (e) {
log.error("apikeys", e); console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,

View File

@@ -1,8 +1,7 @@
const { checkLogin, doubleCheckPassword } = require("../util-server"); const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server");
const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); const { CloudflaredTunnel } = require("node-cloudflared-tunnel");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const { log } = require("../../src/util"); const { log } = require("../../src/util");
const { Settings } = require("../settings");
const io = UptimeKumaServer.getInstance().io; const io = UptimeKumaServer.getInstance().io;
const prefix = "cloudflared_"; const prefix = "cloudflared_";
@@ -41,7 +40,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
socket.join("cloudflared"); socket.join("cloudflared");
io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled()); io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled());
io.to(socket.userID).emit(prefix + "running", cloudflared.running); io.to(socket.userID).emit(prefix + "running", cloudflared.running);
io.to(socket.userID).emit(prefix + "token", await Settings.get("cloudflaredTunnelToken")); io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken"));
} catch (error) { } } catch (error) { }
}); });
@@ -56,7 +55,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
try { try {
checkLogin(socket); checkLogin(socket);
if (token && typeof token === "string") { if (token && typeof token === "string") {
await Settings.set("cloudflaredTunnelToken", token); await setSetting("cloudflaredTunnelToken", token);
cloudflared.token = token; cloudflared.token = token;
} else { } else {
cloudflared.token = null; cloudflared.token = null;
@@ -68,7 +67,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
socket.on(prefix + "stop", async (currentPassword, callback) => { socket.on(prefix + "stop", async (currentPassword, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
const disabledAuth = await Settings.get("disableAuth"); const disabledAuth = await setting("disableAuth");
if (!disabledAuth) { if (!disabledAuth) {
await doubleCheckPassword(socket, currentPassword); await doubleCheckPassword(socket, currentPassword);
} }
@@ -84,7 +83,7 @@ module.exports.cloudflaredSocketHandler = (socket) => {
socket.on(prefix + "removeToken", async () => { socket.on(prefix + "removeToken", async () => {
try { try {
checkLogin(socket); checkLogin(socket);
await Settings.set("cloudflaredTunnelToken", ""); await setSetting("cloudflaredTunnelToken", "");
} catch (error) { } } catch (error) { }
}); });
@@ -97,15 +96,15 @@ module.exports.cloudflaredSocketHandler = (socket) => {
*/ */
module.exports.autoStart = async (token) => { module.exports.autoStart = async (token) => {
if (!token) { if (!token) {
token = await Settings.get("cloudflaredTunnelToken"); token = await setting("cloudflaredTunnelToken");
} else { } else {
// Override the current token via args or env var // Override the current token via args or env var
await Settings.set("cloudflaredTunnelToken", token); await setSetting("cloudflaredTunnelToken", token);
log.info("cloudflare", "Use cloudflared token from args or env var"); console.log("Use cloudflared token from args or env var");
} }
if (token) { if (token) {
log.info("cloudflare", "Start cloudflared"); console.log("Start cloudflared");
cloudflared.token = token; cloudflared.token = token;
cloudflared.start(); cloudflared.start();
} }

View File

@@ -67,7 +67,7 @@ module.exports.maintenanceSocketHandler = (socket) => {
}); });
} catch (e) { } catch (e) {
log.error("maintenance", e); console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,
@@ -177,7 +177,7 @@ module.exports.maintenanceSocketHandler = (socket) => {
ok: true, ok: true,
}); });
} catch (e) { } catch (e) {
log.error("maintenance", e); console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,
@@ -201,7 +201,7 @@ module.exports.maintenanceSocketHandler = (socket) => {
}); });
} catch (e) { } catch (e) {
log.error("maintenance", e); console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,
@@ -225,7 +225,7 @@ module.exports.maintenanceSocketHandler = (socket) => {
}); });
} catch (e) { } catch (e) {
log.error("maintenance", e); console.error(e);
callback({ callback({
ok: false, ok: false,
msg: e.message, msg: e.message,

View File

@@ -1,5 +1,5 @@
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { checkLogin } = require("../util-server"); const { checkLogin, setSetting } = require("../util-server");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { log } = require("../../src/util"); const { log } = require("../../src/util");
const ImageDataURI = require("../image-data-uri"); const ImageDataURI = require("../image-data-uri");
@@ -7,7 +7,6 @@ const Database = require("../database");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
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 { Settings } = require("../settings");
/** /**
* Socket handlers for status page * Socket handlers for status page
@@ -234,7 +233,7 @@ module.exports.statusPageSocketHandler = (socket) => {
// Also change entry page to new slug if it is the default one, and slug is changed. // Also change entry page to new slug if it is the default one, and slug is changed.
if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) { if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) {
server.entryPage = "statusPage-" + statusPage.slug; server.entryPage = "statusPage-" + statusPage.slug;
await Settings.set("entryPage", server.entryPage, "general"); await setSetting("entryPage", server.entryPage, "general");
} }
apicache.clear(); apicache.clear();
@@ -292,7 +291,7 @@ module.exports.statusPageSocketHandler = (socket) => {
}); });
} catch (error) { } catch (error) {
log.error("socket", error); console.error(error);
callback({ callback({
ok: false, ok: false,
msg: error.message, msg: error.message,
@@ -314,7 +313,7 @@ module.exports.statusPageSocketHandler = (socket) => {
// Reset entry page if it is the default one. // Reset entry page if it is the default one.
if (server.entryPage === "statusPage-" + slug) { if (server.entryPage === "statusPage-" + slug) {
server.entryPage = "dashboard"; server.entryPage = "dashboard";
await Settings.set("entryPage", server.entryPage, "general"); await setSetting("entryPage", server.entryPage, "general");
} }
// No need to delete records from `status_page_cname`, because it has cascade foreign key. // No need to delete records from `status_page_cname`, because it has cascade foreign key.

View File

@@ -12,6 +12,7 @@ const { Client } = require("pg");
const postgresConParse = require("pg-connection-string").parse; const postgresConParse = require("pg-connection-string").parse;
const mysql = require("mysql2"); const mysql = require("mysql2");
const { NtlmClient } = require("./modules/axios-ntlm/lib/ntlmClient.js"); const { NtlmClient } = require("./modules/axios-ntlm/lib/ntlmClient.js");
const { Settings } = require("./settings");
const grpc = require("@grpc/grpc-js"); const grpc = require("@grpc/grpc-js");
const protojs = require("protobufjs"); const protojs = require("protobufjs");
const radiusClient = require("node-radius-client"); const radiusClient = require("node-radius-client");
@@ -520,6 +521,46 @@ exports.redisPingAsync = function (dsn, rejectUnauthorized) {
}); });
}; };
/**
* Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve
* @returns {Promise<any>} Value
* @deprecated Use await Settings.get(key)
*/
exports.setting = async function (key) {
return await Settings.get(key);
};
/**
* Sets the specified setting to specified value
* @param {string} key Key of setting to set
* @param {any} value Value to set to
* @param {?string} type Type of setting
* @returns {Promise<void>}
*/
exports.setSetting = async function (key, value, type = null) {
await Settings.set(key, value, type);
};
/**
* Get settings based on type
* @param {string} type The type of setting
* @returns {Promise<Bean>} Settings of requested type
*/
exports.getSettings = async function (type) {
return await Settings.getSettings(type);
};
/**
* Set settings based on type
* @param {string} type Type of settings to set
* @param {object} data Values of settings
* @returns {Promise<void>}
*/
exports.setSettings = async function (type, data) {
await Settings.setSettings(type, data);
};
// ssl-checker by @dyaa // ssl-checker by @dyaa
//https://github.com/dyaa/ssl-checker/blob/master/src/index.ts //https://github.com/dyaa/ssl-checker/blob/master/src/index.ts

View File

@@ -0,0 +1,201 @@
<template>
<div ref="modal" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<div v-if="monitor?.type === 'http'">
<textarea id="curl-debug" v-model="curlCommand" class="form-control mb-3" readonly wrap="off"></textarea>
<button
id="debug-copy-btn" class="btn btn-outline-primary position-absolute top-0 end-0 mt-3 me-3 border-0"
type="button" @click.stop="copyToClipboard"
>
<font-awesome-icon icon="copy" />
</button>
<i18n-t keypath="CurlDebugInfo" tag="p" class="form-text">
<template #newiline>
<br>
</template>
<template #firewalls>
<a href="https://xkcd.com/2259/" target="_blank">{{ $t('firewalls') }}</a>
</template>
<template #dns_resolvers>
<a
href="https://www.reddit.com/r/sysadmin/comments/rxho93/thank_you_for_the_running_its_always_dns_joke_its/"
target="_blank"
>{{ $t('dns resolvers') }}</a>
</template>
<template #docker_networks>
<a href="https://youtu.be/bKFMS5C4CG0" target="_blank">{{ $t('docker networks') }}</a>
</template>
</i18n-t>
<div
v-if="monitor.authMethod === 'oauth2-cc'" class="alert alert-warning d-flex align-items-center gap-2"
role="alert"
>
<div role="img" aria-label="Warning:"></div>
<i18n-t keypath="CurlDebugInfoOAuth2CCUnsupported" tag="div">
<template #curl>
<code>curl</code>
</template>
<template #newline>
<br>
</template>
<template #oauth2_bearer>
<code>--oauth2-bearer TOKEN</code>
</template>
</i18n-t>
</div>
<div v-if="monitor.proxyId" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
<div role="img" aria-label="Warning:"></div>
<i18n-t keypath="CurlDebugInfoProxiesUnsupported" tag="div">
<template #curl>
<code>curl</code>
</template>
</i18n-t>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { Modal } from "bootstrap";
import { version } from "../../package.json";
import { useToast } from "vue-toastification";
const toast = useToast();
export default {
name: "DebugMonitor",
props: {
/** Monitor this represents */
monitor: {
type: Object,
default: null,
},
},
data() {
return {
modal: null,
};
},
computed: {
curlCommand() {
if (this.monitor === null) {
return "";
}
let method = this.monitor.method;
if ([ "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS" ].indexOf(method) === -1) {
// set to a custom value => could lead to injections
method = this.escapeShell(method);
}
const command = [ "curl", "--verbose", "--head", "--request", method, "\\\n" ];
command.push("--user-agent", `'Uptime-Kuma/${version}'`, "\\\n");
if (this.monitor.ignoreTls) {
command.push("--insecure", "\\\n");
}
if (this.monitor.headers) {
try {
for (const [ key, value ] of Object.entries(JSON.parse(this.monitor.headers))) {
command.push("--header", `'${this.escapeShellNoQuotes(key)}: ${this.escapeShellNoQuotes(value)}'`, "\\\n");
}
} catch (e) {
command.push("--header", this.escapeShell(this.monitor.headers), "\\\n");
}
}
if (this.monitor.authMethod === "basic") {
command.push("--basic", "--user", `'${this.escapeShellNoQuotes(this.monitor.basic_auth_user)}:${this.escapeShellNoQuotes(this.monitor.basic_auth_pass)}'`, "\\\n");
} else if (this.monitor.authMethod === "mtls") {
command.push("--cacert", this.escapeShell(this.monitor.tlsCa), "\\\n");
command.push("--key", this.escapeShell(this.monitor.tlsKey), "\\\n");
command.push( "--cert", this.escapeShell(this.monitor.tlsCert), "\\\n");
} else if (this.monitor.authMethod === "ntlm") {
let domain = "";
if (this.monitor.authDomain) {
domain = `${this.monitor.authDomain}/`;
}
command.push("--ntlm", "--user", `'${this.escapeShellNoQuotes(domain)}${this.escapeShellNoQuotes(this.monitor.basic_auth_user)}:${this.escapeShellNoQuotes(this.monitor.basic_auth_pass)}'`, "\\\n");
}
if (this.monitor.body && this.monitor.httpBodyEncoding === "json") {
let json = "";
try {
// trying to parse the supplied data as json to trim whitespace
json = JSON.stringify(JSON.parse(this.monitor.body));
} catch (e) {
json = this.monitor.body;
}
command.push("--header", "'Content-Type: application/json'", "\\\n");
command.push("--data", this.escapeShell(json), "\\\n");
} else if (this.monitor.body && this.monitor.httpBodyEncoding === "xml") {
command.push("--headers", "'Content-Type: application/xml'", "\\\n");
command.push("--data", this.escapeShell(this.monitor.body), "\\\n");
}
if (this.monitor.maxredirects) {
command.push("--location", "--max-redirs", this.escapeShell(this.monitor.maxredirects), "\\\n");
}
if (this.monitor.timeout) {
command.push("--max-time", this.escapeShell(this.monitor.timeout), "\\\n");
}
if (this.monitor.maxretries) {
command.push("--retry", this.escapeShell(this.monitor.maxretries), "\\\n");
}
command.push("--url", this.escapeShell(this.monitor.url));
return command.join(" ");
},
},
mounted() {
this.modal = new Modal(this.$refs.modal);
},
methods: {
/**
* Show the dialog
* @returns {void}
*/
show() {
this.modal.show();
},
/**
* Escape a string for use in a shell
* @param {string|number} s string to escape
* @returns {string} escaped, quoted string
*/
escapeShell(s) {
if (typeof s == "number") {
return s.toString();
}
return "'" + this.escapeShellNoQuotes(s) + "'";
},
/**
* Escape a string for use in a shell
* @param {string} s string to escape
* @returns {string} escaped string
*/
escapeShellNoQuotes(s) {
return s.replace(/(['"$`\\])/g, "\\$1");
},
/**
* Copies a value to the clipboard and shows toasts with the success/error
* @returns {void}
*/
async copyToClipboard() {
try {
await navigator.clipboard.writeText(this.curlCommand);
toast.success(this.$t("CopyToClipboardSuccess"));
} catch (err) {
toast.error(this.$t("CopyToClipboardError", { error: err.message }));
}
},
}
};
</script>
<style lang="scss" scoped>
@import "../assets/vars";
textarea {
min-height: 200px;
}
#curl-debug {
font-family: monospace;
overflow: auto;
}
</style>

View File

@@ -995,7 +995,7 @@
class="btn btn-outline-primary" class="btn btn-outline-primary"
type="button" type="button"
:disabled="processing" :disabled="processing"
@click.stop="modal.show()" @click="$refs.debugMonitorDialog.show()"
> >
{{ $t("Debug") }} {{ $t("Debug") }}
</button> </button>
@@ -1010,58 +1010,10 @@
<RemoteBrowserDialog ref="remoteBrowserDialog" /> <RemoteBrowserDialog ref="remoteBrowserDialog" />
</div> </div>
</transition> </transition>
<div ref="modal" class="modal fade" tabindex="-1"> <DebugMonitorDialog ref="debugMonitorDialog" :monitor="monitor" />
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<textarea id="curl-debug" v-model="curlCommand" class="form-control mb-3" readonly wrap="off"></textarea>
<button id="debug-copy-btn" class="btn btn-outline-primary position-absolute top-0 end-0 mt-3 me-3 border-0" type="button" @click.stop="copyToClipboard">
<font-awesome-icon icon="copy" />
</button>
<i18n-t keypath="CurlDebugInfo" tag="p" class="form-text">
<template #newiline>
<br>
</template>
<template #firewalls>
<a href="https://xkcd.com/2259/" target="_blank">{{ $t('firewalls') }}</a>
</template>
<template #dns_resolvers>
<a href="https://www.reddit.com/r/sysadmin/comments/rxho93/thank_you_for_the_running_its_always_dns_joke_its/" target="_blank">{{ $t('dns resolvers') }}</a>
</template>
<template #docker_networks>
<a href="https://youtu.be/bKFMS5C4CG0" target="_blank">{{ $t('docker networks') }}</a>
</template>
</i18n-t>
<div v-if="monitor.authMethod === 'oauth2-cc'" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
<div role="img" aria-label="Warning:"></div>
<i18n-t keypath="CurlDebugInfoOAuth2CCUnsupported" tag="div">
<template #curl>
<code>curl</code>
</template>
<template #newline>
<br>
</template>
<template #oauth2_bearer>
<code>--oauth2-bearer TOKEN</code>
</template>
</i18n-t>
</div>
<div v-if="monitor.proxyId" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
<div role="img" aria-label="Warning:"></div>
<i18n-t keypath="CurlDebugInfoProxiesUnsupported" tag="div">
<template #curl>
<code>curl</code>
</template>
</i18n-t>
</div>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
import { Modal } from "bootstrap";
import VueMultiselect from "vue-multiselect"; import VueMultiselect from "vue-multiselect";
import { useToast } from "vue-toastification"; import { useToast } from "vue-toastification";
import ActionSelect from "../components/ActionSelect.vue"; import ActionSelect from "../components/ActionSelect.vue";
@@ -1076,8 +1028,7 @@ import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } fro
import { hostNameRegexPattern } from "../util-frontend"; import { hostNameRegexPattern } from "../util-frontend";
import HiddenInput from "../components/HiddenInput.vue"; import HiddenInput from "../components/HiddenInput.vue";
import EditMonitorConditions from "../components/EditMonitorConditions.vue"; import EditMonitorConditions from "../components/EditMonitorConditions.vue";
import { version } from "../../package.json"; import DebugMonitorDialog from "../components/DebugMonitorDialog.vue";
const userAgent = `'Uptime-Kuma/${version}'`;
const toast = useToast(); const toast = useToast();
@@ -1127,6 +1078,7 @@ const monitorDefaults = {
export default { export default {
components: { components: {
DebugMonitorDialog,
HiddenInput, HiddenInput,
ActionSelect, ActionSelect,
ProxyDialog, ProxyDialog,
@@ -1169,58 +1121,9 @@ export default {
}, },
computed: { computed: {
curlCommand() {
const command = [ "curl", "--verbose", "--head", "--request", this.monitor.method, "\\\n", "--user-agent", userAgent, "\\\n" ];
if (this.monitor.ignoreTls) {
command.push("--insecure", "\\\n");
}
if (this.monitor.headers) {
try {
// trying to parse the supplied data as json to trim whitespace
for (const [ key, value ] of Object.entries(JSON.parse(this.monitor.headers))) {
command.push("--header", `'${key}: ${value}'`, "\\\n");
}
} catch (e) {
command.push("--header", `'${this.monitor.headers}'`, "\\\n");
}
}
if (this.monitor.authMethod === "basic") {
command.push("--user", `${this.monitor.basic_auth_user}:${this.monitor.basic_auth_pass}`, "--basic", "\\\n");
} else if (this.monitor.authmethod === "mtls") {
command.push("--cacert", `'${this.monitor.tlsCa}'`, "\\\n", "--key", `'${this.monitor.tlsKey}'`, "\\\n", "--cert", `'${this.monitor.tlsCert}'`, "\\\n");
} else if (this.monitor.authMethod === "ntlm") {
command.push("--user", `'${this.monitor.authDomain ? `${this.monitor.authDomain}/` : ""}${this.monitor.basic_auth_user}:${this.monitor.basic_auth_pass}'`, "--ntlm", "\\\n");
}
if (this.monitor.body && this.monitor.httpBodyEncoding === "json") {
let json = "";
try {
// trying to parse the supplied data as json to trim whitespace
json = JSON.stringify(JSON.parse(this.monitor.body));
} catch (e) {
json = this.monitor.body;
}
command.push("--header", "'Content-Type: application/json'", "\\\n", "--data", `'${json}'`, "\\\n");
} else if (this.monitor.body && this.monitor.httpBodyEncoding === "xml") {
command.push("--headers", "'Content-Type: application/xml'", "\\\n", "--data", `'${this.monitor.body}'`, "\\\n");
}
if (this.monitor.maxredirects) {
command.push("--location", "--max-redirs", this.monitor.maxredirects, "\\\n");
}
if (this.monitor.timeout) {
command.push("--max-time", this.monitor.timeout, "\\\n");
}
if (this.monitor.maxretries) {
command.push("--retry", this.monitor.maxretries, "\\\n");
}
command.push("--url", this.monitor.url);
return command.join(" ");
},
ipRegex() { ipRegex() {
// Allow to test with simple dns server with port (127.0.0.1:5300) // Allow to test with simple dns server with port (127.0.0.1:5300)
if (! isDev) { if (!isDev) {
return this.ipRegexPattern; return this.ipRegexPattern;
} }
return null; return null;
@@ -1573,7 +1476,6 @@ message HealthCheckResponse {
}, },
}, },
mounted() { mounted() {
this.modal = new Modal(this.$refs.modal);
this.init(); this.init();
let acceptedStatusCodeOptions = [ let acceptedStatusCodeOptions = [
@@ -1614,14 +1516,6 @@ message HealthCheckResponse {
this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions; this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions;
}, },
methods: { methods: {
async copyToClipboard() {
try {
await navigator.clipboard.writeText(this.curlCommand);
toast.success(this.$t("CopyToClipboardSuccess"));
} catch (err) {
toast.error(this.$t("CopyToClipboardError", { error: err.message }));
}
},
/** /**
* Initialize the edit monitor form * Initialize the edit monitor form
* @returns {void} * @returns {void}
@@ -1910,8 +1804,4 @@ message HealthCheckResponse {
min-height: 200px; min-height: 200px;
} }
#curl-debug {
font-family: monospace;
overflow: auto;
}
</style> </style>

View File

@@ -8,34 +8,17 @@
// Backend uses the compiled file util.js // Backend uses the compiled file util.js
// Frontend uses util.ts // Frontend uses util.ts
*/ */
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) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (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);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
var _a; var _a;
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.ucfirst = void 0; exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
const dayjs_1 = __importDefault(require("dayjs")); const dayjs_1 = __importDefault(require("dayjs"));
const jsonata = __importStar(require("jsonata")); const dayjs = require("dayjs");
const jsonata = require("jsonata");
exports.isDev = process.env.NODE_ENV === "development"; exports.isDev = process.env.NODE_ENV === "development";
exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node); exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node);
exports.appName = "Uptime Kuma"; exports.appName = "Uptime Kuma";
@@ -142,6 +125,10 @@ function ucfirst(str) {
return firstLetter.toUpperCase() + str.substr(1); return firstLetter.toUpperCase() + str.substr(1);
} }
exports.ucfirst = ucfirst; exports.ucfirst = ucfirst;
function debug(msg) {
exports.log.log("", msg, "debug");
}
exports.debug = debug;
class Logger { class Logger {
constructor() { constructor() {
this.hideLog = { this.hideLog = {
@@ -169,6 +156,8 @@ class Logger {
if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) { if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) {
return; return;
} }
module = module.toUpperCase();
level = level.toUpperCase();
let now; let now;
if (dayjs_1.default.tz) { if (dayjs_1.default.tz) {
now = dayjs_1.default.tz(new Date()).format(); now = dayjs_1.default.tz(new Date()).format();
@@ -178,20 +167,10 @@ class Logger {
} }
const levelColor = consoleLevelColors[level]; const levelColor = consoleLevelColors[level];
const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)]; const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)];
let timePart = now; let timePart;
let modulePart = module; let modulePart;
let levelPart = level; let levelPart;
let msgPart = msg; let msgPart;
if (process.env.UPTIME_KUMA_LOG_FORMAT === "json") {
console.log(JSON.stringify({
time: timePart,
module: modulePart,
level: levelPart,
msg: typeof msg === "string" ? msg : JSON.stringify(msg),
}));
return;
}
module = module.toUpperCase();
if (exports.isNode) { if (exports.isNode) {
switch (level) { switch (level) {
case "DEBUG": case "DEBUG":
@@ -208,17 +187,28 @@ class Logger {
if (typeof msg === "string") { if (typeof msg === "string") {
msgPart = exports.CONSOLE_STYLE_FgRed + msg + exports.CONSOLE_STYLE_Reset; msgPart = exports.CONSOLE_STYLE_FgRed + msg + exports.CONSOLE_STYLE_Reset;
} }
else {
msgPart = msg;
}
break; break;
case "DEBUG": case "DEBUG":
if (typeof msg === "string") { if (typeof msg === "string") {
msgPart = exports.CONSOLE_STYLE_FgGray + msg + exports.CONSOLE_STYLE_Reset; msgPart = exports.CONSOLE_STYLE_FgGray + msg + exports.CONSOLE_STYLE_Reset;
} }
else {
msgPart = msg;
}
break;
default:
msgPart = msg;
break; break;
} }
} }
else { else {
timePart = now;
modulePart = `[${module}]`; modulePart = `[${module}]`;
levelPart = `${level}:`; levelPart = `${level}:`;
msgPart = msg;
} }
switch (level) { switch (level) {
case "ERROR": case "ERROR":
@@ -241,23 +231,23 @@ class Logger {
} }
} }
info(module, msg) { info(module, msg) {
this.log(module, msg, "INFO"); this.log(module, msg, "info");
} }
warn(module, msg) { warn(module, msg) {
this.log(module, msg, "WARN"); this.log(module, msg, "warn");
} }
error(module, msg) { error(module, msg) {
this.log(module, msg, "ERROR"); this.log(module, msg, "error");
} }
debug(module, msg) { debug(module, msg) {
this.log(module, msg, "DEBUG"); this.log(module, msg, "debug");
} }
exception(module, exception, msg) { exception(module, exception, msg) {
let finalMessage = exception; let finalMessage = exception;
if (msg) { if (msg) {
finalMessage = `${msg}: ${exception}`; finalMessage = `${msg}: ${exception}`;
} }
this.log(module, finalMessage, "ERROR"); this.log(module, finalMessage, "error");
} }
} }
exports.log = new Logger(); exports.log = new Logger();

View File

@@ -156,6 +156,15 @@ export function ucfirst(str: string) {
return firstLetter.toUpperCase() + str.substr(1); return firstLetter.toUpperCase() + str.substr(1);
} }
/**
* @deprecated Use log.debug (https://github.com/louislam/uptime-kuma/pull/910)
* @param msg Message to write
* @returns {void}
*/
export function debug(msg: unknown) {
log.log("", msg, "debug");
}
class Logger { class Logger {
/** /**
@@ -197,13 +206,12 @@ class Logger {
/** /**
* Write a message to the log * Write a message to the log
* @private
* @param module The module the log comes from * @param module The module the log comes from
* @param msg Message to write * @param msg Message to write
* @param level {"INFO"|"WARN"|"ERROR"|"DEBUG"} Log level * @param level Log level. One of INFO, WARN, ERROR, DEBUG or can be customized.
* @returns {void} * @returns {void}
*/ */
log(module: string, msg: unknown, level: "INFO"|"WARN"|"ERROR"|"DEBUG"): void { log(module: string, msg: any, level: string) {
if (level === "DEBUG" && !isDev) { if (level === "DEBUG" && !isDev) {
return; return;
} }
@@ -212,6 +220,9 @@ class Logger {
return; return;
} }
module = module.toUpperCase();
level = level.toUpperCase();
let now; let now;
if (dayjs.tz) { if (dayjs.tz) {
now = dayjs.tz(new Date()).format(); now = dayjs.tz(new Date()).format();
@@ -222,23 +233,10 @@ class Logger {
const levelColor = consoleLevelColors[level]; const levelColor = consoleLevelColors[level];
const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)]; const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)];
let timePart: string = now; let timePart: string;
let modulePart: string = module; let modulePart: string;
let levelPart: string = level; let levelPart: string;
let msgPart: unknown = msg; let msgPart: string;
if (process.env.UPTIME_KUMA_LOG_FORMAT === "json") {
console.log(JSON.stringify({
time: timePart,
module: modulePart,
level: levelPart,
msg: typeof msg === "string" ? msg : JSON.stringify(msg),
}));
return;
}
// Console rendering:
module = module.toUpperCase();
if (isNode) { if (isNode) {
// Add console colors // Add console colors
@@ -259,18 +257,27 @@ class Logger {
case "ERROR": case "ERROR":
if (typeof msg === "string") { if (typeof msg === "string") {
msgPart = CONSOLE_STYLE_FgRed + msg + CONSOLE_STYLE_Reset; msgPart = CONSOLE_STYLE_FgRed + msg + CONSOLE_STYLE_Reset;
} else {
msgPart = msg;
} }
break; break;
case "DEBUG": case "DEBUG":
if (typeof msg === "string") { if (typeof msg === "string") {
msgPart = CONSOLE_STYLE_FgGray + msg + CONSOLE_STYLE_Reset; msgPart = CONSOLE_STYLE_FgGray + msg + CONSOLE_STYLE_Reset;
} else {
msgPart = msg;
} }
break; break;
default:
msgPart = msg;
break;
} }
} else { } else {
// No console colors // No console colors
timePart = now;
modulePart = `[${module}]`; modulePart = `[${module}]`;
levelPart = `${level}:`; levelPart = `${level}:`;
msgPart = msg;
} }
// Write to console // Write to console
@@ -301,8 +308,8 @@ class Logger {
* @param msg Message to write * @param msg Message to write
* @returns {void} * @returns {void}
*/ */
info(module: string, msg: string): void { info(module: string, msg: unknown) {
this.log(module, msg, "INFO"); this.log(module, msg, "info");
} }
/** /**
@@ -311,8 +318,8 @@ class Logger {
* @param msg Message to write * @param msg Message to write
* @returns {void} * @returns {void}
*/ */
warn(module: string, msg: string): void { warn(module: string, msg: unknown) {
this.log(module, msg, "WARN"); this.log(module, msg, "warn");
} }
/** /**
@@ -321,8 +328,8 @@ class Logger {
* @param msg Message to write * @param msg Message to write
* @returns {void} * @returns {void}
*/ */
error(module: string, msg: string): void { error(module: string, msg: unknown) {
this.log(module, msg, "ERROR"); this.log(module, msg, "error");
} }
/** /**
@@ -331,8 +338,8 @@ class Logger {
* @param msg Message to write * @param msg Message to write
* @returns {void} * @returns {void}
*/ */
debug(module: string, msg: string): void { debug(module: string, msg: unknown) {
this.log(module, msg, "DEBUG"); this.log(module, msg, "debug");
} }
/** /**
@@ -349,7 +356,7 @@ class Logger {
finalMessage = `${msg}: ${exception}`; finalMessage = `${msg}: ${exception}`;
} }
this.log(module, finalMessage, "ERROR"); this.log(module, finalMessage, "error");
} }
} }
@@ -393,7 +400,7 @@ export class TimeLogger {
* @param name Name of monitor * @param name Name of monitor
* @returns {void} * @returns {void}
*/ */
print(name: string): void { print(name: string) {
if (isDev && process.env.TIMELOGGER === "1") { if (isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
} }