mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-10 12:56:13 +08:00
Compare commits
81 Commits
1.21.0-bet
...
1.21.1
Author | SHA1 | Date | |
---|---|---|---|
|
4ae437dd61 | ||
|
6cb296b07a | ||
|
644c6a872f | ||
|
8c69c18f6d | ||
|
c1a1160767 | ||
|
a0ebd88849 | ||
|
2e9413cf33 | ||
|
278b52ec34 | ||
|
caa757a27c | ||
|
3ce117a943 | ||
|
cefe484b47 | ||
|
a700892709 | ||
|
13d721ccf8 | ||
|
6c66bff518 | ||
|
bea51d048b | ||
|
2e1a0fe4d5 | ||
|
27b0895722 | ||
|
e687698851 | ||
|
fbdeb30ce7 | ||
|
41bda4e1d7 | ||
|
4869e6531c | ||
|
302b9cf644 | ||
|
3c3a192943 | ||
|
b64c835cee | ||
|
5266e713e6 | ||
|
86579d245f | ||
|
b6169408be | ||
|
4f05912276 | ||
|
bf525371d9 | ||
|
89bfc3bf33 | ||
|
a2014278b8 | ||
|
70572af1af | ||
|
b31c23a43b | ||
|
f4ee5271af | ||
|
7330db3563 | ||
|
097567e5f0 | ||
|
35f300c8eb | ||
|
4c9d7ac8ca | ||
|
d9558833fc | ||
|
776a482a1d | ||
|
d2527d7254 | ||
|
6dfca0c163 | ||
|
72317633d9 | ||
|
1973db28bf | ||
|
972ae60cfc | ||
|
56410afc3b | ||
|
df975c2750 | ||
|
0cfd3fa642 | ||
|
0b91391ced | ||
|
02fde56aec | ||
|
da225a225f | ||
|
a40c03f6f3 | ||
|
c238508060 | ||
|
abcbc3c55e | ||
|
350451edc8 | ||
|
b4b2ae55c0 | ||
|
9ecb890f56 | ||
|
4329fc6751 | ||
|
836e125256 | ||
|
0fe98de256 | ||
|
3825dd9f42 | ||
|
c6cd0d9312 | ||
|
82975f8d7b | ||
|
2ebbcc25a9 | ||
|
f2323b012b | ||
|
e5a6238cde | ||
|
61506b1af2 | ||
|
dbe73bd6ae | ||
|
0778549a6d | ||
|
09fa60de55 | ||
|
1e80365b73 | ||
|
491239415e | ||
|
391692a708 | ||
|
f32fcb204f | ||
|
193a273557 | ||
|
d668812df1 | ||
|
f32d3af62c | ||
|
8a115670cd | ||
|
43941fa2c6 | ||
|
faa78443d6 | ||
|
ab3b2bddba |
6
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
6
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
@@ -26,6 +26,12 @@ body:
|
||||
label: "📝 Describe your problem"
|
||||
description: "Please walk us through it step by step."
|
||||
placeholder: "Describe what are you asking for..."
|
||||
- type: textarea
|
||||
id: error-msg
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "📝 Error Message(s) or Log"
|
||||
- type: input
|
||||
id: uptime-kuma-version
|
||||
attributes:
|
||||
|
25
.github/workflows/json-yaml-validate.yml
vendored
Normal file
25
.github/workflows/json-yaml-validate.yml
vendored
Normal 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
|
13
db/patch-monitor-tls.sql
Normal file
13
db/patch-monitor-tls.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD tls_ca TEXT default null;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD tls_cert TEXT default null;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD tls_key TEXT default null;
|
||||
|
||||
COMMIT;
|
@@ -43,10 +43,11 @@ const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));
|
||||
});
|
||||
console.log(result.stdout + result.stderr);
|
||||
|
||||
/*
|
||||
result = await ssh.execCommand("pm2 restart 1", {
|
||||
cwd,
|
||||
});
|
||||
console.log(result.stdout + result.stderr);
|
||||
console.log(result.stdout + result.stderr);*/
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
@@ -13,7 +13,7 @@ lines = lines.filter((line) => line !== "");
|
||||
lines = [ ...new Set(lines) ];
|
||||
|
||||
// Remove @weblate and @UptimeKumaBot
|
||||
lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot");
|
||||
lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot" && line !== "@louislam");
|
||||
|
||||
// Sort the lines
|
||||
lines = lines.sort();
|
||||
|
16947
package-lock.json
generated
16947
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "1.21.0-beta.1",
|
||||
"version": "1.21.1",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"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-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",
|
||||
"setup": "git checkout 1.20.2 && 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",
|
||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||
"reset-password": "node extra/reset-password.js",
|
||||
@@ -69,8 +69,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "~1.7.3",
|
||||
"@louislam/ping": "~0.4.2-mod.2",
|
||||
"@louislam/sqlite3": "15.1.2",
|
||||
"@louislam/ping": "~0.4.4-mod.0",
|
||||
"@louislam/sqlite3": "15.1.6",
|
||||
"args-parser": "~1.3.0",
|
||||
"axios": "~0.27.0",
|
||||
"axios-ntlm": "1.3.0",
|
||||
@@ -172,7 +172,7 @@
|
||||
"v-pagination-3": "~0.1.7",
|
||||
"vite": "~3.1.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vue": "next",
|
||||
"vue": "~3.2.47",
|
||||
"vue-chart-3": "3.0.9",
|
||||
"vue-confirm-dialog": "~1.0.2",
|
||||
"vue-contenteditable": "~3.0.4",
|
||||
|
@@ -73,6 +73,7 @@ class Database {
|
||||
"patch-http-body-encoding.sql": true,
|
||||
"patch-add-description-monitor.sql": true,
|
||||
"patch-api-key-table.sql": true,
|
||||
"patch-monitor-tls.sql": true,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -133,6 +133,9 @@ class Monitor extends BeanModel {
|
||||
mqttPassword: this.mqttPassword,
|
||||
authWorkstation: this.authWorkstation,
|
||||
authDomain: this.authDomain,
|
||||
tlsCa: this.tlsCa,
|
||||
tlsCert: this.tlsCert,
|
||||
tlsKey: this.tlsKey,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -331,6 +334,18 @@ class Monitor extends BeanModel {
|
||||
options.httpsAgent = new https.Agent(httpsAgentOptions);
|
||||
}
|
||||
|
||||
if (this.auth_method === "mtls") {
|
||||
if (this.tlsCert !== null && this.tlsCert !== "") {
|
||||
options.httpsAgent.options.cert = Buffer.from(this.tlsCert);
|
||||
}
|
||||
if (this.tlsCa !== null && this.tlsCa !== "") {
|
||||
options.httpsAgent.options.ca = Buffer.from(this.tlsCa);
|
||||
}
|
||||
if (this.tlsKey !== null && this.tlsKey !== "") {
|
||||
options.httpsAgent.options.key = Buffer.from(this.tlsKey);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
|
||||
log.debug("monitor", `[${this.name}] Axios Request`);
|
||||
|
||||
@@ -622,9 +637,7 @@ class Monitor extends BeanModel {
|
||||
} else if (this.type === "mysql") {
|
||||
let startTime = dayjs().valueOf();
|
||||
|
||||
await mysqlQuery(this.databaseConnectionString, this.databaseQuery);
|
||||
|
||||
bean.msg = "";
|
||||
bean.msg = await mysqlQuery(this.databaseConnectionString, this.databaseQuery);
|
||||
bean.status = UP;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
} else if (this.type === "mongodb") {
|
||||
@@ -836,7 +849,6 @@ class Monitor extends BeanModel {
|
||||
domain: this.authDomain,
|
||||
workstation: this.authWorkstation ? this.authWorkstation : undefined
|
||||
});
|
||||
|
||||
} else {
|
||||
res = await axios.request(options);
|
||||
}
|
||||
@@ -1233,7 +1245,7 @@ class Monitor extends BeanModel {
|
||||
|
||||
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",
|
||||
this.id,
|
||||
targetDays,
|
||||
@@ -1251,7 +1263,7 @@ class Monitor extends BeanModel {
|
||||
for (let notification of notificationList) {
|
||||
try {
|
||||
log.debug("monitor", "Sending to " + notification.name);
|
||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`);
|
||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will expire in ${daysRemaining} days`);
|
||||
sent = true;
|
||||
} catch (e) {
|
||||
log.error("monitor", "Cannot send cert notification to " + notification.name);
|
||||
|
97
server/notification-providers/opsgenie.js
Normal file
97
server/notification-providers/opsgenie.js
Normal 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;
|
41
server/notification-providers/twilio.js
Normal file
41
server/notification-providers/twilio.js
Normal 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;
|
@@ -23,6 +23,7 @@ const Mattermost = require("./notification-providers/mattermost");
|
||||
const Ntfy = require("./notification-providers/ntfy");
|
||||
const Octopush = require("./notification-providers/octopush");
|
||||
const OneBot = require("./notification-providers/onebot");
|
||||
const Opsgenie = require("./notification-providers/opsgenie");
|
||||
const PagerDuty = require("./notification-providers/pagerduty");
|
||||
const PagerTree = require("./notification-providers/pagertree");
|
||||
const PromoSMS = require("./notification-providers/promosms");
|
||||
@@ -41,6 +42,7 @@ const Stackfield = require("./notification-providers/stackfield");
|
||||
const Teams = require("./notification-providers/teams");
|
||||
const TechulusPush = require("./notification-providers/techulus-push");
|
||||
const Telegram = require("./notification-providers/telegram");
|
||||
const Twilio = require("./notification-providers/twilio");
|
||||
const Splunk = require("./notification-providers/splunk");
|
||||
const Webhook = require("./notification-providers/webhook");
|
||||
const WeCom = require("./notification-providers/wecom");
|
||||
@@ -83,6 +85,7 @@ class Notification {
|
||||
new Ntfy(),
|
||||
new Octopush(),
|
||||
new OneBot(),
|
||||
new Opsgenie(),
|
||||
new PagerDuty(),
|
||||
new PagerTree(),
|
||||
new PromoSMS(),
|
||||
@@ -103,6 +106,7 @@ class Notification {
|
||||
new Teams(),
|
||||
new TechulusPush(),
|
||||
new Telegram(),
|
||||
new Twilio(),
|
||||
new Splunk(),
|
||||
new Webhook(),
|
||||
new WeCom(),
|
||||
|
@@ -147,7 +147,11 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
|
||||
const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId);
|
||||
const state = overrideValue !== undefined ? overrideValue : heartbeat.status;
|
||||
|
||||
badgeValues.label = label ?? "Status";
|
||||
if (label === undefined) {
|
||||
badgeValues.label = "Status";
|
||||
} else {
|
||||
badgeValues.label = label;
|
||||
}
|
||||
switch (state) {
|
||||
case DOWN:
|
||||
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
|
||||
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
|
||||
badgeValues.color = color ?? percentageToColor(uptime);
|
||||
@@ -235,7 +239,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
|
||||
labelPrefix,
|
||||
label ?? `Uptime (${requestedDuration}${labelSuffix})`,
|
||||
]);
|
||||
badgeValues.message = filterAndJoin([ prefix, `${cleanUptime * 100}`, suffix ]);
|
||||
badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]);
|
||||
}
|
||||
|
||||
// build the SVG based on given values
|
||||
|
@@ -688,6 +688,9 @@ let needSetup = false;
|
||||
bean.headers = monitor.headers;
|
||||
bean.basic_auth_user = monitor.basic_auth_user;
|
||||
bean.basic_auth_pass = monitor.basic_auth_pass;
|
||||
bean.tlsCa = monitor.tlsCa;
|
||||
bean.tlsCert = monitor.tlsCert;
|
||||
bean.tlsKey = monitor.tlsKey;
|
||||
bean.interval = monitor.interval;
|
||||
bean.retryInterval = monitor.retryInterval;
|
||||
bean.resendInterval = monitor.resendInterval;
|
||||
|
@@ -74,6 +74,7 @@ class UptimeKumaServer {
|
||||
// SSL
|
||||
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 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");
|
||||
this.app = express();
|
||||
@@ -81,7 +82,8 @@ class UptimeKumaServer {
|
||||
log.info("server", "Server Type: HTTPS");
|
||||
this.httpServer = https.createServer({
|
||||
key: fs.readFileSync(sslKey),
|
||||
cert: fs.readFileSync(sslCert)
|
||||
cert: fs.readFileSync(sslCert),
|
||||
passphrase: sslKeyPassphrase,
|
||||
}, this.app);
|
||||
} else {
|
||||
log.info("server", "Server Type: HTTP");
|
||||
|
@@ -322,21 +322,28 @@ exports.postgresQuery = function (connectionString, query) {
|
||||
* Run a query on MySQL/MariaDB
|
||||
* @param {string} connectionString The database connection string
|
||||
* @param {string} query The query to validate the database with
|
||||
* @returns {Promise<(string[]|Object[]|Object)>}
|
||||
* @returns {Promise<(string)>}
|
||||
*/
|
||||
exports.mysqlQuery = function (connectionString, query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const connection = mysql.createConnection(connectionString);
|
||||
connection.promise().query(query)
|
||||
.then(res => {
|
||||
resolve(res);
|
||||
})
|
||||
.catch(err => {
|
||||
|
||||
connection.on("error", (err) => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
connection.query(query, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
connection.destroy();
|
||||
});
|
||||
} else {
|
||||
if (Array.isArray(res)) {
|
||||
resolve("Rows: " + res.length);
|
||||
} else {
|
||||
resolve("No Error, but the result is not an array. Type: " + typeof res);
|
||||
}
|
||||
}
|
||||
connection.destroy();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -159,6 +159,16 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** Clear Form inputs */
|
||||
clearForm() {
|
||||
this.key = {
|
||||
name: "",
|
||||
expires: this.minDate,
|
||||
active: 1,
|
||||
};
|
||||
this.noExpire = false;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -13,6 +13,9 @@
|
||||
: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)">
|
||||
<font-awesome-icon :icon="icon" />
|
||||
</a>
|
||||
@@ -111,24 +114,19 @@ export default {
|
||||
}, 3000);
|
||||
|
||||
// navigator clipboard api needs a secure context (https)
|
||||
// For http, use the text area method (else part)
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
// navigator clipboard api method'
|
||||
return navigator.clipboard.writeText(textToCopy);
|
||||
} else {
|
||||
// text area method
|
||||
let textArea = document.createElement("textarea");
|
||||
let textArea = this.$refs.hiddenTextarea;
|
||||
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.select();
|
||||
return new Promise((res, rej) => {
|
||||
// here the magic happens
|
||||
document.execCommand("copy") ? res() : rej();
|
||||
textArea.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -129,6 +129,7 @@ export default {
|
||||
"ntfy": "Ntfy",
|
||||
"octopush": "Octopush",
|
||||
"OneBot": "OneBot",
|
||||
"Opsgenie": "Opsgenie",
|
||||
"PagerDuty": "PagerDuty",
|
||||
"pushbullet": "Pushbullet",
|
||||
"PushByTechulus": "Push by Techulus",
|
||||
@@ -143,6 +144,7 @@ export default {
|
||||
"stackfield": "Stackfield",
|
||||
"teams": "Microsoft Teams",
|
||||
"telegram": "Telegram",
|
||||
"twilio": "Twilio",
|
||||
"Splunk": "Splunk",
|
||||
"webhook": "Webhook",
|
||||
"GoAlert": "GoAlert",
|
||||
|
36
src/components/notifications/Opsgenie.vue
Normal file
36
src/components/notifications/Opsgenie.vue
Normal 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>
|
@@ -97,7 +97,6 @@
|
||||
(leave blank for default one)<br />
|
||||
{{NAME}}: Service Name<br />
|
||||
{{HOSTNAME_OR_URL}}: Hostname or URL<br />
|
||||
{{URL}}: URL<br />
|
||||
{{STATUS}}: Status<br />
|
||||
</div>
|
||||
</div>
|
||||
|
27
src/components/notifications/Twilio.vue
Normal file
27
src/components/notifications/Twilio.vue
Normal 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>
|
@@ -21,6 +21,7 @@ import Mattermost from "./Mattermost.vue";
|
||||
import Ntfy from "./Ntfy.vue";
|
||||
import Octopush from "./Octopush.vue";
|
||||
import OneBot from "./OneBot.vue";
|
||||
import Opsgenie from "./Opsgenie.vue";
|
||||
import PagerDuty from "./PagerDuty.vue";
|
||||
import PagerTree from "./PagerTree.vue";
|
||||
import PromoSMS from "./PromoSMS.vue";
|
||||
@@ -41,6 +42,7 @@ import STMP from "./SMTP.vue";
|
||||
import Teams from "./Teams.vue";
|
||||
import TechulusPush from "./TechulusPush.vue";
|
||||
import Telegram from "./Telegram.vue";
|
||||
import Twilio from "./Twilio.vue";
|
||||
import Webhook from "./Webhook.vue";
|
||||
import WeCom from "./WeCom.vue";
|
||||
import GoAlert from "./GoAlert.vue";
|
||||
@@ -76,6 +78,7 @@ const NotificationFormList = {
|
||||
"ntfy": Ntfy,
|
||||
"octopush": Octopush,
|
||||
"OneBot": OneBot,
|
||||
"Opsgenie": Opsgenie,
|
||||
"PagerDuty": PagerDuty,
|
||||
"PagerTree": PagerTree,
|
||||
"promosms": PromoSMS,
|
||||
@@ -95,6 +98,7 @@ const NotificationFormList = {
|
||||
"stackfield": Stackfield,
|
||||
"teams": Teams,
|
||||
"telegram": Telegram,
|
||||
"twilio": Twilio,
|
||||
"Splunk": Splunk,
|
||||
"webhook": Webhook,
|
||||
"WeCom": WeCom,
|
||||
|
@@ -41,7 +41,8 @@ const languageList = {
|
||||
"el-GR": "Ελληνικά",
|
||||
"yue": "繁體中文 (廣東話 / 粵語)",
|
||||
"ro": "Limba română",
|
||||
"ur": "Urdu"
|
||||
"ur": "Urdu",
|
||||
"ge": "ქართული"
|
||||
};
|
||||
|
||||
let messages = {
|
||||
|
@@ -681,7 +681,7 @@
|
||||
"infiniteRetention": "Задайте стойност 0 за безкрайно съхранение.",
|
||||
"Monitor": "Монитор | Монитори",
|
||||
"dataRetentionTimeError": "Периодът на съхранение трябва да е 0 или по-голям",
|
||||
"confirmDeleteTagMsg": "Сигурни ли сте, че желаете да изтриете този таг? Мониторите, свързани с него, няма да бъдат изтрити.",
|
||||
"confirmDeleteTagMsg": "Сигурни ли сте, че желаете да изтриете този етикет? Мониторите, свързани с него, няма да бъдат изтрити.",
|
||||
"promosmsAllowLongSMS": "Позволи дълъг SMS",
|
||||
"Packet Size": "Размер на пакет",
|
||||
"Custom Monitor Type": "Потребителски тип монитор",
|
||||
@@ -694,7 +694,7 @@
|
||||
"confirmUninstallPlugin": "Сигурни ли сте, че желаете да деинсталирате този плъгин?",
|
||||
"markdownSupported": "Поддържа се Markdown синтаксис",
|
||||
"Google Analytics ID": "Google Analytics ID",
|
||||
"Edit Tag": "Редактиране на таг",
|
||||
"Edit Tag": "Редактиране на етикет",
|
||||
"Learn More": "Научете повече",
|
||||
"Server Address": "Сървър адрес",
|
||||
"notificationRegional": "Регионални",
|
||||
@@ -734,5 +734,9 @@
|
||||
"wayToGetPagerTreeIntegrationURL": "След като създадете интеграция на Uptime Kuma в PagerTree, копирайте крайната точка. За пълни подробности вижте {0}",
|
||||
"pagertreeIntegrationUrl": "URL Адрес за интеграция",
|
||||
"pagertreeMedium": "Средна",
|
||||
"pagertreeCritical": "Критична"
|
||||
"pagertreeCritical": "Критична",
|
||||
"Add New Tag": "Добави нов етикет",
|
||||
"lunaseaTarget": "Цел",
|
||||
"lunaseaDeviceID": "ID на устройството",
|
||||
"lunaseaUserID": "ID на потребител"
|
||||
}
|
||||
|
@@ -736,7 +736,8 @@
|
||||
"pagertreeHigh": "Nahlas",
|
||||
"wayToGetPagerTreeIntegrationURL": "Po vytvoření integrace Uptime Kuma v aplikaci PagerTree zkopírujte koncový bod. Zobrazit všechny podrobnosti {0}",
|
||||
"Add New Tag": "Přidat nový štítek",
|
||||
"lunaseaTarget": "cíl",
|
||||
"lunaseaTarget": "Cíl",
|
||||
"lunaseaDeviceID": "ID zařízení",
|
||||
"lunaseaUserID": "ID uživatele"
|
||||
"lunaseaUserID": "ID uživatele",
|
||||
"statusPageRefreshIn": "Obnovení za: {0}"
|
||||
}
|
||||
|
@@ -731,5 +731,9 @@
|
||||
"smseagleGroup": "Telefonbuch Gruppenname(n)",
|
||||
"smseagleRecipient": "Empfänger (mehrere müssen durch Komma getrennt werden)",
|
||||
"API Keys": "API Schlüssel",
|
||||
"Continue": "Weiter"
|
||||
"Continue": "Weiter",
|
||||
"Add New Tag": "Neuen Tag hinzufügen",
|
||||
"lunaseaTarget": "Ziel",
|
||||
"lunaseaDeviceID": "Geräte-ID",
|
||||
"lunaseaUserID": "Benutzer-ID"
|
||||
}
|
||||
|
@@ -734,5 +734,9 @@
|
||||
"telegramMessageThreadID": "(Optional) Nachrichten Thread ID",
|
||||
"telegramMessageThreadIDDescription": "Optionale eindeutige Kennung für den Ziel-Thread (Thema) des Forums; nur für Forum-Supergroups",
|
||||
"telegramSendSilently": "Stumm Senden",
|
||||
"telegramSendSilentlyDescription": "Sende die Nachricht stumm. Nutzer bekommen eine Benachrichtigung ohne Ton."
|
||||
"telegramSendSilentlyDescription": "Sende die Nachricht stumm. Nutzer bekommen eine Benachrichtigung ohne Ton.",
|
||||
"Add New Tag": "Neuen Tag hinzufügen",
|
||||
"lunaseaDeviceID": "Geräte-ID",
|
||||
"lunaseaTarget": "Ziel",
|
||||
"lunaseaUserID": "Benutzer-ID"
|
||||
}
|
||||
|
@@ -174,6 +174,7 @@
|
||||
"Avg. Response": "Avg. Response",
|
||||
"Entry Page": "Entry Page",
|
||||
"statusPageNothing": "Nothing here, please add a group or a monitor.",
|
||||
"statusPageRefreshIn": "Refresh in: {0}",
|
||||
"No Services": "No Services",
|
||||
"All Systems Operational": "All Systems Operational",
|
||||
"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}",
|
||||
"lunaseaTarget": "Target",
|
||||
"lunaseaDeviceID": "Device ID",
|
||||
"lunaseaUserID": "User ID"
|
||||
"lunaseaUserID": "User ID",
|
||||
"twilioAccountSID": "Account SID",
|
||||
"twilioAuthToken": "Auth Token",
|
||||
"twilioFromNumber": "From Number",
|
||||
"twilioToNumber": "To Number"
|
||||
}
|
||||
|
@@ -696,5 +696,47 @@
|
||||
"Bark Endpoint": "Endpoint Bark",
|
||||
"WebHookUrl": "WebHookUrl",
|
||||
"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"
|
||||
}
|
||||
|
@@ -73,11 +73,11 @@
|
||||
"Delete": "Supprimer",
|
||||
"Current": "Actuellement",
|
||||
"Uptime": "Disponibilité",
|
||||
"Cert Exp.": "Expiration SSL.",
|
||||
"Cert Exp.": "Expiration SSL",
|
||||
"day": "jour | jours",
|
||||
"-day": "-jour",
|
||||
"hour": "heure",
|
||||
"-hour": "-heure",
|
||||
"-hour": "heures",
|
||||
"Response": "Temps de réponse",
|
||||
"Ping": "Ping",
|
||||
"Monitor Type": "Type de sonde",
|
||||
@@ -734,5 +734,10 @@
|
||||
"pagertreeDoNothing": "Ne fais rien",
|
||||
"pagertreeIntegrationUrl": "URL d'intégration",
|
||||
"pagertreeCritical": "Critique",
|
||||
"wayToGetPagerTreeIntegrationURL": "Après avoir créé l'intégration Uptime Kuma dans PagerTree, copiez le fichier Endpoint. Voir tous les détails {0}"
|
||||
"wayToGetPagerTreeIntegrationURL": "Après avoir créé l'intégration Uptime Kuma dans PagerTree, copiez le fichier Endpoint. Voir tous les détails {0}",
|
||||
"lunaseaDeviceID": "Identifiant de l'appareil",
|
||||
"lunaseaUserID": "Identifiant de l'utilisateur",
|
||||
"Add New Tag": "Ajouter une étiquette",
|
||||
"lunaseaTarget": "Cible",
|
||||
"statusPageRefreshIn": "Actualisation dans : {0}"
|
||||
}
|
||||
|
@@ -510,5 +510,7 @@
|
||||
"Android": "Android",
|
||||
"Huawei": "Huawei",
|
||||
"Device Token": "デバイストークン",
|
||||
"recurringIntervalMessage": "毎日1回実行する|{0} 日に1回実行する"
|
||||
"recurringIntervalMessage": "毎日1回実行する|{0} 日に1回実行する",
|
||||
"Add New Tag": "新しいタグを追加",
|
||||
"statusPageMaintenanceEndDate": "終了日"
|
||||
}
|
||||
|
21
src/lang/ka.json
Normal file
21
src/lang/ka.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Dashboard": "დაფა",
|
||||
"Help": "დახმარება",
|
||||
"New Update": "განახლება",
|
||||
"Language": "ენა",
|
||||
"Appearance": "ვიზუალი",
|
||||
"Theme": "სტილი",
|
||||
"Game": "თამაში",
|
||||
"Version": "ვერსია",
|
||||
"Quick Stats": "თვალის გადავლება",
|
||||
"Up": "მაღლა",
|
||||
"Pending": "მოლოდინი",
|
||||
"languageName": "Georgian",
|
||||
"Settings": "კონფიგურაცია",
|
||||
"General": "ძირითადი",
|
||||
"Check Update On GitHub": "GitHub_ზე განახლების შემოწმება",
|
||||
"List": "სია",
|
||||
"Add": "დამატება",
|
||||
"Add New Monitor": "ახალი მონიტორის დამატება",
|
||||
"Down": "დაბლა"
|
||||
}
|
@@ -208,7 +208,7 @@
|
||||
"smtpBCC": "숨은 참조",
|
||||
"discord": "Discord",
|
||||
"Discord Webhook URL": "Discord 웹훅 URL",
|
||||
"wayToGetDiscordURL": "서버 설정 -> 연동 -> 웹후크 보기 -> 새 웹후크에서 얻을 수 있어요",
|
||||
"wayToGetDiscordURL": "서버 설정 -> 연동 -> 웹훅 보기 -> 새 웹훅 에서 얻을 수 있어요",
|
||||
"Bot Display Name": "표시 이름",
|
||||
"Prefix Custom Message": "접두사 메시지",
|
||||
"Hello @everyone is...": "{'@'}everyone 서버 상태 알림이에요…",
|
||||
@@ -691,5 +691,38 @@
|
||||
"webhookAdditionalHeadersTitle": "추가 헤더",
|
||||
"webhookAdditionalHeadersDesc": "웹훅과 함께 전송될 추가 헤더를 설정해요.",
|
||||
"HTTP Headers": "HTTP 헤더",
|
||||
"Trust Proxy": "프록시 신뢰"
|
||||
"Trust Proxy": "프록시 신뢰",
|
||||
"API Keys": "API 키",
|
||||
"markdownSupported": "Markdown 문법이 지원됨",
|
||||
"telegramMessageThreadID": "(선택) 메시지 스레드 ID",
|
||||
"Clone": "복제",
|
||||
"cloneOf": "{0}의 복제본",
|
||||
"Clone Monitor": "모니터링 복제",
|
||||
"telegramProtectContent": "포워딩/저장 보호",
|
||||
"telegramProtectContentDescription": "활성화 시, 텔레그램 봇 메시지는 포워딩 및 저장으로부터 보호됩니다.",
|
||||
"telegramSendSilentlyDescription": "조용히 메시지를 보냅니다. 사용자들은 무음으로 알림을 받습니다.",
|
||||
"telegramSendSilently": "무음 알림",
|
||||
"Add New Tag": "태그 추가",
|
||||
"Edit Tag": "태그 수정",
|
||||
"Server Address": "서버 주소",
|
||||
"Learn More": "자세히 알아보기",
|
||||
"Continue": "계속",
|
||||
"Key Added": "키 추가됨",
|
||||
"No API Keys": "API 키 없음",
|
||||
"disableAPIKeyMsg": "이 API키를 정말로 비활성화하시겠습니까?",
|
||||
"deleteAPIKeyMsg": "이 API키를 정말로 삭제하시겠습니까?",
|
||||
"Generate": "생성",
|
||||
"Body Encoding": "Body 인코딩",
|
||||
"Expiry": "만료",
|
||||
"Expiry date": "만료 날짜",
|
||||
"Don't expire": "만료되지 않음",
|
||||
"notificationRegional": "지역별",
|
||||
"Google Analytics ID": "Google Analytics ID",
|
||||
"Add API Key": "API 키 추가",
|
||||
"apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않을 것이므로 메모해 두세요.",
|
||||
"pagertreeCritical": "치명적인",
|
||||
"apiKey-active": "사용 가능",
|
||||
"lunaseaUserID": "사용자 ID",
|
||||
"apiKey-expired": "만료됨",
|
||||
"Expires": "만료일"
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
{}
|
@@ -1 +0,0 @@
|
||||
{}
|
@@ -696,5 +696,12 @@
|
||||
"markdownSupported": "Markdown syntax ondersteund",
|
||||
"Resend Notification if Down X times consecutively": "Melding x keer opnieuw sturen als monitor offline is",
|
||||
"loadingError": "Kan de data niet ophalen, probeer het later opnieuw.",
|
||||
"smseagleContact": "Telefoonboek contact namen"
|
||||
"smseagleContact": "Telefoonboek contact namen",
|
||||
"apiKey-active": "Actief",
|
||||
"apiKey-expired": "Verlopen",
|
||||
"pagertreeLow": "Laag",
|
||||
"pagertreeHigh": "Hoog",
|
||||
"Clone": "Dupliceer",
|
||||
"cloneOf": "Duplicaat van {0}",
|
||||
"Add New Tag": "Voeg nieuw label toe"
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"acceptedStatusCodesDescription": "Выберите коды статусов для определения доступности сервиса.",
|
||||
"passwordNotMatchMsg": "Повтор пароля не совпадает.",
|
||||
"notificationDescription": "Привяжите уведомления к мониторам.",
|
||||
"keywordDescription": "Поиск слова в чистом HTML или в JSON-ответе (чувствительно к регистру)",
|
||||
"keywordDescription": "Поиск слова в чистом HTML или в JSON-ответе (чувствительно к регистру).",
|
||||
"pauseDashboardHome": "Пауза",
|
||||
"deleteMonitorMsg": "Вы действительно хотите удалить данный монитор?",
|
||||
"deleteNotificationMsg": "Вы действительно хотите удалить это уведомление для всех мониторов?",
|
||||
@@ -45,9 +45,9 @@
|
||||
"Uptime": "Аптайм",
|
||||
"Cert Exp.": "Сертификат истекает.",
|
||||
"day": "день | дней",
|
||||
"-day": " дней",
|
||||
"-day": "-дней",
|
||||
"hour": "час",
|
||||
"-hour": " часа",
|
||||
"-hour": "-часа",
|
||||
"Response": "Ответ",
|
||||
"Ping": "Пинг",
|
||||
"Monitor Type": "Тип монитора",
|
||||
@@ -124,12 +124,12 @@
|
||||
"Also apply to existing monitors": "Применить к существующим мониторам",
|
||||
"Export": "Экспорт",
|
||||
"Import": "Импорт",
|
||||
"backupDescription": "Вы можете сохранить резервную копию всех мониторов и уведомлений в виде JSON-файла",
|
||||
"backupDescription2": "P.S. История и события сохранены не будут",
|
||||
"backupDescription3": "Важные данные, такие как токены уведомлений, добавляются при экспорте, поэтому храните файлы в безопасном месте",
|
||||
"backupDescription": "Вы можете сохранить резервную копию всех мониторов и уведомлений в виде JSON-файла.",
|
||||
"backupDescription2": "Важно: история и события сохранены не будут.",
|
||||
"backupDescription3": "Важные данные, такие как токены уведомлений, добавляются при экспорте, поэтому храните файлы в безопасном месте.",
|
||||
"alertNoFile": "Выберите файл для импорта.",
|
||||
"alertWrongFileType": "Выберите JSON-файл.",
|
||||
"twoFAVerifyLabel": "Пожалуйста, введите свой токен, чтобы проверить работу 2FA",
|
||||
"twoFAVerifyLabel": "Пожалуйста, введите свой токен, чтобы проверить работу 2FA:",
|
||||
"tokenValidSettingsMsg": "Токен действителен! Теперь вы можете сохранить настройки 2FA.",
|
||||
"confirmEnableTwoFAMsg": "Вы действительно хотите включить 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.",
|
||||
"Page Not Found": "Страница не найдена",
|
||||
"wayToGetCloudflaredURL": "(Скачать cloudflared с {0})",
|
||||
"cloudflareWebsite": "Cloudflare Website",
|
||||
"cloudflareWebsite": "Веб-сайт Cloudflare",
|
||||
"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:",
|
||||
"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.",
|
||||
"HTTP Headers": "HTTP заголовки",
|
||||
"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.": "Текущее соединение может быть потеряно, если вы в данный момент подключаетесь через туннель Cloudflare. Вы уверены, что хотите это остановить? Введите свой текущий пароль, чтобы подтвердить это.",
|
||||
"HTTP Headers": "заголовки HTTP",
|
||||
"Trust Proxy": "Доверять прокси",
|
||||
"Other Software": "Другое программное обеспечение",
|
||||
"For example: nginx, Apache and Traefik.": "К примеру: nginx, Apache и Traefik.",
|
||||
@@ -463,13 +463,13 @@
|
||||
"Proxy": "Прокси",
|
||||
"Date Created": "Дата создания",
|
||||
"HomeAssistant": "Home Assistant",
|
||||
"onebotHttpAddress": "OneBot HTTP Address",
|
||||
"onebotMessageType": "OneBot Message Type",
|
||||
"onebotHttpAddress": "HTTP-адрес OneBot",
|
||||
"onebotMessageType": "Тип сообщения OneBot",
|
||||
"onebotGroupMessage": "Группа",
|
||||
"onebotPrivateMessage": "Private",
|
||||
"onebotUserOrGroupId": "ID группы или пользователя",
|
||||
"onebotSafetyTips": "В целях безопасности необходимо установить токен доступа",
|
||||
"PushDeer Key": "PushDeer Key",
|
||||
"PushDeer Key": "ключ PushDeer",
|
||||
"Footer Text": "Текст нижнего колонтитула",
|
||||
"Show Powered By": "Показывать на чем создано",
|
||||
"Domain Names": "Доменные имена",
|
||||
@@ -488,40 +488,40 @@
|
||||
"From Name/Number": "Имя/номер отправителя",
|
||||
"Leave blank to use a shared sender number.": "Оставьте пустым, чтобы использовать общий номер отправителя.",
|
||||
"Octopush API Version": "Версия API Octopush",
|
||||
"Legacy Octopush-DM": "Legacy Octopush-DM",
|
||||
"endpoint": "endpoint",
|
||||
"Legacy Octopush-DM": "устаревший Octopush-DM",
|
||||
"endpoint": "конечная точка",
|
||||
"octopushAPIKey": "\"API key\" из учетных данных HTTP API в панели управления",
|
||||
"octopushLogin": "\"Login\" из учетных данных HTTP API в панели управления",
|
||||
"promosmsLogin": "Логин API",
|
||||
"promosmsPassword": "Пароль API",
|
||||
"pushoversounds pushover": "Pushover (default)",
|
||||
"pushoversounds bike": "Bike",
|
||||
"pushoversounds bugle": "Bugle",
|
||||
"pushoversounds cashregister": "Cash Register",
|
||||
"pushoversounds pushover": "Pushover (по умолчанию)",
|
||||
"pushoversounds bike": "Велосипед",
|
||||
"pushoversounds bugle": "Горн",
|
||||
"pushoversounds cashregister": "Кассовый аппарат",
|
||||
"pushoversounds classical": "Classical",
|
||||
"pushoversounds cosmic": "Cosmic",
|
||||
"pushoversounds falling": "Falling",
|
||||
"pushoversounds gamelan": "Gamelan",
|
||||
"pushoversounds incoming": "Incoming",
|
||||
"pushoversounds intermission": "Intermission",
|
||||
"pushoversounds magic": "Magic",
|
||||
"pushoversounds mechanical": "Mechanical",
|
||||
"pushoversounds pianobar": "Piano Bar",
|
||||
"pushoversounds siren": "Siren",
|
||||
"pushoversounds spacealarm": "Space Alarm",
|
||||
"pushoversounds tugboat": "Tug Boat",
|
||||
"pushoversounds alien": "Alien Alarm (long)",
|
||||
"pushoversounds climb": "Climb (long)",
|
||||
"pushoversounds persistent": "Persistent (long)",
|
||||
"pushoversounds echo": "Pushover Echo (long)",
|
||||
"pushoversounds updown": "Up Down (long)",
|
||||
"pushoversounds vibrate": "Vibrate Only",
|
||||
"pushoversounds none": "None (silent)",
|
||||
"pushyAPIKey": "Secret API Key",
|
||||
"pushoversounds cosmic": "Космический",
|
||||
"pushoversounds falling": "Падающий",
|
||||
"pushoversounds gamelan": "Гамелан",
|
||||
"pushoversounds incoming": "Входящий",
|
||||
"pushoversounds intermission": "Антракт",
|
||||
"pushoversounds magic": "Магия",
|
||||
"pushoversounds mechanical": "Механический",
|
||||
"pushoversounds pianobar": "Пиано-бар",
|
||||
"pushoversounds siren": "Сирена",
|
||||
"pushoversounds spacealarm": "Космическая сигнализация",
|
||||
"pushoversounds tugboat": "Буксирное судно",
|
||||
"pushoversounds alien": "Инопланетная тревога (долгое)",
|
||||
"pushoversounds climb": "Подъем (долгое)",
|
||||
"pushoversounds persistent": "Стойкий (долгое)",
|
||||
"pushoversounds echo": "Pushover Эхо (долгое)",
|
||||
"pushoversounds updown": "Вверх вниз (долгое)",
|
||||
"pushoversounds vibrate": "Только вибрация",
|
||||
"pushoversounds none": "Нет (тихо)",
|
||||
"pushyAPIKey": "Секретный ключ API",
|
||||
"pushyToken": "Токен устройства",
|
||||
"Using a Reverse Proxy?": "Используете обратный прокси?",
|
||||
"Check how to config it for WebSocket": "Проверьте, как настроить его для WebSocket",
|
||||
"Steam Game Server": "Steam Game Server",
|
||||
"Steam Game Server": "Игровой сервер Steam",
|
||||
"Most likely causes:": "Наиболее вероятные причины:",
|
||||
"The resource is no longer available.": "Ресурс больше не доступен.",
|
||||
"There might be a typing error in the address.": "В адресе может быть опечатка.",
|
||||
@@ -536,24 +536,24 @@
|
||||
"certificationExpiryDescription": "HTTPS Мониторы инициируют уведомление, когда срок действия сертификата TLS истечет:",
|
||||
"Setup Docker Host": "Настроить Docker Host",
|
||||
"Connection Type": "Тип соединения",
|
||||
"Docker Daemon": "Docker Daemon",
|
||||
"deleteDockerHostMsg": "Are you sure want to delete this docker host for all monitors?",
|
||||
"socket": "Socket",
|
||||
"Docker Daemon": "Демон Docker",
|
||||
"deleteDockerHostMsg": "Вы уверены, что хотите удалить этот узел docker для всех мониторов?",
|
||||
"socket": "Сокет",
|
||||
"tcp": "TCP / HTTP",
|
||||
"Docker Container": "Docker контейнер",
|
||||
"Container Name / ID": "Название контейнера / ID",
|
||||
"Docker Host": "Docker Host",
|
||||
"Docker Hosts": "Docker Hosts",
|
||||
"ntfy Topic": "ntfy Topic",
|
||||
"Docker Host": "Хост Docker",
|
||||
"Docker Hosts": "Хосты Docker",
|
||||
"ntfy Topic": "тема ntfy",
|
||||
"Domain": "Домен",
|
||||
"Workstation": "Workstation",
|
||||
"Workstation": "Рабочая станция",
|
||||
"disableCloudflaredNoAuthMsg": "Вы находитесь в режиме без авторизации, пароль не требуется.",
|
||||
"trustProxyDescription": "Доверять заголовкам 'X-Forwarded-*'. Если вы хотите получить правильный IP-адрес клиента, а ваш Uptime Kuma находится под Nginx или Apache, вам следует включить этот параметр.",
|
||||
"wayToGetLineNotifyToken": "Вы можете получить токен доступа в {0}",
|
||||
"Examples": "Примеры",
|
||||
"Home Assistant URL": "Home Assistant URL",
|
||||
"Home Assistant URL": "URL-адрес Home Assistant",
|
||||
"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": "Служба уведомлений",
|
||||
"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 в разделе \"Инструменты разработчика > Службы\", выполнив поиск по слову \"уведомление\", чтобы найти название вашего устройства/телефона.",
|
||||
@@ -618,7 +618,7 @@
|
||||
"Custom CSS": "Пользовательские CSS",
|
||||
"weekdayShortTue": "Вт",
|
||||
"dayOfWeek": "День недели",
|
||||
"confirmDeleteTagMsg": "Вы уверены, что хотите удалить этот тег? Мониторы, связанные с этим тегом не будут удалены.",
|
||||
"confirmDeleteTagMsg": "Вы уверены, что хотите удалить этот тег? Мониторы, связанные с этим тегом не будут удалены.",
|
||||
"loadingError": "Невозможно получить данные, пожалуйста попробуйте позже.",
|
||||
"Packet Size": "Размер пакета",
|
||||
"warningTimezone": "Используется часовой пояс сервера",
|
||||
@@ -676,10 +676,10 @@
|
||||
"Integration URL": "URL интеграции",
|
||||
"do nothing": "ничего не делать",
|
||||
"smseagleTo": "Номер(а) телефона",
|
||||
"smseagleGroup": "Имена групп в телефонной книжке",
|
||||
"smseagleGroup": "Название(я) групп телефонной книги",
|
||||
"smseagleContact": "Имена контактов из телефонной книжки",
|
||||
"smseagleRecipientType": "Тип получателя",
|
||||
"smseagleRecipient": "Получатель (через запятую, если несколько)",
|
||||
"smseagleRecipient": "Получатель(я) (через запятую, если необходимо указать несколько)",
|
||||
"smseagleToken": "Токен доступа API",
|
||||
"smseagleUrl": "URL вашего SMSEagle устройства",
|
||||
"smseagleEncoding": "Отправить в юникоде",
|
||||
@@ -687,9 +687,9 @@
|
||||
"Server Address": "Адрес сервера",
|
||||
"Learn More": "Узнать больше",
|
||||
"topicExplanation": "MQTT топик для мониторинга",
|
||||
"Guild ID": "Guild ID",
|
||||
"Guild ID": "Идентификатор гильдии",
|
||||
"Kook": "Kook",
|
||||
"wayToGetKookBotToken": "Создайте приложение и получите токен вашего бота тут {0}.",
|
||||
"wayToGetKookBotToken": "Создайте приложение и получите токен бота по адресу {0}",
|
||||
"Resend Notification if Down X times consecutively": "Повторная отправка уведомления при падении несколько раз",
|
||||
"telegramProtectContent": "Запретить пересылку/сохранение",
|
||||
"telegramProtectContentDescription": "Если включено, сообщения бота в Telegram будут запрещены для пересылки и сохранения.",
|
||||
@@ -699,5 +699,54 @@
|
||||
"Clone Monitor": "Копия",
|
||||
"Clone": "Копия",
|
||||
"cloneOf": "Копия {0}",
|
||||
"notificationRegional": "Региональный"
|
||||
"notificationRegional": "Региональный",
|
||||
"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}"
|
||||
}
|
||||
|
195
src/lang/sk.json
Normal file
195
src/lang/sk.json
Normal file
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"Settings": "Nastavenia",
|
||||
"Help": "Nápoveda",
|
||||
"New Update": "Nová aktualizácia",
|
||||
"Language": "Jazyk",
|
||||
"Appearance": "Vzhľad",
|
||||
"Theme": "Téma",
|
||||
"General": "Základné",
|
||||
"Primary Base URL": "Základná URL",
|
||||
"Version": "Verzia",
|
||||
"List": "Zoznam",
|
||||
"Add": "Pridať",
|
||||
"Add New Monitor": "Pridať nové Sledovanie",
|
||||
"Quick Stats": "Rýchly prehľad",
|
||||
"Pending": "Čaká sa",
|
||||
"statusMaintenance": "Údržba",
|
||||
"Maintenance": "Údržba",
|
||||
"General Monitor Type": "Základný typ Sledovania",
|
||||
"Passive Monitor Type": "Pasívny typ Sledovania",
|
||||
"Specific Monitor Type": "Špecifický typ Sledovania",
|
||||
"pauseDashboardHome": "Pauza",
|
||||
"Pause": "Pauza",
|
||||
"Status": "Stav",
|
||||
"Message": "Správa",
|
||||
"No important events": "Žiadne dôležité udalosti",
|
||||
"Edit": "Upraviť",
|
||||
"Delete": "Odstrániť",
|
||||
"Current": "Aktuálne",
|
||||
"Cert Exp.": "Platnosť cert.",
|
||||
"day": "deň | dni",
|
||||
"hour": "hodina",
|
||||
"Response": "Odpoveď",
|
||||
"Ping": "Ping",
|
||||
"Keyword": "Kľúčové slovo",
|
||||
"Friendly Name": "Názov",
|
||||
"Port": "Port",
|
||||
"Retries": "Opakovania",
|
||||
"Resend Notification if Down X times consecutively": "Poslať oznámenie znovu, ak je nedostupné X-krát za sebou",
|
||||
"Advanced": "Pokročilé",
|
||||
"checkEverySecond": "Skontrolovať každých {0} sekúnd",
|
||||
"retryCheckEverySecond": "Zopakovať každých {0} sekúnd",
|
||||
"resendEveryXTimes": "Znovu poslať každých {0} krát",
|
||||
"resendDisabled": "Opakované odoslanie vypnuté",
|
||||
"ignoreTLSError": "Ignorovať TLS/SSL chyby pre HTTPS stránky",
|
||||
"upsideDownModeDescription": "Obrátiť stav. Pokiaľ je služba dostupná, zobrazuje sa ako NEDOSTUPNÁ.",
|
||||
"Upside Down Mode": "Obrátený režim",
|
||||
"Max. Redirects": "Max. počet presmerovaní",
|
||||
"Accepted Status Codes": "Akceptované stavové kódy",
|
||||
"Push URL": "Push URL",
|
||||
"Save": "Uložiť",
|
||||
"Notifications": "Notifikácie",
|
||||
"Not available, please setup.": "Nedostupné, prosím nastavte.",
|
||||
"Setup Notification": "Nastavenie notifikácií",
|
||||
"Dark": "Tmavá",
|
||||
"Light": "Svetlá",
|
||||
"Auto": "Automaticky",
|
||||
"Normal": "Normálna",
|
||||
"Bottom": "Dole",
|
||||
"None": "Žiadne",
|
||||
"Timezone": "Časová zóna",
|
||||
"languageName": "Slovenčina",
|
||||
"Dashboard": "Dashboard",
|
||||
"Check Update On GitHub": "Skontrolovať aktualizáciu na GitHub-e",
|
||||
"Up": "Dostupné",
|
||||
"Down": "Nedostupné",
|
||||
"Unknown": "Neznáme",
|
||||
"markdownSupported": "Podpora Markdown syntaxu",
|
||||
"Name": "Názov",
|
||||
"DateTime": "Dátum a čas",
|
||||
"Resume": "Pokračovať",
|
||||
"Uptime": "Doba prevádzky",
|
||||
"Monitor": "Sledovanie | Sledovania",
|
||||
"-day": "-dní",
|
||||
"-hour": "-hodín",
|
||||
"Monitor Type": "Typ Sledovania",
|
||||
"URL": "URL",
|
||||
"Hostname": "Adresa",
|
||||
"Heartbeat Interval": "Heartbeat Interval",
|
||||
"Heartbeat Retry Interval": "Interval opakovania pre Heartbeat",
|
||||
"retriesDescription": "Maximálny počet opakovaní pred tým, ako je služba označená ako nedostupná a je zaslaná notifikácia",
|
||||
"maxRedirectDescription": "Maximálny počet presmerovaní. Hodnota 0 vypne presmerovania.",
|
||||
"needPushEvery": "Tuto adresu by ste mali volať každých {0} sekúnd.",
|
||||
"pushOptionalParams": "Voliteľné parametre: {0}",
|
||||
"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."
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
"retryCheckEverySecond": "ลองใหม่ทุก {0} วินาที",
|
||||
"retriesDescription": "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน",
|
||||
"ignoreTLSError": "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS",
|
||||
"upsideDownModeDescription": "กลับด้านสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้",
|
||||
"upsideDownModeDescription": "สลับสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้",
|
||||
"maxRedirectDescription": "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตั้งเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง",
|
||||
"acceptedStatusCodesDescription": "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ",
|
||||
"passwordNotMatchMsg": "รหัสผ่านไม่ตรงกัน",
|
||||
@@ -30,7 +30,7 @@
|
||||
"Dashboard": "แผงควบคุม",
|
||||
"New Update": "อัพเดทใหม่",
|
||||
"Language": "ภาษา",
|
||||
"Appearance": "รูปร่าง",
|
||||
"Appearance": "หน้าตา",
|
||||
"Theme": "หน้าตา",
|
||||
"General": "ทั่วไป",
|
||||
"Primary Base URL": "URL หลัก",
|
||||
@@ -73,7 +73,7 @@
|
||||
"Retries": "จำนวนครั้งที่จะลองใหม่",
|
||||
"Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ",
|
||||
"Advanced": "ขั้นสูง",
|
||||
"Upside Down Mode": "โหมดกลับด้าน",
|
||||
"Upside Down Mode": "โหมดสลับ",
|
||||
"Max. Redirects": "จำนวนการเปลี่ยนเส้นทางสูงสุด",
|
||||
"Accepted Status Codes": "รหัสสถานะที่ยอมรับ",
|
||||
"Push URL": "URL เป้าหมาย",
|
||||
|
@@ -734,5 +734,10 @@
|
||||
"pagertreeDoNothing": "Hiçbir şey yapma",
|
||||
"wayToGetPagerTreeIntegrationURL": "PagerTree'de Uptime Kuma entegrasyonunu oluşturduktan sonra Endpoint'i kopyalayın. Tüm ayrıntıları görün {0}",
|
||||
"pagertreeIntegrationUrl": "Entegrasyon URL",
|
||||
"pagertreeResolve": "Otomatik Çöz"
|
||||
"pagertreeResolve": "Otomatik Çöz",
|
||||
"lunaseaTarget": "Hedef",
|
||||
"Add New Tag": "Yeni Etiket Ekle",
|
||||
"lunaseaDeviceID": "Cihaz ID",
|
||||
"lunaseaUserID": "Kullanıcı ID",
|
||||
"statusPageRefreshIn": "{0} içinde yenilenecek"
|
||||
}
|
||||
|
@@ -740,5 +740,9 @@
|
||||
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Довготривалий токен доступу можна створити, натиснувши на ім'я вашого профілю (внизу ліворуч), прокрутивши його донизу і натиснувши кнопку Створити токен. ",
|
||||
"high": "високий",
|
||||
"Disable": "Вимкнути",
|
||||
"Resend Notification if Down X times consecutively": "Повторно надіслати сповіщення, якщо було падіння X разів поспіль"
|
||||
"Resend Notification if Down X times consecutively": "Повторно надіслати сповіщення, якщо було падіння X разів поспіль",
|
||||
"lunaseaTarget": "Ціль",
|
||||
"Add New Tag": "Додати новий тег",
|
||||
"lunaseaDeviceID": "ID пристрою",
|
||||
"lunaseaUserID": "ID користувача"
|
||||
}
|
||||
|
@@ -740,5 +740,6 @@
|
||||
"Add New Tag": "添加新标签",
|
||||
"lunaseaDeviceID": "设备ID",
|
||||
"lunaseaTarget": "目标",
|
||||
"lunaseaUserID": "用户ID"
|
||||
"lunaseaUserID": "用户ID",
|
||||
"statusPageRefreshIn": "将于 {0} 后刷新"
|
||||
}
|
||||
|
@@ -228,7 +228,7 @@
|
||||
"smtpCC": "CC",
|
||||
"smtpBCC": "BCC",
|
||||
"Discord Webhook URL": "Discord Webhook 網址",
|
||||
"wayToGetDiscordURL": "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||
"wayToGetDiscordURL": "您可以前往 伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||
"Bot Display Name": "機器人顯示名稱",
|
||||
"Prefix Custom Message": "前綴自訂訊息",
|
||||
"Webhook URL": "Webhook 網址",
|
||||
@@ -376,10 +376,10 @@
|
||||
"default": "預設",
|
||||
"enabled": "啟用",
|
||||
"setAsDefault": "設為預設",
|
||||
"deleteProxyMsg": "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||
"proxyDescription": "必須將代理伺服器指派給監測器才能運作。",
|
||||
"enableProxyDescription": "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||
"setAsDefaultProxyDescription": "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||
"deleteProxyMsg": "您確定要為所有監測器刪除此 Proxy 嗎?",
|
||||
"proxyDescription": "必須將 Proxy 指派給監測器才能運作。",
|
||||
"enableProxyDescription": "此 Proxy 在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用 Proxy。",
|
||||
"setAsDefaultProxyDescription": "預設情況下,新監測器將啟用此 Proxy。您仍可分別停用各監測器的 Proxy。",
|
||||
"Maintenance": "維護",
|
||||
"statusMaintenance": "維護中",
|
||||
"Enable DNS Cache": "啟用 DNS 快取",
|
||||
@@ -430,8 +430,8 @@
|
||||
"Remove Token": "移除 Token",
|
||||
"Start": "開始",
|
||||
"User": "使用者",
|
||||
"trustProxyDescription": "信任 'X-Forwarded-*' 的 Header。如果您想取得正確的 Client IP,且您的 Uptime Kuma 架設於 Nginx 或 Apache 之後,您應啟用此選項。",
|
||||
"Reverse Proxy": "Reverse Proxy",
|
||||
"trustProxyDescription": "信任 'X-Forwarded-*' 的 Header。如果您想取得正確的 Client IP,且您的 Uptime Kuma 架設於 Nginx 或 Apache Proxy 之後,您應啟用此選項。",
|
||||
"Reverse Proxy": "反向 Proxy",
|
||||
"Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "若要取得長期有效 Access Token,請按您的個人檔案名稱 (左下角),捲動至最下方,然後按建立 Token。 ",
|
||||
"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 中查看通知服務的列表,在\"開發者工具 > 服務\"下搜尋\"通知\"來找到您的裝置/手機的名稱。",
|
||||
"loadingError": "未能取得數據,請重新再試。",
|
||||
@@ -483,7 +483,7 @@
|
||||
"API Key": "API Key",
|
||||
"Show update if available": "有更新時顯示",
|
||||
"Also check beta release": "檢查 Beta 版本",
|
||||
"Using a Reverse Proxy?": "正在使用 Reverse Proxy?",
|
||||
"Using a Reverse Proxy?": "正在使用反向代理 (Reverse Proxy)?",
|
||||
"Check how to config it for WebSocket": "查看如何加入 WebSocket 設定",
|
||||
"Steam Game Server": "Steam 遊戲 Server",
|
||||
"Most likely causes:": "最可能原因:",
|
||||
@@ -702,5 +702,12 @@
|
||||
"Platform": "平台",
|
||||
"Device Token": "裝置 Token",
|
||||
"telegramProtectContent": "禁止轉發/儲存",
|
||||
"telegramProtectContentDescription": "如果選擇,用戶將不能轉發/儲存收到的信息。"
|
||||
"telegramProtectContentDescription": "如果選擇,用戶將不能轉發/儲存收到的信息。",
|
||||
"Add New Tag": "加新標籤",
|
||||
"Economy": "經濟",
|
||||
"Lowcost": "平價",
|
||||
"high": "高價",
|
||||
"statusPageRefreshIn": "將於 {0} 後重新整理",
|
||||
"SendKey": "SendKey",
|
||||
"SMSManager API Docs": "SMSManager API 文件 "
|
||||
}
|
||||
|
@@ -546,28 +546,47 @@
|
||||
<option value="ntlm">
|
||||
NTLM
|
||||
</option>
|
||||
<option value="mtls">
|
||||
mTLS
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<template v-if="monitor.authMethod && monitor.authMethod !== null ">
|
||||
<div class="my-3">
|
||||
<label for="basicauth" class="form-label">{{ $t("Username") }}</label>
|
||||
<input id="basicauth-user" v-model="monitor.basic_auth_user" type="text" class="form-control" :placeholder="$t('Username')">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="basicauth" class="form-label">{{ $t("Password") }}</label>
|
||||
<input id="basicauth-pass" v-model="monitor.basic_auth_pass" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Password')">
|
||||
</div>
|
||||
<template v-if="monitor.authMethod === 'ntlm' ">
|
||||
<template v-if="monitor.authMethod === 'mtls' ">
|
||||
<div class="my-3">
|
||||
<label for="basicauth" class="form-label">{{ $t("Domain") }}</label>
|
||||
<input id="basicauth-domain" v-model="monitor.authDomain" type="text" class="form-control" :placeholder="$t('Domain')">
|
||||
<label for="tls-cert" class="form-label">{{ $t("Cert") }}</label>
|
||||
<textarea id="tls-cert" v-model="monitor.tlsCert" class="form-control" :placeholder="$t('Cert body')" required></textarea>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="tls-key" class="form-label">{{ $t("Key") }}</label>
|
||||
<textarea id="tls-key" v-model="monitor.tlsKey" class="form-control" :placeholder="$t('Key body')" required></textarea>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="tls-ca" class="form-label">{{ $t("CA") }}</label>
|
||||
<textarea id="tls-ca" v-model="monitor.tlsCa" class="form-control" :placeholder="$t('Server CA')"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="my-3">
|
||||
<label for="basicauth-user" class="form-label">{{ $t("Username") }}</label>
|
||||
<input id="basicauth-user" v-model="monitor.basic_auth_user" type="text" class="form-control" :placeholder="$t('Username')">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="basicauth" class="form-label">{{ $t("Workstation") }}</label>
|
||||
<input id="basicauth-workstation" v-model="monitor.authWorkstation" type="text" class="form-control" :placeholder="$t('Workstation')">
|
||||
<label for="basicauth-pass" class="form-label">{{ $t("Password") }}</label>
|
||||
<input id="basicauth-pass" v-model="monitor.basic_auth_pass" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Password')">
|
||||
</div>
|
||||
<template v-if="monitor.authMethod === 'ntlm' ">
|
||||
<div class="my-3">
|
||||
<label for="ntlm-domain" class="form-label">{{ $t("Domain") }}</label>
|
||||
<input id="ntlm-domain" v-model="monitor.authDomain" type="text" class="form-control" :placeholder="$t('Domain')">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="ntlm-workstation" class="form-label">{{ $t("Workstation") }}</label>
|
||||
<input id="ntlm-workstation" v-model="monitor.authWorkstation" type="text" class="form-control" :placeholder="$t('Workstation')">
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
@@ -925,6 +944,14 @@ message HealthCheckResponse {
|
||||
} else if (this.isEdit || this.isClone) {
|
||||
this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => {
|
||||
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;
|
||||
|
||||
if (this.isClone) {
|
||||
|
@@ -306,6 +306,11 @@
|
||||
<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>
|
||||
</p>
|
||||
|
||||
<div class="refresh-info mb-2">
|
||||
<div>{{ $t("Last Updated") }}: <date-time :value="lastUpdateTime" /></div>
|
||||
<div>{{ $tc("statusPageRefreshIn", [ updateCountdownText]) }}</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -322,6 +327,7 @@
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
import Favico from "favico.js";
|
||||
// import highlighting library (you can use any library you want just return html string)
|
||||
import { highlight, languages } from "prismjs/components/prism-core";
|
||||
@@ -337,10 +343,12 @@ import DOMPurify from "dompurify";
|
||||
import Confirm from "../components/Confirm.vue";
|
||||
import PublicGroupList from "../components/PublicGroupList.vue";
|
||||
import MaintenanceTime from "../components/MaintenanceTime.vue";
|
||||
import DateTime from "../components/Datetime.vue";
|
||||
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";
|
||||
|
||||
const toast = useToast();
|
||||
dayjs.extend(duration);
|
||||
|
||||
const leavePageMsg = "Do you really want to leave? you have unsaved changes!";
|
||||
|
||||
@@ -359,6 +367,7 @@ export default {
|
||||
Confirm,
|
||||
PrismEditor,
|
||||
MaintenanceTime,
|
||||
DateTime,
|
||||
},
|
||||
|
||||
// Leave Page for vue route change
|
||||
@@ -400,6 +409,10 @@ export default {
|
||||
baseURL: "",
|
||||
clickedEditButton: false,
|
||||
maintenanceList: [],
|
||||
autoRefreshInterval: 5,
|
||||
lastUpdateTime: dayjs(),
|
||||
updateCountdown: null,
|
||||
updateCountdownText: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -637,11 +650,13 @@ export default {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
// 5mins a loop
|
||||
// Configure auto-refresh loop
|
||||
this.updateHeartbeatList();
|
||||
feedInterval = setInterval(() => {
|
||||
this.updateHeartbeatList();
|
||||
}, (300 + 10) * 1000);
|
||||
}, (this.autoRefreshInterval * 60 + 10) * 1000);
|
||||
|
||||
this.updateUpdateTimer();
|
||||
|
||||
// Go to edit page if ?edit present
|
||||
// null means ?edit present, but no value
|
||||
@@ -700,10 +715,29 @@ export default {
|
||||
favicon.badge(downMonitors);
|
||||
|
||||
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 */
|
||||
edit() {
|
||||
if (this.hasToken) {
|
||||
@@ -1118,4 +1152,8 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
.refresh-info {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user