mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-17 18:37:16 +00:00
Merge branch 'master' into snmp-monitor
This commit is contained in:
commit
71f9384c09
33 changed files with 4270 additions and 2364 deletions
|
@ -1,7 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ignorePatterns: [
|
ignorePatterns: [
|
||||||
"test/*.js",
|
"test/*.js",
|
||||||
"server/modules/apicache/*",
|
"server/modules/*",
|
||||||
"src/util.js"
|
"src/util.js"
|
||||||
],
|
],
|
||||||
root: true,
|
root: true,
|
||||||
|
|
|
@ -3,7 +3,6 @@ FROM node:20-bookworm-slim AS base2-slim
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
# Specify --no-install-recommends to skip unused dependencies, make the base much smaller!
|
# Specify --no-install-recommends to skip unused dependencies, make the base much smaller!
|
||||||
# apprise = for notifications (From testing repo)
|
|
||||||
# sqlite3 = for debugging
|
# sqlite3 = for debugging
|
||||||
# iputils-ping = for ping
|
# iputils-ping = for ping
|
||||||
# util-linux = for setpriv (Should be dropped in 2.0.0?)
|
# util-linux = for setpriv (Should be dropped in 2.0.0?)
|
||||||
|
@ -12,10 +11,10 @@ ARG TARGETPLATFORM
|
||||||
# ca-certificates = keep the cert up-to-date
|
# ca-certificates = keep the cert up-to-date
|
||||||
# sudo = for start service nscd with non-root user
|
# sudo = for start service nscd with non-root user
|
||||||
# nscd = for better DNS caching
|
# nscd = for better DNS caching
|
||||||
RUN echo "deb http://deb.debian.org/debian testing main" >> /etc/apt/sources.list && \
|
RUN apt update && \
|
||||||
apt update && \
|
apt --yes --no-install-recommends install \
|
||||||
apt --yes --no-install-recommends -t testing install apprise sqlite3 ca-certificates && \
|
sqlite3 \
|
||||||
apt --yes --no-install-recommends -t stable install \
|
ca-certificates \
|
||||||
iputils-ping \
|
iputils-ping \
|
||||||
util-linux \
|
util-linux \
|
||||||
dumb-init \
|
dumb-init \
|
||||||
|
@ -25,6 +24,15 @@ RUN echo "deb http://deb.debian.org/debian testing main" >> /etc/apt/sources.lis
|
||||||
rm -rf /var/lib/apt/lists/* && \
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
apt --yes autoremove
|
apt --yes autoremove
|
||||||
|
|
||||||
|
# apprise = for notifications (Install from the deb package, as the stable one is too old) (workaround for #4867)
|
||||||
|
# Switching to testing repo is no longer working, as the testing repo is not bookworm anymore.
|
||||||
|
# python3-paho-mqtt (#4859)
|
||||||
|
RUN curl http://ftp.debian.org/debian/pool/main/a/apprise/apprise_1.8.0-2_all.deb --output apprise.deb && \
|
||||||
|
apt update && \
|
||||||
|
apt --yes --no-install-recommends install ./apprise.deb python3-paho-mqtt && \
|
||||||
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
|
rm -f apprise.deb && \
|
||||||
|
apt --yes autoremove
|
||||||
|
|
||||||
# Install cloudflared
|
# Install cloudflared
|
||||||
RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \
|
RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \
|
||||||
|
@ -42,7 +50,9 @@ COPY ./docker/etc/sudoers /etc/sudoers
|
||||||
|
|
||||||
# Full Base Image
|
# Full Base Image
|
||||||
# MariaDB, Chromium and fonts
|
# MariaDB, Chromium and fonts
|
||||||
FROM base2-slim AS base2
|
# Make sure to reuse the slim image here. Uncomment the above line if you want to build it from scratch.
|
||||||
|
# FROM base2-slim AS base2
|
||||||
|
FROM louislam/uptime-kuma:base2-slim AS base2
|
||||||
ENV UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1
|
ENV UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1
|
||||||
RUN apt update && \
|
RUN apt update && \
|
||||||
apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk mariadb-server && \
|
apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk mariadb-server && \
|
||||||
|
|
5327
package-lock.json
generated
5327
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -76,10 +76,10 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "~1.7.3",
|
"@grpc/grpc-js": "~1.7.3",
|
||||||
"@louislam/ping": "~0.4.4-mod.1",
|
"@louislam/ping": "~0.4.4-mod.1",
|
||||||
|
"@louislam/sqlite3": "15.1.6",
|
||||||
"@vvo/tzdb": "^6.125.0",
|
"@vvo/tzdb": "^6.125.0",
|
||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.28.1",
|
"axios": "~0.28.1",
|
||||||
"axios-ntlm": "1.3.0",
|
|
||||||
"badge-maker": "~3.3.1",
|
"badge-maker": "~3.3.1",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"chardet": "~1.4.0",
|
"chardet": "~1.4.0",
|
||||||
|
@ -91,6 +91,7 @@
|
||||||
"compression": "~1.7.4",
|
"compression": "~1.7.4",
|
||||||
"croner": "~6.0.5",
|
"croner": "~6.0.5",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
|
"dev-null": "^0.1.1",
|
||||||
"dotenv": "~16.0.3",
|
"dotenv": "~16.0.3",
|
||||||
"express": "~4.19.2",
|
"express": "~4.19.2",
|
||||||
"express-basic-auth": "~1.2.1",
|
"express-basic-auth": "~1.2.1",
|
||||||
|
@ -115,7 +116,7 @@
|
||||||
"mitt": "~3.0.1",
|
"mitt": "~3.0.1",
|
||||||
"mongodb": "~4.17.1",
|
"mongodb": "~4.17.1",
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~11.0.0",
|
||||||
"mysql2": "~3.9.6",
|
"mysql2": "~3.9.6",
|
||||||
"nanoid": "~3.3.4",
|
"nanoid": "~3.3.4",
|
||||||
"net-snmp": "^3.11.2",
|
"net-snmp": "^3.11.2",
|
||||||
|
@ -137,10 +138,9 @@
|
||||||
"redbean-node": "~0.3.0",
|
"redbean-node": "~0.3.0",
|
||||||
"redis": "~4.5.1",
|
"redis": "~4.5.1",
|
||||||
"semver": "~7.5.4",
|
"semver": "~7.5.4",
|
||||||
"socket.io": "~4.6.1",
|
"socket.io": "~4.7.5",
|
||||||
"socket.io-client": "~4.6.1",
|
"socket.io-client": "~4.7.5",
|
||||||
"socks-proxy-agent": "6.1.1",
|
"socks-proxy-agent": "6.1.1",
|
||||||
"sqlite3": "~5.1.7",
|
|
||||||
"tar": "~6.2.1",
|
"tar": "~6.2.1",
|
||||||
"tcp-ping": "~0.1.1",
|
"tcp-ping": "~0.1.1",
|
||||||
"thirty-two": "~1.0.2",
|
"thirty-two": "~1.0.2",
|
||||||
|
|
|
@ -223,8 +223,11 @@ class Database {
|
||||||
fs.copyFileSync(Database.templatePath, Database.sqlitePath);
|
fs.copyFileSync(Database.templatePath, Database.sqlitePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
|
||||||
|
Dialect.prototype._driver = () => require("@louislam/sqlite3");
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
client: "sqlite3",
|
client: Dialect,
|
||||||
connection: {
|
connection: {
|
||||||
filename: Database.sqlitePath,
|
filename: Database.sqlitePath,
|
||||||
acquireConnectionTimeout: acquireConnectionTimeout,
|
acquireConnectionTimeout: acquireConnectionTimeout,
|
||||||
|
|
|
@ -889,6 +889,7 @@ class Monitor extends BeanModel {
|
||||||
retries = 0;
|
retries = 0;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
if (error?.name === "CanceledError") {
|
if (error?.name === "CanceledError") {
|
||||||
bean.msg = `timeout by AbortSignal (${this.timeout}s)`;
|
bean.msg = `timeout by AbortSignal (${this.timeout}s)`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -961,7 +962,6 @@ class Monitor extends BeanModel {
|
||||||
} else if (bean.status === MAINTENANCE) {
|
} else if (bean.status === MAINTENANCE) {
|
||||||
log.warn("monitor", `Monitor #${this.id} '${this.name}': Under Maintenance | Type: ${this.type}`);
|
log.warn("monitor", `Monitor #${this.id} '${this.name}': Under Maintenance | Type: ${this.type}`);
|
||||||
} else {
|
} else {
|
||||||
beatInterval = this.retryInterval;
|
|
||||||
log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
|
log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
server/modules/axios-ntlm/LICENSE
Normal file
21
server/modules/axios-ntlm/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 CatButtes
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
77
server/modules/axios-ntlm/lib/flags.js
Normal file
77
server/modules/axios-ntlm/lib/flags.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
'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
|
||||||
|
data. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_OEM = 1 << 1;
|
||||||
|
/* Indicates that OEM strings are supported for use in security buffer data. */
|
||||||
|
module.exports.NTLMFLAG_REQUEST_TARGET = 1 << 2;
|
||||||
|
/* Requests that the server's authentication realm be included in the Type 2
|
||||||
|
message. */
|
||||||
|
/* unknown (1<<3) */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_SIGN = 1 << 4;
|
||||||
|
/* Specifies that authenticated communication between the client and server
|
||||||
|
should carry a digital signature (message integrity). */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_SEAL = 1 << 5;
|
||||||
|
/* Specifies that authenticated communication between the client and server
|
||||||
|
should be encrypted (message confidentiality). */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE = 1 << 6;
|
||||||
|
/* Indicates that datagram authentication is being used. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_LM_KEY = 1 << 7;
|
||||||
|
/* Indicates that the LAN Manager session key should be used for signing and
|
||||||
|
sealing authenticated communications. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_NETWARE = 1 << 8;
|
||||||
|
/* unknown purpose */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_NTLM_KEY = 1 << 9;
|
||||||
|
/* Indicates that NTLM authentication is being used. */
|
||||||
|
/* unknown (1<<10) */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_ANONYMOUS = 1 << 11;
|
||||||
|
/* Sent by the client in the Type 3 message to indicate that an anonymous
|
||||||
|
context has been established. This also affects the response fields. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED = 1 << 12;
|
||||||
|
/* Sent by the client in the Type 1 message to indicate that a desired
|
||||||
|
authentication realm is included in the message. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED = 1 << 13;
|
||||||
|
/* Sent by the client in the Type 1 message to indicate that the client
|
||||||
|
workstation's name is included in the message. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_LOCAL_CALL = 1 << 14;
|
||||||
|
/* Sent by the server to indicate that the server and client are on the same
|
||||||
|
machine. Implies that the client may use a pre-established local security
|
||||||
|
context rather than responding to the challenge. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN = 1 << 15;
|
||||||
|
/* Indicates that authenticated communication between the client and server
|
||||||
|
should be signed with a "dummy" signature. */
|
||||||
|
module.exports.NTLMFLAG_TARGET_TYPE_DOMAIN = 1 << 16;
|
||||||
|
/* Sent by the server in the Type 2 message to indicate that the target
|
||||||
|
authentication realm is a domain. */
|
||||||
|
module.exports.NTLMFLAG_TARGET_TYPE_SERVER = 1 << 17;
|
||||||
|
/* Sent by the server in the Type 2 message to indicate that the target
|
||||||
|
authentication realm is a server. */
|
||||||
|
module.exports.NTLMFLAG_TARGET_TYPE_SHARE = 1 << 18;
|
||||||
|
/* Sent by the server in the Type 2 message to indicate that the target
|
||||||
|
authentication realm is a share. Presumably, this is for share-level
|
||||||
|
authentication. Usage is unclear. */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_NTLM2_KEY = 1 << 19;
|
||||||
|
/* Indicates that the NTLM2 signing and sealing scheme should be used for
|
||||||
|
protecting authenticated communications. */
|
||||||
|
module.exports.NTLMFLAG_REQUEST_INIT_RESPONSE = 1 << 20;
|
||||||
|
/* unknown purpose */
|
||||||
|
module.exports.NTLMFLAG_REQUEST_ACCEPT_RESPONSE = 1 << 21;
|
||||||
|
/* unknown purpose */
|
||||||
|
module.exports.NTLMFLAG_REQUEST_NONNT_SESSION_KEY = 1 << 22;
|
||||||
|
/* unknown purpose */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_TARGET_INFO = 1 << 23;
|
||||||
|
/* Sent by the server in the Type 2 message to indicate that it is including a
|
||||||
|
Target Information block in the message. */
|
||||||
|
/* unknown (1<24) */
|
||||||
|
/* unknown (1<25) */
|
||||||
|
/* unknown (1<26) */
|
||||||
|
/* unknown (1<27) */
|
||||||
|
/* unknown (1<28) */
|
||||||
|
module.exports.NTLMFLAG_NEGOTIATE_128 = 1 << 29;
|
||||||
|
/* Indicates that 128-bit encryption is supported. */
|
||||||
|
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
|
122
server/modules/axios-ntlm/lib/hash.js
Normal file
122
server/modules/axios-ntlm/lib/hash.js
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
'use strict';
|
||||||
|
// Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js
|
||||||
|
var crypto = require('crypto');
|
||||||
|
function createLMResponse(challenge, lmhash) {
|
||||||
|
var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0);
|
||||||
|
lmhash.copy(pwBuffer);
|
||||||
|
calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf);
|
||||||
|
calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8);
|
||||||
|
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');
|
||||||
|
if (password.length > 14) {
|
||||||
|
buf.fill(0);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
pwBuffer.fill(0);
|
||||||
|
pwBuffer.write(password.toUpperCase(), 0, 'ascii');
|
||||||
|
return Buffer.concat([
|
||||||
|
calculateDES(pwBuffer.slice(0, 7), magicKey),
|
||||||
|
calculateDES(pwBuffer.slice(7), magicKey)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
function calculateDES(key, message) {
|
||||||
|
var desKey = new Buffer.alloc(8);
|
||||||
|
desKey[0] = key[0] & 0xFE;
|
||||||
|
desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1);
|
||||||
|
desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2);
|
||||||
|
desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3);
|
||||||
|
desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4);
|
||||||
|
desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5);
|
||||||
|
desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6);
|
||||||
|
desKey[7] = (key[6] << 1) & 0xFF;
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
var parity = 0;
|
||||||
|
for (var j = 1; j < 8; j++) {
|
||||||
|
parity += (desKey[i] >> j) % 2;
|
||||||
|
}
|
||||||
|
desKey[i] |= (parity % 2) === 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf);
|
||||||
|
calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8);
|
||||||
|
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'));
|
||||||
|
return md4sum.digest();
|
||||||
|
}
|
||||||
|
function createNTLMv2Hash(ntlmhash, username, authTargetName) {
|
||||||
|
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);
|
||||||
|
//server challenge
|
||||||
|
type2message.challenge.copy(buf, 8);
|
||||||
|
//client nonce
|
||||||
|
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);
|
||||||
|
//the first 8 bytes are spare to store the hashed value before the blob
|
||||||
|
//server challenge
|
||||||
|
type2message.challenge.copy(buf, 8);
|
||||||
|
//blob signature
|
||||||
|
buf.writeUInt32BE(0x01010000, 16);
|
||||||
|
//reserved
|
||||||
|
buf.writeUInt32LE(0, 20);
|
||||||
|
//timestamp
|
||||||
|
//TODO: we are loosing precision here since js is not able to handle those large integers
|
||||||
|
// 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)));
|
||||||
|
buf.writeUInt32LE(timestampLow, 24, false);
|
||||||
|
buf.writeUInt32LE(timestampHigh, 28, false);
|
||||||
|
//random client nonce
|
||||||
|
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex');
|
||||||
|
//zero
|
||||||
|
buf.writeUInt32LE(0, 40);
|
||||||
|
//complete target information block from type 2 message
|
||||||
|
type2message.targetInfo.buffer.copy(buf, 44);
|
||||||
|
//zero
|
||||||
|
buf.writeUInt32LE(0, 44 + type2message.targetInfo.buffer.length);
|
||||||
|
hmac.update(buf.slice(8));
|
||||||
|
var hashedBuffer = hmac.digest();
|
||||||
|
hashedBuffer.copy(buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
function createPseudoRandomValue(length) {
|
||||||
|
var str = '';
|
||||||
|
while (str.length < length) {
|
||||||
|
str += Math.floor(Math.random() * 16).toString(16);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
createLMHash: createLMHash,
|
||||||
|
createNTLMHash: createNTLMHash,
|
||||||
|
createLMResponse: createLMResponse,
|
||||||
|
createNTLMResponse: createNTLMResponse,
|
||||||
|
createLMv2Response: createLMv2Response,
|
||||||
|
createNTLMv2Response: createNTLMv2Response,
|
||||||
|
createPseudoRandomValue: createPseudoRandomValue
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=hash.js.map
|
220
server/modules/axios-ntlm/lib/ntlm.js
Normal file
220
server/modules/axios-ntlm/lib/ntlm.js
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
'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 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;
|
||||||
|
//signature
|
||||||
|
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii');
|
||||||
|
pos += NTLMSIGNATURE.length;
|
||||||
|
//message type
|
||||||
|
buf.writeUInt32LE(1, pos);
|
||||||
|
pos += 4;
|
||||||
|
//flags
|
||||||
|
buf.writeUInt32LE(flags.NTLMFLAG_NEGOTIATE_OEM |
|
||||||
|
flags.NTLMFLAG_REQUEST_TARGET |
|
||||||
|
flags.NTLMFLAG_NEGOTIATE_NTLM_KEY |
|
||||||
|
flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY |
|
||||||
|
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN, pos);
|
||||||
|
pos += 4;
|
||||||
|
//domain security buffer
|
||||||
|
buf.writeUInt16LE(target.length, pos);
|
||||||
|
pos += 2;
|
||||||
|
buf.writeUInt16LE(target.length, pos);
|
||||||
|
pos += 2;
|
||||||
|
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
|
||||||
|
pos += 4;
|
||||||
|
if (target.length > 0) {
|
||||||
|
dataPos += buf.write(target, dataPos, 'ascii');
|
||||||
|
}
|
||||||
|
//workstation security buffer
|
||||||
|
buf.writeUInt16LE(workstation.length, pos);
|
||||||
|
pos += 2;
|
||||||
|
buf.writeUInt16LE(workstation.length, pos);
|
||||||
|
pos += 2;
|
||||||
|
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
|
||||||
|
pos += 4;
|
||||||
|
if (workstation.length > 0) {
|
||||||
|
dataPos += buf.write(workstation, dataPos, 'ascii');
|
||||||
|
}
|
||||||
|
return 'NTLM ' + buf.toString('base64', 0, dataPos);
|
||||||
|
}
|
||||||
|
function decodeType2Message(str) {
|
||||||
|
if (str === undefined) {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
|
||||||
|
if (ntlmMatch) {
|
||||||
|
str = ntlmMatch[1];
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
//check message type
|
||||||
|
if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 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.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1;
|
||||||
|
obj.challenge = buf.slice(24, 32);
|
||||||
|
//read target name
|
||||||
|
obj.targetName = (function () {
|
||||||
|
var length = buf.readUInt16LE(12);
|
||||||
|
//skipping allocated space
|
||||||
|
var offset = buf.readUInt32LE(16);
|
||||||
|
if (length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ((offset + length) > buf.length || offset < 32) {
|
||||||
|
throw new Error('Bad type 2 message');
|
||||||
|
}
|
||||||
|
return buf.toString(obj.encoding, offset, offset + length);
|
||||||
|
})();
|
||||||
|
//read target info
|
||||||
|
if (obj.flags & flags.NTLMFLAG_NEGOTIATE_TARGET_INFO) {
|
||||||
|
obj.targetInfo = (function () {
|
||||||
|
var info = {};
|
||||||
|
var length = buf.readUInt16LE(40);
|
||||||
|
//skipping allocated space
|
||||||
|
var offset = buf.readUInt32LE(44);
|
||||||
|
var targetInfoBuffer = new Buffer.alloc(length);
|
||||||
|
buf.copy(targetInfoBuffer, 0, offset, offset + length);
|
||||||
|
if (length === 0) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
if ((offset + length) > buf.length || offset < 32) {
|
||||||
|
throw new Error('Bad type 2 message');
|
||||||
|
}
|
||||||
|
var pos = offset;
|
||||||
|
while (pos < (offset + length)) {
|
||||||
|
var blockType = buf.readUInt16LE(pos);
|
||||||
|
pos += 2;
|
||||||
|
var blockLength = buf.readUInt16LE(pos);
|
||||||
|
pos += 2;
|
||||||
|
if (blockType === 0) {
|
||||||
|
//reached the terminator subblock
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var blockTypeStr = void 0;
|
||||||
|
switch (blockType) {
|
||||||
|
case 1:
|
||||||
|
blockTypeStr = 'SERVER';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
blockTypeStr = 'DOMAIN';
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
blockTypeStr = 'FQDN';
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
blockTypeStr = 'DNS';
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
blockTypeStr = 'PARENT_DNS';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
blockTypeStr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (blockTypeStr) {
|
||||||
|
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength);
|
||||||
|
}
|
||||||
|
pos += blockLength;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
parsed: info,
|
||||||
|
buffer: targetInfoBuffer
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
function createType3Message(type2Message, username, password, workstation, target) {
|
||||||
|
var dataPos = 52, buf = new Buffer.alloc(1024);
|
||||||
|
if (workstation === undefined) {
|
||||||
|
workstation = os.hostname();
|
||||||
|
}
|
||||||
|
if (target === undefined) {
|
||||||
|
target = type2Message.targetName;
|
||||||
|
}
|
||||||
|
//signature
|
||||||
|
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);
|
||||||
|
//lmv2 security buffer
|
||||||
|
buf.writeUInt16LE(lmv2.length, 12);
|
||||||
|
buf.writeUInt16LE(lmv2.length, 14);
|
||||||
|
buf.writeUInt32LE(dataPos, 16);
|
||||||
|
lmv2.copy(buf, dataPos);
|
||||||
|
dataPos += lmv2.length;
|
||||||
|
//ntlmv2 security buffer
|
||||||
|
buf.writeUInt16LE(ntlmv2.length, 20);
|
||||||
|
buf.writeUInt16LE(ntlmv2.length, 22);
|
||||||
|
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);
|
||||||
|
//lm security buffer
|
||||||
|
buf.writeUInt16LE(lm.length, 12);
|
||||||
|
buf.writeUInt16LE(lm.length, 14);
|
||||||
|
buf.writeUInt32LE(dataPos, 16);
|
||||||
|
lm.copy(buf, dataPos);
|
||||||
|
dataPos += lm.length;
|
||||||
|
//ntlm security buffer
|
||||||
|
buf.writeUInt16LE(ntlm.length, 20);
|
||||||
|
buf.writeUInt16LE(ntlm.length, 22);
|
||||||
|
buf.writeUInt32LE(dataPos, 24);
|
||||||
|
ntlm.copy(buf, dataPos);
|
||||||
|
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.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.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.writeUInt32LE(dataPos, 48);
|
||||||
|
dataPos += buf.write(workstation, dataPos, type2Message.encoding);
|
||||||
|
if (type2Message.version === 2) {
|
||||||
|
//session key security buffer
|
||||||
|
buf.writeUInt16LE(0, 52);
|
||||||
|
buf.writeUInt16LE(0, 54);
|
||||||
|
buf.writeUInt32LE(0, 56);
|
||||||
|
//flags
|
||||||
|
buf.writeUInt32LE(type2Message.flags, 60);
|
||||||
|
}
|
||||||
|
return 'NTLM ' + buf.toString('base64', 0, dataPos);
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
createType1Message: createType1Message,
|
||||||
|
decodeType2Message: decodeType2Message,
|
||||||
|
createType3Message: createType3Message
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=ntlm.js.map
|
127
server/modules/axios-ntlm/lib/ntlmClient.js
Normal file
127
server/modules/axios-ntlm/lib/ntlmClient.js
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
"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;
|
||||||
|
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
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); }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.NtlmClient = void 0;
|
||||||
|
var axios_1 = __importDefault(require("axios"));
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
function NtlmClient(credentials, AxiosConfig) {
|
||||||
|
var _this = this;
|
||||||
|
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
|
||||||
|
if (!config.httpAgent) {
|
||||||
|
config.httpAgent = new http.Agent({ keepAlive: true });
|
||||||
|
}
|
||||||
|
if (!config.httpsAgent) {
|
||||||
|
config.httpsAgent = new https.Agent({ keepAlive: true });
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); });
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
exports.NtlmClient = NtlmClient;
|
||||||
|
//# sourceMappingURL=ntlmClient.js.map
|
|
@ -33,26 +33,6 @@ class Discord extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
let address;
|
|
||||||
|
|
||||||
switch (monitorJSON["type"]) {
|
|
||||||
case "ping":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
break;
|
|
||||||
case "port":
|
|
||||||
case "dns":
|
|
||||||
case "gamedig":
|
|
||||||
case "steam":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
if (monitorJSON["port"]) {
|
|
||||||
address += ":" + monitorJSON["port"];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
address = monitorJSON["url"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
||||||
if (heartbeatJSON["status"] === DOWN) {
|
if (heartbeatJSON["status"] === DOWN) {
|
||||||
let discorddowndata = {
|
let discorddowndata = {
|
||||||
|
@ -68,7 +48,7 @@ class Discord extends NotificationProvider {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
value: this.extractAdress(monitorJSON),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||||
|
@ -105,7 +85,7 @@ class Discord extends NotificationProvider {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
value: this.extractAdress(monitorJSON),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||||
|
|
|
@ -19,6 +19,36 @@ class NotificationProvider {
|
||||||
throw new Error("Have to override Notification.send(...)");
|
throw new Error("Have to override Notification.send(...)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the address from a monitor JSON object based on its type.
|
||||||
|
* @param {?object} monitorJSON Monitor details (For Up/Down only)
|
||||||
|
* @returns {string} The extracted address based on the monitor type.
|
||||||
|
*/
|
||||||
|
extractAdress(monitorJSON) {
|
||||||
|
if (!monitorJSON) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
switch (monitorJSON["type"]) {
|
||||||
|
case "push":
|
||||||
|
return "Heartbeat";
|
||||||
|
case "ping":
|
||||||
|
return monitorJSON["hostname"];
|
||||||
|
case "port":
|
||||||
|
case "dns":
|
||||||
|
case "gamedig":
|
||||||
|
case "steam":
|
||||||
|
if (monitorJSON["port"]) {
|
||||||
|
return monitorJSON["hostname"] + ":" + monitorJSON["port"];
|
||||||
|
}
|
||||||
|
return monitorJSON["hostname"];
|
||||||
|
default:
|
||||||
|
if (![ "https://", "http://", "" ].includes(monitorJSON["url"])) {
|
||||||
|
return monitorJSON["url"];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws an error
|
* Throws an error
|
||||||
* @param {any} error The error to throw
|
* @param {any} error The error to throw
|
||||||
|
|
|
@ -32,28 +32,7 @@ class SevenIO extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
let address = "";
|
let address = this.extractAdress(monitorJSON);
|
||||||
|
|
||||||
switch (monitorJSON["type"]) {
|
|
||||||
case "ping":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
break;
|
|
||||||
case "port":
|
|
||||||
case "dns":
|
|
||||||
case "gamedig":
|
|
||||||
case "steam":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
if (monitorJSON["port"]) {
|
|
||||||
address += ":" + monitorJSON["port"];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (![ "https://", "http://", "" ].includes(monitorJSON["url"])) {
|
|
||||||
address = monitorJSON["url"];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address !== "") {
|
if (address !== "") {
|
||||||
address = `(${address}) `;
|
address = `(${address}) `;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ class Slack extends NotificationProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitorJSON.url) {
|
const address = this.extractAdress(monitorJSON);
|
||||||
|
if (address) {
|
||||||
actions.push({
|
actions.push({
|
||||||
"type": "button",
|
"type": "button",
|
||||||
"text": {
|
"text": {
|
||||||
|
@ -56,7 +57,7 @@ class Slack extends NotificationProvider {
|
||||||
"text": "Visit site",
|
"text": "Visit site",
|
||||||
},
|
},
|
||||||
"value": "Site",
|
"value": "Site",
|
||||||
"url": monitorJSON.url,
|
"url": address,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,12 +93,7 @@ class SMTP extends NotificationProvider {
|
||||||
|
|
||||||
if (monitorJSON !== null) {
|
if (monitorJSON !== null) {
|
||||||
monitorName = monitorJSON["name"];
|
monitorName = monitorJSON["name"];
|
||||||
|
monitorHostnameOrURL = this.extractAdress(monitorJSON);
|
||||||
if (monitorJSON["type"] === "http" || monitorJSON["type"] === "keyword" || monitorJSON["type"] === "json-query") {
|
|
||||||
monitorHostnameOrURL = monitorJSON["url"];
|
|
||||||
} else {
|
|
||||||
monitorHostnameOrURL = monitorJSON["hostname"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let serviceStatus = "⚠️ Test";
|
let serviceStatus = "⚠️ Test";
|
||||||
|
|
|
@ -34,25 +34,7 @@ class Squadcast extends NotificationProvider {
|
||||||
data.status = "resolve";
|
data.status = "resolve";
|
||||||
}
|
}
|
||||||
|
|
||||||
let address;
|
data.tags["AlertAddress"] = this.extractAdress(monitorJSON);
|
||||||
switch (monitorJSON["type"]) {
|
|
||||||
case "ping":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
break;
|
|
||||||
case "port":
|
|
||||||
case "dns":
|
|
||||||
case "steam":
|
|
||||||
address = monitorJSON["hostname"];
|
|
||||||
if (monitorJSON["port"]) {
|
|
||||||
address += ":" + monitorJSON["port"];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
address = monitorJSON["url"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.tags["AlertAddress"] = address;
|
|
||||||
|
|
||||||
monitorJSON["tags"].forEach(tag => {
|
monitorJSON["tags"].forEach(tag => {
|
||||||
data.tags[tag["name"]] = {
|
data.tags[tag["name"]] = {
|
||||||
|
|
|
@ -216,21 +216,6 @@ class Teams extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
let monitorUrl;
|
|
||||||
|
|
||||||
switch (monitorJSON["type"]) {
|
|
||||||
case "http":
|
|
||||||
case "keywork":
|
|
||||||
monitorUrl = monitorJSON["url"];
|
|
||||||
break;
|
|
||||||
case "docker":
|
|
||||||
monitorUrl = monitorJSON["docker_host"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
monitorUrl = monitorJSON["hostname"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseURL = await setting("primaryBaseURL");
|
const baseURL = await setting("primaryBaseURL");
|
||||||
let dashboardUrl;
|
let dashboardUrl;
|
||||||
if (baseURL) {
|
if (baseURL) {
|
||||||
|
@ -240,7 +225,7 @@ class Teams extends NotificationProvider {
|
||||||
const payload = this._notificationPayloadFactory({
|
const payload = this._notificationPayloadFactory({
|
||||||
heartbeatJSON: heartbeatJSON,
|
heartbeatJSON: heartbeatJSON,
|
||||||
monitorName: monitorJSON.name,
|
monitorName: monitorJSON.name,
|
||||||
monitorUrl: monitorUrl,
|
monitorUrl: this.extractAdress(monitorJSON),
|
||||||
dashboardUrl: dashboardUrl,
|
dashboardUrl: dashboardUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
77
server/notification-providers/threema.js
Normal file
77
server/notification-providers/threema.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
class Threema extends NotificationProvider {
|
||||||
|
name = "threema";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
const url = "https://msgapi.threema.ch/send_simple";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
"Accept": "*/*",
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
from: notification.threemaSenderIdentity,
|
||||||
|
secret: notification.threemaSecret,
|
||||||
|
text: msg
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (notification.threemaRecipientType) {
|
||||||
|
case "identity":
|
||||||
|
data.to = notification.threemaRecipient;
|
||||||
|
break;
|
||||||
|
case "phone":
|
||||||
|
data.phone = notification.threemaRecipient;
|
||||||
|
break;
|
||||||
|
case "email":
|
||||||
|
data.email = notification.threemaRecipient;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported recipient type: ${notification.threemaRecipientType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(url, new URLSearchParams(data), config);
|
||||||
|
return "Threema notification sent successfully.";
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = this.handleApiError(error);
|
||||||
|
this.throwGeneralAxiosError(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Threema API errors
|
||||||
|
* @param {any} error The error to handle
|
||||||
|
* @returns {string} Additional error context
|
||||||
|
*/
|
||||||
|
handleApiError(error) {
|
||||||
|
if (!error.response) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
switch (error.response.status) {
|
||||||
|
case 400:
|
||||||
|
return "Invalid recipient identity or account not set up for basic mode (400).";
|
||||||
|
case 401:
|
||||||
|
return "Incorrect API identity or secret (401).";
|
||||||
|
case 402:
|
||||||
|
return "No credits remaining (402).";
|
||||||
|
case 404:
|
||||||
|
return "Recipient not found (404).";
|
||||||
|
case 413:
|
||||||
|
return "Message is too long (413).";
|
||||||
|
case 500:
|
||||||
|
return "Temporary internal server error (500).";
|
||||||
|
default:
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Threema;
|
|
@ -13,9 +13,9 @@ class ZohoCliq extends NotificationProvider {
|
||||||
*/
|
*/
|
||||||
_statusMessageFactory = (status, monitorName) => {
|
_statusMessageFactory = (status, monitorName) => {
|
||||||
if (status === DOWN) {
|
if (status === DOWN) {
|
||||||
return `🔴 Application [${monitorName}] went down\n`;
|
return `🔴 [${monitorName}] went down\n`;
|
||||||
} else if (status === UP) {
|
} else if (status === UP) {
|
||||||
return `✅ Application [${monitorName}] is back online\n`;
|
return `### ✅ [${monitorName}] is back online\n`;
|
||||||
}
|
}
|
||||||
return "Notification\n";
|
return "Notification\n";
|
||||||
};
|
};
|
||||||
|
@ -46,16 +46,11 @@ class ZohoCliq extends NotificationProvider {
|
||||||
monitorUrl,
|
monitorUrl,
|
||||||
}) => {
|
}) => {
|
||||||
const payload = [];
|
const payload = [];
|
||||||
payload.push("### Uptime Kuma\n");
|
|
||||||
payload.push(this._statusMessageFactory(status, monitorName));
|
payload.push(this._statusMessageFactory(status, monitorName));
|
||||||
payload.push(`*Description:* ${monitorMessage}`);
|
payload.push(`*Description:* ${monitorMessage}`);
|
||||||
|
|
||||||
if (monitorName) {
|
|
||||||
payload.push(`*Monitor:* ${monitorName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monitorUrl && monitorUrl !== "https://") {
|
if (monitorUrl && monitorUrl !== "https://") {
|
||||||
payload.push(`*URL:* [${monitorUrl}](${monitorUrl})`);
|
payload.push(`*URL:* ${monitorUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
|
@ -87,24 +82,10 @@ class ZohoCliq extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url;
|
|
||||||
switch (monitorJSON["type"]) {
|
|
||||||
case "http":
|
|
||||||
case "keywork":
|
|
||||||
url = monitorJSON["url"];
|
|
||||||
break;
|
|
||||||
case "docker":
|
|
||||||
url = monitorJSON["docker_host"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
url = monitorJSON["hostname"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = this._notificationPayloadFactory({
|
const payload = this._notificationPayloadFactory({
|
||||||
monitorMessage: heartbeatJSON.msg,
|
monitorMessage: heartbeatJSON.msg,
|
||||||
monitorName: monitorJSON.name,
|
monitorName: monitorJSON.name,
|
||||||
monitorUrl: url,
|
monitorUrl: this.extractAdress(monitorJSON),
|
||||||
status: heartbeatJSON.status
|
status: heartbeatJSON.status
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ const Stackfield = require("./notification-providers/stackfield");
|
||||||
const Teams = require("./notification-providers/teams");
|
const Teams = require("./notification-providers/teams");
|
||||||
const TechulusPush = require("./notification-providers/techulus-push");
|
const TechulusPush = require("./notification-providers/techulus-push");
|
||||||
const Telegram = require("./notification-providers/telegram");
|
const Telegram = require("./notification-providers/telegram");
|
||||||
|
const Threema = require("./notification-providers/threema");
|
||||||
const Twilio = require("./notification-providers/twilio");
|
const Twilio = require("./notification-providers/twilio");
|
||||||
const Splunk = require("./notification-providers/splunk");
|
const Splunk = require("./notification-providers/splunk");
|
||||||
const Webhook = require("./notification-providers/webhook");
|
const Webhook = require("./notification-providers/webhook");
|
||||||
|
@ -133,6 +134,7 @@ class Notification {
|
||||||
new Teams(),
|
new Teams(),
|
||||||
new TechulusPush(),
|
new TechulusPush(),
|
||||||
new Telegram(),
|
new Telegram(),
|
||||||
|
new Threema(),
|
||||||
new Twilio(),
|
new Twilio(),
|
||||||
new Splunk(),
|
new Splunk(),
|
||||||
new Webhook(),
|
new Webhook(),
|
||||||
|
|
|
@ -232,8 +232,8 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
|
||||||
let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h";
|
let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h";
|
||||||
const overrideValue = value && parseFloat(value);
|
const overrideValue = value && parseFloat(value);
|
||||||
|
|
||||||
if (requestedDuration === "24") {
|
if (/^[0-9]+$/.test(requestedDuration)) {
|
||||||
requestedDuration = "24h";
|
requestedDuration = `${requestedDuration}h`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let publicMonitor = await R.getRow(`
|
let publicMonitor = await R.getRow(`
|
||||||
|
@ -265,7 +265,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
|
||||||
// build a label string. If a custom label is given, override the default one (requestedDuration)
|
// build a label string. If a custom label is given, override the default one (requestedDuration)
|
||||||
badgeValues.label = filterAndJoin([
|
badgeValues.label = filterAndJoin([
|
||||||
labelPrefix,
|
labelPrefix,
|
||||||
label ?? `Uptime (${requestedDuration}${labelSuffix})`,
|
label ?? `Uptime (${requestedDuration.slice(0, -1)}${labelSuffix})`,
|
||||||
]);
|
]);
|
||||||
badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]);
|
badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]);
|
||||||
}
|
}
|
||||||
|
@ -302,8 +302,8 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request,
|
||||||
let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h";
|
let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h";
|
||||||
const overrideValue = value && parseFloat(value);
|
const overrideValue = value && parseFloat(value);
|
||||||
|
|
||||||
if (requestedDuration === "24") {
|
if (/^[0-9]+$/.test(requestedDuration)) {
|
||||||
requestedDuration = "24h";
|
requestedDuration = `${requestedDuration}h`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if monitor is public
|
// Check if monitor is public
|
||||||
|
@ -325,7 +325,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request,
|
||||||
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
|
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
|
||||||
badgeValues.labelColor = labelColor ?? "";
|
badgeValues.labelColor = labelColor ?? "";
|
||||||
// build a lable string. If a custom label is given, override the default one (requestedDuration)
|
// build a lable string. If a custom label is given, override the default one (requestedDuration)
|
||||||
badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${requestedDuration}${labelSuffix})` ]);
|
badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${requestedDuration.slice(0, -1)}${labelSuffix})` ]);
|
||||||
badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]);
|
badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -543,7 +543,9 @@ class UptimeCalculator {
|
||||||
if (type === "minute" && num > 24 * 60) {
|
if (type === "minute" && num > 24 * 60) {
|
||||||
throw new Error("The maximum number of minutes is 1440");
|
throw new Error("The maximum number of minutes is 1440");
|
||||||
}
|
}
|
||||||
|
if (type === "day" && num > 365) {
|
||||||
|
throw new Error("The maximum number of days is 365");
|
||||||
|
}
|
||||||
// Get the current time period key based on the type
|
// Get the current time period key based on the type
|
||||||
let key = this.getKey(this.getCurrentDate(), type);
|
let key = this.getKey(this.getCurrentDate(), type);
|
||||||
|
|
||||||
|
@ -741,20 +743,36 @@ class UptimeCalculator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the uptime data by duration
|
* Get the uptime data for given duration.
|
||||||
* @param {'24h'|'30d'|'1y'} duration Only accept 24h, 30d, 1y
|
* @param {string} duration A string with a number and a unit (m,h,d,w,M,y), such as 24h, 30d, 1y.
|
||||||
* @returns {UptimeDataResult} UptimeDataResult
|
* @returns {UptimeDataResult} UptimeDataResult
|
||||||
* @throws {Error} Invalid duration
|
* @throws {Error} Invalid duration / Unsupported unit
|
||||||
*/
|
*/
|
||||||
getDataByDuration(duration) {
|
getDataByDuration(duration) {
|
||||||
if (duration === "24h") {
|
const durationNumStr = duration.slice(0, -1);
|
||||||
return this.get24Hour();
|
|
||||||
} else if (duration === "30d") {
|
if (!/^[0-9]+$/.test(durationNumStr)) {
|
||||||
return this.get30Day();
|
throw new Error(`Invalid duration: ${duration}`);
|
||||||
} else if (duration === "1y") {
|
}
|
||||||
return this.get1Year();
|
const num = Number(durationNumStr);
|
||||||
} else {
|
const unit = duration.slice(-1);
|
||||||
throw new Error("Invalid duration");
|
|
||||||
|
switch (unit) {
|
||||||
|
case "m":
|
||||||
|
return this.getData(num, "minute");
|
||||||
|
case "h":
|
||||||
|
return this.getData(num, "hour");
|
||||||
|
case "d":
|
||||||
|
return this.getData(num, "day");
|
||||||
|
case "w":
|
||||||
|
return this.getData(7 * num, "day");
|
||||||
|
case "M":
|
||||||
|
return this.getData(30 * num, "day");
|
||||||
|
case "y":
|
||||||
|
return this.getData(365 * num, "day");
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported unit (${unit}) for badge duration ${duration}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ const mssql = require("mssql");
|
||||||
const { Client } = require("pg");
|
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("axios-ntlm");
|
const { NtlmClient } = require("./modules/axios-ntlm/lib/ntlmClient.js");
|
||||||
const { Settings } = require("./settings");
|
const { Settings } = require("./settings");
|
||||||
const grpc = require("@grpc/grpc-js");
|
const grpc = require("@grpc/grpc-js");
|
||||||
const protojs = require("protobufjs");
|
const protojs = require("protobufjs");
|
||||||
|
|
|
@ -152,6 +152,7 @@ export default {
|
||||||
"stackfield": "Stackfield",
|
"stackfield": "Stackfield",
|
||||||
"teams": "Microsoft Teams",
|
"teams": "Microsoft Teams",
|
||||||
"telegram": "Telegram",
|
"telegram": "Telegram",
|
||||||
|
"threema": "Threema",
|
||||||
"twilio": "Twilio",
|
"twilio": "Twilio",
|
||||||
"Splunk": "Splunk",
|
"Splunk": "Splunk",
|
||||||
"webhook": "Webhook",
|
"webhook": "Webhook",
|
||||||
|
|
87
src/components/notifications/Threema.vue
Normal file
87
src/components/notifications/Threema.vue
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="threema-recipient">{{ $t("threemaRecipientType") }}</label>
|
||||||
|
<select
|
||||||
|
id="threema-recipient" v-model="$parent.notification.threemaRecipientType" required
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="identity">{{ $t("threemaRecipientTypeIdentity") }}</option>
|
||||||
|
<option value="phone">{{ $t("threemaRecipientTypePhone") }}</option>
|
||||||
|
<option value="email">{{ $t("threemaRecipientTypeEmail") }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div v-if="$parent.notification.threemaRecipientType === 'identity'" class="mb-3">
|
||||||
|
<label class="form-label" for="threema-recipient">{{ $t("threemaRecipient") }} {{ $t("threemaRecipientTypeIdentity") }}</label>
|
||||||
|
<input
|
||||||
|
id="threema-recipient"
|
||||||
|
v-model="$parent.notification.threemaRecipient"
|
||||||
|
class="form-control"
|
||||||
|
minlength="8"
|
||||||
|
maxlength="8"
|
||||||
|
pattern="[A-Z0-9]{8}"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<div class="form-text">
|
||||||
|
<p>{{ $t("threemaRecipientTypeIdentityFormat") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="$parent.notification.threemaRecipientType === 'phone'" class="mb-3">
|
||||||
|
<label class="form-label" for="threema-recipient">{{ $t("threemaRecipient") }} {{ $t("threemaRecipientTypePhone") }}</label>
|
||||||
|
<input
|
||||||
|
id="threema-recipient"
|
||||||
|
v-model="$parent.notification.threemaRecipient"
|
||||||
|
class="form-control"
|
||||||
|
maxlength="15"
|
||||||
|
pattern="\d{1,15}"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<div class="form-text">
|
||||||
|
<p>{{ $t("threemaRecipientTypePhoneFormat") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="$parent.notification.threemaRecipientType === 'email'" class="mb-3">
|
||||||
|
<label class="form-label" for="threema-recipient">{{ $t("threemaRecipient") }} {{ $t("threemaRecipientTypeEmail") }}</label>
|
||||||
|
<input
|
||||||
|
id="threema-recipient"
|
||||||
|
v-model="$parent.notification.threemaRecipient"
|
||||||
|
class="form-control"
|
||||||
|
maxlength="254"
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="threema-sender">{{ $t("threemaSenderIdentity") }}</label>
|
||||||
|
<input
|
||||||
|
id="threema-sender"
|
||||||
|
v-model="$parent.notification.threemaSenderIdentity"
|
||||||
|
class="form-control"
|
||||||
|
minlength="8"
|
||||||
|
maxlength="8"
|
||||||
|
pattern="^\*[A-Z0-9]{7}$"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<div class="form-text">
|
||||||
|
<p>{{ $t("threemaSenderIdentityFormat") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="threema-secret">{{ $t("threemaApiAuthenticationSecret") }}</label>
|
||||||
|
<HiddenInput
|
||||||
|
id="threema-secret" v-model="$parent.notification.threemaSecret" required
|
||||||
|
autocomplete="false"
|
||||||
|
></HiddenInput>
|
||||||
|
</div>
|
||||||
|
<i18n-t class="form-text" keypath="wayToGetThreemaGateway" tag="div">
|
||||||
|
<a href="https://threema.ch/en/gateway" target="_blank">{{ $t("here") }}</a>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t class="form-text" keypath="threemaBasicModeInfo" tag="div">
|
||||||
|
<a href="https://gateway.threema.ch/en/developer/api" target="_blank">{{ $t("here") }}</a>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import HiddenInput from "../HiddenInput.vue";
|
||||||
|
</script>
|
|
@ -52,6 +52,7 @@ import STMP from "./SMTP.vue";
|
||||||
import Teams from "./Teams.vue";
|
import Teams from "./Teams.vue";
|
||||||
import TechulusPush from "./TechulusPush.vue";
|
import TechulusPush from "./TechulusPush.vue";
|
||||||
import Telegram from "./Telegram.vue";
|
import Telegram from "./Telegram.vue";
|
||||||
|
import Threema from "./Threema.vue";
|
||||||
import Twilio from "./Twilio.vue";
|
import Twilio from "./Twilio.vue";
|
||||||
import Webhook from "./Webhook.vue";
|
import Webhook from "./Webhook.vue";
|
||||||
import WeCom from "./WeCom.vue";
|
import WeCom from "./WeCom.vue";
|
||||||
|
@ -119,6 +120,7 @@ const NotificationFormList = {
|
||||||
"stackfield": Stackfield,
|
"stackfield": Stackfield,
|
||||||
"teams": Teams,
|
"teams": Teams,
|
||||||
"telegram": Telegram,
|
"telegram": Telegram,
|
||||||
|
"threema": Threema,
|
||||||
"twilio": Twilio,
|
"twilio": Twilio,
|
||||||
"Splunk": Splunk,
|
"Splunk": Splunk,
|
||||||
"webhook": Webhook,
|
"webhook": Webhook,
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<div
|
||||||
|
v-if="settings.disableAuth"
|
||||||
|
class="mt-5 d-flex align-items-center justify-content-center my-3"
|
||||||
|
>
|
||||||
|
{{ $t("apiKeysDisabledMsg") }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
<div class="add-btn">
|
<div class="add-btn">
|
||||||
<button class="btn btn-primary me-2" type="button" @click="$refs.apiKeyDialog.show()">
|
<button class="btn btn-primary me-2" type="button" @click="$refs.apiKeyDialog.show()">
|
||||||
<font-awesome-icon icon="plus" /> {{ $t("Add API Key") }}
|
<font-awesome-icon icon="plus" /> {{ $t("Add API Key") }}
|
||||||
|
@ -7,7 +14,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span v-if="Object.keys(keyList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
<span
|
||||||
|
v-if="Object.keys(keyList).length === 0"
|
||||||
|
class="d-flex align-items-center justify-content-center my-3"
|
||||||
|
>
|
||||||
{{ $t("No API Keys") }}
|
{{ $t("No API Keys") }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@ -18,9 +28,7 @@
|
||||||
:class="item.status"
|
:class="item.status"
|
||||||
>
|
>
|
||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<div
|
<div class="circle"></div>
|
||||||
class="circle"
|
|
||||||
></div>
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="title">{{ item.name }}</div>
|
<div class="title">{{ item.name }}</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
|
@ -30,7 +38,8 @@
|
||||||
{{ $t("Created") }}: {{ item.createdDate }}
|
{{ $t("Created") }}: {{ item.createdDate }}
|
||||||
</div>
|
</div>
|
||||||
<div class="date">
|
<div class="date">
|
||||||
{{ $t("Expires") }}: {{ item.expires || $t("Never") }}
|
{{ $t("Expires") }}:
|
||||||
|
{{ item.expires || $t("Never") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,6 +61,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-3" style="font-size: 13px;">
|
<div class="text-center mt-3" style="font-size: 13px;">
|
||||||
<a href="https://github.com/louislam/uptime-kuma/wiki/API-Keys" target="_blank">{{ $t("Learn More") }}</a>
|
<a href="https://github.com/louislam/uptime-kuma/wiki/API-Keys" target="_blank">{{ $t("Learn More") }}</a>
|
||||||
|
@ -88,6 +98,9 @@ export default {
|
||||||
let result = Object.values(this.$root.apiKeyList);
|
let result = Object.values(this.$root.apiKeyList);
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
settings() {
|
||||||
|
return this.$parent.$parent.$parent.settings;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -126,7 +139,9 @@ export default {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
disableKey() {
|
disableKey() {
|
||||||
this.$root.getSocket().emit("disableAPIKey", this.selectedKeyID, (res) => {
|
this.$root
|
||||||
|
.getSocket()
|
||||||
|
.emit("disableAPIKey", this.selectedKeyID, (res) => {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -146,22 +161,22 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../assets/vars.scss";
|
@import "../../assets/vars.scss";
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
.item {
|
.item {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
@ -231,9 +246,9 @@ export default {
|
||||||
width: 310px;
|
width: 310px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -246,13 +261,13 @@ export default {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
.item {
|
.item {
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $dark-bg2;
|
background-color: $dark-bg2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -949,5 +949,18 @@
|
||||||
"snmpOIDHelptext": "Enter the OID for the sensor or status you want to monitor. Use network management tools like MIB browsers or SNMP software if you're unsure about the OID.",
|
"snmpOIDHelptext": "Enter the OID for the sensor or status you want to monitor. Use network management tools like MIB browsers or SNMP software if you're unsure about the OID.",
|
||||||
"Condition": "Condition",
|
"Condition": "Condition",
|
||||||
"SNMP Version": "SNMP Version",
|
"SNMP Version": "SNMP Version",
|
||||||
"Please enter a valid OID.": "Please enter a valid OID."
|
"Please enter a valid OID.": "Please enter a valid OID.",
|
||||||
|
"wayToGetThreemaGateway": "You can register for Threema Gateway {0}.",
|
||||||
|
"threemaRecipient": "Recipient",
|
||||||
|
"threemaRecipientType": "Recipient Type",
|
||||||
|
"threemaRecipientTypeIdentity": "Threema-ID",
|
||||||
|
"threemaRecipientTypeIdentityFormat": "8 characters",
|
||||||
|
"threemaRecipientTypePhone": "Phone Number",
|
||||||
|
"threemaRecipientTypePhoneFormat": "E.164, without leading +",
|
||||||
|
"threemaRecipientTypeEmail": "Email Address",
|
||||||
|
"threemaSenderIdentity": "Gateway-ID",
|
||||||
|
"threemaSenderIdentityFormat": "8 characters, usually starts with *",
|
||||||
|
"threemaApiAuthenticationSecret": "Gateway-ID Secret",
|
||||||
|
"threemaBasicModeInfo": "Note: This integration uses Threema Gateway in basic mode (server-based encryption). Further details can be found {0}.",
|
||||||
|
"apiKeysDisabledMsg": "API keys are disabled because authentication is disabled."
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="url">
|
<p class="url">
|
||||||
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'mp-health' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
|
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'mp-health' || monitor.type === 'real-browser' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
|
||||||
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||||
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
||||||
<span v-if="monitor.type === 'keyword'">
|
<span v-if="monitor.type === 'keyword'">
|
||||||
|
|
22
src/util.js
22
src/util.js
|
@ -8,10 +8,14 @@
|
||||||
// Backend uses the compiled file util.js
|
// Backend uses the compiled file util.js
|
||||||
// Frontend uses util.ts
|
// Frontend uses util.ts
|
||||||
*/
|
*/
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (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.debug = 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 = require("dayjs");
|
const dayjs = require("dayjs");
|
||||||
const jsonata = require("jsonata");
|
const jsonata = require("jsonata");
|
||||||
exports.isDev = process.env.NODE_ENV === "development";
|
exports.isDev = process.env.NODE_ENV === "development";
|
||||||
|
@ -153,11 +157,11 @@ class Logger {
|
||||||
module = module.toUpperCase();
|
module = module.toUpperCase();
|
||||||
level = level.toUpperCase();
|
level = level.toUpperCase();
|
||||||
let now;
|
let now;
|
||||||
if (dayjs.tz) {
|
if (dayjs_1.default.tz) {
|
||||||
now = dayjs.tz(new Date()).format();
|
now = dayjs_1.default.tz(new Date()).format();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
now = dayjs().format();
|
now = (0, dayjs_1.default)().format();
|
||||||
}
|
}
|
||||||
const levelColor = consoleLevelColors[level];
|
const levelColor = consoleLevelColors[level];
|
||||||
const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)];
|
const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)];
|
||||||
|
@ -258,11 +262,11 @@ function polyfill() {
|
||||||
exports.polyfill = polyfill;
|
exports.polyfill = polyfill;
|
||||||
class TimeLogger {
|
class TimeLogger {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.startTime = dayjs().valueOf();
|
this.startTime = (0, dayjs_1.default)().valueOf();
|
||||||
}
|
}
|
||||||
print(name) {
|
print(name) {
|
||||||
if (exports.isDev && process.env.TIMELOGGER === "1") {
|
if (exports.isDev && process.env.TIMELOGGER === "1") {
|
||||||
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
|
console.log(name + ": " + ((0, dayjs_1.default)().valueOf() - this.startTime) + "ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,19 +378,19 @@ function parseTimeFromTimeObject(obj) {
|
||||||
}
|
}
|
||||||
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
||||||
function isoToUTCDateTime(input) {
|
function isoToUTCDateTime(input) {
|
||||||
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
return (0, dayjs_1.default)(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
||||||
}
|
}
|
||||||
exports.isoToUTCDateTime = isoToUTCDateTime;
|
exports.isoToUTCDateTime = isoToUTCDateTime;
|
||||||
function utcToISODateTime(input) {
|
function utcToISODateTime(input) {
|
||||||
return dayjs.utc(input).toISOString();
|
return dayjs_1.default.utc(input).toISOString();
|
||||||
}
|
}
|
||||||
exports.utcToISODateTime = utcToISODateTime;
|
exports.utcToISODateTime = utcToISODateTime;
|
||||||
function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) {
|
function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||||
return dayjs.utc(input).local().format(format);
|
return dayjs_1.default.utc(input).local().format(format);
|
||||||
}
|
}
|
||||||
exports.utcToLocal = utcToLocal;
|
exports.utcToLocal = utcToLocal;
|
||||||
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||||
return dayjs(input).utc().format(format);
|
return (0, dayjs_1.default)(input).utc().format(format);
|
||||||
}
|
}
|
||||||
exports.localToUTC = localToUTC;
|
exports.localToUTC = localToUTC;
|
||||||
function intHash(str, length = 10) {
|
function intHash(str, length = 10) {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// Frontend uses util.ts
|
// Frontend uses util.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
// For loading dayjs plugins, don't remove event though it is not used in this file
|
// For loading dayjs plugins, don't remove event though it is not used in this file
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"preserveConstEnums": true,
|
"preserveConstEnums": true,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"strict": true
|
"strict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"./src/util.ts"
|
"./src/util.ts"
|
||||||
|
|
Loading…
Reference in a new issue