Compare commits

...

14 commits

Author SHA1 Message Date
UptimeKumaBot
e18be0860a
Merge 7eefcd743d into c01494ec33 2024-10-20 13:57:15 +00:00
Suven-p
c01494ec33
feat: add RabbitMQ monitor (#5199)
Some checks failed
Auto Test / auto-test (18, ARM64) (push) Has been cancelled
Auto Test / auto-test (18, macos-latest) (push) Has been cancelled
Auto Test / auto-test (18, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (18, windows-latest) (push) Has been cancelled
Auto Test / auto-test (20, ARM64) (push) Has been cancelled
Auto Test / auto-test (20, macos-latest) (push) Has been cancelled
Auto Test / auto-test (20, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (20, windows-latest) (push) Has been cancelled
Auto Test / armv7-simple-test (18, ARMv7) (push) Has been cancelled
Auto Test / armv7-simple-test (20, ARMv7) (push) Has been cancelled
Auto Test / check-linters (push) Has been cancelled
Auto Test / e2e-test (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
json-yaml-validate / json-yaml-validate (push) Has been cancelled
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-10-20 15:57:03 +02:00
Ryo Hanafusa
a7e9bdd43e
fix: expand hover trigger area of beat (#5223)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-10-20 15:30:03 +02:00
KEINOS
7eefcd743d Translated using Weblate (Japanese)
Currently translated at 64.0% (674 of 1053 strings)

Co-authored-by: KEINOS <github@keinos.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
Ryo Hanafusa
b779551ec9 Translated using Weblate (Japanese)
Currently translated at 64.0% (674 of 1053 strings)

Co-authored-by: Ryo Hanafusa <ryo7gumi@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
Ivan Bratović
89140f2f9b Translated using Weblate (Croatian)
Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: Ivan Bratović <ivanbratovic4@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hr/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
stanol
303b57eb59 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: stanol <stanol777@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
Marco
79d8d492e3 Translated using Weblate (German)
Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
AnnAngela
4e35a67a0d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1053 of 1053 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1053 of 1053 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.5% (1017 of 1053 strings)

Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:01 +00:00
Harry Suryapambagya
7e7621e09c Translated using Weblate (Indonesian)
Currently translated at 97.0% (1022 of 1053 strings)

Co-authored-by: Harry Suryapambagya <harsxv@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:00 +00:00
Peace
fa926e0043 Translated using Weblate (German)
Currently translated at 99.3% (1046 of 1053 strings)

Co-authored-by: Peace <Debitor.TooLP@gmx.de>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:00 +00:00
Marco
99fc604061 Translated using Weblate (German (Switzerland))
Currently translated at 100.0% (1053 of 1053 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 96.7% (1019 of 1053 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de_CH/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:00 +00:00
Cyril59310
7582ea826f Translated using Weblate (French)
Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:00 +00:00
MrEddX
f883ae8f41 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (1053 of 1053 strings)

Translated using Weblate (Bulgarian)

Currently translated at 98.9% (1042 of 1053 strings)

Co-authored-by: MrEddX <mreddx@chatrix.one>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bg/
Translation: Uptime Kuma/Uptime Kuma
2024-10-17 13:08:00 +00:00
20 changed files with 632 additions and 48 deletions

View file

@ -0,0 +1,17 @@
exports.up = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.text("rabbitmq_nodes");
table.string("rabbitmq_username");
table.string("rabbitmq_password");
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("rabbitmq_nodes");
table.dropColumn("rabbitmq_username");
table.dropColumn("rabbitmq_password");
});
};

17
package-lock.json generated
View file

@ -93,6 +93,7 @@
"@playwright/test": "~1.39.0",
"@popperjs/core": "~2.10.2",
"@testcontainers/hivemq": "^10.13.1",
"@testcontainers/rabbitmq": "^10.13.2",
"@types/bootstrap": "~5.1.9",
"@types/node": "^20.8.6",
"@typescript-eslint/eslint-plugin": "^6.7.5",
@ -4172,6 +4173,15 @@
"testcontainers": "^10.13.1"
}
},
"node_modules/@testcontainers/rabbitmq": {
"version": "10.13.2",
"resolved": "https://registry.npmjs.org/@testcontainers/rabbitmq/-/rabbitmq-10.13.2.tgz",
"integrity": "sha512-npBKBnq3c6hETmxGZ/gVMke9cc1J/pcftNW9S3WidL48hxFBIPjYNM9FdTfWuoNER/8kuf4xJ8yCuJEYGH3ZAg==",
"dev": true,
"dependencies": {
"testcontainers": "^10.13.2"
}
},
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@ -15925,11 +15935,10 @@
}
},
"node_modules/testcontainers": {
"version": "10.13.1",
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.13.1.tgz",
"integrity": "sha512-JBbOhxmygj/ouH/47GnoVNt+c55Telh/45IjVxEbDoswsLchVmJiuKiw/eF6lE5i7LN+/99xsrSCttI3YRtirg==",
"version": "10.13.2",
"resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.13.2.tgz",
"integrity": "sha512-LfEll+AG/1Ks3n4+IA5lpyBHLiYh/hSfI4+ERa6urwfQscbDU+M2iW1qPQrHQi+xJXQRYy4whyK1IEHdmxWa3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@balena/dockerignore": "^1.0.2",
"@types/dockerode": "^3.3.29",

View file

@ -155,6 +155,7 @@
"@playwright/test": "~1.39.0",
"@popperjs/core": "~2.10.2",
"@testcontainers/hivemq": "^10.13.1",
"@testcontainers/rabbitmq": "^10.13.2",
"@types/bootstrap": "~5.1.9",
"@types/node": "^20.8.6",
"@typescript-eslint/eslint-plugin": "^6.7.5",

View file

@ -153,6 +153,7 @@ class Monitor extends BeanModel {
snmpOid: this.snmpOid,
jsonPathOperator: this.jsonPathOperator,
snmpVersion: this.snmpVersion,
rabbitmqNodes: JSON.parse(this.rabbitmqNodes),
conditions: JSON.parse(this.conditions),
};
@ -183,6 +184,8 @@ class Monitor extends BeanModel {
tlsCert: this.tlsCert,
tlsKey: this.tlsKey,
kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions),
rabbitmqUsername: this.rabbitmqUsername,
rabbitmqPassword: this.rabbitmqPassword,
};
}

View file

@ -0,0 +1,67 @@
const { MonitorType } = require("./monitor-type");
const { log, UP, DOWN } = require("../../src/util");
const { axiosAbortSignal } = require("../util-server");
const axios = require("axios");
class RabbitMqMonitorType extends MonitorType {
name = "rabbitmq";
/**
* @inheritdoc
*/
async check(monitor, heartbeat, server) {
let baseUrls = [];
try {
baseUrls = JSON.parse(monitor.rabbitmqNodes);
} catch (error) {
throw new Error("Invalid RabbitMQ Nodes");
}
heartbeat.status = DOWN;
for (let baseUrl of baseUrls) {
try {
// Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com
if ( !baseUrl.endsWith("/") ) {
baseUrl += "/";
}
const options = {
// Do not start with slash, it will strip the trailing slash from baseUrl
url: new URL("api/health/checks/alarms/", baseUrl).href,
method: "get",
timeout: monitor.timeout * 1000,
headers: {
"Accept": "application/json",
"Authorization": "Basic " + Buffer.from(`${monitor.rabbitmqUsername || ""}:${monitor.rabbitmqPassword || ""}`).toString("base64"),
},
signal: axiosAbortSignal((monitor.timeout + 10) * 1000),
// Capture reason for 503 status
validateStatus: (status) => status === 200 || status === 503,
};
log.debug("monitor", `[${monitor.name}] Axios Request: ${JSON.stringify(options)}`);
const res = await axios.request(options);
log.debug("monitor", `[${monitor.name}] Axios Response: status=${res.status} body=${JSON.stringify(res.data)}`);
if (res.status === 200) {
heartbeat.status = UP;
heartbeat.msg = "OK";
break;
} else if (res.status === 503) {
heartbeat.msg = res.data.reason;
} else {
heartbeat.msg = `${res.status} - ${res.statusText}`;
}
} catch (error) {
if (axios.isCancel(error)) {
heartbeat.msg = "Request timed out";
log.debug("monitor", `[${monitor.name}] Request timed out`);
} else {
log.debug("monitor", `[${monitor.name}] Axios Error: ${JSON.stringify(error.message)}`);
heartbeat.msg = error.message;
}
}
}
}
}
module.exports = {
RabbitMqMonitorType,
};

View file

@ -718,6 +718,8 @@ let needSetup = false;
monitor.conditions = JSON.stringify(monitor.conditions);
monitor.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes);
bean.import(monitor);
bean.user_id = socket.userID;
@ -868,6 +870,9 @@ let needSetup = false;
bean.snmpOid = monitor.snmpOid;
bean.jsonPathOperator = monitor.jsonPathOperator;
bean.timeout = monitor.timeout;
bean.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes);
bean.rabbitmqUsername = monitor.rabbitmqUsername;
bean.rabbitmqPassword = monitor.rabbitmqPassword;
bean.conditions = JSON.stringify(monitor.conditions);
bean.validate();

View file

@ -115,6 +115,7 @@ class UptimeKumaServer {
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
// Allow all CORS origins (polling) in development
let cors = undefined;
@ -552,4 +553,5 @@ const { DnsMonitorType } = require("./monitor-types/dns");
const { MqttMonitorType } = require("./monitor-types/mqtt");
const { SNMPMonitorType } = require("./monitor-types/snmp");
const { MongodbMonitorType } = require("./monitor-types/mongodb");
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
const Monitor = require("./model/monitor");

View file

@ -4,11 +4,17 @@
<div
v-for="(beat, index) in shortBeatList"
:key="index"
class="beat"
:class="{ 'empty': (beat === 0), 'down': (beat.status === 0), 'pending': (beat.status === 2), 'maintenance': (beat.status === 3) }"
:style="beatStyle"
class="beat-hover-area"
:class="{ 'empty': (beat === 0) }"
:style="beatHoverAreaStyle"
:title="getBeatTitle(beat)"
/>
>
<div
class="beat"
:class="{ 'empty': (beat === 0), 'down': (beat.status === 0), 'pending': (beat.status === 2), 'maintenance': (beat.status === 3) }"
:style="beatStyle"
/>
</div>
</div>
<div
v-if="!$root.isMobile && size !== 'small' && beatList.length > 4 && $root.styleElapsedTime !== 'none'"
@ -47,7 +53,7 @@ export default {
beatWidth: 10,
beatHeight: 30,
hoverScale: 1.5,
beatMargin: 4,
beatHoverAreaPadding: 4,
move: false,
maxBeat: -1,
};
@ -123,7 +129,7 @@ export default {
barStyle() {
if (this.move && this.shortBeatList.length > this.maxBeat) {
let width = -(this.beatWidth + this.beatMargin * 2);
let width = -(this.beatWidth + this.beatHoverAreaPadding * 2);
return {
transition: "all ease-in-out 0.25s",
@ -137,12 +143,17 @@ export default {
},
beatHoverAreaStyle() {
return {
padding: this.beatHoverAreaPadding + "px",
"--hover-scale": this.hoverScale,
};
},
beatStyle() {
return {
width: this.beatWidth + "px",
height: this.beatHeight + "px",
margin: this.beatMargin + "px",
"--hover-scale": this.hoverScale,
};
},
@ -152,7 +163,7 @@ export default {
*/
timeStyle() {
return {
"margin-left": this.numPadding * (this.beatWidth + this.beatMargin * 2) + "px",
"margin-left": this.numPadding * (this.beatWidth + this.beatHoverAreaPadding * 2) + "px",
};
},
@ -219,20 +230,20 @@ export default {
if (this.size !== "big") {
this.beatWidth = 5;
this.beatHeight = 16;
this.beatMargin = 2;
this.beatHoverAreaPadding = 2;
}
// Suddenly, have an idea how to handle it universally.
// If the pixel * ratio != Integer, then it causes render issue, round it to solve it!!
const actualWidth = this.beatWidth * window.devicePixelRatio;
const actualMargin = this.beatMargin * window.devicePixelRatio;
const actualHoverAreaPadding = this.beatHoverAreaPadding * window.devicePixelRatio;
if (!Number.isInteger(actualWidth)) {
this.beatWidth = Math.round(actualWidth) / window.devicePixelRatio;
}
if (!Number.isInteger(actualMargin)) {
this.beatMargin = Math.round(actualMargin) / window.devicePixelRatio;
if (!Number.isInteger(actualHoverAreaPadding)) {
this.beatHoverAreaPadding = Math.round(actualHoverAreaPadding) / window.devicePixelRatio;
}
window.addEventListener("resize", this.resize);
@ -245,7 +256,7 @@ export default {
*/
resize() {
if (this.$refs.wrap) {
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2));
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2));
}
},
@ -273,32 +284,41 @@ export default {
}
.hp-bar-big {
.beat {
.beat-hover-area {
display: inline-block;
background-color: $primary;
border-radius: $border-radius;
&.empty {
background-color: aliceblue;
}
&.down {
background-color: $danger;
}
&.pending {
background-color: $warning;
}
&.maintenance {
background-color: $maintenance;
}
&:not(.empty):hover {
transition: all ease-in-out 0.15s;
opacity: 0.8;
transform: scale(var(--hover-scale));
}
.beat {
background-color: $primary;
border-radius: $border-radius;
/*
pointer-events needs to be changed because
tooltip momentarily disappears when crossing between .beat-hover-area and .beat
*/
pointer-events: none;
&.empty {
background-color: aliceblue;
}
&.down {
background-color: $danger;
}
&.pending {
background-color: $warning;
}
&.maintenance {
background-color: $maintenance;
}
}
}
}

View file

@ -1050,5 +1050,43 @@
"less than": "по-малко от",
"greater than": "по-голямо от",
"greater than or equal to": "по-голямо или равно на",
"record": "запис"
"record": "запис",
"CurlDebugInfo": "За да отстраните грешки в монитора, можете или да поставите това в терминала на Вашата машина, или в терминала на машината, на която работи \"Uptime Kuma\" и да видите заявката.{newiline}Моля, вземете под внимание мрежовите разлики като {firewalls}, {dns_resolvers} или {docker_networks}.",
"shrinkDatabaseDescriptionSqlite": "Инициира {vacuum} за база данни тип SQLite. Функцията {auto_vacuum} вече е активирана, но това не дефрагментира базата данни, нито препакетира отделните страници на базата данни по начина, по който го прави командата {vacuum}.",
"ignoredTLSError": "TLS/SSL грешките са игнорирани",
"Debug": "Отстраняване на грешки",
"Copy": "Копирай",
"CopyToClipboardError": "Неуспешно копиране в клипборда: {error}",
"CopyToClipboardSuccess": "Копирано!",
"firewalls": "защитни стени",
"dns resolvers": "DNS преобразуватели",
"docker networks": "docker мрежи",
"CurlDebugInfoOAuth2CCUnsupported": "Пълният oauth клиентски идентификационен поток не се поддържа в {curl}.{newline}Моля, вземете токен на носител и го предайте чрез опцията {oauth2_bearer}.",
"CurlDebugInfoProxiesUnsupported": "Поддръжката на прокси в горната команда {curl} в момента не е внедрена.",
"Message format": "Формат на съобщението",
"Send rich messages": "Изпращай съобщения в \"rich\" формат",
"Sound": "Звук",
"Notification Channel": "Канал за известяване",
"Alphanumerical string and hyphens only": "Само буквено-цифров низ и тирета",
"Arcade": "Arcade",
"Correct": "Правилно",
"Fail": "Грешка",
"Harp": "Арфа",
"Reveal": "Разкрий",
"Bubble": "Балон",
"Doorbell": "Звънец на врата",
"Flute": "Флейта",
"Money": "Пари",
"Scifi": "Nаучна фантастика",
"Clear": "Премахни",
"Elevator": "Асансьор",
"Guitar": "Китара",
"Pop": "Поп",
"Custom sound to override default notification sound": "Персонализиран звук, заменящ звука за известяване по подразбиране",
"Time Sensitive (iOS Only)": "Зависещи от часа (само за iOS)",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Известията от типа \"Зависещи от часа\" ще бъдат доставени незабавно, дори ако устройството е в режим „Не безпокойте“.",
"From": "От",
"Can be found on:": "Можте да се откриете на: {0}",
"The phone number of the recipient in E.164 format.": "Телефонният номер на получателя във формат E.164.",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Идентификационен номер на подателя на текста или телефонен номер във формат E.164, в случай, че желаете да получавате отговори."
}

View file

@ -1048,5 +1048,42 @@
"less than or equal to": "kleiner als oder gleich",
"greater than or equal to": "grösser als oder gleich",
"record": "Eintrag",
"shrinkDatabaseDescriptionSqlite": "Datenbank {vacuum} für SQLite auslösen. {auto_vacuum} ist bereits aktiviert, aber dies defragmentiert die Datenbank nicht und packt auch nicht einzelne Datenbankseiten neu, wie es der Befehl {vacuum} tut."
"shrinkDatabaseDescriptionSqlite": "Datenbank {vacuum} für SQLite auslösen. {auto_vacuum} ist bereits aktiviert, aber dies defragmentiert die Datenbank nicht und packt auch nicht einzelne Datenbankseiten neu, wie es der Befehl {vacuum} tut.",
"ignoredTLSError": "TLS/SSL-Fehler wurden ignoriert",
"Debug": "Debug",
"Copy": "Kopieren",
"CurlDebugInfoOAuth2CCUnsupported": "Der vollständige OAuth-Client-Credential-Flow wird in {curl} nicht unterstützt.{newline}Bitte besorge dir ein Bearer-Token und übergebe ihn über die {oauth2_bearer}-Option.",
"Money": "Geld",
"Custom sound to override default notification sound": "Benutzerdefinierter Ton, um den standardmässigen Benachrichtigungston zu ersetzen",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Entweder eine Text-Absender-ID oder eine Telefonnummer im E.164-Format, wenn du Antworten erhalten möchtest.",
"CopyToClipboardError": "Konnte nicht in die Zwischenablage kopiert werden: {error}",
"CopyToClipboardSuccess": "Kopiert!",
"CurlDebugInfo": "Um den Monitor zu debuggen, kannst du dies entweder in das Terminal deines eigenen Rechners oder in das Terminal der Maschine, auf der Uptime Kuma läuft, einfügen und überprüfen, was du anforderst.{newiline}Bitte beachte Netzwerkunterschiede wie {firewalls}, {dns_resolvers} oder {docker_networks}.",
"firewalls": "Firewalls",
"dns resolvers": "DNS-Resolver",
"docker networks": "Docker-Netzwerke",
"CurlDebugInfoProxiesUnsupported": "Die Unterstützung von Proxys im oben genannten {curl}-Befehl ist derzeit nicht implementiert.",
"Message format": "Nachrichtenformat",
"Send rich messages": "Sende Rich-Text-Nachrichten",
"Notification Channel": "Benachrichtigungskanal",
"Sound": "Benachrichtigungston",
"Alphanumerical string and hyphens only": "Nur alphanumerische Zeichen und Bindestriche",
"Correct": "Korrekt",
"Fail": "Fehlgeschlagen",
"Harp": "Harfe",
"Reveal": "Enthüllen",
"Bubble": "Blase",
"Doorbell": "Türklingel",
"Flute": "Flöte",
"Scifi": "Science Fiction",
"Clear": "Klar",
"Elevator": "Lift",
"Guitar": "Gitarre",
"Pop": "Pop",
"Time Sensitive (iOS Only)": "Zeitkritisch (nur iOS)",
"The phone number of the recipient in E.164 format.": "Die Telefonnummer des Empfängers im E.164-Format.",
"Can be found on:": "Ist zu finden auf: {0}",
"From": "Von",
"Arcade": "Spielhalle",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Zeitkritische Benachrichtigungen werden sofort zugestellt, auch wenn sich das Gerät im Nicht stören-Modus befindet."
}

View file

@ -1051,5 +1051,42 @@
"less than or equal to": "kleiner als oder gleich",
"greater than or equal to": "größer als oder gleich",
"record": "Eintrag",
"shrinkDatabaseDescriptionSqlite": "Datenbank {vacuum} für SQLite auslösen. {auto_vacuum} ist bereits aktiviert, aber dies defragmentiert die Datenbank nicht und packt auch nicht einzelne Datenbankseiten neu, wie es der Befehl {vacuum} tut."
"shrinkDatabaseDescriptionSqlite": "Datenbank {vacuum} für SQLite auslösen. {auto_vacuum} ist bereits aktiviert, aber dies defragmentiert die Datenbank nicht und packt auch nicht einzelne Datenbankseiten neu, wie es der Befehl {vacuum} tut.",
"ignoredTLSError": "TLS/SSL-Fehler wurden ignoriert",
"Message format": "Nachrichtenformat",
"Notification Channel": "Benachrichtigungskanal",
"Custom sound to override default notification sound": "Benutzerdefinierter Ton, um den standardmäßigen Benachrichtigungston zu ersetzen",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Zeitkritische Benachrichtigungen werden sofort zugestellt, auch wenn sich das Gerät im Nicht stören-Modus befindet.",
"Debug": "Debug",
"Copy": "Kopieren",
"CopyToClipboardError": "Konnte nicht in die Zwischenablage kopiert werden: {error}",
"CopyToClipboardSuccess": "Kopiert!",
"CurlDebugInfo": "Um den Monitor zu debuggen, kannst du dies entweder in das Terminal deines eigenen Rechners oder in das Terminal der Maschine, auf der Uptime Kuma läuft, einfügen und überprüfen, was du anforderst.{newiline}Bitte beachte Netzwerkunterschiede wie {firewalls}, {dns_resolvers} oder {docker_networks}.",
"firewalls": "Firewalls",
"dns resolvers": "DNS-Resolver",
"docker networks": "Docker-Netzwerke",
"CurlDebugInfoOAuth2CCUnsupported": "Der vollständige OAuth-Client-Credential-Flow wird in {curl} nicht unterstützt.{newline}Bitte besorge dir ein Bearer-Token und übergebe ihn über die {oauth2_bearer}-Option.",
"CurlDebugInfoProxiesUnsupported": "Die Unterstützung von Proxys im oben genannten {curl}-Befehl ist derzeit nicht implementiert.",
"Alphanumerical string and hyphens only": "Nur alphanumerische Zeichen und Bindestriche",
"Correct": "Korrekt",
"Harp": "Harfe",
"Doorbell": "Türklingel",
"Flute": "Flöte",
"Money": "Geld",
"Scifi": "Science Fiction",
"Elevator": "Aufzug",
"Guitar": "Gitarre",
"Sound": "Benachrichtigungston",
"Time Sensitive (iOS Only)": "Zeitkritisch (nur iOS)",
"From": "Von",
"Can be found on:": "Ist zu finden auf: {0}",
"The phone number of the recipient in E.164 format.": "Die Telefonnummer des Empfängers im E.164-Format.",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Entweder eine Text-Absender-ID oder eine Telefonnummer im E.164-Format, wenn du Antworten erhalten möchtest.",
"Send rich messages": "Sende Rich-Text-Nachrichten",
"Fail": "Fehlgeschlagen",
"Reveal": "Enthüllen",
"Bubble": "Blase",
"Clear": "Klar",
"Pop": "Pop",
"Arcade": "Spielhalle"
}

View file

@ -1052,6 +1052,13 @@
"Can be found on:": "Can be found on: {0}",
"The phone number of the recipient in E.164 format.": "The phone number of the recipient in E.164 format.",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.":"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.",
"RabbitMQ Nodes": "RabbitMQ Management Nodes",
"rabbitmqNodesDescription": "Enter the URL for the RabbitMQ management nodes including protocol and port. Example: {0}",
"rabbitmqNodesRequired": "Please set the nodes for this monitor.",
"rabbitmqNodesInvalid": "Please use a fully qualified (starting with 'http') URL for RabbitMQ nodes.",
"RabbitMQ Username": "RabbitMQ Username",
"RabbitMQ Password": "RabbitMQ Password",
"rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
"SendGrid API Key": "SendGrid API Key",
"Separate multiple email addresses with commas": "Separate multiple email addresses with commas"
}

View file

@ -1051,5 +1051,42 @@
"less than or equal to": "inférieur ou égal à",
"greater than or equal to": "supérieur ou égal à",
"record": "enregistrer",
"shrinkDatabaseDescriptionSqlite": "Déclencher la commande {vacuum} pour la base de données SQLite. {auto_vacuum} est déjà activé, mais cela ne défragmente pas la base de données ni ne réorganise les pages individuelles de la base de données de la même manière que la commande {vacuum}."
"shrinkDatabaseDescriptionSqlite": "Déclencher la commande {vacuum} pour la base de données SQLite. {auto_vacuum} est déjà activé, mais cela ne défragmente pas la base de données ni ne réorganise les pages individuelles de la base de données de la même manière que la commande {vacuum}.",
"ignoredTLSError": "Les erreurs TLS/SSL ont été ignorées",
"CopyToClipboardError": "Impossible de copier dans Clipbard : {error}",
"CurlDebugInfo": "Pour déboguer la sonde, vous pouvez soit le coller dans le terminal de votre propre machine, soit dans le terminal de la machine sur lequel kuma de disponibilité s'exécute et voir ce qui vous est demandé. {newiline} Veuillez être conscient des différences de réseau telles que {firewalls}, {dns_resolvers}. ou {docker_networks}.",
"Notification Channel": "Canal de notification",
"Alphanumerical string and hyphens only": "Chaîne alphanumérique et tirets uniquement",
"Custom sound to override default notification sound": "Son personnalisé pour remplacer le son de notification par défaut",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Les notifications urgentes seront envoyées immédiatement, même si l'appareil est en mode Ne pas déranger.",
"Debug": "Déboguer",
"Copy": "Copie",
"CopyToClipboardSuccess": "Copié !",
"firewalls": "pare-feu",
"dns resolvers": "résolveurs DNS",
"docker networks": "réseaux dockers",
"CurlDebugInfoOAuth2CCUnsupported": "Le flux complet des informations d'identification du client OAuth n'est pas pris en charge dans {curl}. {newline}Veuillez obtenir un jeton d'accès bearer token et le transmettre via l'option {oauth2_bearer}.",
"CurlDebugInfoProxiesUnsupported": "La prise en charge du proxy dans la commande {curl} ci-dessus n'est actuellement pas implémentée.",
"Message format": "Format des messages",
"Send rich messages": "Envoyer des messages enrichis",
"Sound": "Son",
"Arcade": "Arcade",
"Correct": "Correcte",
"Fail": "Échouer",
"Harp": "Harpe",
"Reveal": "Révéler",
"Bubble": "Bulle",
"Doorbell": "Sonnette",
"Flute": "Flûte",
"Money": "Argent",
"Scifi": "Science-fiction",
"Clear": "Effacer",
"Elevator": "Ascenseur",
"Guitar": "Guitare",
"Pop": "Pop",
"Time Sensitive (iOS Only)": "Sensible au temps (iOS uniquement)",
"From": "De",
"Can be found on:": "Disponible sur : {0}",
"The phone number of the recipient in E.164 format.": "Le numéro de téléphone du destinataire au format E.164.",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Soit un identifiant d'expéditeur de texte, soit un numéro de téléphone au format E.164 si vous souhaitez pouvoir recevoir des réponses."
}

View file

@ -983,5 +983,104 @@
"threemaRecipientTypePhone": "Telefonski broj",
"threemaRecipientTypePhoneFormat": "E.164, bez vodećeg znaka +",
"threemaBasicModeInfo": "Napomena: Ova integracija koristi Threema Gateway u osnovnom načinu rada (enkripcija temeljena na poslužitelju). Dodatne pojedinosti možete pronaći na {0}.",
"smspartnerSenderNameInfo": "Mora biti između 3 i 11 znakova"
"smspartnerSenderNameInfo": "Mora biti između 3 i 11 znakova",
"shrinkDatabaseDescriptionSqlite": "Pokreni {vacuum} baze podataka za SQLite. Opcija {auto_vacuum} je već omogućena, ali to ne defragmentira bazu podataka niti ponovno pakira pojedinačne stranice baze podataka na način na koji to radi naredba {vacuum}.",
"ignoredTLSError": "TLS/SSL greške se ignoriraju",
"cacheBusterParam": "Dodaj parametar {0}",
"cacheBusterParamDescription": "Nasumično generirani parametar, za preskakanje predmemorije.",
"snmpCommunityStringHelptext": "Ovaj niz funkcionira kao lozinka za provjeru autentičnosti i kontrolu pristupa uređajima s omogućenim SNMP-om. Uskladite ga sa svojom konfiguracijom SNMP uređaja.",
"privateOnesenderDesc": "Provjerite je li broj telefona valjan. Za slanje poruke na privatni telefonski broj, npr. 628123456789",
"Go back to home page.": "Vratite se na početnu stranicu.",
"signl4Docs": "Više informacija o tome kako konfigurirati SIGNL4 i kako dobiti SIGNL4 URL webhooka možete pronaći na {0}.",
"not starts with": "ne počinje s",
"less than or equal to": "manje od ili jednako",
"Doorbell": "Zvono na vratima",
"Custom sound to override default notification sound": "Prilagođeni zvuk za zamjenu zadanog zvuka obavijesti",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Vremenski osjetljive obavijesti bit će isporučene odmah, čak i ako je uređaj u načinu rada bez ometanja.",
"now": "sada",
"time ago": "prije {0}",
"-year": "-godišnje",
"Json Query Expression": "Upit u JSON obliku",
"Community String": "Zajednički niz teksta",
"conditionAddGroup": "Dodaj grupu",
"conditionDeleteGroup": "Obriši grupu",
"Debug": "Otklanjanje grešaka",
"Copy": "Kopirati",
"CopyToClipboardError": "Greška pri kopiranju u međuspremnik: {error}",
"CopyToClipboardSuccess": "Kopirano!",
"dns resolvers": "DNS razrješivači",
"firewalls": "vatrozidi",
"CurlDebugInfo": "Za otklanjanje grešaka u Monitoru, možete zalijepiti ovo u terminal na vlastitom računalu ili računalu na kojem se Uptime Kuma pokreće, kako biste isprobali Vaš mrežni zahtjev.{newiline}Budite svjesni mrežnih razlika koje mogu činiti {firewalls}, {dns_resolvers} ili {docker_networks}.",
"docker networks": "Dockerove mreže",
"CurlDebugInfoOAuth2CCUnsupported": "Potpuni tok vjerodajnica klijenta OAuth nije podržan u {curl}.{newline}Nabavite token nositelja i proslijedite ga koristeći opciju {oauth2_bearer}.",
"CurlDebugInfoProxiesUnsupported": "Proxy podrška u gornjoj naredbi {curl} trenutno nije implementirana.",
"and": "i",
"Message format": "Format poruke",
"Send rich messages": "Slanje poruka s obogaćenim tekstom",
"OID (Object Identifier)": "OID (Identifikator objekta)",
"snmpOIDHelptext": "Unesite OID za senzor ili status kojeg želite monitorirati. Koristite alate za upravljanje mrežom poput MIB preglednika ili SNMP programa ako niste sigurni koja je vrijednost OID-a.",
"Condition": "Uvjet",
"SNMP Version": "Inačica SNMP-a",
"Please enter a valid OID.": "Unesite važeći OID.",
"Recipient Type": "Tip primatelja",
"Private Number": "Privatni broj",
"wayToGetOnesenderUrlandToken": "URL i token možete dobiti odlaskom na OneSender web stranicu. Više informacija na {0}",
"Token Onesender": "OneSender Token",
"Host Onesender": "Adresa OneSender domaćina",
"Group ID": "Identifikator Grupe",
"groupOnesenderDesc": "Provjerite je li Identifikator Grupe valjan. Za slanje poruke na Grupu, npr. 628123456789-342345",
"Add Remote Browser": "Dodaj udaljeni preglednik",
"New Group": "Nova grupa",
"Group Name": "Naziv grupe",
"OAuth2: Client Credentials": "OAuth2: vjerodajnice klijenta",
"Authentication Method": "Metoda provjere autentičnosti",
"Authorization Header": "Zaglavlje autorizacije",
"Form Data Body": "Tijelo podataka obrasca",
"OAuth Token URL": "URL OAuth tokena",
"Client ID": "Klijentski identifikator",
"Client Secret": "Klijentska tajna",
"OAuth Scope": "OAuth opseg",
"Optional: Space separated list of scopes": "Neobavezno: popis opsega odvojen razmakom",
"No tags found.": "Nema pronađenih oznaka.",
"Lost connection to the socket server.": "Izgubljena veza sa socket poslužiteljem.",
"Cannot connect to the socket server.": "Nije moguće spojiti se na socket poslužitelj.",
"SIGNL4": "SIGNL4",
"SIGNL4 Webhook URL": "SIGNL4 URL webhooka",
"Conditions": "Uvjeti",
"conditionAdd": "Dodaj uvjet",
"conditionDelete": "Obriši uvjet",
"conditionValuePlaceholder": "Vrijednost",
"equals": "je jednako",
"not equals": "nije jednako",
"contains": "sadržava",
"not contains": "ne sadržava",
"starts with": "počinje s",
"ends with": "završava s",
"not ends with": "ne završava s",
"less than": "manje od",
"greater than": "veće od",
"greater than or equal to": "veće od ili jednako",
"record": "zapis",
"Notification Channel": "Kanal obavijesti",
"Sound": "Zvuk",
"Alphanumerical string and hyphens only": "Samo alfanumerički niz i crtice",
"Arcade": "Arkadno",
"Fail": "Neuspjeh",
"Correct": "Ispravno",
"Harp": "Harfa",
"Reveal": "Otkrivanje",
"Bubble": "Mjehurić",
"Flute": "Flauta",
"Money": "Novac",
"Scifi": "Znanstvena fantastika",
"Clear": "Čisto",
"Elevator": "Dizalo",
"Guitar": "Gitara",
"Pop": "Pop",
"Time Sensitive (iOS Only)": "Vremenski osjetljivo (samo iOS)",
"From": "Od",
"Can be found on:": "Može se pronaći na: {0}",
"The phone number of the recipient in E.164 format.": "Telefonski broj primatelja u formatu E.164.",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ili identifikator pošiljatelja tekstualne poruke ili telefonski broj u formatu E.164 ako želite primati odgovore.",
"jsonQueryDescription": "Izvršite JSON upit nad primljenim odgovorom i provjerite očekivanu povratnu vrijednost. Koristite \"$\" za zahtjeve u kojima ne očekujete JSON odgovor. Povratna vrijednost će se za usporedbu pretvoriti u niz znakova (string). Pogledajte stranicu {0} za dokumentaciju o jeziku upita. Testno okruženje možete pronaći na {1}."
}

View file

@ -1046,5 +1046,11 @@
"Group Name": "Nama Grup",
"OAuth2: Client Credentials": "OAuth2: Kredensial Klien",
"Authentication Method": "Metode Autentikasi",
"shrinkDatabaseDescriptionSqlite": "Memicu pangkalan data {vacuum} untuk SQLite. {auto_vacuum} sudah diaktifkan, tetapi tidak mendefragmentasi pangkalan data atau mengemas ulang halaman individual dari pangkalan data seperti yang dilakukan oleh perintah {vacuum}."
"shrinkDatabaseDescriptionSqlite": "Memicu pangkalan data {vacuum} untuk SQLite. {auto_vacuum} sudah diaktifkan, tetapi tidak mendefragmentasi pangkalan data atau mengemas ulang halaman individual dari pangkalan data seperti yang dilakukan oleh perintah {vacuum}.",
"ignoredTLSError": "Galat TLS/SSL sudah diabaikan",
"Debug": "Awakutu",
"Copy": "Salin",
"CopyToClipboardError": "Tidak bisa menyalin ke papan klip: {galat}",
"CopyToClipboardSuccess": "Tersalin!",
"CurlDebugInfo": "Untuk pengawakutuan monitor, Anda bisa menempelkan ini ke terminal mesin Anda sendiri atau ke terminal mesin di mana uptime kuma sedang berjalan dan melihat apa yang Anda harapkan. Mohon perhatikan perbedaan jaringan seperti {firewalls}, {dns_resolvers}, atau {docker_networks}."
}

View file

@ -672,5 +672,12 @@
"emailCustomBody": "カスタム本文",
"emailTemplateServiceName": "サービス名",
"smtpLiquidIntroduction": "次の 2 つのテンプレート・フィールドは Liquid テンプレート言語で記述できます。これらの使い方は {0} にあります。以下は利用可能な変数です:",
"emailTemplateStatus": "ステータス"
"emailTemplateStatus": "ステータス",
"now": "現在",
"time ago": "{0}前",
"-year": "年",
"Json Query Expression": "Jsonクエリ表現",
"ignoredTLSError": "TLS/SSLエラーは無視されました",
"locally configured mail transfer agent": "独自設定されたメール転送エージェント",
"ignoreTLSErrorGeneral": "接続確立時に TLS/SSL エラーを無視する"
}

View file

@ -1057,5 +1057,42 @@
"less than or equal to": "менше або дорівнює",
"greater than or equal to": "більше або дорівнює",
"record": "запис",
"shrinkDatabaseDescriptionSqlite": "Запускає команду {vacuum} для бази даних SQLite. Команда {auto_vacuum} вже увімкнена, але вона не дефрагментує базу даних і не перепаковує окремі сторінки бази даних так, як це робить команда {vacuum}."
"shrinkDatabaseDescriptionSqlite": "Запускає команду {vacuum} для бази даних SQLite. Команда {auto_vacuum} вже увімкнена, але вона не дефрагментує базу даних і не перепаковує окремі сторінки бази даних так, як це робить команда {vacuum}.",
"ignoredTLSError": "Помилки TLS/SSL проігноровано",
"Debug": "Налагодження",
"Copy": "Копіювати",
"CopyToClipboardError": "Не вдалося скопіювати в буфер обміну: {error}",
"CopyToClipboardSuccess": "Скопійовано!",
"firewalls": "фаєрволи",
"dns resolvers": "dns сервери",
"docker networks": "docker-мережі",
"CurlDebugInfoProxiesUnsupported": "Підтримка проксі у наведеній вище команді {curl} наразі не реалізована.",
"Message format": "Формат повідомлення",
"Send rich messages": "Надіслати розгорнуті повідомлення",
"Notification Channel": "Канал сповіщення",
"Sound": "Звук",
"Alphanumerical string and hyphens only": "Тільки алфавітно-цифровий рядок і дефіси",
"Arcade": "Аркада",
"Correct": "Вірно",
"Fail": "Невдача",
"Harp": "Арфа",
"Reveal": "Розкриття",
"Bubble": "Бульбашка",
"Doorbell": "Дзвінок",
"Flute": "Флейта",
"Money": "Гроші",
"Scifi": "Наукова фантастика",
"Clear": "Чисто",
"Elevator": "Ліфт",
"Guitar": "Гітара",
"Pop": "Поп",
"Custom sound to override default notification sound": "Користувацький звук для заміни звуку сповіщень за замовчуванням",
"Time Sensitive (iOS Only)": "Чутливий до часу (лише для iOS)",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Сповіщення, що залежать від часу, будуть доставлені негайно, навіть якщо пристрій перебуває в режимі «Не турбувати».",
"From": "Від",
"The phone number of the recipient in E.164 format.": "Номер телефону одержувача у форматі E.164.",
"CurlDebugInfo": "Для налагодження монітора ви можете вставити цей файл у термінал вашої власної машини або у термінал машини, на якій запущено uptime kuma, і подивитися, що ви запитуєте.{newline}Зверніть увагу на мережеві відмінності, такі як {firewalls}, {dns_resolvers} або {docker_networks}.",
"CurlDebugInfoOAuth2CCUnsupported": "Повний потік облікових даних клієнта oauth не підтримується в {curl}.{newline}Потрібно отримати bearer-токен і передати його за допомогою опції {oauth2_bearer}.",
"Can be found on:": "Можна знайти на: {0}",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Або ідентифікатор відправника тексту, або номер телефону у форматі E.164, якщо ви хочете отримувати відповіді."
}

View file

@ -1,5 +1,5 @@
{
"languageName": "English",
"languageName": "简体中文",
"checkEverySecond": "检测频率 {0} 秒",
"retryCheckEverySecond": "重试间隔 {0} 秒",
"resendEveryXTimes": "每 {0} 次失败则重复发送一次",
@ -1053,5 +1053,42 @@
"greater than or equal to": "不少于",
"record": "记录",
"jsonQueryDescription": "使用 JSON 查询解析并提取服务器 JSON 响应中的特定数据,或者,如果不期望得到 JSON 响应,则可使用 \"$\" 获取原始响应。然后将结果转为字符串并与期望值进行字符串比较。有关更多文档,请参阅 {0},亦可使用 {1} 来尝试查询。",
"shrinkDatabaseDescriptionSqlite": "触发 SQLite 数据库的 {vacuum} 命令。{auto_vacuum} 已经启用,但它不会像 {vacuum} 命令那样对数据库进行碎片整理,也不会重新打包各个数据库页面。"
"shrinkDatabaseDescriptionSqlite": "触发 SQLite 数据库的 {vacuum} 命令。{auto_vacuum} 已经启用,但它不会像 {vacuum} 命令那样对数据库进行碎片整理,也不会重新打包各个数据库页面。",
"Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "如需可被回复,请输入发送者 ID 或 E.164 格式的手机号码。",
"Copy": "复制",
"Debug": "Debug",
"CopyToClipboardSuccess": "已复制!",
"CopyToClipboardError": "无法复制到剪贴板:{error}",
"CurlDebugInfoProxiesUnsupported": "上述 {curl} 命令中的代理支持目前尚未实现。",
"docker networks": "Docker 网络",
"dns resolvers": "DNS 解析器",
"firewalls": "防火墙",
"Message format": "消息格式",
"Send rich messages": "发送富文本消息",
"Sound": "声音",
"Notification Channel": "通知频道",
"Bubble": "Bubble气泡",
"Reveal": "Reveal揭示",
"Harp": "Harp竖琴",
"Correct": "Correct成功音",
"Fail": "Fail失败音",
"Arcade": "Arcade拱廊",
"Alphanumerical string and hyphens only": "仅限字母、数字和连字符(-",
"Custom sound to override default notification sound": "自定义声音,用以覆盖默认通知声音",
"Pop": "Pop流行音乐",
"Guitar": "Guitar吉他",
"Elevator": "Elevator电梯",
"Clear": "Clear清除声",
"Scifi": "Scifi科幻",
"Flute": "Flute长笛",
"Doorbell": "Doorbell门铃",
"The phone number of the recipient in E.164 format.": "收件人的 E.164 格式电话号码。",
"Can be found on:": "可在此找到:{0}",
"From": "发件人",
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使设备处于专注模式,即时通知也会立即发送。",
"Time Sensitive (iOS Only)": "即时通知(仅 iOS 可用)",
"Money": "Money",
"CurlDebugInfoOAuth2CCUnsupported": "{curl} 不支持完整的 oauth 客户端凭证流。{newline}请获取承载令牌bearer token并通过 {oauth2_bearer} 选项传递。",
"CurlDebugInfo": "要调试监控项,您可以将该命令粘贴到您自己的或 uptime kuma 正在运行的机器的命令行终端中,并查看结果。{newiline}请注意网络差异,例如 {firewalls}、{dns_resolvers} 或 {docker_networks}。",
"ignoredTLSError": "TLS/SSL 错误已被忽略"
}

View file

@ -64,6 +64,9 @@
<option value="mqtt">
MQTT
</option>
<option value="rabbitmq">
RabbitMQ
</option>
<option value="kafka-producer">
Kafka Producer
</option>
@ -90,6 +93,13 @@
</option>
</optgroup>
</select>
<i18n-t v-if="monitor.type === 'rabbitmq'" keypath="rabbitmqHelpText" tag="div" class="form-text">
<template #rabitmq_documentation>
<a href="https://www.rabbitmq.com/management" target="_blank" rel="noopener noreferrer">
RabbitMQ documentation
</a>
</template>
</i18n-t>
</div>
<div v-if="monitor.type === 'tailscale-ping'" class="alert alert-warning" role="alert">
@ -233,6 +243,43 @@
</div>
</template>
<template v-if="monitor.type === 'rabbitmq'">
<!-- RabbitMQ Nodes List -->
<div class="my-3">
<label for="rabbitmqNodes" class="form-label">{{ $t("RabbitMQ Nodes") }}</label>
<VueMultiselect
id="rabbitmqNodes"
v-model="monitor.rabbitmqNodes"
:required="true"
:multiple="true"
:options="[]"
:placeholder="$t('Enter the list of nodes')"
:tag-placeholder="$t('Press Enter to add node')"
:max-height="500"
:taggable="true"
:show-no-options="false"
:close-on-select="false"
:clear-on-select="false"
:preserve-search="false"
:preselect-first="false"
@tag="addRabbitmqNode"
></VueMultiselect>
<div class="form-text">
{{ $t("rabbitmqNodesDescription", ["https://node1.rabbitmq.com:15672"]) }}
</div>
</div>
<div class="my-3">
<label for="rabbitmqUsername" class="form-label">RabbitMQ {{ $t("RabbitMQ Username") }}</label>
<input id="rabbitmqUsername" v-model="monitor.rabbitmqUsername" type="text" required class="form-control">
</div>
<div class="my-3">
<label for="rabbitmqPassword" class="form-label">{{ $t("RabbitMQ Password") }}</label>
<HiddenInput id="rabbitmqPassword" v-model="monitor.rabbitmqPassword" autocomplete="false" required="true"></HiddenInput>
</div>
</template>
<!-- Hostname -->
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / SNMP only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'snmp'" class="my-3">
@ -549,7 +596,7 @@
</div>
<!-- Timeout: HTTP / Keyword / SNMP only -->
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'snmp'" class="my-3">
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'snmp' || monitor.type === 'rabbitmq'" class="my-3">
<label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
<input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
</div>
@ -1122,6 +1169,9 @@ const monitorDefaults = {
kafkaProducerAllowAutoTopicCreation: false,
gamedigGivenPortOnly: true,
remote_browser: null,
rabbitmqNodes: [],
rabbitmqUsername: "",
rabbitmqPassword: "",
conditions: []
};
@ -1709,6 +1759,10 @@ message HealthCheckResponse {
this.monitor.kafkaProducerBrokers.push(newBroker);
},
addRabbitmqNode(newNode) {
this.monitor.rabbitmqNodes.push(newNode);
},
/**
* Validate form input
* @returns {boolean} Is the form input valid?
@ -1736,6 +1790,17 @@ message HealthCheckResponse {
return false;
}
}
if (this.monitor.type === "rabbitmq") {
if (this.monitor.rabbitmqNodes.length === 0) {
toast.error(this.$t("rabbitmqNodesRequired"));
return false;
}
if (!this.monitor.rabbitmqNodes.every(node => node.startsWith("http://") || node.startsWith("https://"))) {
toast.error(this.$t("rabbitmqNodesInvalid"));
return false;
}
}
return true;
},

View file

@ -0,0 +1,53 @@
const { describe, test } = require("node:test");
const assert = require("node:assert");
const { RabbitMQContainer } = require("@testcontainers/rabbitmq");
const { RabbitMqMonitorType } = require("../../server/monitor-types/rabbitmq");
const { UP, DOWN, PENDING } = require("../../src/util");
describe("RabbitMQ Single Node", {
skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"),
}, () => {
test("RabbitMQ is running", async () => {
// The default timeout of 30 seconds might not be enough for the container to start
const rabbitMQContainer = await new RabbitMQContainer().withStartupTimeout(60000).start();
const rabbitMQMonitor = new RabbitMqMonitorType();
const connectionString = `http://${rabbitMQContainer.getHost()}:${rabbitMQContainer.getMappedPort(15672)}`;
const monitor = {
rabbitmqNodes: JSON.stringify([ connectionString ]),
rabbitmqUsername: "guest",
rabbitmqPassword: "guest",
};
const heartbeat = {
msg: "",
status: PENDING,
};
try {
await rabbitMQMonitor.check(monitor, heartbeat, {});
assert.strictEqual(heartbeat.status, UP);
assert.strictEqual(heartbeat.msg, "OK");
} finally {
rabbitMQContainer.stop();
}
});
test("RabbitMQ is not running", async () => {
const rabbitMQMonitor = new RabbitMqMonitorType();
const monitor = {
rabbitmqNodes: JSON.stringify([ "http://localhost:15672" ]),
rabbitmqUsername: "rabbitmqUser",
rabbitmqPassword: "rabbitmqPass",
};
const heartbeat = {
msg: "",
status: PENDING,
};
await rabbitMQMonitor.check(monitor, heartbeat, {});
assert.strictEqual(heartbeat.status, DOWN);
});
});