Skip to content

Commit 87308a7

Browse files
committed
Merge branch 'master' into fix-weblate-conflict
# Conflicts: # src/lang/bg-BG.json # src/lang/de-CH.json # src/lang/de-DE.json # src/lang/fi.json # src/lang/fr-FR.json # src/lang/it-IT.json # src/lang/ja.json # src/lang/ko-KR.json # src/lang/nl-NL.json # src/lang/pl.json # src/lang/pt-BR.json # src/lang/ru-RU.json # src/lang/uk-UA.json # src/lang/zh-CN.json
2 parents ab41834 + 8d710e2 commit 87308a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2226
-1156
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
exports.up = function (knex) {
2+
return knex.schema
3+
.alterTable("monitor", function (table) {
4+
table.string("smtp_security").defaultTo(null);
5+
});
6+
};
7+
8+
exports.down = function (knex) {
9+
return knex.schema.alterTable("monitor", function (table) {
10+
table.dropColumn("smtp_security");
11+
});
12+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* SQL:
2+
ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
3+
ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
4+
ALTER TABLE monitor ADD ping_per_request_timeout INTEGER default 2 not null;
5+
*/
6+
exports.up = function (knex) {
7+
// Add new columns to table monitor
8+
return knex.schema
9+
.alterTable("monitor", function (table) {
10+
table.integer("ping_count").defaultTo(1).notNullable();
11+
table.boolean("ping_numeric").defaultTo(true).notNullable();
12+
table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
13+
});
14+
15+
};
16+
17+
exports.down = function (knex) {
18+
return knex.schema
19+
.alterTable("monitor", function (table) {
20+
table.dropColumn("ping_count");
21+
table.dropColumn("ping_numeric");
22+
table.dropColumn("ping_per_request_timeout");
23+
});
24+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Add column custom_url to monitor_group table
2+
exports.up = function (knex) {
3+
return knex.schema
4+
.alterTable("monitor_group", function (table) {
5+
table.text("custom_url", "text");
6+
});
7+
};
8+
9+
exports.down = function (knex) {
10+
return knex.schema.alterTable("monitor_group", function (table) {
11+
table.dropColumn("custom_url");
12+
});
13+
};

package-lock.json

Lines changed: 1028 additions & 1001 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "uptime-kuma",
3-
"version": "2.0.0-beta.2",
3+
"version": "2.0.0-beta.3",
44
"license": "MIT",
55
"repository": {
66
"type": "git",
@@ -32,7 +32,7 @@
3232
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
3333
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
3434
"playwright-show-report": "playwright show-report ./private/playwright-report",
35-
"tsc": "tsc",
35+
"tsc": "tsc --project ./tsconfig-backend.json",
3636
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
3737
"build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push",
3838
"build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push",

server/database.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ class Database {
736736
if (Database.dbConfig.type === "sqlite") {
737737
return "DATETIME('now', ? || ' hours')";
738738
} else {
739-
return "DATE_ADD(NOW(), INTERVAL ? HOUR)";
739+
return "DATE_ADD(UTC_TIMESTAMP(), INTERVAL ? HOUR)";
740740
}
741741
}
742742

server/model/group.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class Group extends BeanModel {
3333
*/
3434
async getMonitorList() {
3535
return R.convertToBeans("monitor", await R.getAll(`
36-
SELECT monitor.*, monitor_group.send_url FROM monitor, monitor_group
36+
SELECT monitor.*, monitor_group.send_url, monitor_group.custom_url FROM monitor, monitor_group
3737
WHERE monitor.id = monitor_group.monitor_id
3838
AND group_id = ?
3939
ORDER BY monitor_group.weight

server/model/monitor.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ const dayjs = require("dayjs");
22
const axios = require("axios");
33
const { Prometheus } = require("../prometheus");
44
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
5-
SQL_DATETIME_FORMAT, evaluateJsonQuery
5+
SQL_DATETIME_FORMAT, evaluateJsonQuery,
6+
PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX, PING_PACKET_SIZE_DEFAULT,
7+
PING_GLOBAL_TIMEOUT_MIN, PING_GLOBAL_TIMEOUT_MAX, PING_GLOBAL_TIMEOUT_DEFAULT,
8+
PING_COUNT_MIN, PING_COUNT_MAX, PING_COUNT_DEFAULT,
9+
PING_PER_REQUEST_TIMEOUT_MIN, PING_PER_REQUEST_TIMEOUT_MAX, PING_PER_REQUEST_TIMEOUT_DEFAULT
610
} = require("../../src/util");
711
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
812
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@@ -53,7 +57,7 @@ class Monitor extends BeanModel {
5357
};
5458

5559
if (this.sendUrl) {
56-
obj.url = this.url;
60+
obj.url = this.customUrl ?? this.url;
5761
}
5862

5963
if (showTags) {
@@ -153,8 +157,14 @@ class Monitor extends BeanModel {
153157
snmpOid: this.snmpOid,
154158
jsonPathOperator: this.jsonPathOperator,
155159
snmpVersion: this.snmpVersion,
160+
smtpSecurity: this.smtpSecurity,
156161
rabbitmqNodes: JSON.parse(this.rabbitmqNodes),
157162
conditions: JSON.parse(this.conditions),
163+
164+
// ping advanced options
165+
ping_numeric: this.isPingNumeric(),
166+
ping_count: this.ping_count,
167+
ping_per_request_timeout: this.ping_per_request_timeout,
158168
};
159169

160170
if (includeSensitiveData) {
@@ -247,6 +257,14 @@ class Monitor extends BeanModel {
247257
return Boolean(this.expiryNotification);
248258
}
249259

260+
/**
261+
* Check if ping should use numeric output only
262+
* @returns {boolean} True if IP addresses will be output instead of symbolic hostnames
263+
*/
264+
isPingNumeric() {
265+
return Boolean(this.ping_numeric);
266+
}
267+
250268
/**
251269
* Parse to boolean
252270
* @returns {boolean} Should TLS errors be ignored?
@@ -584,7 +602,7 @@ class Monitor extends BeanModel {
584602
bean.status = UP;
585603

586604
} else if (this.type === "ping") {
587-
bean.ping = await ping(this.hostname, this.packetSize);
605+
bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.timeout, this.ping_per_request_timeout);
588606
bean.msg = "";
589607
bean.status = UP;
590608
} else if (this.type === "push") { // Type: Push
@@ -656,7 +674,7 @@ class Monitor extends BeanModel {
656674
bean.msg = res.data.response.servers[0].name;
657675

658676
try {
659-
bean.ping = await ping(this.hostname, this.packetSize);
677+
bean.ping = await ping(this.hostname, PING_COUNT_DEFAULT, "", true, this.packetSize, PING_GLOBAL_TIMEOUT_DEFAULT, PING_PER_REQUEST_TIMEOUT_DEFAULT);
660678
} catch (_) { }
661679
} else {
662680
throw new Error("Server not found on Steam");
@@ -1294,7 +1312,8 @@ class Monitor extends BeanModel {
12941312
try {
12951313
const heartbeatJSON = bean.toJSON();
12961314
const monitorData = [{ id: monitor.id,
1297-
active: monitor.active
1315+
active: monitor.active,
1316+
name: monitor.name
12981317
}];
12991318
const preloadData = await Monitor.preparePreloadData(monitorData);
13001319
// Prevent if the msg is undefined, notifications such as Discord cannot send out.
@@ -1467,6 +1486,31 @@ class Monitor extends BeanModel {
14671486
if (this.interval < MIN_INTERVAL_SECOND) {
14681487
throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
14691488
}
1489+
1490+
if (this.type === "ping") {
1491+
// ping parameters validation
1492+
if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
1493+
throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
1494+
}
1495+
1496+
if (this.ping_per_request_timeout && (this.ping_per_request_timeout < PING_PER_REQUEST_TIMEOUT_MIN || this.ping_per_request_timeout > PING_PER_REQUEST_TIMEOUT_MAX)) {
1497+
throw new Error(`Per-ping timeout must be between ${PING_PER_REQUEST_TIMEOUT_MIN} and ${PING_PER_REQUEST_TIMEOUT_MAX} seconds (default: ${PING_PER_REQUEST_TIMEOUT_DEFAULT})`);
1498+
}
1499+
1500+
if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
1501+
throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})`);
1502+
}
1503+
1504+
if (this.timeout) {
1505+
const pingGlobalTimeout = Math.round(Number(this.timeout));
1506+
1507+
if (pingGlobalTimeout < this.ping_per_request_timeout || pingGlobalTimeout < PING_GLOBAL_TIMEOUT_MIN || pingGlobalTimeout > PING_GLOBAL_TIMEOUT_MAX) {
1508+
throw new Error(`Timeout must be between ${PING_GLOBAL_TIMEOUT_MIN} and ${PING_GLOBAL_TIMEOUT_MAX} seconds (default: ${PING_GLOBAL_TIMEOUT_DEFAULT})`);
1509+
}
1510+
1511+
this.timeout = pingGlobalTimeout;
1512+
}
1513+
}
14701514
}
14711515

14721516
/**

server/monitor-types/smtp.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const { MonitorType } = require("./monitor-type");
2+
const { UP } = require("../../src/util");
3+
const nodemailer = require("nodemailer");
4+
5+
class SMTPMonitorType extends MonitorType {
6+
name = "smtp";
7+
8+
/**
9+
* @inheritdoc
10+
*/
11+
async check(monitor, heartbeat, _server) {
12+
let options = {
13+
port: monitor.port || 25,
14+
host: monitor.hostname,
15+
secure: monitor.smtpSecurity === "secure", // use SMTPS (not STARTTLS)
16+
ignoreTLS: monitor.smtpSecurity === "nostarttls", // don't use STARTTLS even if it's available
17+
requireTLS: monitor.smtpSecurity === "starttls", // use STARTTLS or fail
18+
};
19+
let transporter = nodemailer.createTransport(options);
20+
try {
21+
await transporter.verify();
22+
23+
heartbeat.status = UP;
24+
heartbeat.msg = "SMTP connection verifies successfully";
25+
} catch (e) {
26+
throw new Error(`SMTP connection doesn't verify: ${e}`);
27+
} finally {
28+
transporter.close();
29+
}
30+
}
31+
}
32+
33+
module.exports = {
34+
SMTPMonitorType,
35+
};

server/notification-providers/discord.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ class Discord extends NotificationProvider {
4646
name: "Service Name",
4747
value: monitorJSON["name"],
4848
},
49-
{
49+
...(!notification.disableUrl ? [{
5050
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
5151
value: this.extractAddress(monitorJSON),
52-
},
52+
}] : []),
5353
{
5454
name: `Time (${heartbeatJSON["timezone"]})`,
5555
value: heartbeatJSON["localDateTime"],
@@ -83,10 +83,10 @@ class Discord extends NotificationProvider {
8383
name: "Service Name",
8484
value: monitorJSON["name"],
8585
},
86-
{
86+
...(!notification.disableUrl ? [{
8787
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
8888
value: this.extractAddress(monitorJSON),
89-
},
89+
}] : []),
9090
{
9191
name: `Time (${heartbeatJSON["timezone"]})`,
9292
value: heartbeatJSON["localDateTime"],

server/notification-providers/flashduty.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ class FlashDuty extends NotificationProvider {
7373
}
7474
const options = {
7575
method: "POST",
76-
url: "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey,
76+
url: notification.flashdutyIntegrationKey.startsWith("http") ? notification.flashdutyIntegrationKey : "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey,
7777
headers: { "Content-Type": "application/json" },
7878
data: {
7979
description: `[${title}] [${monitorInfo.name}] ${body}`,
8080
title,
8181
event_status: eventStatus || "Info",
82-
alert_key: String(monitorInfo.id) || Math.random().toString(36).substring(7),
82+
alert_key: monitorInfo.id ? String(monitorInfo.id) : Math.random().toString(36).substring(7),
8383
labels,
8484
}
8585
};

server/notification-providers/mattermost.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,11 @@ class Mattermost extends NotificationProvider {
7979
fallback:
8080
"Your " +
8181
monitorJSON.pathName +
82-
monitorJSON.name +
8382
" service went " +
8483
statusText,
8584
color: color,
8685
title:
8786
monitorJSON.pathName +
88-
monitorJSON.name +
8987
" service went " +
9088
statusText,
9189
title_link: monitorJSON.url,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { getMonitorRelativeURL, UP } = require("../../src/util");
2+
const { setting } = require("../util-server");
3+
const NotificationProvider = require("./notification-provider");
4+
const axios = require("axios");
5+
6+
class Notifery extends NotificationProvider {
7+
name = "notifery";
8+
9+
/**
10+
* @inheritdoc
11+
*/
12+
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
13+
const okMsg = "Sent Successfully.";
14+
const url = "https://api.notifery.com/event";
15+
16+
let data = {
17+
title: notification.notiferyTitle || "Uptime Kuma Alert",
18+
message: msg,
19+
};
20+
21+
if (notification.notiferyGroup) {
22+
data.group = notification.notiferyGroup;
23+
}
24+
25+
// Link to the monitor
26+
const baseURL = await setting("primaryBaseURL");
27+
if (baseURL && monitorJSON) {
28+
data.message += `\n\nMonitor: ${baseURL}${getMonitorRelativeURL(monitorJSON.id)}`;
29+
}
30+
31+
if (heartbeatJSON) {
32+
data.code = heartbeatJSON.status === UP ? 0 : 1;
33+
34+
if (heartbeatJSON.ping) {
35+
data.duration = heartbeatJSON.ping;
36+
}
37+
}
38+
39+
try {
40+
const headers = {
41+
"Content-Type": "application/json",
42+
"x-api-key": notification.notiferyApiKey,
43+
};
44+
45+
await axios.post(url, data, { headers });
46+
return okMsg;
47+
} catch (error) {
48+
this.throwGeneralAxiosError(error);
49+
}
50+
}
51+
}
52+
53+
module.exports = Notifery;

0 commit comments

Comments
 (0)