Compare commits

...

45 Commits

Author SHA1 Message Date
Louis Lam
4ae437dd61 Update to 1.21.1 2023-03-27 20:09:24 +08:00
Louis Lam
6cb296b07a Update SQLite and Vue 2023-03-27 19:05:18 +08:00
Louis Lam
644c6a872f Merge pull request #2978 from UptimeKumaBot/weblate-uptime-kuma-uptime-kuma
Translations Update from Weblate
2023-03-27 17:54:50 +08:00
Weblate
8c69c18f6d Merge remote-tracking branch 'origin/master' 2023-03-27 08:38:00 +00:00
Mirinek
c1a1160767 Translated using Weblate (Czech)
Currently translated at 100.0% (709 of 709 strings)

Co-authored-by: Mirinek <mirek.nozicka77@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
Louis Lam
a0ebd88849 Translated using Weblate (Chinese (Traditional, Hong Kong))
Currently translated at 96.0% (681 of 709 strings)

Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hant_HK/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
401Unauthorized
2e9413cf33 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (709 of 709 strings)

Co-authored-by: 401Unauthorized <hi@4o1.to>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
Taha İPEK
278b52ec34 Translated using Weblate (Turkish)
Currently translated at 100.0% (709 of 709 strings)

Co-authored-by: Taha İPEK <ti.tahaipek@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/tr/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
Alex
caa757a27c Translated using Weblate (Russian)
Currently translated at 99.8% (708 of 709 strings)

Co-authored-by: Alex <proactivecam@inbox.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
Cyril59310
3ce117a943 Translated using Weblate (French)
Currently translated at 100.0% (709 of 709 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
2023-03-27 08:37:51 +00:00
Peter Petrík
cefe484b47 Translated using Weblate (Slovak)
Currently translated at 27.2% (193 of 708 strings)

Co-authored-by: Peter Petrík <peter.petrik.fefe@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sk/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:51 +00:00
ilya12077
a700892709 Translated using Weblate (Russian)
Currently translated at 100.0% (708 of 708 strings)

Co-authored-by: ilya12077 <mr.ilya.1207@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:50 +00:00
Alex
13d721ccf8 Translated using Weblate (Russian)
Currently translated at 100.0% (708 of 708 strings)

Co-authored-by: Alex <proactivecam@inbox.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:50 +00:00
DoyunShin
6c66bff518 Translated using Weblate (Korean)
Currently translated at 97.7% (692 of 708 strings)

Co-authored-by: DoyunShin <doyun.shin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ko/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:50 +00:00
AmadeusGraves
bea51d048b Translated using Weblate (Spanish)
Currently translated at 99.2% (703 of 708 strings)

Co-authored-by: AmadeusGraves <angelfx19@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2023-03-27 08:37:50 +00:00
Louis Lam
2e1a0fe4d5 Merge pull request #2983 from GrantBirki/master
GitHub Actions - Validate json / yaml files 📁
2023-03-27 16:36:20 +08:00
grantbirki
27b0895722 fix branch names 2023-03-26 22:35:00 +01:00
grantbirki
e687698851 add json / yaml validate job 2023-03-26 22:34:25 +01:00
Louis Lam
fbdeb30ce7 Merge pull request #2973 from chakflying/fix/limit-precision
Fix: Apply toPrecision as last step
2023-03-26 15:46:00 +08:00
Louis Lam
41bda4e1d7 Merge pull request #2975 from chakflying/fix/badge-no-label
Fix: Allow status badge with empty label
2023-03-26 15:42:12 +08:00
Louis Lam
4869e6531c Merge pull request #2980 from Genc/feature/twilio-notification-provider
Add Twilio Sms Notification Provider
2023-03-26 15:38:53 +08:00
Louis Lam
302b9cf644 Merge pull request #2956 from wwniclask25/feature/opsgenie-alerts
Feat: Add opsgenie notification provider
2023-03-26 15:36:50 +08:00
Louis Lam
3c3a192943 Merge pull request #2906 from chakflying/fix/duplicate-expiry-notif
Fix: Check for TLS expiry notified days smaller than target
2023-03-26 15:34:26 +08:00
Faruk Genç
b64c835cee Add Twilio Sms Notification Provider 2023-03-25 19:56:01 +03:00
Louis Lam
5266e713e6 Update ask-for-help.yaml 2023-03-25 23:06:51 +08:00
Louis Lam
86579d245f Update ask-for-help.yaml 2023-03-25 23:06:23 +08:00
Louis Lam
b6169408be Merge pull request #2962 from chakflying/fix/missing-clear-form
Fix: Add missing clearForm func.
2023-03-25 14:31:47 +08:00
Nelson Chan
4f05912276 Fix: Allow status badge with empty label 2023-03-25 02:44:15 +08:00
Nelson Chan
bf525371d9 Fix: Apply toPrecision as last step 2023-03-24 22:42:50 +08:00
Louis Lam
89bfc3bf33 Merge pull request #2908 from chakflying/chore/encrypted-private-key
Chore: Add support for encrypted SSL-key
2023-03-24 21:36:22 +08:00
Louis Lam
a2014278b8 Fix #2969 2023-03-24 19:16:12 +08:00
Louis Lam
70572af1af Fix #2969 2023-03-24 18:40:05 +08:00
Louis Lam
b31c23a43b Merge pull request #2970 from louislam/fix-2968
Fix crash issue caused by mysqlQuery()
2023-03-24 18:20:15 +08:00
Louis Lam
f4ee5271af Improve error handling of mysqlQuery and return row count as result 2023-03-24 16:24:00 +08:00
Louis Lam
7330db3563 Improve error handling of mysqlQuery and return row count as result 2023-03-24 16:08:30 +08:00
Louis Lam
097567e5f0 Merge pull request #2878 from chakflying/feat/status-page-countdown
Feat: Add status page countdown to refresh
2023-03-23 17:18:57 +08:00
Louis Lam
35f300c8eb Improve and reuse language keys 2023-03-23 17:17:53 +08:00
Nelson Chan
4c9d7ac8ca Fix: Add missing clearForm func. 2023-03-22 15:06:38 +08:00
niclas.koegl
d9558833fc Fix linting 2023-03-21 19:45:44 +01:00
niclas.koegl
776a482a1d Add Opsgenie notification provider 2023-03-21 19:29:37 +01:00
niclas.koegl
d2527d7254 Merge branch 'master' into feature/opsgenie-alerts 2023-03-21 18:10:10 +01:00
niclas.koegl
6dfca0c163 Add Opsgenie notification provider 2023-03-21 18:07:19 +01:00
Nelson Chan
391692a708 Chore: Add support for encrypted SSL-key 2023-03-09 00:00:07 +08:00
Nelson Chan
f32fcb204f Fix: Check for notified days smaller than target 2023-03-08 22:26:19 +08:00
Nelson Chan
193a273557 Feat: Add status page countdown to refresh 2023-03-03 08:25:41 +08:00
29 changed files with 1891 additions and 15736 deletions

View File

@@ -26,6 +26,12 @@ body:
label: "📝 Describe your problem" label: "📝 Describe your problem"
description: "Please walk us through it step by step." description: "Please walk us through it step by step."
placeholder: "Describe what are you asking for..." placeholder: "Describe what are you asking for..."
- type: textarea
id: error-msg
validations:
required: false
attributes:
label: "📝 Error Message(s) or Log"
- type: input - type: input
id: uptime-kuma-version id: uptime-kuma-version
attributes: attributes:

View File

@@ -0,0 +1,25 @@
name: json-yaml-validate
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
pull-requests: write # enable write permissions for pull request comments
jobs:
json-yaml-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: json-yaml-validate
id: json-yaml-validate
uses: GrantBirki/json-yaml-validate@v1.2.0
with:
comment: "true" # enable comment mode

16909
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.21.0", "version": "1.21.1",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -39,7 +39,7 @@
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.21.0 && npm ci --production && npm run download-dist", "setup": "git checkout 1.21.1 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",
@@ -70,7 +70,7 @@
"dependencies": { "dependencies": {
"@grpc/grpc-js": "~1.7.3", "@grpc/grpc-js": "~1.7.3",
"@louislam/ping": "~0.4.4-mod.0", "@louislam/ping": "~0.4.4-mod.0",
"@louislam/sqlite3": "15.1.2", "@louislam/sqlite3": "15.1.6",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.27.0", "axios": "~0.27.0",
"axios-ntlm": "1.3.0", "axios-ntlm": "1.3.0",
@@ -172,7 +172,7 @@
"v-pagination-3": "~0.1.7", "v-pagination-3": "~0.1.7",
"vite": "~3.1.0", "vite": "~3.1.0",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vue": "next", "vue": "~3.2.47",
"vue-chart-3": "3.0.9", "vue-chart-3": "3.0.9",
"vue-confirm-dialog": "~1.0.2", "vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4", "vue-contenteditable": "~3.0.4",

View File

@@ -637,9 +637,7 @@ class Monitor extends BeanModel {
} else if (this.type === "mysql") { } else if (this.type === "mysql") {
let startTime = dayjs().valueOf(); let startTime = dayjs().valueOf();
await mysqlQuery(this.databaseConnectionString, this.databaseQuery); bean.msg = await mysqlQuery(this.databaseConnectionString, this.databaseQuery);
bean.msg = "";
bean.status = UP; bean.status = UP;
bean.ping = dayjs().valueOf() - startTime; bean.ping = dayjs().valueOf() - startTime;
} else if (this.type === "mongodb") { } else if (this.type === "mongodb") {
@@ -1247,7 +1245,7 @@ class Monitor extends BeanModel {
if (notificationList.length > 0) { if (notificationList.length > 0) {
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days = ?", [ let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
"certificate", "certificate",
this.id, this.id,
targetDays, targetDays,

View File

@@ -0,0 +1,97 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP, DOWN } = require("../../src/util");
const opsgenieAlertsUrlEU = "https://api.eu.opsgenie.com/v2/alerts";
const opsgenieAlertsUrlUS = "https://api.opsgenie.com/v2/alerts";
let okMsg = "Sent Successfully.";
class Opsgenie extends NotificationProvider {
name = "Opsgenie";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let opsgenieAlertsUrl;
let priority = (notification.opsgeniePriority == "") ? 3 : notification.opsgeniePriority;
const textMsg = "Uptime Kuma Alert";
try {
switch (notification.opsgenieRegion) {
case "US":
opsgenieAlertsUrl = opsgenieAlertsUrlUS;
break;
case "EU":
opsgenieAlertsUrl = opsgenieAlertsUrlEU;
break;
default:
opsgenieAlertsUrl = opsgenieAlertsUrlUS;
}
if (heartbeatJSON == null) {
let notificationTestAlias = "uptime-kuma-notification-test";
let data = {
"message": msg,
"alias": notificationTestAlias,
"source": "Uptime Kuma",
"priority": "P5"
};
return this.post(notification, opsgenieAlertsUrl, data);
}
if (heartbeatJSON.status === DOWN) {
let data = {
"message": monitorJSON ? textMsg + `: ${monitorJSON.name}` : textMsg,
"alias": monitorJSON.name,
"description": msg,
"source": "Uptime Kuma",
"priority": `P${priority}`
};
return this.post(notification, opsgenieAlertsUrl, data);
}
if (heartbeatJSON.status === UP) {
let opsgenieAlertsCloseUrl = `${opsgenieAlertsUrl}/${encodeURIComponent(monitorJSON.name)}/close?identifierType=alias`;
let data = {
"source": "Uptime Kuma",
};
return this.post(notification, opsgenieAlertsCloseUrl, data);
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
/**
*
* @param {BeanModel} notification
* @param {string} url Request url
* @param {Object} data Request body
* @returns {Promise<string>}
*/
async post(notification, url, data) {
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": `GenieKey ${notification.opsgenieApiKey}`,
}
};
let res = await axios.post(url, data, config);
if (res.status == null) {
return "Opsgenie notification failed with invalid response!";
}
if (res.status < 200 || res.status >= 300) {
return `Opsgenie notification failed with status code ${res.status}`;
}
return okMsg;
}
}
module.exports = Opsgenie;

View File

@@ -0,0 +1,41 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Twilio extends NotificationProvider {
name = "twilio";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let accountSID = notification.twilioAccountSID;
let authToken = notification.twilioAuthToken;
try {
let config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Authorization": "Basic " + Buffer.from(accountSID + ":" + authToken).toString("base64"),
}
};
let data = new URLSearchParams();
data.append("To", notification.twilioToNumber);
data.append("From", notification.twilioFromNumber);
data.append("Body", msg);
let url = "https://api.twilio.com/2010-04-01/Accounts/" + accountSID + "/Messages.json";
await axios.post(url, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Twilio;

View File

@@ -23,6 +23,7 @@ const Mattermost = require("./notification-providers/mattermost");
const Ntfy = require("./notification-providers/ntfy"); const Ntfy = require("./notification-providers/ntfy");
const Octopush = require("./notification-providers/octopush"); const Octopush = require("./notification-providers/octopush");
const OneBot = require("./notification-providers/onebot"); const OneBot = require("./notification-providers/onebot");
const Opsgenie = require("./notification-providers/opsgenie");
const PagerDuty = require("./notification-providers/pagerduty"); const PagerDuty = require("./notification-providers/pagerduty");
const PagerTree = require("./notification-providers/pagertree"); const PagerTree = require("./notification-providers/pagertree");
const PromoSMS = require("./notification-providers/promosms"); const PromoSMS = require("./notification-providers/promosms");
@@ -41,6 +42,7 @@ const Stackfield = require("./notification-providers/stackfield");
const Teams = require("./notification-providers/teams"); const Teams = require("./notification-providers/teams");
const TechulusPush = require("./notification-providers/techulus-push"); const TechulusPush = require("./notification-providers/techulus-push");
const Telegram = require("./notification-providers/telegram"); const Telegram = require("./notification-providers/telegram");
const Twilio = require("./notification-providers/twilio");
const Splunk = require("./notification-providers/splunk"); const Splunk = require("./notification-providers/splunk");
const Webhook = require("./notification-providers/webhook"); const Webhook = require("./notification-providers/webhook");
const WeCom = require("./notification-providers/wecom"); const WeCom = require("./notification-providers/wecom");
@@ -83,6 +85,7 @@ class Notification {
new Ntfy(), new Ntfy(),
new Octopush(), new Octopush(),
new OneBot(), new OneBot(),
new Opsgenie(),
new PagerDuty(), new PagerDuty(),
new PagerTree(), new PagerTree(),
new PromoSMS(), new PromoSMS(),
@@ -103,6 +106,7 @@ class Notification {
new Teams(), new Teams(),
new TechulusPush(), new TechulusPush(),
new Telegram(), new Telegram(),
new Twilio(),
new Splunk(), new Splunk(),
new Webhook(), new Webhook(),
new WeCom(), new WeCom(),

View File

@@ -147,7 +147,11 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId);
const state = overrideValue !== undefined ? overrideValue : heartbeat.status; const state = overrideValue !== undefined ? overrideValue : heartbeat.status;
badgeValues.label = label ?? "Status"; if (label === undefined) {
badgeValues.label = "Status";
} else {
badgeValues.label = label;
}
switch (state) { switch (state) {
case DOWN: case DOWN:
badgeValues.color = downColor; badgeValues.color = downColor;
@@ -224,7 +228,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
); );
// limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits
const cleanUptime = parseFloat(uptime.toPrecision(4)); const cleanUptime = (uptime * 100).toPrecision(4);
// use a given, custom color or calculate one based on the uptime value // use a given, custom color or calculate one based on the uptime value
badgeValues.color = color ?? percentageToColor(uptime); badgeValues.color = color ?? percentageToColor(uptime);
@@ -235,7 +239,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
labelPrefix, labelPrefix,
label ?? `Uptime (${requestedDuration}${labelSuffix})`, label ?? `Uptime (${requestedDuration}${labelSuffix})`,
]); ]);
badgeValues.message = filterAndJoin([ prefix, `${cleanUptime * 100}`, suffix ]); badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]);
} }
// build the SVG based on given values // build the SVG based on given values

View File

@@ -74,6 +74,7 @@ class UptimeKumaServer {
// SSL // SSL
const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined; const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined; const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_SSL_KEY_PASSPHRASE || process.env.SSL_KEY_PASSPHRASE || undefined;
log.info("server", "Creating express and socket.io instance"); log.info("server", "Creating express and socket.io instance");
this.app = express(); this.app = express();
@@ -81,7 +82,8 @@ class UptimeKumaServer {
log.info("server", "Server Type: HTTPS"); log.info("server", "Server Type: HTTPS");
this.httpServer = https.createServer({ this.httpServer = https.createServer({
key: fs.readFileSync(sslKey), key: fs.readFileSync(sslKey),
cert: fs.readFileSync(sslCert) cert: fs.readFileSync(sslCert),
passphrase: sslKeyPassphrase,
}, this.app); }, this.app);
} else { } else {
log.info("server", "Server Type: HTTP"); log.info("server", "Server Type: HTTP");

View File

@@ -322,21 +322,28 @@ exports.postgresQuery = function (connectionString, query) {
* Run a query on MySQL/MariaDB * Run a query on MySQL/MariaDB
* @param {string} connectionString The database connection string * @param {string} connectionString The database connection string
* @param {string} query The query to validate the database with * @param {string} query The query to validate the database with
* @returns {Promise<(string[]|Object[]|Object)>} * @returns {Promise<(string)>}
*/ */
exports.mysqlQuery = function (connectionString, query) { exports.mysqlQuery = function (connectionString, query) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const connection = mysql.createConnection(connectionString); const connection = mysql.createConnection(connectionString);
connection.promise().query(query)
.then(res => { connection.on("error", (err) => {
resolve(res); reject(err);
}) });
.catch(err => {
connection.query(query, (err, res) => {
if (err) {
reject(err); reject(err);
}) } else {
.finally(() => { if (Array.isArray(res)) {
connection.destroy(); resolve("Rows: " + res.length);
}); } else {
resolve("No Error, but the result is not an array. Type: " + typeof res);
}
}
connection.destroy();
});
}); });
}; };

View File

@@ -159,6 +159,16 @@ export default {
} }
}); });
}, },
/** Clear Form inputs */
clearForm() {
this.key = {
name: "",
expires: this.minDate,
active: 1,
};
this.noExpire = false;
},
} }
}; };
</script> </script>

View File

@@ -13,6 +13,9 @@
:disabled="disabled" :disabled="disabled"
> >
<!-- A hidden textarea for copying text on non-https -->
<textarea ref="hiddenTextarea" style="position: fixed; left: -999999px; top: -999999px;"></textarea>
<a class="btn btn-outline-primary" @click="copyToClipboard(model)"> <a class="btn btn-outline-primary" @click="copyToClipboard(model)">
<font-awesome-icon :icon="icon" /> <font-awesome-icon :icon="icon" />
</a> </a>
@@ -111,24 +114,19 @@ export default {
}, 3000); }, 3000);
// navigator clipboard api needs a secure context (https) // navigator clipboard api needs a secure context (https)
// For http, use the text area method (else part)
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
// navigator clipboard api method' // navigator clipboard api method'
return navigator.clipboard.writeText(textToCopy); return navigator.clipboard.writeText(textToCopy);
} else { } else {
// text area method // text area method
let textArea = document.createElement("textarea"); let textArea = this.$refs.hiddenTextarea;
textArea.value = textToCopy; textArea.value = textToCopy;
// make the textarea out of viewport
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus(); textArea.focus();
textArea.select(); textArea.select();
return new Promise((res, rej) => { return new Promise((res, rej) => {
// here the magic happens // here the magic happens
document.execCommand("copy") ? res() : rej(); document.execCommand("copy") ? res() : rej();
textArea.remove();
}); });
} }
} }

View File

@@ -129,6 +129,7 @@ export default {
"ntfy": "Ntfy", "ntfy": "Ntfy",
"octopush": "Octopush", "octopush": "Octopush",
"OneBot": "OneBot", "OneBot": "OneBot",
"Opsgenie": "Opsgenie",
"PagerDuty": "PagerDuty", "PagerDuty": "PagerDuty",
"pushbullet": "Pushbullet", "pushbullet": "Pushbullet",
"PushByTechulus": "Push by Techulus", "PushByTechulus": "Push by Techulus",
@@ -143,6 +144,7 @@ export default {
"stackfield": "Stackfield", "stackfield": "Stackfield",
"teams": "Microsoft Teams", "teams": "Microsoft Teams",
"telegram": "Telegram", "telegram": "Telegram",
"twilio": "Twilio",
"Splunk": "Splunk", "Splunk": "Splunk",
"webhook": "Webhook", "webhook": "Webhook",
"GoAlert": "GoAlert", "GoAlert": "GoAlert",

View File

@@ -0,0 +1,36 @@
<template>
<div class="mb-3">
<label for="opsgenie-region" class="form-label">{{ $t("Region") }}<span style="color: red;"><sup>*</sup></span></label>
<select id="opsgenie-region" v-model="$parent.notification.opsgenieRegion" class="form-select" required>
<option value="us">
US (Default)
</option>
<option value="eu">
EU
</option>
</select>
</div>
<div class="mb-3">
<label for="opsgenie-apikey" class="form-label">{{ $t("API Key") }}<span style="color: red;"><sup>*</sup></span></label>
<HiddenInput id="opsgenie-apikey" v-model="$parent.notification.opsgenieApiKey" required="true" autocomplete="false"></HiddenInput>
</div>
<div class="mb-3">
<label for="opsgenie-priority" class="form-label">{{ $t("Priority") }}</label>
<input id="opsgenie-priority" v-model="$parent.notification.opsgeniePriority" type="number" class="form-control" min="1" max="5" step="1">
</div>
<div class="form-text">
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
<a href="https://docs.opsgenie.com/docs/alert-api" target="_blank">https://docs.opsgenie.com/docs/alert-api</a>
</i18n-t>
</div>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
},
};
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div class="mb-3">
<label for="twilio-account-sid" class="form-label">{{ $t("Account SID") }}</label>
<input id="twilio-account-sid" v-model="$parent.notification.twilioAccountSID" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label for="twilio-auth-token" class="form-label">{{ $t("Auth Token") }}</label>
<input id="twilio-auth-token" v-model="$parent.notification.twilioAuthToken" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label for="twilio-from-number" class="form-label">{{ $t("From Number") }}</label>
<input id="twilio-from-number" v-model="$parent.notification.twilioFromNumber" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label for="twilio-to-number" class="form-label">{{ $t("To Number") }}</label>
<input id="twilio-to-number" v-model="$parent.notification.twilioToNumber" type="text" class="form-control" required>
</div>
<div class="mb-3">
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
<a href="https://www.twilio.com/docs/sms" target="_blank">https://www.twilio.com/docs/sms</a>
</i18n-t>
</div>
</template>

View File

@@ -21,6 +21,7 @@ import Mattermost from "./Mattermost.vue";
import Ntfy from "./Ntfy.vue"; import Ntfy from "./Ntfy.vue";
import Octopush from "./Octopush.vue"; import Octopush from "./Octopush.vue";
import OneBot from "./OneBot.vue"; import OneBot from "./OneBot.vue";
import Opsgenie from "./Opsgenie.vue";
import PagerDuty from "./PagerDuty.vue"; import PagerDuty from "./PagerDuty.vue";
import PagerTree from "./PagerTree.vue"; import PagerTree from "./PagerTree.vue";
import PromoSMS from "./PromoSMS.vue"; import PromoSMS from "./PromoSMS.vue";
@@ -41,6 +42,7 @@ import STMP from "./SMTP.vue";
import Teams from "./Teams.vue"; import Teams from "./Teams.vue";
import TechulusPush from "./TechulusPush.vue"; import TechulusPush from "./TechulusPush.vue";
import Telegram from "./Telegram.vue"; import Telegram from "./Telegram.vue";
import Twilio from "./Twilio.vue";
import Webhook from "./Webhook.vue"; import Webhook from "./Webhook.vue";
import WeCom from "./WeCom.vue"; import WeCom from "./WeCom.vue";
import GoAlert from "./GoAlert.vue"; import GoAlert from "./GoAlert.vue";
@@ -76,6 +78,7 @@ const NotificationFormList = {
"ntfy": Ntfy, "ntfy": Ntfy,
"octopush": Octopush, "octopush": Octopush,
"OneBot": OneBot, "OneBot": OneBot,
"Opsgenie": Opsgenie,
"PagerDuty": PagerDuty, "PagerDuty": PagerDuty,
"PagerTree": PagerTree, "PagerTree": PagerTree,
"promosms": PromoSMS, "promosms": PromoSMS,
@@ -95,6 +98,7 @@ const NotificationFormList = {
"stackfield": Stackfield, "stackfield": Stackfield,
"teams": Teams, "teams": Teams,
"telegram": Telegram, "telegram": Telegram,
"twilio": Twilio,
"Splunk": Splunk, "Splunk": Splunk,
"webhook": Webhook, "webhook": Webhook,
"WeCom": WeCom, "WeCom": WeCom,

View File

@@ -738,5 +738,6 @@
"Add New Tag": "Přidat nový štítek", "Add New Tag": "Přidat nový štítek",
"lunaseaTarget": "Cíl", "lunaseaTarget": "Cíl",
"lunaseaDeviceID": "ID zařízení", "lunaseaDeviceID": "ID zařízení",
"lunaseaUserID": "ID uživatele" "lunaseaUserID": "ID uživatele",
"statusPageRefreshIn": "Obnovení za: {0}"
} }

View File

@@ -174,6 +174,7 @@
"Avg. Response": "Avg. Response", "Avg. Response": "Avg. Response",
"Entry Page": "Entry Page", "Entry Page": "Entry Page",
"statusPageNothing": "Nothing here, please add a group or a monitor.", "statusPageNothing": "Nothing here, please add a group or a monitor.",
"statusPageRefreshIn": "Refresh in: {0}",
"No Services": "No Services", "No Services": "No Services",
"All Systems Operational": "All Systems Operational", "All Systems Operational": "All Systems Operational",
"Partially Degraded Service": "Partially Degraded Service", "Partially Degraded Service": "Partially Degraded Service",
@@ -706,5 +707,9 @@
"wayToGetPagerTreeIntegrationURL": "After creating the Uptime Kuma integration in PagerTree, copy the Endpoint. See full details {0}", "wayToGetPagerTreeIntegrationURL": "After creating the Uptime Kuma integration in PagerTree, copy the Endpoint. See full details {0}",
"lunaseaTarget": "Target", "lunaseaTarget": "Target",
"lunaseaDeviceID": "Device ID", "lunaseaDeviceID": "Device ID",
"lunaseaUserID": "User ID" "lunaseaUserID": "User ID",
"twilioAccountSID": "Account SID",
"twilioAuthToken": "Auth Token",
"twilioFromNumber": "From Number",
"twilioToNumber": "To Number"
} }

View File

@@ -696,5 +696,47 @@
"Bark Endpoint": "Endpoint Bark", "Bark Endpoint": "Endpoint Bark",
"WebHookUrl": "WebHookUrl", "WebHookUrl": "WebHookUrl",
"High": "Alto", "High": "Alto",
"alertaApiEndpoint": "Endpoint API" "alertaApiEndpoint": "Endpoint API",
"Body Encoding": "Codificación del cuerpo",
"Expiry date": "Fecha de expiración",
"Expiry": "Expiración",
"API Keys": "Claves API",
"Key Added": "Clave añadida",
"Add Another": "Añadir otro",
"Continue": "Continuar",
"Don't expire": "No caduca",
"apiKey-inactive": "Inactivo",
"apiKey-expired": "Expirado",
"apiKey-active": "Activo",
"No API Keys": "No hay claves API",
"Add API Key": "Añadir clave API",
"apiKeyAddedMsg": "Su clave API ha sido añadida. Anótala, ya que no se volverá a mostrar.",
"Clone": "Clonar",
"cloneOf": "Clon de {0}",
"pagertreeDoNothing": "No hacer nada",
"pagertreeResolve": "Resolución automática",
"pagertreeCritical": "Crítico",
"pagertreeHigh": "Alto",
"pagertreeMedium": "Medio",
"pagertreeLow": "Bajo",
"pagertreeSilent": "Silencio",
"pagertreeUrgency": "Urgencia",
"pagertreeIntegrationUrl": "URL de integración",
"lunaseaTarget": "Objetivo",
"wayToGetPagerTreeIntegrationURL": "Después de crear la integración Uptime Kuma en PagerTree, copie el Endpoint. Ver todos los detalles {0}",
"Generate": "Generar",
"deleteAPIKeyMsg": "¿Está seguro de que desea eliminar esta clave API?",
"telegramMessageThreadID": "(Opcional) ID del hilo de mensajes",
"telegramMessageThreadIDDescription": "Opcional Identificador único para el hilo de mensajes de destino (asunto) del foro; solo para supergrupos de foros",
"telegramProtectContent": "Proteger Forwarding/Saving",
"telegramProtectContentDescription": "Si se activa, los mensajes del bot en Telegram estarán protegidos contra el reenvío y el guardado.",
"notificationRegional": "Regional",
"Clone Monitor": "Clonar Monitor",
"telegramSendSilently": "Enviar en silencio",
"telegramSendSilentlyDescription": "Envía el mensaje en silencio. Los usuarios recibirán una notificación sin sonido.",
"Add New Tag": "Añadir nueva etiqueta",
"lunaseaUserID": "ID Usuario",
"lunaseaDeviceID": "ID Dispositivo",
"disableAPIKeyMsg": "¿Está seguro de que desea desactivar esta clave API?",
"Expires": "Expira"
} }

View File

@@ -738,5 +738,6 @@
"lunaseaDeviceID": "Identifiant de l'appareil", "lunaseaDeviceID": "Identifiant de l'appareil",
"lunaseaUserID": "Identifiant de l'utilisateur", "lunaseaUserID": "Identifiant de l'utilisateur",
"Add New Tag": "Ajouter une étiquette", "Add New Tag": "Ajouter une étiquette",
"lunaseaTarget": "Cible" "lunaseaTarget": "Cible",
"statusPageRefreshIn": "Actualisation dans: {0}"
} }

View File

@@ -719,5 +719,10 @@
"notificationRegional": "지역별", "notificationRegional": "지역별",
"Google Analytics ID": "Google Analytics ID", "Google Analytics ID": "Google Analytics ID",
"Add API Key": "API 키 추가", "Add API Key": "API 키 추가",
"apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않을 것이므로 메모해 두세요." "apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않을 것이므로 메모해 두세요.",
"pagertreeCritical": "치명적인",
"apiKey-active": "사용 가능",
"lunaseaUserID": "사용자 ID",
"apiKey-expired": "만료됨",
"Expires": "만료일"
} }

View File

@@ -8,7 +8,7 @@
"acceptedStatusCodesDescription": "Выберите коды статусов для определения доступности сервиса.", "acceptedStatusCodesDescription": "Выберите коды статусов для определения доступности сервиса.",
"passwordNotMatchMsg": "Повтор пароля не совпадает.", "passwordNotMatchMsg": "Повтор пароля не совпадает.",
"notificationDescription": "Привяжите уведомления к мониторам.", "notificationDescription": "Привяжите уведомления к мониторам.",
"keywordDescription": "Поиск слова в чистом HTML или в JSON-ответе (чувствительно к регистру)", "keywordDescription": "Поиск слова в чистом HTML или в JSON-ответе (чувствительно к регистру).",
"pauseDashboardHome": "Пауза", "pauseDashboardHome": "Пауза",
"deleteMonitorMsg": "Вы действительно хотите удалить данный монитор?", "deleteMonitorMsg": "Вы действительно хотите удалить данный монитор?",
"deleteNotificationMsg": "Вы действительно хотите удалить это уведомление для всех мониторов?", "deleteNotificationMsg": "Вы действительно хотите удалить это уведомление для всех мониторов?",
@@ -45,9 +45,9 @@
"Uptime": "Аптайм", "Uptime": "Аптайм",
"Cert Exp.": "Сертификат истекает.", "Cert Exp.": "Сертификат истекает.",
"day": "день | дней", "day": "день | дней",
"-day": " дней", "-day": "-дней",
"hour": "час", "hour": "час",
"-hour": " часа", "-hour": "-часа",
"Response": "Ответ", "Response": "Ответ",
"Ping": "Пинг", "Ping": "Пинг",
"Monitor Type": "Тип монитора", "Monitor Type": "Тип монитора",
@@ -124,12 +124,12 @@
"Also apply to existing monitors": "Применить к существующим мониторам", "Also apply to existing monitors": "Применить к существующим мониторам",
"Export": "Экспорт", "Export": "Экспорт",
"Import": "Импорт", "Import": "Импорт",
"backupDescription": "Вы можете сохранить резервную копию всех мониторов и уведомлений в виде JSON-файла", "backupDescription": "Вы можете сохранить резервную копию всех мониторов и уведомлений в виде JSON-файла.",
"backupDescription2": "P.S. История и события сохранены не будут", "backupDescription2": "Важно: история и события сохранены не будут.",
"backupDescription3": "Важные данные, такие как токены уведомлений, добавляются при экспорте, поэтому храните файлы в безопасном месте", "backupDescription3": "Важные данные, такие как токены уведомлений, добавляются при экспорте, поэтому храните файлы в безопасном месте.",
"alertNoFile": "Выберите файл для импорта.", "alertNoFile": "Выберите файл для импорта.",
"alertWrongFileType": "Выберите JSON-файл.", "alertWrongFileType": "Выберите JSON-файл.",
"twoFAVerifyLabel": "Пожалуйста, введите свой токен, чтобы проверить работу 2FA", "twoFAVerifyLabel": "Пожалуйста, введите свой токен, чтобы проверить работу 2FA:",
"tokenValidSettingsMsg": "Токен действителен! Теперь вы можете сохранить настройки 2FA.", "tokenValidSettingsMsg": "Токен действителен! Теперь вы можете сохранить настройки 2FA.",
"confirmEnableTwoFAMsg": "Вы действительно хотите включить 2FA?", "confirmEnableTwoFAMsg": "Вы действительно хотите включить 2FA?",
"confirmDisableTwoFAMsg": "Вы действительно хотите выключить 2FA?", "confirmDisableTwoFAMsg": "Вы действительно хотите выключить 2FA?",
@@ -444,11 +444,11 @@
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
"Page Not Found": "Страница не найдена", "Page Not Found": "Страница не найдена",
"wayToGetCloudflaredURL": "(Скачать cloudflared с {0})", "wayToGetCloudflaredURL": "(Скачать cloudflared с {0})",
"cloudflareWebsite": "Cloudflare Website", "cloudflareWebsite": "Веб-сайт Cloudflare",
"Message:": "Сообщение:", "Message:": "Сообщение:",
"Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:", "Don't know how to get the token? Please read the guide:": "Не знаете, как получить токен? Пожалуйста, прочтите руководство:",
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.", "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Текущее соединение может быть потеряно, если вы в данный момент подключаетесь через туннель Cloudflare. Вы уверены, что хотите это остановить? Введите свой текущий пароль, чтобы подтвердить это.",
"HTTP Headers": "HTTP заголовки", "HTTP Headers": "заголовки HTTP",
"Trust Proxy": "Доверять прокси", "Trust Proxy": "Доверять прокси",
"Other Software": "Другое программное обеспечение", "Other Software": "Другое программное обеспечение",
"For example: nginx, Apache and Traefik.": "К примеру: nginx, Apache и Traefik.", "For example: nginx, Apache and Traefik.": "К примеру: nginx, Apache и Traefik.",
@@ -463,13 +463,13 @@
"Proxy": "Прокси", "Proxy": "Прокси",
"Date Created": "Дата создания", "Date Created": "Дата создания",
"HomeAssistant": "Home Assistant", "HomeAssistant": "Home Assistant",
"onebotHttpAddress": "OneBot HTTP Address", "onebotHttpAddress": "HTTP-адрес OneBot",
"onebotMessageType": "OneBot Message Type", "onebotMessageType": "Тип сообщения OneBot",
"onebotGroupMessage": "Группа", "onebotGroupMessage": "Группа",
"onebotPrivateMessage": "Private", "onebotPrivateMessage": "Private",
"onebotUserOrGroupId": "ID группы или пользователя", "onebotUserOrGroupId": "ID группы или пользователя",
"onebotSafetyTips": "В целях безопасности необходимо установить токен доступа", "onebotSafetyTips": "В целях безопасности необходимо установить токен доступа",
"PushDeer Key": "PushDeer Key", "PushDeer Key": "ключ PushDeer",
"Footer Text": "Текст нижнего колонтитула", "Footer Text": "Текст нижнего колонтитула",
"Show Powered By": "Показывать на чем создано", "Show Powered By": "Показывать на чем создано",
"Domain Names": "Доменные имена", "Domain Names": "Доменные имена",
@@ -488,40 +488,40 @@
"From Name/Number": "Имя/номер отправителя", "From Name/Number": "Имя/номер отправителя",
"Leave blank to use a shared sender number.": "Оставьте пустым, чтобы использовать общий номер отправителя.", "Leave blank to use a shared sender number.": "Оставьте пустым, чтобы использовать общий номер отправителя.",
"Octopush API Version": "Версия API Octopush", "Octopush API Version": "Версия API Octopush",
"Legacy Octopush-DM": "Legacy Octopush-DM", "Legacy Octopush-DM": "устаревший Octopush-DM",
"endpoint": "endpoint", "endpoint": "конечная точка",
"octopushAPIKey": "\"API key\" из учетных данных HTTP API в панели управления", "octopushAPIKey": "\"API key\" из учетных данных HTTP API в панели управления",
"octopushLogin": "\"Login\" из учетных данных HTTP API в панели управления", "octopushLogin": "\"Login\" из учетных данных HTTP API в панели управления",
"promosmsLogin": "Логин API", "promosmsLogin": "Логин API",
"promosmsPassword": "Пароль API", "promosmsPassword": "Пароль API",
"pushoversounds pushover": "Pushover (default)", "pushoversounds pushover": "Pushover (по умолчанию)",
"pushoversounds bike": "Bike", "pushoversounds bike": "Велосипед",
"pushoversounds bugle": "Bugle", "pushoversounds bugle": "Горн",
"pushoversounds cashregister": "Cash Register", "pushoversounds cashregister": "Кассовый аппарат",
"pushoversounds classical": "Classical", "pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic", "pushoversounds cosmic": "Космический",
"pushoversounds falling": "Falling", "pushoversounds falling": "Падающий",
"pushoversounds gamelan": "Gamelan", "pushoversounds gamelan": "Гамелан",
"pushoversounds incoming": "Incoming", "pushoversounds incoming": "Входящий",
"pushoversounds intermission": "Intermission", "pushoversounds intermission": "Антракт",
"pushoversounds magic": "Magic", "pushoversounds magic": "Магия",
"pushoversounds mechanical": "Mechanical", "pushoversounds mechanical": "Механический",
"pushoversounds pianobar": "Piano Bar", "pushoversounds pianobar": "Пиано-бар",
"pushoversounds siren": "Siren", "pushoversounds siren": "Сирена",
"pushoversounds spacealarm": "Space Alarm", "pushoversounds spacealarm": "Космическая сигнализация",
"pushoversounds tugboat": "Tug Boat", "pushoversounds tugboat": "Буксирное судно",
"pushoversounds alien": "Alien Alarm (long)", "pushoversounds alien": "Инопланетная тревога (долгое)",
"pushoversounds climb": "Climb (long)", "pushoversounds climb": "Подъем (долгое)",
"pushoversounds persistent": "Persistent (long)", "pushoversounds persistent": "Стойкий (долгое)",
"pushoversounds echo": "Pushover Echo (long)", "pushoversounds echo": "Pushover Эхо (долгое)",
"pushoversounds updown": "Up Down (long)", "pushoversounds updown": "Вверх вниз (долгое)",
"pushoversounds vibrate": "Vibrate Only", "pushoversounds vibrate": "Только вибрация",
"pushoversounds none": "None (silent)", "pushoversounds none": "Нет (тихо)",
"pushyAPIKey": "Secret API Key", "pushyAPIKey": "Секретный ключ API",
"pushyToken": "Токен устройства", "pushyToken": "Токен устройства",
"Using a Reverse Proxy?": "Используете обратный прокси?", "Using a Reverse Proxy?": "Используете обратный прокси?",
"Check how to config it for WebSocket": "Проверьте, как настроить его для WebSocket", "Check how to config it for WebSocket": "Проверьте, как настроить его для WebSocket",
"Steam Game Server": "Steam Game Server", "Steam Game Server": "Игровой сервер Steam",
"Most likely causes:": "Наиболее вероятные причины:", "Most likely causes:": "Наиболее вероятные причины:",
"The resource is no longer available.": "Ресурс больше не доступен.", "The resource is no longer available.": "Ресурс больше не доступен.",
"There might be a typing error in the address.": "В адресе может быть опечатка.", "There might be a typing error in the address.": "В адресе может быть опечатка.",
@@ -536,24 +536,24 @@
"certificationExpiryDescription": "HTTPS Мониторы инициируют уведомление, когда срок действия сертификата TLS истечет:", "certificationExpiryDescription": "HTTPS Мониторы инициируют уведомление, когда срок действия сертификата TLS истечет:",
"Setup Docker Host": "Настроить Docker Host", "Setup Docker Host": "Настроить Docker Host",
"Connection Type": "Тип соединения", "Connection Type": "Тип соединения",
"Docker Daemon": "Docker Daemon", "Docker Daemon": "Демон Docker",
"deleteDockerHostMsg": "Are you sure want to delete this docker host for all monitors?", "deleteDockerHostMsg": "Вы уверены, что хотите удалить этот узел docker для всех мониторов?",
"socket": "Socket", "socket": "Сокет",
"tcp": "TCP / HTTP", "tcp": "TCP / HTTP",
"Docker Container": "Docker контейнер", "Docker Container": "Docker контейнер",
"Container Name / ID": "Название контейнера / ID", "Container Name / ID": "Название контейнера / ID",
"Docker Host": "Docker Host", "Docker Host": "Хост Docker",
"Docker Hosts": "Docker Hosts", "Docker Hosts": "Хосты Docker",
"ntfy Topic": "ntfy Topic", "ntfy Topic": "тема ntfy",
"Domain": "Домен", "Domain": "Домен",
"Workstation": "Workstation", "Workstation": "Рабочая станция",
"disableCloudflaredNoAuthMsg": "Вы находитесь в режиме без авторизации, пароль не требуется.", "disableCloudflaredNoAuthMsg": "Вы находитесь в режиме без авторизации, пароль не требуется.",
"trustProxyDescription": "Доверять заголовкам 'X-Forwarded-*'. Если вы хотите получить правильный IP-адрес клиента, а ваш Uptime Kuma находится под Nginx или Apache, вам следует включить этот параметр.", "trustProxyDescription": "Доверять заголовкам 'X-Forwarded-*'. Если вы хотите получить правильный IP-адрес клиента, а ваш Uptime Kuma находится под Nginx или Apache, вам следует включить этот параметр.",
"wayToGetLineNotifyToken": "Вы можете получить токен доступа в {0}", "wayToGetLineNotifyToken": "Вы можете получить токен доступа в {0}",
"Examples": "Примеры", "Examples": "Примеры",
"Home Assistant URL": "Home Assistant URL", "Home Assistant URL": "URL-адрес Home Assistant",
"Long-Lived Access Token": "Токен доступа с длительным сроком службы", "Long-Lived Access Token": "Токен доступа с длительным сроком службы",
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ", "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Токен доступа с длительным сроком действия можно создать, нажав на имя вашего профиля (внизу слева) и прокрутив его вниз, затем нажмите Создать токен. ",
"Notification Service": "Служба уведомлений", "Notification Service": "Служба уведомлений",
"default: notify all devices": "по стандарту: уведомлять все устройства", "default: notify all devices": "по стандарту: уведомлять все устройства",
"A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Список служб уведомлений можно найти в Home Assistant в разделе \"Инструменты разработчика > Службы\", выполнив поиск по слову \"уведомление\", чтобы найти название вашего устройства/телефона.", "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Список служб уведомлений можно найти в Home Assistant в разделе \"Инструменты разработчика > Службы\", выполнив поиск по слову \"уведомление\", чтобы найти название вашего устройства/телефона.",
@@ -618,7 +618,7 @@
"Custom CSS": "Пользовательские CSS", "Custom CSS": "Пользовательские CSS",
"weekdayShortTue": "Вт", "weekdayShortTue": "Вт",
"dayOfWeek": "День недели", "dayOfWeek": "День недели",
"confirmDeleteTagMsg": "Вы уверены, что хотите удалить этот тег? Мониторы, связанные с этим тегом не будут удалены.", "confirmDeleteTagMsg": "Вы уверены, что хотите удалить этот тег? Мониторы, связанные с этим тегом не будут удалены.",
"loadingError": "Невозможно получить данные, пожалуйста попробуйте позже.", "loadingError": "Невозможно получить данные, пожалуйста попробуйте позже.",
"Packet Size": "Размер пакета", "Packet Size": "Размер пакета",
"warningTimezone": "Используется часовой пояс сервера", "warningTimezone": "Используется часовой пояс сервера",
@@ -676,10 +676,10 @@
"Integration URL": "URL интеграции", "Integration URL": "URL интеграции",
"do nothing": "ничего не делать", "do nothing": "ничего не делать",
"smseagleTo": "Номер(а) телефона", "smseagleTo": "Номер(а) телефона",
"smseagleGroup": "Имена групп в телефонной книжке", "smseagleGroup": "Название(я) групп телефонной книги",
"smseagleContact": "Имена контактов из телефонной книжки", "smseagleContact": "Имена контактов из телефонной книжки",
"smseagleRecipientType": "Тип получателя", "smseagleRecipientType": "Тип получателя",
"smseagleRecipient": "Получатель (через запятую, если несколько)", "smseagleRecipient": "Получатель(я) (через запятую, если необходимо указать несколько)",
"smseagleToken": "Токен доступа API", "smseagleToken": "Токен доступа API",
"smseagleUrl": "URL вашего SMSEagle устройства", "smseagleUrl": "URL вашего SMSEagle устройства",
"smseagleEncoding": "Отправить в юникоде", "smseagleEncoding": "Отправить в юникоде",
@@ -687,9 +687,9 @@
"Server Address": "Адрес сервера", "Server Address": "Адрес сервера",
"Learn More": "Узнать больше", "Learn More": "Узнать больше",
"topicExplanation": "MQTT топик для мониторинга", "topicExplanation": "MQTT топик для мониторинга",
"Guild ID": "Guild ID", "Guild ID": "Идентификатор гильдии",
"Kook": "Kook", "Kook": "Kook",
"wayToGetKookBotToken": "Создайте приложение и получите токен вашего бота тут {0}.", "wayToGetKookBotToken": "Создайте приложение и получите токен бота по адресу {0}",
"Resend Notification if Down X times consecutively": "Повторная отправка уведомления при падении несколько раз", "Resend Notification if Down X times consecutively": "Повторная отправка уведомления при падении несколько раз",
"telegramProtectContent": "Запретить пересылку/сохранение", "telegramProtectContent": "Запретить пересылку/сохранение",
"telegramProtectContentDescription": "Если включено, сообщения бота в Telegram будут запрещены для пересылки и сохранения.", "telegramProtectContentDescription": "Если включено, сообщения бота в Telegram будут запрещены для пересылки и сохранения.",
@@ -700,5 +700,53 @@
"Clone": "Копия", "Clone": "Копия",
"cloneOf": "Копия {0}", "cloneOf": "Копия {0}",
"notificationRegional": "Региональный", "notificationRegional": "Региональный",
"Add New Tag": "Добавить тег" "Add New Tag": "Добавить тег",
"Body Encoding": "Тип содержимого запроса.(JSON or XML)",
"Strategy": "Стратегия",
"Free Mobile User Identifier": "Бесплатный идентификатор мобильного пользователя",
"Auto resolve or acknowledged": "Автоматическое разрешение или подтверждение",
"auto acknowledged": "автоматическое подтверждение",
"auto resolve": "автоматическое разрешение",
"API Keys": "Ключи API",
"Expiry": "Истекает",
"Expiry date": "Дата окончания действия",
"Don't expire": "Не истекает",
"Continue": "Продолжать",
"Add Another": "Добавьте еще один",
"Key Added": "Ключ добавлен",
"Add API Key": "Добавить ключ API",
"No API Keys": "Нет API ключей",
"apiKey-active": "Активный",
"apiKey-expired": "Истёк",
"apiKey-inactive": "Неактивный",
"Expires": "Истекает",
"disableAPIKeyMsg": "Вы уверены, что хотите отключить этот ключ?",
"Generate": "Создать",
"pagertreeResolve": "Автоматическое разрешение",
"pagertreeDoNothing": "ничего не делать",
"lunaseaTarget": "Цель",
"lunaseaDeviceID": "Идентификатор устройства",
"lunaseaUserID": "Идентификатор пользователя",
"Lowcost": "Низкая стоимость",
"pagertreeIntegrationUrl": "URL-адрес интеграции",
"pagertreeUrgency": "Срочность",
"pagertreeSilent": "Тихий",
"pagertreeLow": "Низкий",
"pagertreeMedium": "Средний",
"pagertreeHigh": "Высокий",
"pagertreeCritical": "Критический",
"high": "высокий",
"promosmsAllowLongSMS": "Разрешить длинные SMS-сообщения",
"Economy": "Экономия",
"wayToGetPagerDutyKey": "Вы можете получить это, перейдя в службу -> Каталог служб -> (Выберите службу) -> Интеграции -> Добавить интеграцию. Здесь вы можете выполнить поиск по \"Events API V2\". Дополнительная информация {0}",
"apiKeyAddedMsg": "Ваш API ключ был добавлен. Пожалуйста, запишите это, так как оно больше не будет показан.",
"deleteAPIKeyMsg": "Вы уверены, что хотите удалить этот ключ?",
"wayToGetPagerTreeIntegrationURL": "После создания интеграции Uptime Kuma в PagerTree, скопируйте конечную точку. Смотрите полную информацию {0}",
"telegramMessageThreadIDDescription": "Необязательный уникальный идентификатор для цепочки сообщений (темы) форума; только для форумов-супергрупп",
"grpcMethodDescription": "Название метода - преобразовать в формат cammelCase, такой как sayHello, check и т.д.",
"Proto Service Name": "название службы Proto",
"Proto Method": "Метод Proto",
"Proto Content": "Содержание Proto",
"telegramMessageThreadID": "(Необязательно) ID цепочки сообщений",
"statusPageRefreshIn": "Обновлять каждые: {0}"
} }

View File

@@ -64,7 +64,7 @@
"Up": "Dostupné", "Up": "Dostupné",
"Down": "Nedostupné", "Down": "Nedostupné",
"Unknown": "Neznáme", "Unknown": "Neznáme",
"markdownSupported": "Podpora Markdown syntaxe", "markdownSupported": "Podpora Markdown syntaxu",
"Name": "Názov", "Name": "Názov",
"DateTime": "Dátum a čas", "DateTime": "Dátum a čas",
"Resume": "Pokračovať", "Resume": "Pokračovať",
@@ -81,5 +81,115 @@
"maxRedirectDescription": "Maximálny počet presmerovaní. Hodnota 0 vypne presmerovania.", "maxRedirectDescription": "Maximálny počet presmerovaní. Hodnota 0 vypne presmerovania.",
"needPushEvery": "Tuto adresu by ste mali volať každých {0} sekúnd.", "needPushEvery": "Tuto adresu by ste mali volať každých {0} sekúnd.",
"pushOptionalParams": "Voliteľné parametre: {0}", "pushOptionalParams": "Voliteľné parametre: {0}",
"Theme - Heartbeat Bar": "Téma - Heartbeat riadok" "Theme - Heartbeat Bar": "Téma - Heartbeat riadok",
"Game": "Hra",
"Search Engine Visibility": "Viditeľnosť vyhľadávačmi",
"Allow indexing": "Povoliť indexovanie",
"Change Password": "Zmeniť heslo",
"Current Password": "Aktuálne heslo",
"New Password": "Nové heslo",
"Repeat New Password": "Zopakovať nové heslo",
"Update Password": "Aktualizovať heslo",
"Disable Auth": "Vypnúť autentifikáciu",
"Enable Auth": "Zapnúť autentifikáciu",
"Please use this option carefully!": "Túto možnosť používajte opatrne!",
"Logout": "Odhlásiť sa",
"Leave": "Odísť",
"I understand, please disable": "Rozumiem, vypnite to",
"Yes": "Áno",
"No": "Nie",
"Username": "Používateľské meno",
"Password": "Heslo",
"Login": "Prihlásiť sa",
"No Monitors, please": "Žiadne sledovanie, prosím",
"add one": "pridať jeden",
"Notification Type": "Typ notifikácie",
"Email": "E-mail",
"Test": "Test",
"Certificate Info": "Informácie o certifikáte",
"Resolver Server": "DNS server",
"Last Result": "Posledný výsledok",
"Repeat Password": "Zopakovať heslo",
"Import Backup": "Importovať zálohu",
"Export Backup": "Exportovať zálohu",
"Export": "Exportovať",
"Import": "Importovať",
"respTime": "Čas odozvy (ms)",
"notAvailableShort": "Nie je číslo",
"Default enabled": "Predvolene povolené",
"Create": "Vytvoriť",
"Clear Data": "Vyčistiť dáta",
"Events": "Udalosti",
"Heartbeats": "Odpovede",
"Auto Get": "Získať automaticky",
"Schedule maintenance": "Naplánovať údržbu",
"Affected Monitors": "Dotknuté sledovania",
"Pick Affected Monitors...": "Vybrať dotknuté sledovania…",
"Start of maintenance": "Začiatok údržby",
"All Status Pages": "Všetky stavové stránky",
"Select status pages...": "Vybrať stránky stavu…",
"alertNoFile": "Vyberte súbor na import.",
"alertWrongFileType": "Vyberte súbor JSON.",
"Clear all statistics": "Vymazať všetky štatistiky",
"Skip existing": "Preskočiť existujúce",
"Overwrite": "Prepísať",
"Options": "Možnosti",
"Keep both": "Ponechať obe",
"Setup 2FA": "Nastavenie 2FA",
"Disable 2FA": "Zakázať 2FA",
"2FA Settings": "Nastavenia 2FA",
"Two Factor Authentication": "Dvojfaktorová autentifikácia",
"Inactive": "Neaktívne",
"Token": "Token",
"Show URI": "Zobraziť URI",
"Tags": "Značky",
"Add New below or Select...": "Pridať novú nižšie alebo vybrať…",
"Tag with this value already exist.": "Značka s touto hodnotou už existuje.",
"color": "Farba",
"value (optional)": "hodnota (voliteľné)",
"Gray": "Šedá",
"Red": "Červená",
"Orange": "Oranžová",
"Green": "Zelená",
"Indigo": "Indigo",
"Purple": "Fialová",
"Pink": "Ružová",
"Custom": "Vlastná",
"Avg. Ping": "Priemerný ping",
"Avg. Response": "Priemerný čas odpovede",
"Entry Page": "Vstupná stránka",
"No Services": "Žiadne služby",
"All Systems Operational": "Všetky systémy funkčné",
"Partially Degraded Service": "Čiastočne zhoršená služba",
"Degraded Service": "Degradovaná služba",
"Add Group": "Pridať skupinu",
"Add a monitor": "Pridať sledovanie",
"Edit Status Page": "Upraviť stavovú stránku",
"Go to Dashboard": "Prejdite na informačný panel",
"Status Page": "Stavová stránka",
"Status Pages": "Stavové stránky",
"defaultNotificationName": "Moje {notification} upozornenie ({number})",
"here": "tu",
"Required": "Povinné",
"Post URL": "Post URL",
"Content Type": "Druh obsahu",
"webhookJsonDesc": "{0} je vhodný pre všetky moderné servery HTTP, ako napríklad Express.js",
"webhookFormDataDesc": "{multipart} je dobré pre PHP. JSON bude potrebné analyzovať pomocou {decodeFunction}",
"Generate": "Generovať",
"Discourage search engines from indexing site": "Odradiť vyhľadávacie nástroje od indexovania stránky",
"disableauth.message1": "Ste si istý, že chcete <strong>vypnúť autentifikáciu</strong>?",
"disableauth.message2": "Je navrhnutý pre scenáre, <strong>kde máte v úmysle implementovať autentifikáciu treťou stranou</strong> pred Uptime Kuma, ako je Cloudflare Access, Authelia alebo iné autentifikačné mechanizmy.",
"Confirm": "Potvrdiť",
"Remember me": "Zapamätať si ma",
"Resource Record Type": "Typ záznamu",
"Create your admin account": "Vytvorte si účet administrátora",
"Apply on all existing monitors": "Aplikujte na všetky existujúce sledovania",
"Verify Token": "Overiť token",
"Enable 2FA": "Povoliť 2FA",
"Active": "Aktívne",
"Add New Tag": "Pridať novú značku",
"Tag with this name already exist.": "Značka s týmto názvom už existuje.",
"Blue": "Modrá",
"Search...": "Hľadať…",
"statusPageNothing": "Nič tu nie je, pridajte skupinu alebo sledovanie."
} }

View File

@@ -738,5 +738,6 @@
"lunaseaTarget": "Hedef", "lunaseaTarget": "Hedef",
"Add New Tag": "Yeni Etiket Ekle", "Add New Tag": "Yeni Etiket Ekle",
"lunaseaDeviceID": "Cihaz ID", "lunaseaDeviceID": "Cihaz ID",
"lunaseaUserID": "Kullanıcı ID" "lunaseaUserID": "Kullanıcı ID",
"statusPageRefreshIn": "{0} içinde yenilenecek"
} }

View File

@@ -740,5 +740,6 @@
"Add New Tag": "添加新标签", "Add New Tag": "添加新标签",
"lunaseaDeviceID": "设备ID", "lunaseaDeviceID": "设备ID",
"lunaseaTarget": "目标", "lunaseaTarget": "目标",
"lunaseaUserID": "用户ID" "lunaseaUserID": "用户ID",
"statusPageRefreshIn": "将于 {0} 后刷新"
} }

View File

@@ -228,7 +228,7 @@
"smtpCC": "CC", "smtpCC": "CC",
"smtpBCC": "BCC", "smtpBCC": "BCC",
"Discord Webhook URL": "Discord Webhook 網址", "Discord Webhook URL": "Discord Webhook 網址",
"wayToGetDiscordURL": "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得", "wayToGetDiscordURL": "您可以前往 伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
"Bot Display Name": "機器人顯示名稱", "Bot Display Name": "機器人顯示名稱",
"Prefix Custom Message": "前綴自訂訊息", "Prefix Custom Message": "前綴自訂訊息",
"Webhook URL": "Webhook 網址", "Webhook URL": "Webhook 網址",
@@ -706,5 +706,8 @@
"Add New Tag": "加新標籤", "Add New Tag": "加新標籤",
"Economy": "經濟", "Economy": "經濟",
"Lowcost": "平價", "Lowcost": "平價",
"high": "高價" "high": "高價",
"statusPageRefreshIn": "將於 {0} 後重新整理",
"SendKey": "SendKey",
"SMSManager API Docs": "SMSManager API 文件 "
} }

View File

@@ -944,6 +944,14 @@ message HealthCheckResponse {
} else if (this.isEdit || this.isClone) { } else if (this.isEdit || this.isClone) {
this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => { this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => {
if (res.ok) { if (res.ok) {
if (this.isClone) {
// Reset push token for cloned monitors
if (res.monitor.type === "push") {
res.monitor.pushToken = undefined;
}
}
this.monitor = res.monitor; this.monitor = res.monitor;
if (this.isClone) { if (this.isClone) {

View File

@@ -306,6 +306,11 @@
<p v-if="config.showPoweredBy"> <p v-if="config.showPoweredBy">
{{ $t("Powered by") }} <a target="_blank" rel="noopener noreferrer" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a> {{ $t("Powered by") }} <a target="_blank" rel="noopener noreferrer" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
</p> </p>
<div class="refresh-info mb-2">
<div>{{ $t("Last Updated") }}: <date-time :value="lastUpdateTime" /></div>
<div>{{ $tc("statusPageRefreshIn", [ updateCountdownText]) }}</div>
</div>
</footer> </footer>
</div> </div>
@@ -322,6 +327,7 @@
<script> <script>
import axios from "axios"; import axios from "axios";
import dayjs from "dayjs"; import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import Favico from "favico.js"; import Favico from "favico.js";
// import highlighting library (you can use any library you want just return html string) // import highlighting library (you can use any library you want just return html string)
import { highlight, languages } from "prismjs/components/prism-core"; import { highlight, languages } from "prismjs/components/prism-core";
@@ -337,10 +343,12 @@ import DOMPurify from "dompurify";
import Confirm from "../components/Confirm.vue"; import Confirm from "../components/Confirm.vue";
import PublicGroupList from "../components/PublicGroupList.vue"; import PublicGroupList from "../components/PublicGroupList.vue";
import MaintenanceTime from "../components/MaintenanceTime.vue"; import MaintenanceTime from "../components/MaintenanceTime.vue";
import DateTime from "../components/Datetime.vue";
import { getResBaseURL } from "../util-frontend"; import { getResBaseURL } from "../util-frontend";
import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE } from "../util.ts"; import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE } from "../util.ts";
const toast = useToast(); const toast = useToast();
dayjs.extend(duration);
const leavePageMsg = "Do you really want to leave? you have unsaved changes!"; const leavePageMsg = "Do you really want to leave? you have unsaved changes!";
@@ -359,6 +367,7 @@ export default {
Confirm, Confirm,
PrismEditor, PrismEditor,
MaintenanceTime, MaintenanceTime,
DateTime,
}, },
// Leave Page for vue route change // Leave Page for vue route change
@@ -400,6 +409,10 @@ export default {
baseURL: "", baseURL: "",
clickedEditButton: false, clickedEditButton: false,
maintenanceList: [], maintenanceList: [],
autoRefreshInterval: 5,
lastUpdateTime: dayjs(),
updateCountdown: null,
updateCountdownText: null,
}; };
}, },
computed: { computed: {
@@ -637,11 +650,13 @@ export default {
console.log(error); console.log(error);
}); });
// 5mins a loop // Configure auto-refresh loop
this.updateHeartbeatList(); this.updateHeartbeatList();
feedInterval = setInterval(() => { feedInterval = setInterval(() => {
this.updateHeartbeatList(); this.updateHeartbeatList();
}, (300 + 10) * 1000); }, (this.autoRefreshInterval * 60 + 10) * 1000);
this.updateUpdateTimer();
// Go to edit page if ?edit present // Go to edit page if ?edit present
// null means ?edit present, but no value // null means ?edit present, but no value
@@ -700,10 +715,29 @@ export default {
favicon.badge(downMonitors); favicon.badge(downMonitors);
this.loadedData = true; this.loadedData = true;
this.lastUpdateTime = dayjs();
this.updateUpdateTimer();
}); });
} }
}, },
/**
* Setup timer to display countdown to refresh
* @returns {void}
*/
updateUpdateTimer() {
clearInterval(this.updateCountdown);
this.updateCountdown = setInterval(() => {
const countdown = dayjs.duration(this.lastUpdateTime.add(this.autoRefreshInterval, "minutes").add(10, "seconds").diff(dayjs()));
if (countdown.as("seconds") < 0) {
clearInterval(this.updateCountdown);
} else {
this.updateCountdownText = countdown.format("mm:ss");
}
}, 1000);
},
/** Enable editing mode */ /** Enable editing mode */
edit() { edit() {
if (this.hasToken) { if (this.hasToken) {
@@ -1118,4 +1152,8 @@ footer {
} }
} }
.refresh-info {
opacity: 0.7;
}
</style> </style>