mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-14 15:36:59 +08:00
Compare commits
48 Commits
escaped-de
...
revert-437
Author | SHA1 | Date | |
---|---|---|---|
|
e3681ce57e | ||
|
85dfe1f5d1 | ||
|
b719d11500 | ||
|
b7d2cedf2e | ||
|
f3ee9c2cad | ||
|
582fb6c5ad | ||
|
dda40610c7 | ||
|
bafca6bd37 | ||
|
bbc75b840b | ||
|
019702f8e5 | ||
|
365aa8d814 | ||
|
7e37dacb9a | ||
|
e8c650797c | ||
|
958f96f06d | ||
|
130d8d0177 | ||
|
9905ca574c | ||
|
fc429108ac | ||
|
10ffde2595 | ||
|
b9b48e1b2d | ||
|
a760898281 | ||
|
289b72d07d | ||
|
9257a7a19e | ||
|
8084c7e61c | ||
|
324d879aad | ||
|
d2a44487b3 | ||
|
35668219ec | ||
|
895f6d2ff1 | ||
|
3088cc6141 | ||
|
98415bd419 | ||
|
087d20b775 | ||
|
ec7923f4fd | ||
|
86b3ff6bfd | ||
|
e6159d9ab4 | ||
|
e781325633 | ||
|
72478090e7 | ||
|
50ec9fec05 | ||
|
62c55f0e25 | ||
|
3ab35c38fc | ||
|
1d86fa2b5c | ||
|
cef072cae9 | ||
|
abcc98c836 | ||
|
aa38344c3d | ||
|
9ff0ae67df | ||
|
a14d05daab | ||
|
93c5ab0bd8 | ||
|
d27a9e7d7f | ||
|
c120c37030 | ||
|
acd1e7211a |
@@ -1,6 +1,6 @@
|
||||
# Project Info
|
||||
|
||||
First of all, I want to thank everyone who have wrote issues or shared pull requests for Uptime Kuma.
|
||||
First of all, I want to thank everyone who has submitted issues or shared pull requests for Uptime Kuma.
|
||||
I never thought the GitHub community would be so nice!
|
||||
Because of this, I also never thought that other people would actually read and edit my code.
|
||||
Parts of the code are not very well-structured or commented, sorry about that.
|
||||
@@ -9,7 +9,7 @@ The project was created with `vite.js` and is written in `vue3`.
|
||||
Our backend lives in the `server`-directory and mostly communicates via websockets.
|
||||
Both frontend and backend share the same `package.json`.
|
||||
|
||||
For production, the frontend is build into `dist`-directory and the server (`express.js`) exposes the `dist` directory as the root of the endpoint.
|
||||
For production, the frontend is built into the `dist`-directory and the server (`express.js`) exposes the `dist` directory as the root of the endpoint.
|
||||
For development, we run vite in development mode on another port.
|
||||
|
||||
## Directories
|
||||
@@ -28,7 +28,7 @@ For development, we run vite in development mode on another port.
|
||||
## Can I create a pull request for Uptime Kuma?
|
||||
|
||||
Yes or no, it depends on what you will try to do.
|
||||
Both your and our maintainers time is precious, and we don't want to waste both time.
|
||||
Both yours and our maintainers' time is precious, and we don't want to waste either.
|
||||
|
||||
If you have any questions about any process/.. is not clear, you are likely not alone => please ask them ^^
|
||||
|
||||
@@ -49,11 +49,11 @@ Different guidelines exist for different types of pull requests (PRs):
|
||||
<p>
|
||||
|
||||
If you come across a bug and think you can solve, we appreciate your work.
|
||||
Please make sure that you follow by these rules:
|
||||
Please make sure that you follow these rules:
|
||||
- keep the PR as small as possible, fix only one thing at a time => keeping it reviewable
|
||||
- test that your code does what you came it does.
|
||||
- test that your code does what you claim it does.
|
||||
|
||||
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
<sub>Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
</p>
|
||||
</details>
|
||||
- <details><summary><b>translations / internationalisation (i18n)</b></summary>
|
||||
@@ -68,7 +68,7 @@ Different guidelines exist for different types of pull requests (PRs):
|
||||
- language keys need to be **added to `en.json`** to be visible in weblate. If this has not happened, a PR is appreciated.
|
||||
- **Adding a new language** requires a new file see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md)
|
||||
|
||||
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
<sub>Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
</p>
|
||||
</details>
|
||||
- <details><summary><b>new notification providers</b></summary>
|
||||
@@ -102,7 +102,7 @@ Different guidelines exist for different types of pull requests (PRs):
|
||||
Therefore, making sure that they work is also really important.
|
||||
Because testing notification providers is quite time intensive, we mostly offload this onto the person contributing a notification provider.
|
||||
|
||||
To make shure you have tested the notification provider, please include screenshots of the following events in the pull-request description:
|
||||
To make sure you have tested the notification provider, please include screenshots of the following events in the pull-request description:
|
||||
- `UP`/`DOWN`
|
||||
- Certificate Expiry via https://expired.badssl.com/
|
||||
- Testing (the test button on the notification provider setup page)
|
||||
@@ -117,7 +117,7 @@ Different guidelines exist for different types of pull requests (PRs):
|
||||
| Testing | paste-image-here | paste-image-here |
|
||||
```
|
||||
|
||||
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
<sub>Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
</p>
|
||||
</details>
|
||||
- <details><summary><b>new monitoring types</b></summary>
|
||||
@@ -138,14 +138,14 @@ Different guidelines exist for different types of pull requests (PRs):
|
||||
-
|
||||
|
||||
|
||||
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
<sub>Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.</sub>
|
||||
</p>
|
||||
</details>
|
||||
- <details><summary><b>new features/ major changes / breaking bugfixes</b></summary>
|
||||
<p>
|
||||
|
||||
be sure to **create an empty draft pull request or open an issue, so we can have a discussion first**.
|
||||
This is especially important for a large pull request or you don't know if it will be merged or not.
|
||||
This is especially important for a large pull request or when you don't know if it will be merged or not.
|
||||
|
||||
<sub>Because of the large impact of this work, only senior maintainers may merge PRs in this area.</sub>
|
||||
</p>
|
||||
@@ -201,7 +201,7 @@ The rationale behind this is that we can align the direction and scope of the fe
|
||||
|
||||
## Project Styles
|
||||
|
||||
I personally do not like something that requires so many configurations before you can finally start the app.
|
||||
I personally do not like something that requires a lot of configuration before you can finally start the app.
|
||||
The goal is to make the Uptime Kuma installation as easy as installing a mobile app.
|
||||
|
||||
- Easy to install for non-Docker users
|
||||
@@ -260,7 +260,7 @@ Port `3000` and port `3001` will be used.
|
||||
npm run dev
|
||||
```
|
||||
|
||||
But sometimes, you would like to restart the server, but not the frontend, you can run these commands in two terminals:
|
||||
But sometimes you may want to restart the server without restarting the frontend. In that case, you can run these commands in two terminals:
|
||||
|
||||
```bash
|
||||
npm run start-frontend-dev
|
||||
@@ -409,7 +409,7 @@ https://github.com/louislam/uptime-kuma/issues?q=sort%3Aupdated-desc
|
||||
|
||||
### What is a maintainer and what are their roles?
|
||||
|
||||
This project has multiple maintainers which specialise in different areas.
|
||||
This project has multiple maintainers who specialise in different areas.
|
||||
Currently, there are 3 maintainers:
|
||||
|
||||
| Person | Role | Main Area |
|
||||
|
@@ -1508,10 +1508,8 @@ class Monitor extends BeanModel {
|
||||
return await R.getAll(`
|
||||
SELECT monitor_notification.monitor_id, monitor_notification.notification_id
|
||||
FROM monitor_notification
|
||||
WHERE monitor_notification.monitor_id IN (?)
|
||||
`, [
|
||||
monitorIDs,
|
||||
]);
|
||||
WHERE monitor_notification.monitor_id IN (${monitorIDs.map((_) => "?").join(",")})
|
||||
`, monitorIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1521,13 +1519,11 @@ class Monitor extends BeanModel {
|
||||
*/
|
||||
static async getMonitorTag(monitorIDs) {
|
||||
return await R.getAll(`
|
||||
SELECT monitor_tag.monitor_id, tag.name, tag.color
|
||||
SELECT monitor_tag.monitor_id, monitor_tag.tag_id, tag.name, tag.color
|
||||
FROM monitor_tag
|
||||
JOIN tag ON monitor_tag.tag_id = tag.id
|
||||
WHERE monitor_tag.monitor_id IN (?)
|
||||
`, [
|
||||
monitorIDs,
|
||||
]);
|
||||
WHERE monitor_tag.monitor_id IN (${monitorIDs.map((_) => "?").join(",")})
|
||||
`, monitorIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1567,6 +1563,7 @@ class Monitor extends BeanModel {
|
||||
tagsMap.set(row.monitor_id, []);
|
||||
}
|
||||
tagsMap.get(row.monitor_id).push({
|
||||
tag_id: row.tag_id,
|
||||
name: row.name,
|
||||
color: row.color
|
||||
});
|
||||
|
35
server/notification-providers/46elks.js
Normal file
35
server/notification-providers/46elks.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
|
||||
class Elks extends NotificationProvider {
|
||||
name = "Elks";
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
const okMsg = "Sent Successfully.";
|
||||
const url = "https://api.46elks.com/a1/sms";
|
||||
|
||||
try {
|
||||
let data = new URLSearchParams();
|
||||
data.append("from", notification.elksFromNumber);
|
||||
data.append("to", notification.elksToNumber );
|
||||
data.append("message", msg);
|
||||
|
||||
const config = {
|
||||
headers: {
|
||||
"Authorization": "Basic " + Buffer.from(`${notification.elksUsername}:${notification.elksAuthToken}`).toString("base64")
|
||||
}
|
||||
};
|
||||
|
||||
await axios.post(url, data, config);
|
||||
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Elks;
|
@@ -87,7 +87,6 @@ class DingDing extends NotificationProvider {
|
||||
* @returns {string} Status
|
||||
*/
|
||||
statusToString(status) {
|
||||
// TODO: Move to notification-provider.js to avoid repetition in classes
|
||||
switch (status) {
|
||||
case DOWN:
|
||||
return "DOWN";
|
||||
|
@@ -48,7 +48,7 @@ class Discord extends NotificationProvider {
|
||||
},
|
||||
{
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: this.extractAdress(monitorJSON),
|
||||
value: this.extractAddress(monitorJSON),
|
||||
},
|
||||
{
|
||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||
@@ -85,7 +85,7 @@ class Discord extends NotificationProvider {
|
||||
},
|
||||
{
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: this.extractAdress(monitorJSON),
|
||||
value: this.extractAddress(monitorJSON),
|
||||
},
|
||||
{
|
||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||
|
@@ -24,7 +24,7 @@ class NotificationProvider {
|
||||
* @param {?object} monitorJSON Monitor details (For Up/Down only)
|
||||
* @returns {string} The extracted address based on the monitor type.
|
||||
*/
|
||||
extractAdress(monitorJSON) {
|
||||
extractAddress(monitorJSON) {
|
||||
if (!monitorJSON) {
|
||||
return "";
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class SevenIO extends NotificationProvider {
|
||||
return okMsg;
|
||||
}
|
||||
|
||||
let address = this.extractAdress(monitorJSON);
|
||||
let address = this.extractAddress(monitorJSON);
|
||||
if (address !== "") {
|
||||
address = `(${address}) `;
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ class SIGNL4 extends NotificationProvider {
|
||||
msg,
|
||||
// Source system
|
||||
"X-S4-SourceSystem": "UptimeKuma",
|
||||
monitorUrl: this.extractAdress(monitorJSON),
|
||||
monitorUrl: this.extractAddress(monitorJSON),
|
||||
};
|
||||
|
||||
const config = {
|
||||
|
@@ -48,7 +48,7 @@ class Slack extends NotificationProvider {
|
||||
|
||||
}
|
||||
|
||||
const address = this.extractAdress(monitorJSON);
|
||||
const address = this.extractAddress(monitorJSON);
|
||||
if (address) {
|
||||
actions.push({
|
||||
"type": "button",
|
||||
|
@@ -93,7 +93,7 @@ class SMTP extends NotificationProvider {
|
||||
|
||||
if (monitorJSON !== null) {
|
||||
monitorName = monitorJSON["name"];
|
||||
monitorHostnameOrURL = this.extractAdress(monitorJSON);
|
||||
monitorHostnameOrURL = this.extractAddress(monitorJSON);
|
||||
}
|
||||
|
||||
let serviceStatus = "⚠️ Test";
|
||||
|
@@ -34,7 +34,7 @@ class Squadcast extends NotificationProvider {
|
||||
data.status = "resolve";
|
||||
}
|
||||
|
||||
data.tags["AlertAddress"] = this.extractAdress(monitorJSON);
|
||||
data.tags["AlertAddress"] = this.extractAddress(monitorJSON);
|
||||
|
||||
monitorJSON["tags"].forEach(tag => {
|
||||
data.tags[tag["name"]] = {
|
||||
|
@@ -225,7 +225,7 @@ class Teams extends NotificationProvider {
|
||||
const payload = this._notificationPayloadFactory({
|
||||
heartbeatJSON: heartbeatJSON,
|
||||
monitorName: monitorJSON.name,
|
||||
monitorUrl: this.extractAdress(monitorJSON),
|
||||
monitorUrl: this.extractAddress(monitorJSON),
|
||||
dashboardUrl: dashboardUrl,
|
||||
});
|
||||
|
||||
|
@@ -10,11 +10,22 @@ class TechulusPush extends NotificationProvider {
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
const okMsg = "Sent Successfully.";
|
||||
|
||||
let data = {
|
||||
"title": notification?.pushTitle?.length ? notification.pushTitle : "Uptime-Kuma",
|
||||
"body": msg,
|
||||
"timeSensitive": notification.pushTimeSensitive ?? true,
|
||||
};
|
||||
|
||||
if (notification.pushChannel) {
|
||||
data.channel = notification.pushChannel;
|
||||
}
|
||||
|
||||
if (notification.pushSound) {
|
||||
data.sound = notification.pushSound;
|
||||
}
|
||||
|
||||
try {
|
||||
await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, {
|
||||
"title": "Uptime-Kuma",
|
||||
"body": msg,
|
||||
});
|
||||
await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, data);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
|
@@ -85,7 +85,7 @@ class ZohoCliq extends NotificationProvider {
|
||||
const payload = this._notificationPayloadFactory({
|
||||
monitorMessage: heartbeatJSON.msg,
|
||||
monitorName: monitorJSON.name,
|
||||
monitorUrl: this.extractAdress(monitorJSON),
|
||||
monitorUrl: this.extractAddress(monitorJSON),
|
||||
status: heartbeatJSON.status
|
||||
});
|
||||
|
||||
|
@@ -11,6 +11,7 @@ const CallMeBot = require("./notification-providers/call-me-bot");
|
||||
const SMSC = require("./notification-providers/smsc");
|
||||
const DingDing = require("./notification-providers/dingding");
|
||||
const Discord = require("./notification-providers/discord");
|
||||
const Elks = require("./notification-providers/46elks");
|
||||
const Feishu = require("./notification-providers/feishu");
|
||||
const FreeMobile = require("./notification-providers/freemobile");
|
||||
const GoogleChat = require("./notification-providers/google-chat");
|
||||
@@ -95,6 +96,7 @@ class Notification {
|
||||
new SMSC(),
|
||||
new DingDing(),
|
||||
new Discord(),
|
||||
new Elks(),
|
||||
new Feishu(),
|
||||
new FreeMobile(),
|
||||
new GoogleChat(),
|
||||
|
@@ -118,6 +118,7 @@ export default {
|
||||
"clicksendsms": "ClickSend SMS",
|
||||
"CallMeBot": "CallMeBot (WhatsApp, Telegram Call, Facebook Messanger)",
|
||||
"discord": "Discord",
|
||||
"Elks": "46elks",
|
||||
"GoogleChat": "Google Chat (Google Workspace)",
|
||||
"gorush": "Gorush",
|
||||
"gotify": "Gotify",
|
||||
|
@@ -33,7 +33,7 @@
|
||||
<template #item="monitor">
|
||||
<div class="item" data-testid="monitor">
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-4 small-padding">
|
||||
<div class="col-9 col-md-8 small-padding">
|
||||
<div class="info">
|
||||
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
|
||||
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :key="$root.userHeartbeatBar" class="col-6 col-md-8">
|
||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
||||
<HeartbeatBar size="mid" :monitor-id="monitor.element.id" />
|
||||
</div>
|
||||
</div>
|
||||
|
48
src/components/notifications/46elks.vue
Normal file
48
src/components/notifications/46elks.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="ElksUsername" class="form-label">{{ $t("Username") }}</label>
|
||||
<input id="ElksUsername" v-model="$parent.notification.elksUsername" type="text" class="form-control" required>
|
||||
<label for="ElksPassword" class="form-label">{{ $t("Password") }}</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<HiddenInput id="ElksPassword" v-model="$parent.notification.elksAuthToken" :required="true" autocomplete="new-password"></HiddenInput>
|
||||
<i18n-t tag="p" keypath="Can be found on:">
|
||||
<a href="https://46elks.com/account" target="_blank">https://46elks.com/account</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="Elks-from-number" class="form-label">{{ $t("From") }}</label>
|
||||
<input id="Elks-from-number" v-model="$parent.notification.elksFromNumber" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
{{ $t("Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.") }}
|
||||
<i18n-t tag="p" keypath="More info on:">
|
||||
<a href="https://46elks.se/kb/text-sender-id" target="_blank">https://46elks.se/kb/text-sender-id</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Elks-to-number" class="form-label">{{ $t("To Number") }}</label>
|
||||
<input id="Elks-to-number" v-model="$parent.notification.elksToNumber" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
{{ $t("The phone number of the recipient in E.164 format.") }}
|
||||
<i18n-t tag="p" keypath="More info on:">
|
||||
<a href="https://46elks.se/kb/e164" target="_blank">https://46elks.se/kb/e164</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
|
||||
<a href="https://46elks.com/docs/send-sms" target="_blank">https://46elks.com/docs/send-sms</a>
|
||||
</i18n-t>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
@@ -4,6 +4,53 @@
|
||||
<HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="new-password"></HiddenInput>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="push-api-title" class="form-label">{{ $t("Title") }}</label>
|
||||
<input id="push-api-title" v-model="$parent.notification.pushTitle" type="text" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="push-api-channel" class="form-label">{{ $t("Notification Channel") }}</label>
|
||||
<input id="push-api-channel" v-model="$parent.notification.pushChannel" type="text" class="form-control" patttern="[A-Za-z0-9-]+">
|
||||
<div class="form-text">
|
||||
{{ $t("Alphanumerical string and hyphens only") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="push-api-sound" class="form-label">{{ $t("Sound") }}</label>
|
||||
<select id="push-api-sound" v-model="$parent.notification.pushSound" class="form-select">
|
||||
<option value="default">{{ $t("Default") }}</option>
|
||||
<option value="arcade">{{ $t("Arcade") }}</option>
|
||||
<option value="correct">{{ $t("Correct") }}</option>
|
||||
<option value="fail">{{ $t("Fail") }}</option>
|
||||
<option value="harp">{{ $t("Harp") }}</option>
|
||||
<option value="reveal">{{ $t("Reveal") }}</option>
|
||||
<option value="bubble">{{ $t("Bubble") }}</option>
|
||||
<option value="doorbell">{{ $t("Doorbell") }}</option>
|
||||
<option value="flute">{{ $t("Flute") }}</option>
|
||||
<option value="money">{{ $t("Money") }}</option>
|
||||
<option value="scifi">{{ $t("Scifi") }}</option>
|
||||
<option value="clear">{{ $t("Clear") }}</option>
|
||||
<option value="elevator">{{ $t("Elevator") }}</option>
|
||||
<option value="guitar">{{ $t("Guitar") }}</option>
|
||||
<option value="pop">{{ $t("Pop") }}</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
{{ $t("Custom sound to override default notification sound") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input v-model="$parent.notification.pushTimeSensitive" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label">{{ $t("Time Sensitive (iOS Only)") }}</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
{{ $t("Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
|
||||
<a href="https://docs.push.techulus.com" target="_blank">https://docs.push.techulus.com</a>
|
||||
</i18n-t>
|
||||
@@ -16,5 +63,19 @@ export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
mounted() {
|
||||
if (typeof this.$parent.notification.pushTitle === "undefined") {
|
||||
this.$parent.notification.pushTitle = "Uptime-Kuma";
|
||||
}
|
||||
if (typeof this.$parent.notification.pushChannel === "undefined") {
|
||||
this.$parent.notification.pushChannel = "uptime-kuma";
|
||||
}
|
||||
if (typeof this.$parent.notification.pushSound === "undefined") {
|
||||
this.$parent.notification.pushSound = "default";
|
||||
}
|
||||
if (typeof this.$parent.notification.pushTimeSensitive === "undefined") {
|
||||
this.$parent.notification.pushTimeSensitive = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@@ -9,6 +9,7 @@ import CallMeBot from "./CallMeBot.vue";
|
||||
import SMSC from "./SMSC.vue";
|
||||
import DingDing from "./DingDing.vue";
|
||||
import Discord from "./Discord.vue";
|
||||
import Elks from "./46elks.vue";
|
||||
import Feishu from "./Feishu.vue";
|
||||
import FreeMobile from "./FreeMobile.vue";
|
||||
import GoogleChat from "./GoogleChat.vue";
|
||||
@@ -82,6 +83,7 @@ const NotificationFormList = {
|
||||
"smsc": SMSC,
|
||||
"DingDing": DingDing,
|
||||
"discord": Discord,
|
||||
"Elks": Elks,
|
||||
"Feishu": Feishu,
|
||||
"FreeMobile": FreeMobile,
|
||||
"GoogleChat": GoogleChat,
|
||||
|
1
src/lang/ab.json
Normal file
1
src/lang/ab.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
@@ -1011,5 +1011,45 @@
|
||||
"OAuth2: Client Credentials": "OAuth2: přihlašovací údaje klienta",
|
||||
"Authentication Method": "Metoda ověřování",
|
||||
"Authorization Header": "Hlavička autorizace",
|
||||
"Form Data Body": "Tělo formuláře s daty"
|
||||
"Form Data Body": "Tělo formuláře s daty",
|
||||
"threemaRecipientTypePhoneFormat": "E.164, bez počátečního +",
|
||||
"jsonQueryDescription": "Pro zpracování a získání konkrétních dat z JSON odpovědi serveru použijte JSON dotaz - případně \"$\" pro zdrojovou (raw) odpověď, pokud neočekáváte JSON výstup. Výsledek bude následně porovnán jako řetězec vůči očekávaní hodnotě. Dokumentaci naleznete na {0} a pro testování dotazů můžete využít {1}.",
|
||||
"shrinkDatabaseDescriptionSqlite": "Podmínka spuštění příkazu {vacuum} nad SQLite databází. Příkaz {auto_vacuum} je již zapnutý, ale nedochází k defragmentaci databáze ani k přebalení jednotlivých stránek databáze tak, jak to dělá příkaz {vacuum}.",
|
||||
"Community String": "Řetězec komunity",
|
||||
"Host Onesender": "Onesender hostitel",
|
||||
"Token Onesender": "Onesender token",
|
||||
"snmpOIDHelptext": "Zadejte OID senzoru nebo stavu, který chcete monitorovat. Pokud si nejste jisti identifikátorem OID, použijte nástroje pro správu sítě, jako jsou prohlížeče MIB nebo SNMP software.",
|
||||
"snmpCommunityStringHelptext": "Tento řetězec slouží jako heslo pro ověřování a řízení přístupu k zařízením podporujícím protokol SNMP. Shodujte se s konfigurací zařízení SNMP.",
|
||||
"record": "záznam",
|
||||
"Go back to home page.": "Vrátit se domovskou stránku.",
|
||||
"No tags found.": "Nenalezeny žádné štítky.",
|
||||
"Lost connection to the socket server.": "Ztraceno socketové spojení se serverem.",
|
||||
"Cannot connect to the socket server.": "Nelze navázat socketové spojení se serverem.",
|
||||
"SIGNL4": "SIGNL4",
|
||||
"SIGNL4 Webhook URL": "URL adresa webhooku SIGNL4",
|
||||
"signl4Docs": "Další informace související s konfigurací SIGNL4 a postup jak získat URL webhooku SIGNL4 naleznete na {0}.",
|
||||
"Conditions": "Podmínky",
|
||||
"conditionAdd": "Přidat podmínku",
|
||||
"conditionDelete": "Vymazat podmínku",
|
||||
"conditionAddGroup": "Přidat skupinu",
|
||||
"conditionDeleteGroup": "Smazat skupinu",
|
||||
"conditionValuePlaceholder": "Hodnota",
|
||||
"equals": "rovná se",
|
||||
"not equals": "nerovná se",
|
||||
"contains": "obsahuje",
|
||||
"not contains": "neobsahuje",
|
||||
"starts with": "začíná na",
|
||||
"not starts with": "nezačíná na",
|
||||
"ends with": "končí na",
|
||||
"not ends with": "nekončí na",
|
||||
"less than": "menší než",
|
||||
"greater than": "větší než",
|
||||
"less than or equal to": "menší nebo rovno",
|
||||
"greater than or equal to": "větší nebo rovno",
|
||||
"groupOnesenderDesc": "Ujistěte se, že jste zadali platné GroupID. Pro odeslání zprávy do skupiny zadejte například 628123456789-342345",
|
||||
"OAuth Token URL": "URL OAuth tokenu",
|
||||
"Client ID": "ID klienta",
|
||||
"Client Secret": "Tajemství klienta",
|
||||
"OAuth Scope": "OAuth rozsah",
|
||||
"Optional: Space separated list of scopes": "Volitelné: seznam rozsahů oddělte mezerami"
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"languageName": "Deutsch (Schweiz)",
|
||||
"Settings": "Einstellungen",
|
||||
"Dashboard": "Dashboard",
|
||||
"Dashboard": "Überblick",
|
||||
"New Update": "Update verfügbar",
|
||||
"Language": "Sprache",
|
||||
"Appearance": "Erscheinungsbild",
|
||||
@@ -1047,5 +1047,6 @@
|
||||
"greater than": "mehr als",
|
||||
"less than or equal to": "kleiner als oder gleich",
|
||||
"greater than or equal to": "grösser als oder gleich",
|
||||
"record": "Eintrag"
|
||||
"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."
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"languageName": "Deutsch",
|
||||
"Settings": "Einstellungen",
|
||||
"Dashboard": "Dashboard",
|
||||
"Dashboard": "Überblick",
|
||||
"New Update": "Aktualisierung verfügbar",
|
||||
"Language": "Sprache",
|
||||
"Appearance": "Erscheinungsbild",
|
||||
@@ -1050,5 +1050,6 @@
|
||||
"less than": "weniger als",
|
||||
"less than or equal to": "kleiner als oder gleich",
|
||||
"greater than or equal to": "größer als oder gleich",
|
||||
"record": "Eintrag"
|
||||
"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."
|
||||
}
|
||||
|
@@ -1027,5 +1027,29 @@
|
||||
"greater than": "greater than",
|
||||
"less than or equal to": "less than or equal to",
|
||||
"greater than or equal to": "greater than or equal to",
|
||||
"record": "record"
|
||||
"record": "record",
|
||||
"Notification Channel": "Notification Channel",
|
||||
"Sound": "Sound",
|
||||
"Alphanumerical string and hyphens only": "Alphanumerical string and hyphens only",
|
||||
"Arcade": "Arcade",
|
||||
"Correct": "Correct",
|
||||
"Fail":"Fail",
|
||||
"Harp":"Harp",
|
||||
"Reveal":"Reveal",
|
||||
"Bubble":"Bubble",
|
||||
"Doorbell":"Doorbell",
|
||||
"Flute":"Flute",
|
||||
"Money":"Money",
|
||||
"Scifi":"Scifi",
|
||||
"Clear":"Clear",
|
||||
"Elevator":"Elevator",
|
||||
"Guitar":"Guitar",
|
||||
"Pop":"Pop",
|
||||
"Custom sound to override default notification sound": "Custom sound to override default notification sound",
|
||||
"Time Sensitive (iOS Only)": "Time Sensitive (iOS Only)",
|
||||
"Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.",
|
||||
"From":"From",
|
||||
"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."
|
||||
}
|
||||
|
1
src/lang/enm.json
Normal file
1
src/lang/enm.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
@@ -106,7 +106,7 @@
|
||||
"disableauth.message2": "Egoera jakin batzuetarako diseinatuta dago, Uptime Kumaren {intendThirdPartyAuth} (Cloudflare Access, Authelia edo beste autentifikazio-mekanismo batzuk).",
|
||||
"where you intend to implement third-party authentication": "aurrean hirugarrengo autentifikazio batzuek jartzeko",
|
||||
"Please use this option carefully!": "Mesedez, kontuz erabili aukera hau!",
|
||||
"Logout": "Saioa amaitu",
|
||||
"Logout": "Itxi saioa",
|
||||
"Leave": "Utzi",
|
||||
"I understand, please disable": "Ulertzen dut, mesedez desgaitu",
|
||||
"Confirm": "Baieztatu",
|
||||
@@ -115,7 +115,7 @@
|
||||
"Username": "Erabiltzailea",
|
||||
"Password": "Pasahitza",
|
||||
"Remember me": "Gogora nazazu",
|
||||
"Login": "Saioa hasi",
|
||||
"Login": "Hasi saioa",
|
||||
"No Monitors, please": "Monitorizaziorik ez, mesedez",
|
||||
"add one": "gehitu bat",
|
||||
"Notification Type": "Jakinarazpen mota",
|
||||
@@ -164,11 +164,11 @@
|
||||
"Add New below or Select...": "Gehitu beste bat behean edo hautatu…",
|
||||
"Tag with this name already exist.": "Izen hau duen etiketa dagoeneko badago.",
|
||||
"Tag with this value already exist.": "Balio hau duen etiketa dagoeneko badago.",
|
||||
"color": "kolorea",
|
||||
"color": "Kolorea",
|
||||
"value (optional)": "balioa (hautazkoa)",
|
||||
"Gray": "Grisa",
|
||||
"Red": "Gorria",
|
||||
"Orange": "Naranja",
|
||||
"Orange": "Laranja",
|
||||
"Green": "Berdea",
|
||||
"Blue": "Urdina",
|
||||
"Indigo": "Indigo",
|
||||
@@ -190,7 +190,7 @@
|
||||
"Status Page": "Egoera orria",
|
||||
"Status Pages": "Egoera orriak",
|
||||
"defaultNotificationName": "Nire {notification} Alerta ({number})",
|
||||
"here": "Hemen",
|
||||
"here": "hemen",
|
||||
"Required": "Beharrezkoa",
|
||||
"telegram": "Telegram",
|
||||
"ZohoCliq": "ZohoCliq",
|
||||
@@ -582,6 +582,10 @@
|
||||
"Mechanism": "Mekanismoa",
|
||||
"Home": "Hasiera",
|
||||
"filterActive": "Aktibo",
|
||||
"filterActivePaused": "Geldituta",
|
||||
"Expected Value": "Esperotako balioa"
|
||||
"filterActivePaused": "Pausatua",
|
||||
"Expected Value": "Esperotako balioa",
|
||||
"statusPageRefreshIn": "{0} barru freskatuko da.",
|
||||
"now": "orain",
|
||||
"time ago": "duela {0}",
|
||||
"-year": "-urte"
|
||||
}
|
||||
|
@@ -1050,5 +1050,6 @@
|
||||
"greater than": "supérieur à",
|
||||
"less than or equal to": "inférieur ou égal à",
|
||||
"greater than or equal to": "supérieur ou égal à",
|
||||
"record": "enregistrer"
|
||||
"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}."
|
||||
}
|
||||
|
@@ -1015,5 +1015,6 @@
|
||||
"less than": "níos lú ná",
|
||||
"greater than": "níos mó ná",
|
||||
"less than or equal to": "níos lú ná nó cothrom le",
|
||||
"record": "taifead"
|
||||
"record": "taifead",
|
||||
"shrinkDatabaseDescriptionSqlite": "Bunachar sonraí truicear {vacuum} le haghaidh SQLite. Tá {auto_vacuum} cumasaithe cheana féin ach ní dhéanann sé seo scoilt ar an mbunachar sonraí ná athphacáil leathanaigh aonair an bhunachair sonraí mar a dhéanann an t-ordú {vacuum}."
|
||||
}
|
||||
|
@@ -1045,5 +1045,6 @@
|
||||
"New Group": "Grup Baru",
|
||||
"Group Name": "Nama Grup",
|
||||
"OAuth2: Client Credentials": "OAuth2: Kredensial Klien",
|
||||
"Authentication Method": "Metode Autentikasi"
|
||||
"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}."
|
||||
}
|
||||
|
@@ -989,5 +989,9 @@
|
||||
"wayToGetThreemaGateway": "Możesz zarejestrować się w Threema Gateway {0}.",
|
||||
"threemaSenderIdentityFormat": "8 znaków, zwykle zaczyna się od *",
|
||||
"threemaBasicModeInfo": "Uwaga: Ta integracja korzysta z Threema Gateway w trybie podstawowym (szyfrowanie po stronie serwera). Więcej szczegółów można znaleźć {0}.",
|
||||
"apiKeysDisabledMsg": "Klucze API są wyłączone, ponieważ wyłączone jest uwierzytelnianie."
|
||||
"apiKeysDisabledMsg": "Klucze API są wyłączone, ponieważ wyłączone jest uwierzytelnianie.",
|
||||
"-year": "-rok",
|
||||
"and": "i",
|
||||
"now": "teraz",
|
||||
"cacheBusterParam": "Dodaj parametr {0}"
|
||||
}
|
||||
|
@@ -1050,5 +1050,6 @@
|
||||
"less than": "daha küçük",
|
||||
"greater than or equal to": "büyük veya eşit",
|
||||
"record": "kayıt",
|
||||
"jsonQueryDescription": "JSON sorgusunu kullanarak sunucunun JSON yanıtından belirli verileri ayrıştırın ve çıkarın. JSON beklemiyorsanız ham yanıt için \"$\" sembolünü kullanın. Sonuç daha sonra metin olarak beklenen değerle karşılaştırılır. Belgeler için {0}'a bakın ve sorgularla denemeler yapmak için {1}'i kullanın."
|
||||
"jsonQueryDescription": "JSON sorgusunu kullanarak sunucunun JSON yanıtından belirli verileri ayrıştırın ve çıkarın. JSON beklemiyorsanız ham yanıt için \"$\" sembolünü kullanın. Sonuç daha sonra metin olarak beklenen değerle karşılaştırılır. Belgeler için {0}'a bakın ve sorgularla denemeler yapmak için {1}'i kullanın.",
|
||||
"shrinkDatabaseDescriptionSqlite": "SQLite için {vacuum} veritabanını tetikle. {auto_vacuum} zaten etkin ancak bu, {vacuum} komutunun yaptığı gibi veritabanını birleştirmez veya tek tek veritabanı sayfalarını yeniden paketlemez."
|
||||
}
|
||||
|
1
src/lang/ug.json
Normal file
1
src/lang/ug.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
@@ -1056,5 +1056,6 @@
|
||||
"greater than": "більше, ніж",
|
||||
"less than or equal to": "менше або дорівнює",
|
||||
"greater than or equal to": "більше або дорівнює",
|
||||
"record": "запис"
|
||||
"record": "запис",
|
||||
"shrinkDatabaseDescriptionSqlite": "Запускає команду {vacuum} для бази даних SQLite. Команда {auto_vacuum} вже увімкнена, але вона не дефрагментує базу даних і не перепаковує окремі сторінки бази даних так, як це робить команда {vacuum}."
|
||||
}
|
||||
|
@@ -1052,5 +1052,6 @@
|
||||
"less than or equal to": "不多于",
|
||||
"greater than or equal to": "不少于",
|
||||
"record": "记录",
|
||||
"jsonQueryDescription": "使用 JSON 查询解析并提取服务器 JSON 响应中的特定数据,或者,如果不期望得到 JSON 响应,则可使用 \"$\" 获取原始响应。然后将结果转为字符串并与期望值进行字符串比较。有关更多文档,请参阅 {0},亦可使用 {1} 来尝试查询。"
|
||||
"jsonQueryDescription": "使用 JSON 查询解析并提取服务器 JSON 响应中的特定数据,或者,如果不期望得到 JSON 响应,则可使用 \"$\" 获取原始响应。然后将结果转为字符串并与期望值进行字符串比较。有关更多文档,请参阅 {0},亦可使用 {1} 来尝试查询。",
|
||||
"shrinkDatabaseDescriptionSqlite": "触发 SQLite 数据库的 {vacuum} 命令。{auto_vacuum} 已经启用,但它不会像 {vacuum} 命令那样对数据库进行碎片整理,也不会重新打包各个数据库页面。"
|
||||
}
|
||||
|
@@ -458,4 +458,4 @@ async function evaluateJsonQuery(data, jsonPath, jsonPathOperator, expectedValue
|
||||
throw new Error(`Error evaluating JSON query: ${err.message}. Response from server was: ${response}`);
|
||||
}
|
||||
}
|
||||
exports.evaluateJsonQuery = evaluateJsonQuery;
|
||||
exports.evaluateJsonQuery = evaluateJsonQuery;
|
@@ -120,7 +120,11 @@ export const badgeConstants = {
|
||||
defaultCertExpireDownDays: "7"
|
||||
};
|
||||
|
||||
/** Flip the status of s */
|
||||
/**
|
||||
* Flip the status of s between UP and DOWN if this is possible
|
||||
* @param s {number} status
|
||||
* @returns {number} flipped status
|
||||
*/
|
||||
export function flipStatus(s: number) {
|
||||
if (s === UP) {
|
||||
return DOWN;
|
||||
|
Reference in New Issue
Block a user