mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-10 12:56:13 +08:00
Compare commits
182 Commits
1.16.0-bet
...
1.17.1
Author | SHA1 | Date | |
---|---|---|---|
|
0ecaa2cbd7 | ||
|
3c3dc05621 | ||
|
1f5466a3e8 | ||
|
d5da5af174 | ||
|
c36d9a4b8b | ||
|
a7063b8aca | ||
|
0a8046c98e | ||
|
7ba717ee55 | ||
|
ea400ac35f | ||
|
15db2c060d | ||
|
89717495dc | ||
|
7b710af12c | ||
|
5b278ca500 | ||
|
b97019eea8 | ||
|
d65abe5b8c | ||
|
bcd616a4d0 | ||
|
7533041696 | ||
|
7b0deb5e20 | ||
|
b4a4171178 | ||
|
012be23509 | ||
|
dd09351c8e | ||
|
ffad990ca4 | ||
|
f7afe121e3 | ||
|
42848bcd2e | ||
|
a3b94aa532 | ||
|
fdbdf83a0d | ||
|
81d5360520 | ||
|
8f1e193de3 | ||
|
da91317760 | ||
|
bef0febede | ||
|
7d63b700e1 | ||
|
0223f86a2a | ||
|
c170b1edd0 | ||
|
5943514a92 | ||
|
62acd2edb1 | ||
|
483cbfb636 | ||
|
660005b143 | ||
|
98f3c126e5 | ||
|
6995a29980 | ||
|
cf2ca71dee | ||
|
0bd1c42080 | ||
|
9b21b86e70 | ||
|
f723930d11 | ||
|
e425e408a2 | ||
|
c690d1c3a1 | ||
|
8abbc9fd15 | ||
|
af7c905b44 | ||
|
0e8f6d2f85 | ||
|
d16be6fb7d | ||
|
f25ca96308 | ||
|
6682839ec8 | ||
|
817f6db4fd | ||
|
dc2302244f | ||
|
7a27d3752a | ||
|
f0e8f34aeb | ||
|
69273a6c41 | ||
|
6424fe77ab | ||
|
fa60672cce | ||
|
6e43ef1dd3 | ||
|
f4f2b8ddb8 | ||
|
436bc13aeb | ||
|
b72a279361 | ||
|
a28ef56553 | ||
|
7f432bd916 | ||
|
f570d41142 | ||
|
d4485fe62f | ||
|
e1681ce370 | ||
|
69d6633e6d | ||
|
04e22f17a9 | ||
|
11243a6ca1 | ||
|
87428231ad | ||
|
a68d945cdc | ||
|
2c0180f323 | ||
|
4fdaa1abb6 | ||
|
6ee7b3696a | ||
|
cc258dce14 | ||
|
fb420fa1b1 | ||
|
a707b51053 | ||
|
a927f5cd15 | ||
|
0e28707307 | ||
|
c94dcf1533 | ||
|
b0476cfb5b | ||
|
2170229031 | ||
|
213aca4fc3 | ||
|
2b42c3c828 | ||
|
d939d03690 | ||
|
07888e43f1 | ||
|
c6c1bb5b5c | ||
|
3210264e28 | ||
|
54e948c2ca | ||
|
80094ec4e1 | ||
|
091158cfe7 | ||
|
abb6ce2366 | ||
|
e4ad8cbfc8 | ||
|
a674caa520 | ||
|
179e3569b5 | ||
|
43527f2f40 | ||
|
26ff6f45a0 | ||
|
c095767f4a | ||
|
ffb7ba176c | ||
|
857e88b27e | ||
|
90fe25e8ad | ||
|
46a593534b | ||
|
7a4b54f4ee | ||
|
ea10d89f51 | ||
|
7f46223d68 | ||
|
df4ce811d9 | ||
|
30858ab038 | ||
|
e25d406fa5 | ||
|
10e16782b1 | ||
|
107a44885c | ||
|
ef9f66fad9 | ||
|
46dae99695 | ||
|
edd9bf3887 | ||
|
ab4edf2092 | ||
|
334cb57fed | ||
|
cfa5b551a5 | ||
|
46ee149b70 | ||
|
0a8c922abf | ||
|
058e5442af | ||
|
ea1725737f | ||
|
5566b038c8 | ||
|
5830f1e0b5 | ||
|
35b8e89457 | ||
|
d892b2c549 | ||
|
f23baf9c22 | ||
|
54184350a4 | ||
|
14dbe7c334 | ||
|
122e6a842b | ||
|
77ef22bdb4 | ||
|
59f983d506 | ||
|
71f031c14e | ||
|
6ae2a48584 | ||
|
7373747906 | ||
|
9d87f8d390 | ||
|
73b965c867 | ||
|
751e5ac477 | ||
|
93e5023ead | ||
|
b7ba6330db | ||
|
4c3aa20eb0 | ||
|
f779c6286a | ||
|
9fc5a3329f | ||
|
23c4ece2a5 | ||
|
175556f9fc | ||
|
ef73af391f | ||
|
44f6fca945 | ||
|
23ce7c6623 | ||
|
c346ea7864 | ||
|
f0ad32a252 | ||
|
5720017fb4 | ||
|
b7dc8e3ef8 | ||
|
5bba19f866 | ||
|
e198f2f1ab | ||
|
f91e5b98f9 | ||
|
87f933df4f | ||
|
332b9ab248 | ||
|
7cc89979f0 | ||
|
668e97c5a9 | ||
|
90473e7924 | ||
|
fdd781b081 | ||
|
373bd9b962 | ||
|
59be9bb971 | ||
|
201a25c659 | ||
|
cd3fbc80b4 | ||
|
bb7d67f717 | ||
|
24e52726b2 | ||
|
e0a0a5db4c | ||
|
cbfecab850 | ||
|
25cc54bf72 | ||
|
3700b16c5b | ||
|
d0546afe71 | ||
|
f4515ad8c5 | ||
|
3c01e8732c | ||
|
39df4eea92 | ||
|
d1a3cd047a | ||
|
a680331dd7 | ||
|
8c8eeaf627 | ||
|
b893d50e45 | ||
|
e38742a2d0 | ||
|
a4be651118 | ||
|
244a7b3671 | ||
|
ee90d2713f |
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,3 +1,5 @@
|
||||
👉 Delete this line if you have read and agree our pull request rules and guidelines: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma
|
||||
|
||||
# Description
|
||||
|
||||
Fixes #(issue)
|
||||
|
@@ -27,17 +27,30 @@ The frontend code build into "dist" directory. The server (express.js) exposes t
|
||||
|
||||
## Can I create a pull request for Uptime Kuma?
|
||||
|
||||
(Updated 2022-04-24) Since I don't want to waste your time, be sure to create empty draft pull request, so we can discuss first.
|
||||
Yes, you can. However, since I don't want to waste your time, be sure to **create empty draft pull request, so we can discuss first** if it is a large pull request or you don't know it will be merged or not.
|
||||
|
||||
Also, please don't rush or ask for ETA, because I have to understand the pull request, make sure it is no breaking changes and stick to my vision of this project, especially for large pull requests.
|
||||
|
||||
I will mark your pull request in the [milestones](https://github.com/louislam/uptime-kuma/milestones), if I am plan to review and merge it.
|
||||
|
||||
✅ Accept:
|
||||
- Bug/Security fix
|
||||
- Translations
|
||||
- Adding notification providers
|
||||
|
||||
⚠️ Discuss First
|
||||
⚠️ Discussion First
|
||||
- Large pull requests
|
||||
- New features
|
||||
|
||||
❌ Won't Merge
|
||||
- Do not pass auto test
|
||||
- Any breaking changes
|
||||
- Duplicated pull request
|
||||
- Buggy
|
||||
- Existing logic is completely modified or deleted for no reason
|
||||
- A function that is completely out of scope
|
||||
|
||||
|
||||
### Recommended Pull Request Guideline
|
||||
|
||||
Before deep into coding, discussion first is preferred. Creating an empty pull request for discussion would be recommended.
|
||||
@@ -53,22 +66,15 @@ Before deep into coding, discussion first is preferred. Creating an empty pull r
|
||||
1. Click "Change to draft"
|
||||
1. Discussion
|
||||
|
||||
#### ❌ Won't Merge
|
||||
|
||||
- Any breaking changes
|
||||
- Duplicated pull request
|
||||
- Buggy
|
||||
- Existing logic is completely modified or deleted
|
||||
- A function that is completely out of scope
|
||||
|
||||
## Project Styles
|
||||
|
||||
I personally do not like something need to learn so much and need to config so much before you can finally start the app.
|
||||
|
||||
- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort to get it run
|
||||
- Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go
|
||||
- Settings should be configurable in the frontend. Env var is not encouraged.
|
||||
- Settings should be configurable in the frontend. Environment variable is not encouraged, unless it is related to startup such as `DATA_DIR`.
|
||||
- Easy to use
|
||||
- The web UI styling should be consistent and nice.
|
||||
|
||||
## Coding Styles
|
||||
|
||||
@@ -80,8 +86,8 @@ I personally do not like something need to learn so much and need to config so m
|
||||
## Name convention
|
||||
|
||||
- Javascript/Typescript: camelCaseType
|
||||
- SQLite: underscore_type
|
||||
- CSS/SCSS: dash-type
|
||||
- SQLite: snake_case (Underscore)
|
||||
- CSS/SCSS: kebab-case (Dash)
|
||||
|
||||
## Tools
|
||||
|
||||
|
@@ -8,9 +8,6 @@ Do not use the issue tracker or discuss it in the public as it will cause more d
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
### Uptime Kuma Versions
|
||||
|
||||
You should use or upgrade to the latest version of Uptime Kuma. All `1.X.X` versions are upgradable to the lastest version.
|
||||
|
@@ -1,18 +1,32 @@
|
||||
import legacy from "@vitejs/plugin-legacy";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { defineConfig } from "vite";
|
||||
import visualizer from "rollup-plugin-visualizer";
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
|
||||
const postCssScss = require("postcss-scss");
|
||||
const postcssRTLCSS = require("postcss-rtlcss");
|
||||
|
||||
const viteCompressionFilter = /\.(js|mjs|json|css|html|svg)$/i;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
legacy({
|
||||
targets: [ "ie > 11" ],
|
||||
additionalLegacyPolyfills: [ "regenerator-runtime/runtime" ]
|
||||
})
|
||||
targets: [ "since 2015" ],
|
||||
}),
|
||||
visualizer({
|
||||
filename: "tmp/dist-stats.html"
|
||||
}),
|
||||
viteCompression({
|
||||
algorithm: "gzip",
|
||||
filter: viteCompressionFilter,
|
||||
}),
|
||||
viteCompression({
|
||||
algorithm: "brotliCompress",
|
||||
filter: viteCompressionFilter,
|
||||
}),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
@@ -21,4 +35,13 @@ export default defineConfig({
|
||||
"plugins": [ postcssRTLCSS ]
|
||||
}
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id, { getModuleInfo, getModuleIds }) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
18
db/patch-add-other-auth.sql
Normal file
18
db/patch-add-other-auth.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD auth_method VARCHAR(250);
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD auth_domain TEXT;
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD auth_workstation TEXT;
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
UPDATE monitor
|
||||
SET auth_method = 'basic'
|
||||
WHERE basic_auth_user is not null;
|
||||
COMMIT;
|
10
db/patch-add-sqlserver-monitor.sql
Normal file
10
db/patch-add-sqlserver-monitor.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD database_connection_string VARCHAR(2000);
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD database_query TEXT;
|
||||
|
||||
|
||||
COMMIT
|
@@ -12,7 +12,8 @@ RUN apt update && \
|
||||
apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \
|
||||
sqlite3 iputils-ping util-linux dumb-init && \
|
||||
pip3 --no-cache-dir install apprise==0.9.8.3 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt --yes autoremove
|
||||
|
||||
# Install cloudflared
|
||||
# dpkg --add-architecture arm: cloudflared do not provide armhf, this is workaround. Read more: https://github.com/cloudflare/cloudflared/issues/583
|
||||
@@ -22,5 +23,6 @@ RUN node ./extra/download-cloudflared.js $TARGETPLATFORM && \
|
||||
apt update && \
|
||||
apt --yes --no-install-recommends install ./cloudflared.deb && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm -f cloudflared.deb
|
||||
rm -f cloudflared.deb && \
|
||||
apt --yes autoremove
|
||||
|
||||
|
3863
package-lock.json
generated
3863
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "1.16.0-beta.0",
|
||||
"version": "1.17.1",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
|
||||
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --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.15.1 && npm ci --production && npm run download-dist",
|
||||
"setup": "git checkout 1.17.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",
|
||||
@@ -57,7 +57,8 @@
|
||||
"ncu-patch": "npm-check-updates -u -t patch",
|
||||
"release-final": "node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js",
|
||||
"release-beta": "node extra/beta/update-version.js && npm run build && node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:beta . --target release --push && node ./extra/press-any-key.js && npm run upload-artifacts",
|
||||
"git-remove-tag": "git tag -d"
|
||||
"git-remove-tag": "git tag -d",
|
||||
"build-dist-and-restart": "npm run build && npm run start-server-dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "~1.2.36",
|
||||
@@ -68,6 +69,7 @@
|
||||
"@popperjs/core": "~2.10.2",
|
||||
"args-parser": "~1.3.0",
|
||||
"axios": "~0.26.1",
|
||||
"axios-ntlm": "^1.3.0",
|
||||
"badge-maker": "^3.3.1",
|
||||
"bcryptjs": "~2.4.3",
|
||||
"bootstrap": "5.1.3",
|
||||
@@ -76,12 +78,15 @@
|
||||
"chart.js": "~3.6.2",
|
||||
"chartjs-adapter-dayjs": "~1.0.0",
|
||||
"check-password-strength": "^2.0.5",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"chroma-js": "^2.1.2",
|
||||
"command-exists": "~1.2.9",
|
||||
"compare-versions": "~3.6.0",
|
||||
"dayjs": "~1.10.8",
|
||||
"compression": "^1.7.4",
|
||||
"dayjs": "^1.11.0",
|
||||
"express": "~4.17.3",
|
||||
"express-basic-auth": "~1.2.1",
|
||||
"express-static-gzip": "^2.1.7",
|
||||
"favico.js": "^0.3.10",
|
||||
"form-data": "~4.0.0",
|
||||
"http-graceful-shutdown": "~3.1.7",
|
||||
@@ -92,6 +97,7 @@
|
||||
"jwt-decode": "^3.1.2",
|
||||
"limiter": "^2.1.0",
|
||||
"mqtt": "^4.2.8",
|
||||
"mssql": "^8.1.0",
|
||||
"node-cloudflared-tunnel": "~1.0.9",
|
||||
"nodemailer": "~6.6.5",
|
||||
"notp": "~2.0.3",
|
||||
@@ -102,7 +108,7 @@
|
||||
"prom-client": "~13.2.0",
|
||||
"prometheus-api-metrics": "~3.2.1",
|
||||
"qrcode": "~1.5.0",
|
||||
"redbean-node": "0.1.3",
|
||||
"redbean-node": "0.1.4",
|
||||
"socket.io": "~4.4.1",
|
||||
"socket.io-client": "~4.4.1",
|
||||
"socks-proxy-agent": "^6.1.1",
|
||||
@@ -129,9 +135,9 @@
|
||||
"@babel/eslint-parser": "~7.17.0",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@types/bootstrap": "~5.1.9",
|
||||
"@vitejs/plugin-legacy": "~1.6.4",
|
||||
"@vitejs/plugin-vue": "~1.9.4",
|
||||
"@vue/compiler-sfc": "~3.2.31",
|
||||
"@vitejs/plugin-legacy": "~1.8.2",
|
||||
"@vitejs/plugin-vue": "~2.3.3",
|
||||
"@vue/compiler-sfc": "~3.2.36",
|
||||
"aedes": "^0.46.3",
|
||||
"babel-plugin-rewire": "~1.2.0",
|
||||
"concurrently": "^7.1.0",
|
||||
@@ -145,11 +151,13 @@
|
||||
"npm-check-updates": "^12.5.9",
|
||||
"postcss-html": "^1.3.1",
|
||||
"puppeteer": "~13.1.3",
|
||||
"rollup-plugin-visualizer": "^5.6.0",
|
||||
"sass": "~1.42.1",
|
||||
"stylelint": "~14.7.1",
|
||||
"stylelint-config-standard": "~25.0.0",
|
||||
"typescript": "~4.4.4",
|
||||
"vite": "~2.6.14",
|
||||
"vite": "~2.9.9",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"wait-on": "^6.0.1"
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,10 @@ async function sendNotificationList(socket) {
|
||||
]);
|
||||
|
||||
for (let bean of list) {
|
||||
result.push(bean.export());
|
||||
let notificationObject = bean.export();
|
||||
notificationObject.isDefault = (notificationObject.isDefault === 1);
|
||||
notificationObject.active = (notificationObject.active === 1);
|
||||
result.push(notificationObject);
|
||||
}
|
||||
|
||||
io.to(socket.userID).emit("notificationList", result);
|
||||
|
@@ -58,6 +58,8 @@ class Database {
|
||||
"patch-monitor-expiry-notification.sql": true,
|
||||
"patch-status-page-footer-css.sql": true,
|
||||
"patch-added-mqtt-monitor.sql": true,
|
||||
"patch-add-sqlserver-monitor.sql": true,
|
||||
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -7,7 +7,7 @@ dayjs.extend(timezone);
|
||||
const axios = require("axios");
|
||||
const { Prometheus } = require("../prometheus");
|
||||
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync } = require("../util-server");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync, setSetting, httpNtlm } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { Notification } = require("../notification");
|
||||
@@ -87,7 +87,12 @@ class Monitor extends BeanModel {
|
||||
mqttUsername: this.mqttUsername,
|
||||
mqttPassword: this.mqttPassword,
|
||||
mqttTopic: this.mqttTopic,
|
||||
mqttSuccessMessage: this.mqttSuccessMessage
|
||||
mqttSuccessMessage: this.mqttSuccessMessage,
|
||||
databaseConnectionString: this.databaseConnectionString,
|
||||
databaseQuery: this.databaseQuery,
|
||||
authMethod: this.authMethod,
|
||||
authWorkstation: this.authWorkstation,
|
||||
authDomain: this.authDomain,
|
||||
};
|
||||
|
||||
if (includeSensitiveData) {
|
||||
@@ -192,7 +197,7 @@ class Monitor extends BeanModel {
|
||||
|
||||
let bean = R.dispense("heartbeat");
|
||||
bean.monitor_id = this.id;
|
||||
bean.time = R.isoDateTime(dayjs.utc());
|
||||
bean.time = R.isoDateTimeMillis(dayjs.utc());
|
||||
bean.status = DOWN;
|
||||
|
||||
if (this.isUpsideDown()) {
|
||||
@@ -213,7 +218,7 @@ class Monitor extends BeanModel {
|
||||
|
||||
// HTTP basic auth
|
||||
let basicAuthHeader = {};
|
||||
if (this.basic_auth_user) {
|
||||
if (this.auth_method === "basic") {
|
||||
basicAuthHeader = {
|
||||
"Authorization": "Basic " + this.encodeBase64(this.basic_auth_user, this.basic_auth_pass),
|
||||
};
|
||||
@@ -264,7 +269,21 @@ class Monitor extends BeanModel {
|
||||
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
|
||||
log.debug("monitor", `[${this.name}] Axios Request`);
|
||||
|
||||
let res = await axios.request(options);
|
||||
let res;
|
||||
if (this.auth_method === "ntlm") {
|
||||
options.httpsAgent.keepAlive = true;
|
||||
|
||||
res = await httpNtlm(options, {
|
||||
username: this.basic_auth_user,
|
||||
password: this.basic_auth_pass,
|
||||
domain: this.authDomain,
|
||||
workstation: this.authWorkstation ? this.authWorkstation : undefined
|
||||
});
|
||||
|
||||
} else {
|
||||
res = await axios.request(options);
|
||||
}
|
||||
|
||||
bean.msg = `${res.status} - ${res.statusText}`;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
|
||||
@@ -312,7 +331,11 @@ class Monitor extends BeanModel {
|
||||
bean.msg += ", keyword is found";
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but keyword is not found");
|
||||
data = data.replace(/<[^>]*>?|[\n\r]|\s+/gm, " ");
|
||||
if (data.length > 50) {
|
||||
data = data.substring(0, 47) + "...";
|
||||
}
|
||||
throw new Error(bean.msg + ", but keyword is not in [" + data + "]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -330,7 +353,7 @@ class Monitor extends BeanModel {
|
||||
let startTime = dayjs().valueOf();
|
||||
let dnsMessage = "";
|
||||
|
||||
let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.dns_resolve_type);
|
||||
let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.port, this.dns_resolve_type);
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
|
||||
if (this.dns_resolve_type === "A" || this.dns_resolve_type === "AAAA" || this.dns_resolve_type === "TXT") {
|
||||
@@ -367,22 +390,33 @@ class Monitor extends BeanModel {
|
||||
bean.msg = dnsMessage;
|
||||
bean.status = UP;
|
||||
} else if (this.type === "push") { // Type: Push
|
||||
const time = R.isoDateTime(dayjs.utc().subtract(this.interval, "second"));
|
||||
log.debug("monitor", `[${this.name}] Checking monitor at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`);
|
||||
const bufferTime = 1000; // 1s buffer to accommodate clock differences
|
||||
|
||||
let heartbeatCount = await R.count("heartbeat", " monitor_id = ? AND time > ? ", [
|
||||
this.id,
|
||||
time
|
||||
]);
|
||||
if (previousBeat) {
|
||||
const msSinceLastBeat = dayjs.utc().valueOf() - dayjs.utc(previousBeat.time).valueOf();
|
||||
|
||||
log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time);
|
||||
log.debug("monitor", `[${this.name}] msSinceLastBeat = ${msSinceLastBeat}`);
|
||||
|
||||
if (heartbeatCount <= 0) {
|
||||
throw new Error("No heartbeat in the time window");
|
||||
// If the previous beat was down or pending we use the regular
|
||||
// beatInterval/retryInterval in the setTimeout further below
|
||||
if (previousBeat.status !== (this.isUpsideDown() ? DOWN : UP) || msSinceLastBeat > beatInterval * 1000 + bufferTime) {
|
||||
throw new Error("No heartbeat in the time window");
|
||||
} else {
|
||||
let timeout = beatInterval * 1000 - msSinceLastBeat;
|
||||
if (timeout < 0) {
|
||||
timeout = bufferTime;
|
||||
} else {
|
||||
timeout += bufferTime;
|
||||
}
|
||||
// No need to insert successful heartbeat for push type, so end here
|
||||
retries = 0;
|
||||
log.debug("monitor", `[${this.name}] timeout = ${timeout}`);
|
||||
this.heartbeatInterval = setTimeout(beat, timeout);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// No need to insert successful heartbeat for push type, so end here
|
||||
retries = 0;
|
||||
this.heartbeatInterval = setTimeout(beat, beatInterval * 1000);
|
||||
return;
|
||||
throw new Error("No heartbeat in the time window");
|
||||
}
|
||||
|
||||
} else if (this.type === "steam") {
|
||||
@@ -432,6 +466,14 @@ class Monitor extends BeanModel {
|
||||
interval: this.interval,
|
||||
});
|
||||
bean.status = UP;
|
||||
} else if (this.type === "sqlserver") {
|
||||
let startTime = dayjs().valueOf();
|
||||
|
||||
await mssqlQuery(this.databaseConnectionString, this.databaseQuery);
|
||||
|
||||
bean.msg = "";
|
||||
bean.status = UP;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
} else {
|
||||
bean.msg = "Unknown Monitor Type";
|
||||
bean.status = PENDING;
|
||||
@@ -482,7 +524,7 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
|
||||
if (bean.status === UP) {
|
||||
log.info("monitor", `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
log.debug("monitor", `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
} else if (bean.status === PENDING) {
|
||||
if (this.retryInterval > 0) {
|
||||
beatInterval = this.retryInterval;
|
||||
@@ -826,10 +868,19 @@ class Monitor extends BeanModel {
|
||||
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
||||
const notificationList = await Monitor.getNotificationList(this);
|
||||
|
||||
log.debug("monitor", "call sendCertNotificationByTargetDays");
|
||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList);
|
||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList);
|
||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList);
|
||||
let notifyDays = await setting("tlsExpiryNotifyDays");
|
||||
if (notifyDays == null || !Array.isArray(notifyDays)) {
|
||||
// Reset Default
|
||||
setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general");
|
||||
notifyDays = [ 7, 14, 21 ];
|
||||
}
|
||||
|
||||
if (notifyDays != null && Array.isArray(notifyDays)) {
|
||||
for (const day of notifyDays) {
|
||||
log.debug("monitor", "call sendCertNotificationByTargetDays", day);
|
||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,104 @@
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { R } = require("redbean-node");
|
||||
const cheerio = require("cheerio");
|
||||
const { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||
|
||||
class StatusPage extends BeanModel {
|
||||
|
||||
/**
|
||||
* Like this: { "test-uptime.kuma.pet": "default" }
|
||||
* @type {{}}
|
||||
*/
|
||||
static domainMappingList = { };
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Response} response
|
||||
* @param {string} indexHTML
|
||||
* @param {string} slug
|
||||
*/
|
||||
static async handleStatusPageResponse(response, indexHTML, slug) {
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
]);
|
||||
|
||||
if (statusPage) {
|
||||
response.send(await StatusPage.renderHTML(indexHTML, statusPage));
|
||||
} else {
|
||||
response.status(404).send(UptimeKumaServer.getInstance().indexHTML);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSR for status pages
|
||||
* @param {string} indexHTML
|
||||
* @param {StatusPage} statusPage
|
||||
*/
|
||||
static async renderHTML(indexHTML, statusPage) {
|
||||
const $ = cheerio.load(indexHTML);
|
||||
const description155 = statusPage.description?.substring(0, 155);
|
||||
|
||||
$("title").text(statusPage.title);
|
||||
$("meta[name=description]").attr("content", description155);
|
||||
|
||||
if (statusPage.icon) {
|
||||
$("link[rel=icon]")
|
||||
.attr("href", statusPage.icon)
|
||||
.removeAttr("type");
|
||||
}
|
||||
|
||||
const head = $("head");
|
||||
|
||||
// OG Meta Tags
|
||||
head.append(`<meta property="og:title" content="${statusPage.title}" />`);
|
||||
head.append(`<meta property="og:description" content="${description155}" />`);
|
||||
|
||||
// Preload data
|
||||
const json = JSON.stringify(await StatusPage.getStatusPageData(statusPage));
|
||||
head.append(`
|
||||
<script>
|
||||
window.preloadData = ${json}
|
||||
</script>
|
||||
`);
|
||||
|
||||
return $.root().html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all status page data in one call
|
||||
* @param {StatusPage} statusPage
|
||||
*/
|
||||
static async getStatusPageData(statusPage) {
|
||||
// Incident
|
||||
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
|
||||
statusPage.id,
|
||||
]);
|
||||
|
||||
if (incident) {
|
||||
incident = incident.toPublicJSON();
|
||||
}
|
||||
|
||||
// Public Group List
|
||||
const publicGroupList = [];
|
||||
const showTags = !!statusPage.show_tags;
|
||||
|
||||
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
|
||||
statusPage.id
|
||||
]);
|
||||
|
||||
for (let groupBean of list) {
|
||||
let monitorGroup = await groupBean.toPublicJSON(showTags);
|
||||
publicGroupList.push(monitorGroup);
|
||||
}
|
||||
|
||||
// Response
|
||||
return {
|
||||
config: await statusPage.toPublicJSON(),
|
||||
incident,
|
||||
publicGroupList
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads domain mapping from DB
|
||||
* Return object like this: { "test-uptime.kuma.pet": "default" }
|
||||
|
@@ -55,8 +55,8 @@ class Discord extends NotificationProvider {
|
||||
value: monitorJSON["name"],
|
||||
},
|
||||
{
|
||||
name: "Service URL / Address",
|
||||
value: address,
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
||||
},
|
||||
{
|
||||
name: "Time (UTC)",
|
||||
@@ -90,8 +90,8 @@ class Discord extends NotificationProvider {
|
||||
value: monitorJSON["name"],
|
||||
},
|
||||
{
|
||||
name: "Service URL",
|
||||
value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
|
||||
},
|
||||
{
|
||||
name: "Time (UTC)",
|
||||
@@ -99,7 +99,7 @@ class Discord extends NotificationProvider {
|
||||
},
|
||||
{
|
||||
name: "Ping",
|
||||
value: heartbeatJSON["ping"] + "ms",
|
||||
value: heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms",
|
||||
},
|
||||
],
|
||||
}],
|
||||
|
@@ -14,7 +14,7 @@ class LunaSea extends NotificationProvider {
|
||||
if (heartbeatJSON == null) {
|
||||
let testdata = {
|
||||
"title": "Uptime Kuma Alert",
|
||||
"body": "Testing Successful.",
|
||||
"body": msg,
|
||||
};
|
||||
await axios.post(lunaseadevice, testdata);
|
||||
return okMsg;
|
||||
|
26
server/notification-providers/ntfy.js
Normal file
26
server/notification-providers/ntfy.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
|
||||
class Ntfy extends NotificationProvider {
|
||||
|
||||
name = "ntfy";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
try {
|
||||
await axios.post(`${notification.ntfyserverurl}`, {
|
||||
"topic": notification.ntfytopic,
|
||||
"message": msg,
|
||||
"priority": notification.ntfyPriority || 4,
|
||||
"title": "Uptime-Kuma",
|
||||
});
|
||||
|
||||
return okMsg;
|
||||
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Ntfy;
|
113
server/notification-providers/pagerduty.js
Normal file
113
server/notification-providers/pagerduty.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
|
||||
const { setting } = require("../util-server");
|
||||
let successMessage = "Sent Successfully.";
|
||||
|
||||
class PagerDuty extends NotificationProvider {
|
||||
name = "PagerDuty";
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
try {
|
||||
if (heartbeatJSON == null) {
|
||||
const title = "Uptime Kuma Alert";
|
||||
const monitor = {
|
||||
type: "ping",
|
||||
url: "Uptime Kuma Test Button",
|
||||
};
|
||||
return this.postNotification(notification, title, msg, monitor);
|
||||
}
|
||||
|
||||
if (heartbeatJSON.status === UP) {
|
||||
const title = "Uptime Kuma Monitor ✅ Up";
|
||||
const eventAction = notification.pagerdutyAutoResolve || null;
|
||||
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, eventAction);
|
||||
}
|
||||
|
||||
if (heartbeatJSON.status === DOWN) {
|
||||
const title = "Uptime Kuma Monitor 🔴 Down";
|
||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger");
|
||||
}
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if result is successful, result code should be in range 2xx
|
||||
* @param {Object} result Axios response object
|
||||
* @throws {Error} The status code is not in range 2xx
|
||||
*/
|
||||
checkResult(result) {
|
||||
if (result.status == null) {
|
||||
throw new Error("PagerDuty notification failed with invalid response!");
|
||||
}
|
||||
if (result.status < 200 || result.status >= 300) {
|
||||
throw new Error("PagerDuty notification failed with status code " + result.status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message
|
||||
* @param {BeanModel} notification Message title
|
||||
* @param {string} title Message title
|
||||
* @param {string} body Message
|
||||
* @param {Object} monitorInfo Monitor details (For Up/Down only)
|
||||
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
|
||||
* @returns {string}
|
||||
*/
|
||||
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {
|
||||
|
||||
if (eventAction == null) {
|
||||
return "No action required";
|
||||
}
|
||||
|
||||
let monitorUrl;
|
||||
if (monitorInfo.type === "port") {
|
||||
monitorUrl = monitorInfo.hostname;
|
||||
if (monitorInfo.port) {
|
||||
monitorUrl += ":" + monitorInfo.port;
|
||||
}
|
||||
} else if (monitorInfo.hostname != null) {
|
||||
monitorUrl = monitorInfo.hostname;
|
||||
} else {
|
||||
monitorUrl = monitorInfo.url;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: notification.pagerdutyIntegrationUrl,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
data: {
|
||||
payload: {
|
||||
summary: `[${title}] [${monitorInfo.name}] ${body}`,
|
||||
severity: notification.pagerdutyPriority || "warning",
|
||||
source: monitorUrl,
|
||||
},
|
||||
routing_key: notification.pagerdutyIntegrationKey,
|
||||
event_action: eventAction,
|
||||
dedup_key: "Uptime Kuma/" + monitorInfo.id,
|
||||
}
|
||||
};
|
||||
|
||||
const baseURL = await setting("primaryBaseURL");
|
||||
if (baseURL && monitorInfo) {
|
||||
options.client = "Uptime Kuma";
|
||||
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);
|
||||
}
|
||||
|
||||
let result = await axios.request(options);
|
||||
this.checkResult(result);
|
||||
if (result.statusText != null) {
|
||||
return "PagerDuty notification succeed: " + result.statusText;
|
||||
}
|
||||
|
||||
return successMessage;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PagerDuty;
|
@@ -2,6 +2,7 @@ const { R } = require("redbean-node");
|
||||
const Apprise = require("./notification-providers/apprise");
|
||||
const Discord = require("./notification-providers/discord");
|
||||
const Gotify = require("./notification-providers/gotify");
|
||||
const Ntfy = require("./notification-providers/ntfy");
|
||||
const Line = require("./notification-providers/line");
|
||||
const LunaSea = require("./notification-providers/lunasea");
|
||||
const Mattermost = require("./notification-providers/mattermost");
|
||||
@@ -29,6 +30,7 @@ const SerwerSMS = require("./notification-providers/serwersms");
|
||||
const Stackfield = require("./notification-providers/stackfield");
|
||||
const WeCom = require("./notification-providers/wecom");
|
||||
const GoogleChat = require("./notification-providers/google-chat");
|
||||
const PagerDuty = require("./notification-providers/pagerduty");
|
||||
const Gorush = require("./notification-providers/gorush");
|
||||
const Alerta = require("./notification-providers/alerta");
|
||||
const OneBot = require("./notification-providers/onebot");
|
||||
@@ -51,6 +53,7 @@ class Notification {
|
||||
new Discord(),
|
||||
new Teams(),
|
||||
new Gotify(),
|
||||
new Ntfy(),
|
||||
new Line(),
|
||||
new LunaSea(),
|
||||
new Feishu(),
|
||||
@@ -74,6 +77,7 @@ class Notification {
|
||||
new Stackfield(),
|
||||
new WeCom(),
|
||||
new GoogleChat(),
|
||||
new PagerDuty(),
|
||||
new Gorush(),
|
||||
new Alerta(),
|
||||
new OneBot(),
|
||||
|
@@ -1,5 +1,5 @@
|
||||
let express = require("express");
|
||||
const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin } = require("../util-server");
|
||||
const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin, send403 } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const apicache = require("../modules/apicache");
|
||||
const Monitor = require("../model/monitor");
|
||||
@@ -59,7 +59,7 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
let duration = 0;
|
||||
|
||||
let bean = R.dispense("heartbeat");
|
||||
bean.time = R.isoDateTime(dayjs.utc());
|
||||
bean.time = R.isoDateTimeMillis(dayjs.utc());
|
||||
|
||||
if (previousHeartbeat) {
|
||||
isFirstBeat = false;
|
||||
@@ -67,6 +67,7 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
||||
}
|
||||
|
||||
log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`);
|
||||
log.debug("router", "PreviousStatus: " + previousStatus);
|
||||
log.debug("router", "Current Status: " + status);
|
||||
|
||||
@@ -91,115 +92,13 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
response.json({
|
||||
response.status(404).json({
|
||||
ok: false,
|
||||
msg: e.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Status page config, incident, monitor list
|
||||
router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => {
|
||||
allowDevAllOrigin(response);
|
||||
let slug = request.params.slug;
|
||||
|
||||
// Get Status Page
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
]);
|
||||
|
||||
if (!statusPage) {
|
||||
response.statusCode = 404;
|
||||
response.json({
|
||||
msg: "Not Found"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Incident
|
||||
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
|
||||
statusPage.id,
|
||||
]);
|
||||
|
||||
if (incident) {
|
||||
incident = incident.toPublicJSON();
|
||||
}
|
||||
|
||||
// Public Group List
|
||||
const publicGroupList = [];
|
||||
const showTags = !!statusPage.show_tags;
|
||||
|
||||
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
|
||||
statusPage.id
|
||||
]);
|
||||
|
||||
for (let groupBean of list) {
|
||||
let monitorGroup = await groupBean.toPublicJSON(showTags);
|
||||
publicGroupList.push(monitorGroup);
|
||||
}
|
||||
|
||||
// Response
|
||||
response.json({
|
||||
config: await statusPage.toPublicJSON(),
|
||||
incident,
|
||||
publicGroupList
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
send403(response, error.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Status Page Polling Data
|
||||
// Can fetch only if published
|
||||
router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (request, response) => {
|
||||
allowDevAllOrigin(response);
|
||||
|
||||
try {
|
||||
let heartbeatList = {};
|
||||
let uptimeList = {};
|
||||
|
||||
let slug = request.params.slug;
|
||||
let statusPageID = await StatusPage.slugToID(slug);
|
||||
|
||||
let monitorIDList = await R.getCol(`
|
||||
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
|
||||
WHERE monitor_group.group_id = \`group\`.id
|
||||
AND public = 1
|
||||
AND \`group\`.status_page_id = ?
|
||||
`, [
|
||||
statusPageID
|
||||
]);
|
||||
|
||||
for (let monitorID of monitorIDList) {
|
||||
let list = await R.getAll(`
|
||||
SELECT * FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY time DESC
|
||||
LIMIT 50
|
||||
`, [
|
||||
monitorID,
|
||||
]);
|
||||
|
||||
list = R.convertToBeans("heartbeat", list);
|
||||
heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
|
||||
|
||||
const type = 24;
|
||||
uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID);
|
||||
}
|
||||
|
||||
response.json({
|
||||
heartbeatList,
|
||||
uptimeList
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
send403(response, error.message);
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response) => {
|
||||
allowAllOrigin(response);
|
||||
|
||||
@@ -376,16 +275,4 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request,
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send a 403 response
|
||||
* @param {Object} res Express response object
|
||||
* @param {string} [msg=""] Message to send
|
||||
*/
|
||||
function send403(res, msg = "") {
|
||||
res.status(403).json({
|
||||
"status": "fail",
|
||||
"msg": msg,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
110
server/routers/status-page-router.js
Normal file
110
server/routers/status-page-router.js
Normal file
@@ -0,0 +1,110 @@
|
||||
let express = require("express");
|
||||
const apicache = require("../modules/apicache");
|
||||
const { UptimeKumaServer } = require("../uptime-kuma-server");
|
||||
const StatusPage = require("../model/status_page");
|
||||
const { allowDevAllOrigin, send403 } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const Monitor = require("../model/monitor");
|
||||
|
||||
let router = express.Router();
|
||||
|
||||
let cache = apicache.middleware;
|
||||
const server = UptimeKumaServer.getInstance();
|
||||
|
||||
router.get("/status/:slug", cache("5 minutes"), async (request, response) => {
|
||||
let slug = request.params.slug;
|
||||
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
|
||||
});
|
||||
|
||||
router.get("/status", cache("5 minutes"), async (request, response) => {
|
||||
let slug = "default";
|
||||
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
|
||||
});
|
||||
|
||||
router.get("/status-page", cache("5 minutes"), async (request, response) => {
|
||||
let slug = "default";
|
||||
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
|
||||
});
|
||||
|
||||
// Status page config, incident, monitor list
|
||||
router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => {
|
||||
allowDevAllOrigin(response);
|
||||
let slug = request.params.slug;
|
||||
|
||||
try {
|
||||
// Get Status Page
|
||||
let statusPage = await R.findOne("status_page", " slug = ? ", [
|
||||
slug
|
||||
]);
|
||||
|
||||
if (!statusPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let statusPageData = await StatusPage.getStatusPageData(statusPage);
|
||||
|
||||
if (!statusPageData) {
|
||||
response.statusCode = 404;
|
||||
response.json({
|
||||
msg: "Not Found"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Response
|
||||
response.json(statusPageData);
|
||||
|
||||
} catch (error) {
|
||||
send403(response, error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Status Page Polling Data
|
||||
// Can fetch only if published
|
||||
router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (request, response) => {
|
||||
allowDevAllOrigin(response);
|
||||
|
||||
try {
|
||||
let heartbeatList = {};
|
||||
let uptimeList = {};
|
||||
|
||||
let slug = request.params.slug;
|
||||
let statusPageID = await StatusPage.slugToID(slug);
|
||||
|
||||
let monitorIDList = await R.getCol(`
|
||||
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
|
||||
WHERE monitor_group.group_id = \`group\`.id
|
||||
AND public = 1
|
||||
AND \`group\`.status_page_id = ?
|
||||
`, [
|
||||
statusPageID
|
||||
]);
|
||||
|
||||
for (let monitorID of monitorIDList) {
|
||||
let list = await R.getAll(`
|
||||
SELECT * FROM heartbeat
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY time DESC
|
||||
LIMIT 50
|
||||
`, [
|
||||
monitorID,
|
||||
]);
|
||||
|
||||
list = R.convertToBeans("heartbeat", list);
|
||||
heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
|
||||
|
||||
const type = 24;
|
||||
uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID);
|
||||
}
|
||||
|
||||
response.json({
|
||||
heartbeatList,
|
||||
uptimeList
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
send403(response, error.message);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
@@ -16,7 +16,7 @@ if (nodeVersion < requiredVersion) {
|
||||
}
|
||||
|
||||
const args = require("args-parser")(process.argv);
|
||||
const { sleep, log, getRandomInt, genSecret, debug, isDev } = require("../src/util");
|
||||
const { sleep, log, getRandomInt, genSecret, isDev } = require("../src/util");
|
||||
const config = require("./config");
|
||||
|
||||
log.info("server", "Welcome to Uptime Kuma");
|
||||
@@ -35,6 +35,7 @@ const fs = require("fs");
|
||||
log.info("server", "Importing 3rd-party libraries");
|
||||
log.debug("server", "Importing express");
|
||||
const express = require("express");
|
||||
const expressStaticGzip = require("express-static-gzip");
|
||||
log.debug("server", "Importing redbean-node");
|
||||
const { R } = require("redbean-node");
|
||||
log.debug("server", "Importing jsonwebtoken");
|
||||
@@ -148,22 +149,6 @@ let jwtSecret = null;
|
||||
*/
|
||||
let needSetup = false;
|
||||
|
||||
/**
|
||||
* Cache Index HTML
|
||||
* @type {string}
|
||||
*/
|
||||
let indexHTML = "";
|
||||
|
||||
try {
|
||||
indexHTML = fs.readFileSync("./dist/index.html").toString();
|
||||
} catch (e) {
|
||||
// "dist/index.html" is not necessary for development
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
Database.init(args);
|
||||
await initDatabase(testMode);
|
||||
@@ -179,13 +164,17 @@ try {
|
||||
|
||||
// Entry Page
|
||||
app.get("/", async (request, response) => {
|
||||
debug(`Request Domain: ${request.hostname}`);
|
||||
log.debug("entry", `Request Domain: ${request.hostname}`);
|
||||
|
||||
if (request.hostname in StatusPage.domainMappingList) {
|
||||
debug("This is a status page domain");
|
||||
response.send(indexHTML);
|
||||
log.debug("entry", "This is a status page domain");
|
||||
|
||||
let slug = StatusPage.domainMappingList[request.hostname];
|
||||
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
|
||||
|
||||
} else if (exports.entryPage && exports.entryPage.startsWith("statusPage-")) {
|
||||
response.redirect("/status/" + exports.entryPage.replace("statusPage-", ""));
|
||||
|
||||
} else {
|
||||
response.redirect("/dashboard");
|
||||
}
|
||||
@@ -214,7 +203,9 @@ try {
|
||||
// With Basic Auth using the first user's username/password
|
||||
app.get("/metrics", basicAuth, prometheusAPIMetrics());
|
||||
|
||||
app.use("/", express.static("dist"));
|
||||
app.use("/", expressStaticGzip("dist", {
|
||||
enableBrotli: true,
|
||||
}));
|
||||
|
||||
// ./data/upload
|
||||
app.use("/upload", express.static(Database.uploadDir));
|
||||
@@ -227,12 +218,16 @@ try {
|
||||
const apiRouter = require("./routers/api-router");
|
||||
app.use(apiRouter);
|
||||
|
||||
// Status Page Router
|
||||
const statusPageRouter = require("./routers/status-page-router");
|
||||
app.use(statusPageRouter);
|
||||
|
||||
// Universal Route Handler, must be at the end of all express routes.
|
||||
app.get("*", async (_request, response) => {
|
||||
if (_request.originalUrl.startsWith("/upload/")) {
|
||||
response.status(404).send("File not found.");
|
||||
} else {
|
||||
response.send(indexHTML);
|
||||
response.send(server.indexHTML);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -659,7 +654,7 @@ try {
|
||||
bean.retryInterval = monitor.retryInterval;
|
||||
bean.hostname = monitor.hostname;
|
||||
bean.maxretries = monitor.maxretries;
|
||||
bean.port = monitor.port;
|
||||
bean.port = parseInt(monitor.port);
|
||||
bean.keyword = monitor.keyword;
|
||||
bean.ignoreTls = monitor.ignoreTls;
|
||||
bean.expiryNotification = monitor.expiryNotification;
|
||||
@@ -674,6 +669,11 @@ try {
|
||||
bean.mqttPassword = monitor.mqttPassword;
|
||||
bean.mqttTopic = monitor.mqttTopic;
|
||||
bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
|
||||
bean.databaseConnectionString = monitor.databaseConnectionString;
|
||||
bean.databaseQuery = monitor.databaseQuery;
|
||||
bean.authMethod = monitor.authMethod;
|
||||
bean.authWorkstation = monitor.authWorkstation;
|
||||
bean.authDomain = monitor.authDomain;
|
||||
|
||||
await R.store(bean);
|
||||
|
||||
@@ -1247,8 +1247,11 @@ try {
|
||||
method: monitorListData[i].method || "GET",
|
||||
body: monitorListData[i].body,
|
||||
headers: monitorListData[i].headers,
|
||||
authMethod: monitorListData[i].authMethod,
|
||||
basic_auth_user: monitorListData[i].basic_auth_user,
|
||||
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
||||
authWorkstation: monitorListData[i].authWorkstation,
|
||||
authDomain: monitorListData[i].authDomain,
|
||||
interval: monitorListData[i].interval,
|
||||
retryInterval: retryInterval,
|
||||
hostname: monitorListData[i].hostname,
|
||||
|
@@ -63,7 +63,10 @@ module.exports.cloudflaredSocketHandler = (socket) => {
|
||||
socket.on(prefix + "stop", async (currentPassword, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
await doubleCheckPassword(socket, currentPassword);
|
||||
const disabledAuth = await setting("disableAuth");
|
||||
if (!disabledAuth) {
|
||||
await doubleCheckPassword(socket, currentPassword);
|
||||
}
|
||||
cloudflared.stop();
|
||||
} catch (error) {
|
||||
callback({
|
||||
|
@@ -29,6 +29,12 @@ class UptimeKumaServer {
|
||||
httpServer = undefined;
|
||||
io = undefined;
|
||||
|
||||
/**
|
||||
* Cache Index HTML
|
||||
* @type {string}
|
||||
*/
|
||||
indexHTML = "";
|
||||
|
||||
static getInstance(args) {
|
||||
if (UptimeKumaServer.instance == null) {
|
||||
UptimeKumaServer.instance = new UptimeKumaServer(args);
|
||||
@@ -55,6 +61,16 @@ class UptimeKumaServer {
|
||||
this.httpServer = http.createServer(this.app);
|
||||
}
|
||||
|
||||
try {
|
||||
this.indexHTML = fs.readFileSync("./dist/index.html").toString();
|
||||
} catch (e) {
|
||||
// "dist/index.html" is not necessary for development
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
this.io = new Server(this.httpServer);
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,8 @@ const chardet = require("chardet");
|
||||
const mqtt = require("mqtt");
|
||||
const chroma = require("chroma-js");
|
||||
const { badgeConstants } = require("./config");
|
||||
const mssql = require("mssql");
|
||||
const { NtlmClient } = require("axios-ntlm");
|
||||
|
||||
// From ping-lite
|
||||
exports.WIN = /^win/.test(process.platform);
|
||||
@@ -172,16 +174,40 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Use NTLM Auth for a http request.
|
||||
* @param {Object} options The http request options
|
||||
* @param {Object} ntlmOptions The auth options
|
||||
* @returns {Promise<(string[]|Object[]|Object)>}
|
||||
*/
|
||||
exports.httpNtlm = function (options, ntlmOptions) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let client = NtlmClient(ntlmOptions);
|
||||
|
||||
client(options)
|
||||
.then((resp) => {
|
||||
resolve(resp);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a given record using the specified DNS server
|
||||
* @param {string} hostname The hostname of the record to lookup
|
||||
* @param {string} resolverServer The DNS server to use
|
||||
* @param {string} resolverPort Port the DNS server is listening on
|
||||
* @param {string} rrtype The type of record to request
|
||||
* @returns {Promise<(string[]|Object[]|Object)>}
|
||||
*/
|
||||
exports.dnsResolve = function (hostname, resolverServer, rrtype) {
|
||||
exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) {
|
||||
const resolver = new Resolver();
|
||||
resolver.setServers([ resolverServer ]);
|
||||
// Remove brackets from IPv6 addresses so we can re-add them to
|
||||
// prevent issues with ::1:5300 (::1 port 5300)
|
||||
resolverServer = resolverServer.replace("[", "").replace("]", "");
|
||||
resolver.setServers([ `[${resolverServer}]:${resolverPort}` ]);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (rrtype === "PTR") {
|
||||
resolver.reverse(hostname, (err, records) => {
|
||||
@@ -203,6 +229,31 @@ exports.dnsResolve = function (hostname, resolverServer, rrtype) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a query on SQL Server
|
||||
* @param {string} connectionString The database connection string
|
||||
* @param {string} query The query to validate the database with
|
||||
* @returns {Promise<(string[]|Object[]|Object)>}
|
||||
*/
|
||||
exports.mssqlQuery = function (connectionString, query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
mssql.on("error", err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
mssql.connect(connectionString).then(pool => {
|
||||
return pool.request()
|
||||
.query(query);
|
||||
}).then(result => {
|
||||
resolve(result);
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
}).finally(() => {
|
||||
mssql.close();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve value of setting based on key
|
||||
* @param {string} key Key of setting to retrieve
|
||||
@@ -554,3 +605,15 @@ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => {
|
||||
exports.filterAndJoin = (parts, connector = "") => {
|
||||
return parts.filter((part) => !!part && part !== "").join(connector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a 403 response
|
||||
* @param {Object} res Express response object
|
||||
* @param {string} [msg=""] Message to send
|
||||
*/
|
||||
module.exports.send403 = (res, msg = "") => {
|
||||
res.status(403).json({
|
||||
"status": "fail",
|
||||
"msg": msg,
|
||||
});
|
||||
};
|
||||
|
@@ -34,6 +34,25 @@ textarea.form-control {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// optgroup
|
||||
optgroup {
|
||||
color: #b1b1b1;
|
||||
option {
|
||||
color: #212529;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
optgroup {
|
||||
color: #535864;
|
||||
option {
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollbar
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 20px;
|
||||
@@ -363,11 +382,17 @@ textarea.form-control {
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 65px);
|
||||
}
|
||||
|
||||
@media (max-width: 770px) {
|
||||
&.scrollbar {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 15px;
|
||||
padding: 13px 15px 10px 15px;
|
||||
border-radius: 10px;
|
||||
transition: all ease-in-out 0.15s;
|
||||
|
||||
@@ -473,6 +498,14 @@ textarea.form-control {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
h5.settings-subheading::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 50%;
|
||||
padding-top: 8px;
|
||||
border-bottom: 1px solid $dark-border-color;
|
||||
}
|
||||
|
||||
// Localization
|
||||
|
||||
@import "localization.scss";
|
||||
|
86
src/components/ActionInput.vue
Normal file
86
src/components/ActionInput.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
ref="input"
|
||||
v-model="model"
|
||||
class="form-control"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:disabled="!enabled"
|
||||
>
|
||||
<a class="btn btn-outline-primary" @click="action()">
|
||||
<font-awesome-icon :icon="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Generic input field with a customizable action on the right.
|
||||
* Action is passed in as a function.
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* The value of the input field.
|
||||
*/
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* Whether the input field is enabled / disabled.
|
||||
*/
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* Placeholder text for the input field.
|
||||
*/
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* The icon displayed in the right button of the input field.
|
||||
* Accepts a Font Awesome icon string identifier.
|
||||
* @example "plus"
|
||||
*/
|
||||
icon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* The input type of the input field.
|
||||
* @example "email"
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
/**
|
||||
* The action to be performed when the button is clicked.
|
||||
* Action is passed in as a function.
|
||||
*/
|
||||
action: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
}
|
||||
},
|
||||
emits: [ "update:modelValue" ],
|
||||
computed: {
|
||||
/**
|
||||
* Send value update to parent on change.
|
||||
*/
|
||||
model: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
@@ -25,10 +25,12 @@ export default {
|
||||
CertificateInfoRow,
|
||||
},
|
||||
props: {
|
||||
/** Object representing certificate */
|
||||
certInfo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
/** Is the TLS certificate valid? */
|
||||
valid: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
|
@@ -56,12 +56,19 @@ export default {
|
||||
Datetime,
|
||||
},
|
||||
props: {
|
||||
/** Object representing certificate */
|
||||
cert: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Format the subject of the certificate
|
||||
* @param {Object} subject Object representing the certificates
|
||||
* subject
|
||||
* @returns {string}
|
||||
*/
|
||||
formatSubject(subject) {
|
||||
if (subject.O && subject.CN && subject.C) {
|
||||
return `${subject.CN} - ${subject.O} (${subject.C})`;
|
||||
|
@@ -29,14 +29,17 @@ import { Modal } from "bootstrap";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** Style of button */
|
||||
btnStyle: {
|
||||
type: String,
|
||||
default: "btn-primary",
|
||||
},
|
||||
/** Text to use as yes */
|
||||
yesText: {
|
||||
type: String,
|
||||
default: "Yes", // TODO: No idea what to translate this
|
||||
},
|
||||
/** Text to use as no */
|
||||
noText: {
|
||||
type: String,
|
||||
default: "No",
|
||||
@@ -50,9 +53,13 @@ export default {
|
||||
this.modal = new Modal(this.$refs.modal);
|
||||
},
|
||||
methods: {
|
||||
/** Show the confirm dialog */
|
||||
show() {
|
||||
this.modal.show();
|
||||
},
|
||||
/**
|
||||
* @emits string "yes" Notify the parent when Yes is pressed
|
||||
*/
|
||||
yes() {
|
||||
this.$emit("yes");
|
||||
},
|
||||
|
@@ -25,33 +25,41 @@ let timeout;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** ID of this input */
|
||||
id: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/** Type of input */
|
||||
type: {
|
||||
type: String,
|
||||
default: "text"
|
||||
},
|
||||
/** The value of the input */
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/** A placeholder to use */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/** Should the field auto complete */
|
||||
autocomplete: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
/** Is the input required? */
|
||||
required: {
|
||||
type: Boolean
|
||||
},
|
||||
/** Should the input be read only? */
|
||||
readonly: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
/** Is the input disabled? */
|
||||
disabled: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
@@ -79,14 +87,21 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** Show the input */
|
||||
showInput() {
|
||||
this.visibility = "text";
|
||||
},
|
||||
|
||||
/** Hide the input */
|
||||
hideInput() {
|
||||
this.visibility = "password";
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the provided text to the users clipboard
|
||||
* @param {string} textToCopy
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
copyToClipboard(textToCopy) {
|
||||
this.icon = "check";
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import { sleep } from "../util.ts";
|
||||
export default {
|
||||
|
||||
props: {
|
||||
/** Value to count */
|
||||
value: {
|
||||
type: [ String, Number ],
|
||||
default: 0,
|
||||
@@ -18,6 +19,7 @@ export default {
|
||||
type: Number,
|
||||
default: 0.3,
|
||||
},
|
||||
/** Unit of the value */
|
||||
unit: {
|
||||
type: String,
|
||||
default: "ms",
|
||||
@@ -43,9 +45,7 @@ export default {
|
||||
let frames = 12;
|
||||
let step = Math.floor(diff / frames);
|
||||
|
||||
if (isNaN(step) || ! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0) {
|
||||
// Lazy to NOT this condition, hahaha.
|
||||
} else {
|
||||
if (! (isNaN(step) || ! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0)) {
|
||||
for (let i = 1; i < frames; i++) {
|
||||
this.output += step;
|
||||
await sleep(15);
|
||||
|
@@ -13,10 +13,12 @@ dayjs.extend(relativeTime);
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** Value of date time */
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
/** Should only the date be displayed? */
|
||||
dateOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div ref="wrap" class="wrap" :style="wrapStyle">
|
||||
<div class="hp-bar-big d-flex" :style="barStyle">
|
||||
<div class="hp-bar-big" :style="barStyle">
|
||||
<div
|
||||
v-for="(beat, index) in shortBeatList"
|
||||
:key="index"
|
||||
@@ -8,11 +8,7 @@
|
||||
:class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }"
|
||||
:style="beatStyle"
|
||||
:title="getBeatTitle(beat)"
|
||||
@mouseenter="toggleActivateSibling"
|
||||
@mouseleave="toggleActivateSibling"
|
||||
>
|
||||
<div class="beat-inner" />
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -21,14 +17,17 @@
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** Size of the heartbeat bar */
|
||||
size: {
|
||||
type: String,
|
||||
default: "big",
|
||||
},
|
||||
/** ID of the monitor */
|
||||
monitorId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
/** Array of the monitors heartbeats */
|
||||
heartbeatList: {
|
||||
type: Array,
|
||||
default: null,
|
||||
@@ -164,38 +163,23 @@ export default {
|
||||
this.resize();
|
||||
},
|
||||
methods: {
|
||||
/** Resize the heartbeat bar */
|
||||
resize() {
|
||||
if (this.$refs.wrap) {
|
||||
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the title of the beat.
|
||||
* Used as the hover tooltip on the heartbeat bar.
|
||||
* @param {Object} beat Beat to get title from
|
||||
* @returns {string}
|
||||
*/
|
||||
getBeatTitle(beat) {
|
||||
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
|
||||
},
|
||||
|
||||
// Toggling the activeSibling class on hover over the current hover item
|
||||
toggleActivateSibling(e) {
|
||||
// Variable definition
|
||||
const element = e.target;
|
||||
const previous = element.previousSibling;
|
||||
const next = element.nextSibling;
|
||||
|
||||
// Return if the hovered element has empty class
|
||||
if (element.classList.contains("empty")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if Previous Sibling is heartbar element and doesn't have the empty class
|
||||
if (previous.children && !previous.classList.contains("empty")) {
|
||||
previous.classList.toggle("active-sibling");
|
||||
}
|
||||
|
||||
// Check if Next Sibling is heartbar element and doesn't have the empty class
|
||||
if (next.children && !next.classList.contains("empty")) {
|
||||
next.classList.toggle("active-sibling");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -211,10 +195,9 @@ export default {
|
||||
|
||||
.hp-bar-big {
|
||||
.beat {
|
||||
display: inline-block;
|
||||
background-color: $primary;
|
||||
border-radius: $border-radius;
|
||||
display: inline-block;
|
||||
transition: all ease 0.6s;
|
||||
|
||||
&.empty {
|
||||
background-color: aliceblue;
|
||||
@@ -228,23 +211,11 @@ export default {
|
||||
background-color: $warning;
|
||||
}
|
||||
|
||||
.beat-inner {
|
||||
border-radius: $border-radius;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&:not(.empty):hover {
|
||||
transition: all ease 0.15s;
|
||||
transition: all ease-in-out 0.15s;
|
||||
opacity: 0.8;
|
||||
transform: scale(var(--hover-scale));
|
||||
}
|
||||
|
||||
&.active-sibling {
|
||||
transform: scale(1.15);
|
||||
transition: all ease 0.15s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,25 +24,31 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/** The value of the input */
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/** A placeholder to use */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/** Maximum length of the input */
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 255
|
||||
},
|
||||
/** Should the field auto complete */
|
||||
autocomplete: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
/** Is the input required? */
|
||||
required: {
|
||||
type: Boolean
|
||||
},
|
||||
/** Should the input be read only? */
|
||||
readonly: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
@@ -68,9 +74,11 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/** Show users input in plain text */
|
||||
showInput() {
|
||||
this.visibility = "text";
|
||||
},
|
||||
/** Censor users input */
|
||||
hideInput() {
|
||||
this.visibility = "password";
|
||||
},
|
||||
|
@@ -55,6 +55,7 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** Submit the user details and attempt to log in */
|
||||
submit() {
|
||||
this.processing = true;
|
||||
|
||||
|
@@ -58,6 +58,7 @@ export default {
|
||||
Tag,
|
||||
},
|
||||
props: {
|
||||
/** Should the scrollbar be shown */
|
||||
scrollbar: {
|
||||
type: Boolean,
|
||||
},
|
||||
@@ -69,10 +70,22 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Improve the sticky appearance of the list by increasing its
|
||||
* height as user scrolls down.
|
||||
* Not used on mobile.
|
||||
*/
|
||||
boxStyle() {
|
||||
return {
|
||||
height: `calc(100vh - 160px + ${this.windowTop}px)`,
|
||||
};
|
||||
if (window.innerWidth > 550) {
|
||||
return {
|
||||
height: `calc(100vh - 160px + ${this.windowTop}px)`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
height: "calc(100vh - 160px)",
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
sortedMonitorList() {
|
||||
@@ -124,6 +137,7 @@ export default {
|
||||
window.removeEventListener("scroll", this.onScroll);
|
||||
},
|
||||
methods: {
|
||||
/** Handle user scroll */
|
||||
onScroll() {
|
||||
if (window.top.scrollY <= 133) {
|
||||
this.windowTop = window.top.scrollY;
|
||||
@@ -131,9 +145,15 @@ export default {
|
||||
this.windowTop = 133;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get URL of monitor
|
||||
* @param {number} id ID of monitor
|
||||
* @returns {string} Relative URL of monitor
|
||||
*/
|
||||
monitorURL(id) {
|
||||
return getMonitorRelativeURL(id);
|
||||
},
|
||||
/** Clear the search bar */
|
||||
clearSearchText() {
|
||||
this.searchText = "";
|
||||
}
|
||||
|
@@ -125,11 +125,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** Show dialog to confirm deletion */
|
||||
deleteConfirm() {
|
||||
this.modal.hide();
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show settings for specified notification
|
||||
* @param {number} notificationID ID of notification to show
|
||||
*/
|
||||
show(notificationID) {
|
||||
if (notificationID) {
|
||||
this.id = notificationID;
|
||||
@@ -152,6 +157,7 @@ export default {
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
/** Submit the form to the server */
|
||||
submit() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
|
||||
@@ -170,6 +176,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Test the notification endpoint */
|
||||
test() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("testNotification", this.notification, (res) => {
|
||||
@@ -178,6 +185,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Delete the notification endpoint */
|
||||
deleteNotification() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("deleteNotification", this.id, (res) => {
|
||||
@@ -190,6 +198,7 @@ export default {
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Get a unique default name for the notification
|
||||
* @param {keyof NotificationFormList} notificationKey
|
||||
* @return {string}
|
||||
*/
|
||||
|
@@ -35,6 +35,7 @@ Chart.register(LineController, BarController, LineElement, PointElement, TimeSca
|
||||
export default {
|
||||
components: { LineChart },
|
||||
props: {
|
||||
/** ID of monitor */
|
||||
monitorId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@@ -130,11 +130,16 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Show dialog to confirm deletion */
|
||||
deleteConfirm() {
|
||||
this.modal.hide();
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show settings for specified proxy
|
||||
* @param {number} proxyID ID of proxy to show
|
||||
*/
|
||||
show(proxyID) {
|
||||
if (proxyID) {
|
||||
this.id = proxyID;
|
||||
@@ -163,6 +168,7 @@ export default {
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
/** Submit form data for saving */
|
||||
submit() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("addProxy", this.proxy, this.id, (res) => {
|
||||
@@ -180,6 +186,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Delete this proxy */
|
||||
deleteProxy() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("deleteProxy", this.id, (res) => {
|
||||
|
@@ -33,19 +33,19 @@
|
||||
<template #item="monitor">
|
||||
<div class="item">
|
||||
<div class="row">
|
||||
<div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap">
|
||||
<div class="info d-flex align-items-center gap-3 w-100">
|
||||
<div class="col-9 col-md-8 small-padding">
|
||||
<div class="info">
|
||||
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
|
||||
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
|
||||
|
||||
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
||||
{{ monitor.element.name }}
|
||||
</div>
|
||||
<div v-if="showTags && monitor.element.tags.length > 0" class="tags">
|
||||
<div v-if="showTags" class="tags">
|
||||
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
|
||||
</div>
|
||||
</div>
|
||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center">
|
||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
||||
<HeartbeatBar size="small" :monitor-id="monitor.element.id" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,10 +72,12 @@ export default {
|
||||
Tag,
|
||||
},
|
||||
props: {
|
||||
/** Are we in edit mode? */
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
/** Should tags be shown? */
|
||||
showTags: {
|
||||
type: Boolean,
|
||||
}
|
||||
@@ -94,10 +96,20 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Remove the specified group
|
||||
* @param {number} index Index of group to remove
|
||||
*/
|
||||
removeGroup(index) {
|
||||
this.$root.publicGroupList.splice(index, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a monitor from a group
|
||||
* @param {number} groupIndex Index of group to remove monitor
|
||||
* from
|
||||
* @param {number} index Index of monitor to remove
|
||||
*/
|
||||
removeMonitor(groupIndex, index) {
|
||||
this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1);
|
||||
},
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/** Current status of monitor */
|
||||
status: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
|
@@ -20,14 +20,20 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/** Object representing tag */
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
/** Function to remove tag */
|
||||
remove: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* Size of tag
|
||||
* @values normal, small
|
||||
*/
|
||||
size: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
|
@@ -139,6 +139,7 @@ export default {
|
||||
VueMultiselect,
|
||||
},
|
||||
props: {
|
||||
/** Array of tags to be pre-selected */
|
||||
preSelectedTags: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
@@ -241,9 +242,11 @@ export default {
|
||||
this.getExistingTags();
|
||||
},
|
||||
methods: {
|
||||
/** Show the add tag dialog */
|
||||
showAddDialog() {
|
||||
this.modal.show();
|
||||
},
|
||||
/** Get all existing tags */
|
||||
getExistingTags() {
|
||||
this.$root.getSocket().emit("getTags", (res) => {
|
||||
if (res.ok) {
|
||||
@@ -253,6 +256,10 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Delete the specified tag
|
||||
* @param {Object} tag Object representing tag to delete
|
||||
*/
|
||||
deleteTag(item) {
|
||||
if (item.new) {
|
||||
// Undo Adding a new Tag
|
||||
@@ -262,6 +269,13 @@ export default {
|
||||
this.deleteTags.push(item);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get colour of text inside the tag
|
||||
* @param {Object} option The tag that needs to be displayed.
|
||||
* Defaults to "white" unless the tag has no color, which will
|
||||
* then return the body color (based on application theme)
|
||||
* @returns string
|
||||
*/
|
||||
textColor(option) {
|
||||
if (option.color) {
|
||||
return "white";
|
||||
@@ -269,6 +283,7 @@ export default {
|
||||
return this.$root.theme === "light" ? "var(--bs-body-color)" : "inherit";
|
||||
}
|
||||
},
|
||||
/** Add a draft tag */
|
||||
addDraftTag() {
|
||||
console.log("Adding Draft Tag: ", this.newDraftTag);
|
||||
if (this.newDraftTag.select != null) {
|
||||
@@ -296,6 +311,7 @@ export default {
|
||||
}
|
||||
this.clearDraftTag();
|
||||
},
|
||||
/** Remove a draft tag */
|
||||
clearDraftTag() {
|
||||
this.newDraftTag = {
|
||||
name: null,
|
||||
@@ -307,26 +323,51 @@ export default {
|
||||
};
|
||||
this.modal.hide();
|
||||
},
|
||||
/**
|
||||
* Add a tag asynchronously
|
||||
* @param {Object} newTag Object representing new tag to add
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addTagAsync(newTag) {
|
||||
return new Promise((resolve) => {
|
||||
this.$root.getSocket().emit("addTag", newTag, resolve);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Add a tag to a monitor asynchronously
|
||||
* @param {number} tagId ID of tag to add
|
||||
* @param {number} monitorId ID of monitor to add tag to
|
||||
* @param {string} value Value of tag
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addMonitorTagAsync(tagId, monitorId, value) {
|
||||
return new Promise((resolve) => {
|
||||
this.$root.getSocket().emit("addMonitorTag", tagId, monitorId, value, resolve);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Delete a tag from a monitor asynchronously
|
||||
* @param {number} tagId ID of tag to remove
|
||||
* @param {number} monitorId ID of monitor to remove tag from
|
||||
* @param {string} value Value of tag
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
deleteMonitorTagAsync(tagId, monitorId, value) {
|
||||
return new Promise((resolve) => {
|
||||
this.$root.getSocket().emit("deleteMonitorTag", tagId, monitorId, value, resolve);
|
||||
});
|
||||
},
|
||||
/** Handle pressing Enter key when inside the modal */
|
||||
onEnter() {
|
||||
if (!this.validateDraftTag.invalid) {
|
||||
this.addDraftTag();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Submit the form data
|
||||
* @param {number} monitorId ID of monitor this change affects
|
||||
* @returns {void}
|
||||
*/
|
||||
async submit(monitorId) {
|
||||
console.log(`Submitting tag changes for monitor ${monitorId}...`);
|
||||
this.processing = true;
|
||||
|
@@ -29,10 +29,12 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/** Heading of the section */
|
||||
heading: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
/** Should the section be open by default? */
|
||||
defaultOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@@ -100,18 +100,22 @@ export default {
|
||||
this.getStatus();
|
||||
},
|
||||
methods: {
|
||||
/** Show the dialog */
|
||||
show() {
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
/** Show dialog to confirm enabling 2FA */
|
||||
confirmEnableTwoFA() {
|
||||
this.$refs.confirmEnableTwoFA.show();
|
||||
},
|
||||
|
||||
/** Show dialog to confirm disabling 2FA */
|
||||
confirmDisableTwoFA() {
|
||||
this.$refs.confirmDisableTwoFA.show();
|
||||
},
|
||||
|
||||
/** Prepare 2FA configuration */
|
||||
prepare2FA() {
|
||||
this.processing = true;
|
||||
|
||||
@@ -126,6 +130,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Save the current 2FA configuration */
|
||||
save2FA() {
|
||||
this.processing = true;
|
||||
|
||||
@@ -143,6 +148,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Disable 2FA for this user */
|
||||
disable2FA() {
|
||||
this.processing = true;
|
||||
|
||||
@@ -160,6 +166,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Verify the token generated by the user */
|
||||
verifyToken() {
|
||||
this.$root.getSocket().emit("verifyToken", this.token, this.currentPassword, (res) => {
|
||||
if (res.ok) {
|
||||
@@ -170,6 +177,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Get current status of 2FA */
|
||||
getStatus() {
|
||||
this.$root.getSocket().emit("twoFAStatus", (res) => {
|
||||
if (res.ok) {
|
||||
|
@@ -5,14 +5,17 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/** Monitor this represents */
|
||||
monitor: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
/** Type of monitor */
|
||||
type: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
/** Is this a pill? */
|
||||
pill: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
30
src/components/notifications/Ntfy.vue
Normal file
30
src/components/notifications/Ntfy.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="ntfy-ntfytopic" class="form-label">{{ $t("ntfy Topic") }}</label>
|
||||
<div class="input-group mb-3">
|
||||
<input id="ntfy-ntfytopic" v-model="$parent.notification.ntfytopic" type="text" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="ntfy-server-url" class="form-label">{{ $t("Server URL") }}</label>
|
||||
<div class="input-group mb-3">
|
||||
<input id="ntfy-server-url" v-model="$parent.notification.ntfyserverurl" type="text" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="ntfy-priority" class="form-label">{{ $t("Priority") }}</label>
|
||||
<input id="ntfy-priority" v-model="$parent.notification.ntfyPriority" type="number" class="form-control" required min="1" max="5" step="1">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
if (typeof this.$parent.notification.ntfyPriority === "undefined") {
|
||||
this.$parent.notification.ntfyserverurl = "https://ntfy.sh";
|
||||
this.$parent.notification.ntfyPriority = 5;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
45
src/components/notifications/PagerDuty.vue
Normal file
45
src/components/notifications/PagerDuty.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="pagerduty-integration-key" class="form-label">{{ $t("Integration Key") }}</label>
|
||||
<HiddenInput id="pagerduty-integration-key" v-model="$parent.notification.pagerdutyIntegrationKey" :required="true" autocomplete="false"></HiddenInput>
|
||||
<i18n-t tag="div" keypath="wayToGetPagerDutyKey" class="form-text">
|
||||
<a href="https://support.pagerduty.com/docs/services-and-integrations" target="_blank">{{ $t("here") }}</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pagerduty-integration-url" class="form-label">{{ $t("Integration URL") }}</label>
|
||||
<input id="pagerduty-integration-url" v-model="$parent.notification.pagerdutyIntegrationUrl" type="text" class="form-control" autocomplete="false">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pagerduty-priority" class="form-label">{{ $t("Priority") }}</label>
|
||||
<select id="pagerduty-priority" v-model="$parent.notification.pagerdutyPriority" class="form-select">
|
||||
<option value="info">{{ $t("info") }}</option>
|
||||
<option value="warning" selected="selected">{{ $t("warning") }}</option>
|
||||
<option value="error">{{ $t("error") }}</option>
|
||||
<option value="critical">{{ $t("critical") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pagerduty-resolve" class="form-label">{{ $t("Auto resolve or acknowledged") }}</label>
|
||||
<select id="pagerduty-resolve" v-model="$parent.notification.pagerdutyAutoResolve" class="form-select">
|
||||
<option value="0" selected="selected">{{ $t("do nothing") }}</option>
|
||||
<option value="acknowledge">{{ $t("auto acknowledged") }}</option>
|
||||
<option value="resolve">{{ $t("auto resolve") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
mounted() {
|
||||
if (typeof this.$parent.notification.pagerdutyIntegrationUrl === "undefined") {
|
||||
this.$parent.notification.pagerdutyIntegrationUrl = "https://events.pagerduty.com/v2/enqueue";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label>
|
||||
<label for="promosms-login" class="form-label">{{ $t("promosmsLogin") }}</label>
|
||||
<input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
|
||||
<label for="promosms-key" class="form-label">{{ $("promosmsPassword") }}</label>
|
||||
<label for="promosms-key" class="form-label">{{ $t("promosmsPassword") }}</label>
|
||||
<HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
|
@@ -4,6 +4,7 @@ import Discord from "./Discord.vue";
|
||||
import Webhook from "./Webhook.vue";
|
||||
import Signal from "./Signal.vue";
|
||||
import Gotify from "./Gotify.vue";
|
||||
import Ntfy from "./Ntfy.vue";
|
||||
import Slack from "./Slack.vue";
|
||||
import RocketChat from "./RocketChat.vue";
|
||||
import Teams from "./Teams.vue";
|
||||
@@ -27,6 +28,7 @@ import SerwerSMS from "./SerwerSMS.vue";
|
||||
import Stackfield from "./Stackfield.vue";
|
||||
import WeCom from "./WeCom.vue";
|
||||
import GoogleChat from "./GoogleChat.vue";
|
||||
import PagerDuty from "./PagerDuty.vue";
|
||||
import Gorush from "./Gorush.vue";
|
||||
import Alerta from "./Alerta.vue";
|
||||
import OneBot from "./OneBot.vue";
|
||||
@@ -45,6 +47,7 @@ const NotificationFormList = {
|
||||
"teams": Teams,
|
||||
"signal": Signal,
|
||||
"gotify": Gotify,
|
||||
"ntfy": Ntfy,
|
||||
"slack": Slack,
|
||||
"rocket.chat": RocketChat,
|
||||
"pushover": Pushover,
|
||||
@@ -67,6 +70,7 @@ const NotificationFormList = {
|
||||
"stackfield": Stackfield,
|
||||
"WeCom": WeCom,
|
||||
"GoogleChat": GoogleChat,
|
||||
"PagerDuty": PagerDuty,
|
||||
"gorush": Gorush,
|
||||
"alerta": Alerta,
|
||||
"OneBot": OneBot,
|
||||
|
@@ -133,10 +133,15 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Show the confimation dialog confirming the configuration
|
||||
* be imported
|
||||
*/
|
||||
confirmImport() {
|
||||
this.$refs.confirmImport.show();
|
||||
},
|
||||
|
||||
/** Download a backup of the configuration */
|
||||
downloadBackup() {
|
||||
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
|
||||
let fileName = `Uptime_Kuma_Backup_${time}.json`;
|
||||
@@ -157,6 +162,10 @@ export default {
|
||||
downloadItem.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Import the specified backup file
|
||||
* @returns {?string}
|
||||
*/
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("import-backend").files;
|
||||
|
@@ -178,10 +178,12 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Save the settings */
|
||||
saveGeneral() {
|
||||
localStorage.timezone = this.$root.userTimezone;
|
||||
this.saveSettings();
|
||||
},
|
||||
/** Get the base URL of the application */
|
||||
autoGetPrimaryBaseURL() {
|
||||
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
|
||||
},
|
||||
|
@@ -90,6 +90,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Get the current size of the database */
|
||||
loadDatabaseSize() {
|
||||
log.debug("monitorhistory", "load database size");
|
||||
this.$root.getSocket().emit("getDatabaseSize", (res) => {
|
||||
@@ -102,6 +103,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Request that the database is shrunk */
|
||||
shrinkDatabase() {
|
||||
this.$root.getSocket().emit("shrinkDatabase", (res) => {
|
||||
if (res.ok) {
|
||||
@@ -113,10 +115,12 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Show the dialog to confirm clearing stats */
|
||||
confirmClearStatistics() {
|
||||
this.$refs.confirmClearStatistics.show();
|
||||
},
|
||||
|
||||
/** Send the request to clear stats */
|
||||
clearStatistics() {
|
||||
this.$root.clearStatistics((res) => {
|
||||
if (res.ok) {
|
||||
|
@@ -20,16 +20,92 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="my-4 pt-4">
|
||||
<h5 class="my-4 settings-subheading">{{ $t("settingsCertificateExpiry") }}</h5>
|
||||
<p>{{ $t("certificationExpiryDescription") }}</p>
|
||||
<p>{{ $t("notificationDescription") }}</p>
|
||||
<div class="mt-1 mb-3 ps-2 cert-exp-days col-12 col-xl-6">
|
||||
<div v-for="day in settings.tlsExpiryNotifyDays" :key="day" class="d-flex align-items-center justify-content-between cert-exp-day-row py-2">
|
||||
<span>{{ day }} {{ $tc("day", day) }}</span>
|
||||
<button type="button" class="btn-rm-expiry btn btn-outline-danger ms-2 py-1" @click="removeExpiryNotifDay(day)">
|
||||
<font-awesome-icon class="" icon="times" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-6">
|
||||
<ActionInput v-model="expiryNotifInput" :type="'number'" :placeholder="$t('day')" :icon="'plus'" :action="() => addExpiryNotifDay(expiryNotifInput)" />
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" type="button" @click="saveSettings()">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NotificationDialog ref="notificationDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NotificationDialog from "../../components/NotificationDialog.vue";
|
||||
import ActionInput from "../ActionInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotificationDialog
|
||||
NotificationDialog,
|
||||
ActionInput,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* Variable to store the input for new certificate expiry day.
|
||||
*/
|
||||
expiryNotifInput: null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Remove a day from expiry notification days.
|
||||
* @param {number} day The day to remove.
|
||||
*/
|
||||
removeExpiryNotifDay(day) {
|
||||
this.settings.tlsExpiryNotifyDays = this.settings.tlsExpiryNotifyDays.filter(d => d !== day);
|
||||
},
|
||||
/**
|
||||
* Add a new expiry notification day.
|
||||
* Will verify:
|
||||
* - day is not null or empty string.
|
||||
* - day is a number.
|
||||
* - day is > 0.
|
||||
* - The day is not already in the list.
|
||||
* @param {number} day The day number to add.
|
||||
*/
|
||||
addExpiryNotifDay(day) {
|
||||
if (day != null && day !== "") {
|
||||
const parsedDay = parseInt(day);
|
||||
if (parsedDay != null && !isNaN(parsedDay) && parsedDay > 0) {
|
||||
if (!this.settings.tlsExpiryNotifyDays.includes(parsedDay)) {
|
||||
this.settings.tlsExpiryNotifyDays.push(parseInt(day));
|
||||
this.settings.tlsExpiryNotifyDays.sort((a, b) => a - b);
|
||||
this.expiryNotifInput = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -37,10 +113,27 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.btn-rm-expiry {
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
|
||||
.cert-exp-days .cert-exp-day-row {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
||||
|
||||
.dark & {
|
||||
border-bottom: 1px solid $dark-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.cert-exp-days .cert-exp-day-row:last-child {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
@@ -68,7 +68,9 @@
|
||||
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
|
||||
{{ $t("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.") }}
|
||||
|
||||
<div class="mt-3">
|
||||
<p class="mt-2">{{ $t("disableCloudflaredNoAuthMsg") }}</p>
|
||||
|
||||
<div v-if="!settings.disableAuth" class="mt-3">
|
||||
<label for="current-password2" class="form-label">
|
||||
{{ $t("Current Password") }}
|
||||
</label>
|
||||
@@ -108,7 +110,9 @@ export default {
|
||||
return this.$root.cloudflared;
|
||||
},
|
||||
computed: {
|
||||
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -120,14 +124,17 @@ export default {
|
||||
this.$root.getSocket().emit(prefix + "leave");
|
||||
},
|
||||
methods: {
|
||||
/** Start the Cloudflare tunnel */
|
||||
start() {
|
||||
this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken);
|
||||
},
|
||||
/** Stop the Cloudflare tunnel */
|
||||
stop() {
|
||||
this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
},
|
||||
/** Remove the token for the Cloudflare tunnel */
|
||||
removeToken() {
|
||||
this.$root.getSocket().emit(prefix + "removeToken");
|
||||
this.cloudflareTunnelToken = "";
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<button v-if="! settings.disableAuth" id="logout-btn" class="btn btn-danger ms-4 me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button>
|
||||
</p>
|
||||
|
||||
<h5 class="my-4">{{ $t("Change Password") }}</h5>
|
||||
<h5 class="my-4 settings-subheading">{{ $t("Change Password") }}</h5>
|
||||
<form class="mb-3" @submit.prevent="savePassword">
|
||||
<div class="mb-3">
|
||||
<label for="current-password" class="form-label">
|
||||
@@ -62,7 +62,7 @@
|
||||
</template>
|
||||
|
||||
<div v-if="! settings.disableAuth" class="mt-5 mb-3">
|
||||
<h5 class="my-4">
|
||||
<h5 class="my-4 settings-subheading">
|
||||
{{ $t("Two Factor Authentication") }}
|
||||
</h5>
|
||||
<div class="mb-4">
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<div class="my-4">
|
||||
<!-- Advanced -->
|
||||
<h5 class="my-4">{{ $t("Advanced") }}</h5>
|
||||
<h5 class="my-4 settings-subheading">{{ $t("Advanced") }}</h5>
|
||||
|
||||
<div class="mb-4">
|
||||
<button v-if="settings.disableAuth" id="enableAuth-btn" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button>
|
||||
@@ -90,156 +90,11 @@
|
||||
<TwoFADialog ref="TwoFADialog" />
|
||||
|
||||
<Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth">
|
||||
<template v-if="$i18n.locale === 'es-ES' ">
|
||||
<p>Seguro que deseas <strong>deshabilitar la autenticación</strong>?</p>
|
||||
<p>Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.</p>
|
||||
<p>Por favor usar con cuidado.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'pt-BR' ">
|
||||
<p>Você tem certeza que deseja <strong>desativar a autenticação</strong>?</p>
|
||||
<p>Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.</p>
|
||||
<p>Por favor, utilize isso com cautela.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'zh-HK' ">
|
||||
<p>你是否確認<strong>取消登入認証</strong>?</p>
|
||||
<p>這個功能是設計給已有<strong>第三方認証</strong>的用家,例如 Cloudflare Access。</p>
|
||||
<p>請小心使用。</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'zh-CN' ">
|
||||
<p>是否确定 <strong>取消登录验证</strong>?</p>
|
||||
<p>这是为 <strong>有第三方认证</strong> 的用户提供的功能,如 Cloudflare Access</p>
|
||||
<p>请谨慎使用!</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'zh-TW' ">
|
||||
<p>你是否要<strong>取消登入驗證</strong>?</p>
|
||||
<p>此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。</p>
|
||||
<p>請謹慎使用。</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'de-DE' ">
|
||||
<p>Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?</p>
|
||||
<p>Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.</p>
|
||||
<p>Bitte mit Vorsicht nutzen.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'sl-SI' ">
|
||||
<p>Ali ste prepričani, da želite onemogočiti <strong>avtentikacijo</strong>?</p>
|
||||
<p>Namenjen je <strong>nekomu, ki ima pred programom Uptime Kuma vklopljeno zunanje preverjanje pristnosti</strong>, na primer Cloudflare Access.</p>
|
||||
<p>Uporabljajte previdno.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'sr' ">
|
||||
<p>Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?</p>
|
||||
<p>То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.</p>
|
||||
<p>Молим Вас користите ово са пажњом.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'sr-latn' ">
|
||||
<p>Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?</p>
|
||||
<p>To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.</p>
|
||||
<p>Molim Vas koristite ovo sa pažnjom.</p>
|
||||
</template>
|
||||
|
||||
<template v-if="$i18n.locale === 'hr-HR' ">
|
||||
<p>Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?</p>
|
||||
<p>To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.</p>
|
||||
<p>Pažljivo koristite ovu opciju.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'tr-TR' ">
|
||||
<p><strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?</p>
|
||||
<p>Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.</p>
|
||||
<p>Lütfen dikkatli kullanın.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'ko-KR' ">
|
||||
<p>정말로 <strong>인증 기능을 끌까요</strong>?</p>
|
||||
<p>이 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong>을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.</p>
|
||||
<p>신중하게 사용하세요.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'pl' ">
|
||||
<p>Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?</p>
|
||||
<p>Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.</p>
|
||||
<p>Proszę używać ostrożnie.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'et-EE' ">
|
||||
<p>Kas soovid <strong>lülitada autentimise välja</strong>?</p>
|
||||
<p>Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.</p>
|
||||
<p>Palun kasuta vastutustundlikult.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'it-IT' ">
|
||||
<p><strong>Disabilitare l'autenticazione?</strong></p>
|
||||
<p><strong>Questa opzione è per chi un sistema di autenticazione gestito da terze parti</strong> messo davanti ad Uptime Kuma, ad esempio Cloudflare Access.</p>
|
||||
<p>Utilizzare con attenzione!</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'id-ID' ">
|
||||
<p>Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?</p>
|
||||
<p>Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.</p>
|
||||
<p>Gunakan dengan hati-hati.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'ru-RU' ">
|
||||
<p>Вы уверены, что хотите <strong>отключить авторизацию</strong>?</p>
|
||||
<p>Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.</p>
|
||||
<p>Пожалуйста, используйте с осторожностью.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'uk-UA' ">
|
||||
<p>Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?</p>
|
||||
<p>Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.</p>
|
||||
<p>Будь ласка, використовуйте з обережністю.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'fa' ">
|
||||
<p>آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?</p>
|
||||
<p>این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.</p>
|
||||
<p>لطفا از این امکان با دقت استفاده کنید.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'bg-BG' ">
|
||||
<p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p>
|
||||
<p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p>
|
||||
<p>Моля, използвайте с повишено внимание.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'hu' ">
|
||||
<p>Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?</p>
|
||||
<p>Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.</p>
|
||||
<p>Használja megfontoltan!</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'nb-NO' ">
|
||||
<p>Er du sikker på at du vil <strong>deaktiver autentisering</strong>?</p>
|
||||
<p>Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.</p>
|
||||
<p>Vennligst vær forsiktig.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'cs-CZ' ">
|
||||
<p>Opravdu chcete <strong>deaktivovat autentifikaci</strong>?</p>
|
||||
<p>Tato možnost je určena pro případy, kdy <strong>máte autentifikaci zajištěnou třetí stranou</strong> ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.</p>
|
||||
<p>Používejte ji prosím s rozmyslem.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'vi-VN' ">
|
||||
<p>Bạn có muốn <strong>TẮT XÁC THỰC</strong> không?</p>
|
||||
<p>Điều này rất nguy hiểm<strong>BẤT KỲ AI</strong> cũng có thể truy cập và cướp quyền điều khiển.</p>
|
||||
<p>Vui lòng <strong>cẩn thận</strong>.</p>
|
||||
</template>
|
||||
|
||||
<!-- English (en) -->
|
||||
<template v-else>
|
||||
<p>Are you sure want to <strong>disable authentication</strong>?</p>
|
||||
<p>It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.</p>
|
||||
<p>Please use this option carefully!</p>
|
||||
</template>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-html="$t('disableauth.message1')"></p>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-html="$t('disableauth.message2')"></p>
|
||||
<p>{{ $t("Please use this option carefully!") }}</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="current-password2" class="form-label">
|
||||
@@ -297,6 +152,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Check new passwords match before saving them */
|
||||
savePassword() {
|
||||
if (this.password.newPassword !== this.password.repeatNewPassword) {
|
||||
this.invalidPassword = true;
|
||||
@@ -314,6 +170,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/** Disable authentication for web app access */
|
||||
disableAuth() {
|
||||
this.settings.disableAuth = true;
|
||||
|
||||
@@ -326,6 +183,7 @@ export default {
|
||||
}, this.password.currentPassword);
|
||||
},
|
||||
|
||||
/** Enable authentication for web app access */
|
||||
enableAuth() {
|
||||
this.settings.disableAuth = false;
|
||||
this.saveSettings();
|
||||
@@ -340,15 +198,3 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
h5::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 50%;
|
||||
padding-top: 8px;
|
||||
border-bottom: 1px solid $dark-border-color;
|
||||
}
|
||||
</style>
|
||||
|
@@ -31,6 +31,7 @@ const languageList = {
|
||||
"vi-VN": "Tiếng Việt",
|
||||
"zh-TW": "繁體中文 (台灣)",
|
||||
"uk-UA": "Український",
|
||||
"th-TH": "ไทย",
|
||||
};
|
||||
|
||||
let messages = {
|
||||
|
@@ -4,8 +4,7 @@
|
||||
2. Create a language file (e.g. `zh-TW.js`). The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm
|
||||
3. Run `npm run update-language-files`. You can also use this command to check if there are new strings to translate for your language.
|
||||
4. Your language file should be filled in. You can translate now.
|
||||
5. Translate `src/components/settings/Security.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`).
|
||||
6. Add it into `languageList` constant.
|
||||
7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
|
||||
5. Add it into `languageList` constant.
|
||||
6. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.
|
||||
|
||||
If you do not have programming skills, let me know in [the issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Текущ",
|
||||
Uptime: "Достъпност",
|
||||
"Cert Exp.": "Вал. сертификат",
|
||||
days: "дни",
|
||||
day: "ден",
|
||||
day: "ден | дни",
|
||||
"-day": "-дни",
|
||||
hour: "час",
|
||||
"-hour": "-часa",
|
||||
@@ -90,13 +89,16 @@ export default {
|
||||
"Search Engine Visibility": "Видимост за търсачки",
|
||||
"Allow indexing": "Разреши индексиране",
|
||||
"Discourage search engines from indexing site": "Не позволявай на търсачките да индексират този сайт",
|
||||
"Change Password": "Промени парола",
|
||||
"Change Password": "Промяна на парола",
|
||||
"Current Password": "Текуща парола",
|
||||
"New Password": "Нова парола",
|
||||
"Repeat New Password": "Повторете новата парола",
|
||||
"Update Password": "Актуализирай парола",
|
||||
"Update Password": "Актуализирай паролата",
|
||||
"Disable Auth": "Изключи удостоверяване",
|
||||
"Enable Auth": "Включи удостоверяване",
|
||||
"disableauth.message1": "Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?",
|
||||
"disableauth.message2": "Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access, Authelia или друг механизъм за удостоверяване.",
|
||||
"Please use this option carefully!": "Моля, използвайте с повишено внимание.",
|
||||
Logout: "Изход от профила",
|
||||
Leave: "Отказ",
|
||||
"I understand, please disable": "Разбирам. Моля, изключи",
|
||||
@@ -145,7 +147,7 @@ export default {
|
||||
"Setup 2FA": "Настройка 2FA",
|
||||
"Enable 2FA": "Включи 2FA",
|
||||
"Disable 2FA": "Изключи 2FA",
|
||||
"2FA Settings": "Настройки 2FA",
|
||||
"2FA Settings": "Настройка за 2FA",
|
||||
"Two Factor Authentication": "Двуфакторно удостоверяване",
|
||||
Active: "Активно",
|
||||
Inactive: "Неактивно",
|
||||
@@ -299,7 +301,7 @@ export default {
|
||||
HeadersInvalidFormat: "Заявените хедъри не са валидни JSON: ",
|
||||
BodyInvalidFormat: "Заявеното съобщение не е валиден JSON: ",
|
||||
"Monitor History": "История на мониторите",
|
||||
clearDataOlderThan: "Ще се съхранява {0} дни.",
|
||||
clearDataOlderThan: "Ще се съхранява за {0} дни.",
|
||||
records: "записа",
|
||||
"One record": "Един запис",
|
||||
steamApiKeyDescription: "За да мониторирате Steam Gameserver се нуждаете от Steam Web-API ключ. Може да регистрирате Вашия API ключ тук: ",
|
||||
@@ -308,12 +310,12 @@ export default {
|
||||
PasswordsDoNotMatch: "Паролите не съвпадат.",
|
||||
"Current User": "Текущ потребител",
|
||||
recent: "Скорошни",
|
||||
shrinkDatabaseDescription: "Инициира \"VACUUM\" за \"SQLite\" база данни. Ако Вашата база данни е създадена след версия 1.10.0, \"AUTO_VACUUM\" функцията е активна и това действие не нужно.",
|
||||
shrinkDatabaseDescription: "Инициира \"VACUUM\" за \"SQLite\" база данни. Ако Вашата база данни е създадена след версия 1.10.0, \"AUTO_VACUUM\" функцията е активна и това действие не е нужно.",
|
||||
Done: "Готово",
|
||||
Info: "Информация",
|
||||
Security: "Сигурност",
|
||||
"Steam API Key": "Steam API ключ",
|
||||
"Shrink Database": "Редуциране база данни",
|
||||
"Shrink Database": "Редуцирай базата данни",
|
||||
"Pick a RR-Type...": "Изберете вида на ресурсния запис за мониторитане...",
|
||||
"Pick Accepted Status Codes...": "Изберете статус кодове, които да се считат за успешен отговор...",
|
||||
Default: "По подразбиране",
|
||||
@@ -422,6 +424,7 @@ export default {
|
||||
Next: "Следващ",
|
||||
"The slug is already taken. Please choose another slug.": "Този слъг вече се използва. Моля изберете друг.",
|
||||
"No Proxy": "Без прокси",
|
||||
Authentication: "Удостоверяване",
|
||||
"HTTP Basic Auth": "HTTP основно удостоверяване",
|
||||
"New Status Page": "Нова статус страница",
|
||||
"Page Not Found": "Страницата не е открита",
|
||||
@@ -464,4 +467,73 @@ export default {
|
||||
"Domain Names": "Домейни",
|
||||
signedInDisp: "Вписан като {0}",
|
||||
signedInDispDisabled: "Удостоверяването е изключено.",
|
||||
"Certificate Expiry Notification": "Известие за изтичане валидността на сертификата",
|
||||
"API Username": "API Потребител",
|
||||
"API Key": "API Ключ",
|
||||
"Recipient Number": "Номер на получателя",
|
||||
"From Name/Number": "От Име/Номер",
|
||||
"Leave blank to use a shared sender number.": "Оставете празно, за да използвате споделен номер на подател.",
|
||||
"Octopush API Version": "Octopush API версия",
|
||||
"Legacy Octopush-DM": "Octopush-DM старa версия",
|
||||
endpoint: "крайна точка",
|
||||
octopushAPIKey: "\"API ключ\" от HTTP API удостоверяване в контролния панел",
|
||||
octopushLogin: "\"Вписване\" от HTTP API удостоверяване в контролния панел",
|
||||
promosmsLogin: "API Потребителско име",
|
||||
promosmsPassword: "API Парола",
|
||||
"pushoversounds pushover": "Pushover (по подразбиране)",
|
||||
"pushoversounds bike": "Велосипед",
|
||||
"pushoversounds bugle": "Тромпет",
|
||||
"pushoversounds cashregister": "Касов апарат",
|
||||
"pushoversounds classical": "Класическа музика",
|
||||
"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: "Токен на устройство",
|
||||
"Show update if available": "Покажи актуализация, ако е налична",
|
||||
"Also check beta release": "Проверявай и за бета версии",
|
||||
"Using a Reverse Proxy?": "Използвате ревърс прокси?",
|
||||
"Check how to config it for WebSocket": "Проверете как да го конфигурирате за WebSocket",
|
||||
"Steam Game Server": "Steam Game сървър",
|
||||
"Most likely causes:": "Най-вероятни причини:",
|
||||
"The resource is no longer available.": "Ресурсът вече не е наличен.",
|
||||
"There might be a typing error in the address.": "Възможно е да е допусната грешка при изписването на адреса.",
|
||||
"What you can try:": "Може да опитате:",
|
||||
"Retype the address.": "Повторно въвеждане на адреса.",
|
||||
"Go back to the previous page.": "Да се върнете към предишната страница.",
|
||||
"Coming Soon": "Очаквайте скоро",
|
||||
wayToGetClickSendSMSToken: "Може да получите API потребителско име и API ключ от {0} .",
|
||||
dnsPortDescription: "DNS порт на сървъра. По подразбиране е 53, но може да бъде променен по всяко време.",
|
||||
error: "грешка",
|
||||
critical: "критично",
|
||||
wayToGetPagerDutyKey: "Може да го получите като посетите Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Тук трябва да потърсите \"Events API V2\". Повече информация {0}",
|
||||
"Integration Key": "Ключ за интегриране",
|
||||
"Integration URL": "URL адрес за интеграция",
|
||||
"Auto resolve or acknowledged": "Автоматично разрешаване или потвърждаване",
|
||||
"do nothing": "не прави нищо",
|
||||
"auto acknowledged": "автоматично потвърждаване",
|
||||
"auto resolve": "автоматично разрешаване",
|
||||
"Connection String": "Стринг за връзка",
|
||||
Query: "Заявка",
|
||||
settingsCertificateExpiry: "Изтичане валидността на TLS сертификата",
|
||||
certificationExpiryDescription: "HTTPS мониторите ще задействат известие, ако е наличен изтичащ TLS сертификат, през следващите:",
|
||||
"ntfy Topic": "ntfy Тема",
|
||||
Domain: "Домейн",
|
||||
Workstation: "Работна станция",
|
||||
disableCloudflaredNoAuthMsg: "Тъй като сте в режим \"No Auth mode\", парола не се изисква.",
|
||||
};
|
||||
|
@@ -56,8 +56,7 @@ export default {
|
||||
Current: "Aktuální",
|
||||
Uptime: "Doba provozu",
|
||||
"Cert Exp.": "Platnost certifikátu",
|
||||
days: "dny/í",
|
||||
day: "den",
|
||||
day: "den | dny/í",
|
||||
"-day": "-dní",
|
||||
hour: "hodina",
|
||||
"-hour": "-hodin",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "Aktualizovat heslo",
|
||||
"Disable Auth": "Deaktivovat ověřování",
|
||||
"Enable Auth": "Povolit ověřování",
|
||||
"disableauth.message1": "Opravdu chcete <strong>deaktivovat autentifikaci</strong>?",
|
||||
"disableauth.message2": "Tato možnost je určena pro případy, kdy <strong>máte autentifikaci zajištěnou třetí stranou</strong> ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.",
|
||||
"Please use this option carefully!": "Používejte ji prosím s rozmyslem.",
|
||||
Logout: "Odhlášení",
|
||||
Leave: "Odejít",
|
||||
"I understand, please disable": "Rozumím, chci ji deaktivovat",
|
||||
|
@@ -30,8 +30,7 @@ export default {
|
||||
Current: "Aktuelt",
|
||||
Uptime: "Oppetid",
|
||||
"Cert Exp.": "Certifikatets udløb",
|
||||
days: "Dage",
|
||||
day: "Dag",
|
||||
day: "Dag | Dage",
|
||||
"-day": "-Dage",
|
||||
hour: "Timer",
|
||||
"-hour": "-Timer",
|
||||
|
@@ -30,8 +30,7 @@ export default {
|
||||
Current: "Aktuell",
|
||||
Uptime: "Verfügbarkeit",
|
||||
"Cert Exp.": "Zertifikatsablauf",
|
||||
days: "Tage",
|
||||
day: "Tag",
|
||||
day: "Tag | Tage",
|
||||
"-day": "-Tage",
|
||||
hour: "Stunde",
|
||||
"-hour": "-Stunden",
|
||||
@@ -78,6 +77,9 @@ export default {
|
||||
"Update Password": "Passwort aktualisieren",
|
||||
"Disable Auth": "Authentifizierung deaktivieren",
|
||||
"Enable Auth": "Authentifizierung aktivieren",
|
||||
"disableauth.message1": "Bist du sicher das du die <strong>Authentifizierung deaktivieren</strong> möchtest?",
|
||||
"disableauth.message2": "Es ist für <strong>jemanden der eine externe Authentifizierung</strong> vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access.",
|
||||
"Please use this option carefully!": "Bitte mit Vorsicht nutzen.",
|
||||
Logout: "Ausloggen",
|
||||
notificationDescription: "Benachrichtigungen müssen einem Monitor zugewiesen werden, damit diese funktionieren.",
|
||||
Leave: "Verlassen",
|
||||
@@ -422,6 +424,7 @@ export default {
|
||||
Next: "Weiter",
|
||||
"The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.",
|
||||
"No Proxy": "Kein Proxy",
|
||||
Authentication: "Authentifizierung",
|
||||
"HTTP Basic Auth": "HTTP Basisauthentifizierung",
|
||||
"New Status Page": "Neue Status-Seite",
|
||||
"Page Not Found": "Seite nicht gefunden",
|
||||
|
@@ -13,6 +13,7 @@ export default {
|
||||
pauseDashboardHome: "Pause",
|
||||
deleteMonitorMsg: "Are you sure want to delete this monitor?",
|
||||
deleteNotificationMsg: "Are you sure want to delete this notification for all monitors?",
|
||||
dnsPortDescription: "DNS server port. Defaults to 53. You can change the port at any time.",
|
||||
resolverserverDescription: "Cloudflare is the default server. You can change the resolver server anytime.",
|
||||
rrtypeDescription: "Select the RR type you want to monitor",
|
||||
pauseMonitorMsg: "Are you sure want to pause?",
|
||||
@@ -56,8 +57,7 @@ export default {
|
||||
Current: "Current",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Cert Exp.",
|
||||
days: "days",
|
||||
day: "day",
|
||||
day: "day | days",
|
||||
"-day": "-day",
|
||||
hour: "hour",
|
||||
"-hour": "-hour",
|
||||
@@ -101,6 +101,9 @@ export default {
|
||||
"Update Password": "Update Password",
|
||||
"Disable Auth": "Disable Auth",
|
||||
"Enable Auth": "Enable Auth",
|
||||
"disableauth.message1": "Are you sure want to <strong>disable authentication</strong>?",
|
||||
"disableauth.message2": "It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.",
|
||||
"Please use this option carefully!": "Please use this option carefully!",
|
||||
Logout: "Logout",
|
||||
Leave: "Leave",
|
||||
"I understand, please disable": "I understand, please disable",
|
||||
@@ -330,6 +333,8 @@ export default {
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
danger: "danger",
|
||||
error: "error",
|
||||
critical: "critical",
|
||||
primary: "primary",
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
@@ -370,6 +375,13 @@ export default {
|
||||
smtpDkimHashAlgo: "Hash Algorithm (Optional)",
|
||||
smtpDkimheaderFieldNames: "Header Keys to sign (Optional)",
|
||||
smtpDkimskipFields: "Header Keys not to sign (Optional)",
|
||||
wayToGetPagerDutyKey: "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}",
|
||||
"Integration Key": "Integration Key",
|
||||
"Integration URL": "Integration URL",
|
||||
"Auto resolve or acknowledged": "Auto resolve or acknowledged",
|
||||
"do nothing": "do nothing",
|
||||
"auto acknowledged": "auto acknowledged",
|
||||
"auto resolve": "auto resolve",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
@@ -429,6 +441,7 @@ export default {
|
||||
Next: "Next",
|
||||
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
|
||||
"No Proxy": "No Proxy",
|
||||
Authentication: "Authentication",
|
||||
"HTTP Basic Auth": "HTTP Basic Auth",
|
||||
"New Status Page": "New Status Page",
|
||||
"Page Not Found": "Page Not Found",
|
||||
@@ -515,4 +528,12 @@ export default {
|
||||
"Go back to the previous page.": "Go back to the previous page.",
|
||||
"Coming Soon": "Coming Soon",
|
||||
wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .",
|
||||
"Connection String": "Connection String",
|
||||
"Query": "Query",
|
||||
settingsCertificateExpiry: "TLS Certificate Expiry",
|
||||
certificationExpiryDescription: "HTTPS Monitors trigger notification when TLS certificate expires in:",
|
||||
"ntfy Topic": "ntfy Topic",
|
||||
"Domain": "Domain",
|
||||
"Workstation": "Workstation",
|
||||
disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
|
||||
};
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Actual",
|
||||
Uptime: "Tiempo activo",
|
||||
"Cert Exp.": "Caducidad cert.",
|
||||
days: "días",
|
||||
day: "día",
|
||||
day: "día | días",
|
||||
"-day": "-día",
|
||||
hour: "hora",
|
||||
"-hour": "-hora",
|
||||
@@ -85,6 +84,9 @@ export default {
|
||||
"Update Password": "Actualizar contraseña",
|
||||
"Disable Auth": "Deshabilitar Autenticación",
|
||||
"Enable Auth": "Habilitar Autenticación",
|
||||
"disableauth.message1": "Seguro que deseas <strong>deshabilitar la autenticación</strong>?",
|
||||
"disableauth.message2": "Es para <strong>quien implementa autenticación de terceros</strong> ante Uptime Kuma como por ejemplo Cloudflare Access.",
|
||||
"Please use this option carefully!": "Por favor usar con cuidado.",
|
||||
Logout: "Cerrar sesión",
|
||||
Leave: "Salir",
|
||||
"I understand, please disable": "Entiendo, por favor deshabilitar",
|
||||
|
@@ -47,8 +47,7 @@ export default {
|
||||
Current: "Hetkeseisund",
|
||||
Uptime: "Eluiga",
|
||||
"Cert Exp.": "Sert. aegumine",
|
||||
days: "päeva",
|
||||
day: "päev",
|
||||
day: "päev | päeva",
|
||||
"-day": "-päev",
|
||||
hour: "tund",
|
||||
"-hour": "-tund",
|
||||
@@ -88,6 +87,9 @@ export default {
|
||||
"Update Password": "Uuenda salasõna",
|
||||
"Disable Auth": "Lülita autentimine välja",
|
||||
"Enable Auth": "Lülita autentimine sisse",
|
||||
"disableauth.message1": "Kas soovid <strong>lülitada autentimise välja</strong>?",
|
||||
"disableauth.message2": "Kastuamiseks <strong>välise autentimispakkujaga</strong>, näiteks Cloudflare Access.",
|
||||
"Please use this option carefully!": "Palun kasuta vastutustundlikult.",
|
||||
Logout: "Logi välja",
|
||||
Leave: "Lahku",
|
||||
"I understand, please disable": "Olen tutvunud riskidega, lülita välja",
|
||||
|
@@ -55,7 +55,6 @@ export default {
|
||||
Current: "فعلی",
|
||||
Uptime: "آپتایم",
|
||||
"Cert Exp.": "تاریخ انقضای SSL",
|
||||
days: "روز",
|
||||
day: "روز",
|
||||
"-day": "-روز",
|
||||
hour: "ساعت",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "بروز رسانی رمز عبور",
|
||||
"Disable Auth": "غیر فعال سازی تایید هویت",
|
||||
"Enable Auth": "فعال سازی تایید هویت",
|
||||
"disableauth.message1": "آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?",
|
||||
"disableauth.message2": "این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.",
|
||||
"Please use this option carefully!": "لطفا از این امکان با دقت استفاده کنید.",
|
||||
Logout: "خروج",
|
||||
Leave: "منصرف شدم",
|
||||
"I understand, please disable": "متوجه هستم، لطفا غیرفعال کنید!",
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Actuellement",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Expiration SSL",
|
||||
days: "jours",
|
||||
day: "jour",
|
||||
day: "jour | jours",
|
||||
"-day": "-jours",
|
||||
hour: "-heure",
|
||||
"-hour": "-heures",
|
||||
|
@@ -56,8 +56,7 @@ export default {
|
||||
Current: "Trenutno",
|
||||
Uptime: "Dostupnost",
|
||||
"Cert Exp.": "Istek cert.",
|
||||
days: "dana",
|
||||
day: "dan",
|
||||
day: "dan | dana",
|
||||
"-day": "-dnevno",
|
||||
hour: "sat",
|
||||
"-hour": "-satno",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "Spremi novu lozinku",
|
||||
"Disable Auth": "Onemogući autentikaciju",
|
||||
"Enable Auth": "Omogući autentikaciju",
|
||||
"disableauth.message1": "Jeste li sigurni da želite <strong>isključiti autentikaciju</strong>?",
|
||||
"disableauth.message2": "To je za <strong>korisnike koji imaju vanjsku autentikaciju stranice</strong> ispred Uptime Kume, poput usluge Cloudflare Access.",
|
||||
"Please use this option carefully!": "Pažljivo koristite ovu opciju.",
|
||||
Logout: "Odjava",
|
||||
Leave: "Poništi",
|
||||
"I understand, please disable": "Razumijem, svejedno onemogući",
|
||||
|
@@ -55,7 +55,6 @@ export default {
|
||||
Current: "Aktuális",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "SSL lejárat",
|
||||
days: "nap",
|
||||
day: "nap",
|
||||
"-day": " nap",
|
||||
hour: "óra",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "Jelszó módosítása",
|
||||
"Disable Auth": "Hitelesítés tiltása",
|
||||
"Enable Auth": "Hitelesítés engedélyezése",
|
||||
"disableauth.message1": "Biztos benne, hogy <strong>kikapcsolja a hitelesítést</strong>?",
|
||||
"disableauth.message2": "Akkor érdemes, ha <strong>van 3rd-party hitelesítés</strong> az Uptime Kuma-t megelőzően mint a Cloudflare Access.",
|
||||
"Please use this option carefully!": "Használja megfontoltan!",
|
||||
Logout: "Kijelentkezés",
|
||||
Leave: "Elhagy",
|
||||
"I understand, please disable": "Megértettem, kérem tiltsa le",
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Saat ini",
|
||||
Uptime: "Waktu aktif",
|
||||
"Cert Exp.": "Cert Exp.",
|
||||
days: "hari-hari",
|
||||
day: "hari",
|
||||
day: "hari | hari-hari",
|
||||
"-day": "-hari",
|
||||
hour: "Jam",
|
||||
"-hour": "-Jam",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "Perbarui Kata Sandi",
|
||||
"Disable Auth": "Nonaktifkan Autentikasi",
|
||||
"Enable Auth": "Aktifkan Autentikasi",
|
||||
"disableauth.message1": "Apakah Anda yakin ingin <strong>menonaktifkan autentikasi</strong>?",
|
||||
"disableauth.message2": "Ini untuk <strong>mereka yang memiliki autentikasi pihak ketiga</strong> diletakkan di depan Uptime Kuma, misalnya akses Cloudflare.",
|
||||
"Please use this option carefully!": "Gunakan dengan hati-hati.",
|
||||
Logout: "Keluar",
|
||||
Leave: "Pergi",
|
||||
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan",
|
||||
|
@@ -56,8 +56,7 @@ export default {
|
||||
Current: "Corrente",
|
||||
Uptime: "Tempo di attività",
|
||||
"Cert Exp.": "Scadenza certificato",
|
||||
days: "giorni",
|
||||
day: "giorno",
|
||||
day: "giorno | giorni",
|
||||
"-day": "-giorni",
|
||||
hour: "ora",
|
||||
"-hour": "-ore",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "Modifica password",
|
||||
"Disable Auth": "Disabilita autenticazione",
|
||||
"Enable Auth": "Abilita autenticazione",
|
||||
"disableauth.message1": "<strong>Disabilitare l'autenticazione?</strong>",
|
||||
"disableauth.message2": "<strong>Questa opzione è per chi un sistema di autenticazione gestito da terze parti</strong> messo davanti ad Uptime Kuma, ad esempio Cloudflare Access.",
|
||||
"Please use this option carefully!": "Utilizzare con attenzione!",
|
||||
Logout: "Esci",
|
||||
Leave: "Annulla",
|
||||
"I understand, please disable": "Lo capisco, disabilitare l'autenticazione.",
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "現在",
|
||||
Uptime: "起動時間",
|
||||
"Cert Exp.": "証明書有効期限",
|
||||
days: "日間",
|
||||
day: "日",
|
||||
day: "日 | 日間",
|
||||
"-day": "-日",
|
||||
hour: "時間",
|
||||
"-hour": "-時間",
|
||||
|
@@ -55,7 +55,6 @@ export default {
|
||||
Current: "현재",
|
||||
Uptime: "업타임",
|
||||
"Cert Exp.": "인증서 만료",
|
||||
days: "일",
|
||||
day: "일",
|
||||
"-day": "-일",
|
||||
hour: "시간",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "비밀번호 변경",
|
||||
"Disable Auth": "인증 비활성화",
|
||||
"Enable Auth": "인증 활성화",
|
||||
"disableauth.message1": "정말로 <strong>인증 기능을 끌까요</strong>?",
|
||||
"disableauth.message2": "이 기능은 <strong>Cloudflare Access와 같은 서드파티 인증</strong>을 Uptime Kuma 앞에 둔 사용자를 위한 기능이에요.",
|
||||
"Please use this option carefully!": "신중하게 사용하세요.",
|
||||
Logout: "로그아웃",
|
||||
Leave: "나가기",
|
||||
"I understand, please disable": "기능에 대해 이해했으니 꺼주세요.",
|
||||
@@ -187,9 +189,9 @@ export default {
|
||||
"Bot Token": "봇 토큰",
|
||||
wayToGetTelegramToken: "토큰은 여기서 얻을 수 있어요: {0}.",
|
||||
"Chat ID": "채팅 ID",
|
||||
supportTelegramChatID: "Direct Chat / Group / Channel's Chat ID를 지원해요.",
|
||||
supportTelegramChatID: "개인 채팅 / 그룹 / 채널의 ID를 지원해요.",
|
||||
wayToGetTelegramChatID: "봇에 메시지를 보내 채팅 ID를 얻고 밑에 URL로 이동해 chat_id를 볼 수 있어요.",
|
||||
"YOUR BOT TOKEN HERE": "여기에 BOT 토큰을 적어주세요.",
|
||||
"YOUR BOT TOKEN HERE": "봇 토큰",
|
||||
chatIDNotFound: "채팅 ID를 찾을 수 없어요. 먼저 봇에게 메시지를 보내주세요.",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
@@ -305,13 +307,13 @@ export default {
|
||||
PasswordsDoNotMatch: "비밀번호가 일치하지 않아요.",
|
||||
records: "records",
|
||||
"One record": "One record",
|
||||
steamApiKeyDescription: "스팀 게임 서버를 모니터링하려면 Steam Web API 키가 필요해요. API 키는 하단 사이트에서 등록할 수 있어요: ",
|
||||
steamApiKeyDescription: "스팀 게임 서버를 모니터링하려면 Steam Web API 키가 필요해요. API 키는 하단 웹사이트에서 등록할 수 있어요: ",
|
||||
"Current User": "현재 사용자",
|
||||
recent: "최근",
|
||||
Done: "완료",
|
||||
Info: "정보",
|
||||
Security: "보안",
|
||||
"Steam API Key": "Steam API Key",
|
||||
"Steam API Key": "스팀 API 키",
|
||||
"Shrink Database": "데이터베이스 축소",
|
||||
"Pick a RR-Type...": "RR-Type을 골라주세요...",
|
||||
"Pick Accepted Status Codes...": "상태 코드를 골라주세요...",
|
||||
@@ -352,4 +354,178 @@ export default {
|
||||
serwersmsPhoneNumber: "휴대전화 번호",
|
||||
serwersmsSenderName: "보내는 사람 이름 (customer portal를 통해 가입된 정보)",
|
||||
stackfield: "Stackfield",
|
||||
dnsPortDescription: "DNS 서버 포트, 기본값은 53 이에요. 포트는 언제나 변경할 수 있어요.",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
GoogleChat: "Google Chat (Google Workspace only)",
|
||||
topic: "Topic",
|
||||
topicExplanation: "모니터링할 MQTT Topic",
|
||||
successMessage: "성공 메시지",
|
||||
successMessageExplanation: "성공으로 간주되는 MQTT 메시지",
|
||||
error: "error",
|
||||
critical: "critical",
|
||||
Customize: "커스터마이즈",
|
||||
"Custom Footer": "커스텀 Footer",
|
||||
"Custom CSS": "커스텀 CSS",
|
||||
smtpDkimSettings: "DKIM 설정",
|
||||
smtpDkimDesc: "사용 방법은 DKIM {0}를 참조하세요.",
|
||||
documentation: "문서",
|
||||
smtpDkimDomain: "도메인 이름",
|
||||
smtpDkimKeySelector: "Key Selector",
|
||||
smtpDkimPrivateKey: "Private Key",
|
||||
smtpDkimHashAlgo: "해시 알고리즘 (선택)",
|
||||
smtpDkimheaderFieldNames: "서명할 헤더 키 (선택)",
|
||||
smtpDkimskipFields: "서명하지 않을 헤더 키 (선택)",
|
||||
wayToGetPagerDutyKey: "Service -> Service Directory -> (서비스 선택) -> Integrations -> Add integration. 에서 찾을 수 있어요. 자세히 알아보려면 {0}에서 \"Events API V2\"를 검색해봐요.",
|
||||
"Integration Key": "Integration 키",
|
||||
"Integration URL": "Integration URL",
|
||||
"Auto resolve or acknowledged": "자동 해결 혹은 승인",
|
||||
"do nothing": "아무것도 하지 않기",
|
||||
"auto acknowledged": "자동 승인 (acknowledged)",
|
||||
"auto resolve": "자동 해결 (resolve)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "환경변수",
|
||||
alertaApiKey: "API 키",
|
||||
alertaAlertState: "경고 상태",
|
||||
alertaRecoverState: "해결된 상태",
|
||||
deleteStatusPageMsg: "정말 이 상태 페이지를 삭제할까요?",
|
||||
Proxies: "프록시",
|
||||
default: "Default",
|
||||
enabled: "활성화",
|
||||
setAsDefault: "기본 프록시로 설정",
|
||||
deleteProxyMsg: "정말 이 프록시를 모든 모니터링에서 삭제할까요?",
|
||||
proxyDescription: "프록시가 작동하려면 모니터에 할당되어야 해요.",
|
||||
enableProxyDescription: "이 프록시는 활성화될 때까지 영향을 미치지 않아요. 활성화 상태에 따라 모든 모니터에서 프록시를 일시정지할 수 있어요.",
|
||||
setAsDefaultProxyDescription: "새로 추가하는 모든 모니터링에 이 프록시를 기본적으로 활성화해요. 각 모니터에 대해 별도로 프록시를 비활성화할 수 있어요.",
|
||||
"Certificate Chain": "인증서 체인",
|
||||
Valid: "유효",
|
||||
Invalid: "유효하지 않음",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
PhoneNumbers: "휴대전화 번호",
|
||||
TemplateCode: "템플릿 코드",
|
||||
SignName: "SignName",
|
||||
"Sms template must contain parameters: ": "Sms 템플릿은 다음과 같은 파라미터가 포함되어야 해요:",
|
||||
"Bark Endpoint": "Bark Endpoint",
|
||||
WebHookUrl: "웹훅 URL",
|
||||
SecretKey: "Secret Key",
|
||||
"For safety, must use secret key": "안전을 위해 꼭 Secret Key를 사용하세요.",
|
||||
"Device Token": "기기 Token",
|
||||
Platform: "플랫폼",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "High",
|
||||
Retry: "재시도",
|
||||
Topic: "Topic",
|
||||
"WeCom Bot Key": "WeCom Bot Key",
|
||||
"Setup Proxy": "프록시 설정",
|
||||
"Proxy Protocol": "프록시 프로토콜",
|
||||
"Proxy Server": "프록시 서버",
|
||||
"Proxy server has authentication": "프록시 서버에 인증 절차가 있음",
|
||||
User: "사용자",
|
||||
Installed: "설치됨",
|
||||
"Not installed": "설치되어 있지 않음",
|
||||
Running: "작동 중",
|
||||
"Not running": "작동하고 있지 않음",
|
||||
"Remove Token": "토큰 삭제",
|
||||
Start: "시작",
|
||||
Stop: "정지",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "새로운 상태 페이지 만들기",
|
||||
Slug: "주소",
|
||||
"Accept characters:": "허용되는 문자열:",
|
||||
startOrEndWithOnly: "{0}로 시작하거나 끝나야 해요.",
|
||||
"No consecutive dashes": "연속되는 대시는 허용되지 않아요",
|
||||
Next: "다음",
|
||||
"The slug is already taken. Please choose another slug.": "이미 존재하는 주소에요. 다른 주소를 사용해 주세요.",
|
||||
"No Proxy": "프록시 없음",
|
||||
Authentication: "인증",
|
||||
"HTTP Basic Auth": "HTTP 인증",
|
||||
"New Status Page": "새로운 상태 페이지",
|
||||
"Page Not Found": "페이지를 찾을 수 없어요",
|
||||
"Reverse Proxy": "리버스 프록시",
|
||||
Backup: "백업",
|
||||
About: "정보",
|
||||
wayToGetCloudflaredURL: "({0}에서 Cloudflare 다운로드 하기)",
|
||||
cloudflareWebsite: "Cloudflare 웹사이트",
|
||||
"Message:": "메시지:",
|
||||
"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 Tunnel를 연결하면 현재 연결이 끊길 수 있어요. 정말 중지할까요? 비밀번호를 입력해 확인하세요.",
|
||||
"Other Software": "다른 소프트웨어",
|
||||
"For example: nginx, Apache and Traefik.": "nginx, Apache, Traefik 등을 사용할 수 있어요.",
|
||||
"Please read": "이 문서를 참조하세요:",
|
||||
"Subject:": "Subject:",
|
||||
"Valid To:": "Valid To:",
|
||||
"Days Remaining:": "남은 일수:",
|
||||
"Issuer:": "Issuer:",
|
||||
"Fingerprint:": "Fingerprint:",
|
||||
"No status pages": "상태 페이지 없음",
|
||||
"Domain Name Expiry Notification": "도메인 이름 만료 알림",
|
||||
Proxy: "프록시",
|
||||
"Date Created": "생성된 날짜",
|
||||
onebotHttpAddress: "OneBot HTTP 주소",
|
||||
onebotMessageType: "OneBot 메시지 종류",
|
||||
onebotGroupMessage: "그룹 메시지",
|
||||
onebotPrivateMessage: "개인 메시지",
|
||||
onebotUserOrGroupId: "그룹/사용자 ID",
|
||||
onebotSafetyTips: "안전을 위해 Access 토큰을 설정하세요.",
|
||||
"PushDeer Key": "PushDeer 키",
|
||||
"Footer Text": "Footer 문구",
|
||||
"Show Powered By": "Powered By 문구 표시하기",
|
||||
"Domain Names": "도메인 이름",
|
||||
signedInDisp: "{0} 로그인됨",
|
||||
signedInDispDisabled: "인증 비활성화됨.",
|
||||
"Certificate Expiry Notification": "인증서 만료 알림",
|
||||
"API Username": "API 사용자 이름",
|
||||
"API Key": "API 키",
|
||||
"Recipient Number": "받는 사람 번호",
|
||||
"From Name/Number": "발신자 이름/번호",
|
||||
"Leave blank to use a shared sender number.": "공유 발신자 번호를 사용하려면 공백으로 두세요.",
|
||||
"Octopush API Version": "Octopush API 버전",
|
||||
"Legacy Octopush-DM": "레거시 Octopush-DM",
|
||||
endpoint: "endpoint",
|
||||
octopushAPIKey: "제어판 HTTP API credentials 에서 \"API key\"",
|
||||
octopushLogin: "제어판 HTTP API credentials 에서 \"Login\"",
|
||||
promosmsLogin: "API 로그인 이름",
|
||||
promosmsPassword: "API 비밀번호",
|
||||
"pushoversounds pushover": "Pushover (기본)",
|
||||
"pushoversounds bike": "Bike",
|
||||
"pushoversounds bugle": "Bugle",
|
||||
"pushoversounds cashregister": "Cash Register",
|
||||
"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": "진동만",
|
||||
"pushoversounds none": "없음 (무음)",
|
||||
pushyAPIKey: "비밀 API 키",
|
||||
pushyToken: "기기 토큰",
|
||||
"Show update if available": "사용 가능한 경우에 업데이트 표시",
|
||||
"Also check beta release": "베타 릴리즈 확인",
|
||||
"Using a Reverse Proxy?": "리버스 프록시를 사용하시나요?",
|
||||
"Check how to config it for WebSocket": "웹소켓에 대한 설정 방법 확인",
|
||||
"Steam Game Server": "스팀 게임 서버",
|
||||
"Most likely causes:": "원인:",
|
||||
"The resource is no longer available.": "더이상 사용할 수 없어요.",
|
||||
"There might be a typing error in the address.": "주소에 오탈자가 있을 수 있어요.",
|
||||
"What you can try:": "해결 방법:",
|
||||
"Retype the address.": "주소 다시 입력하기",
|
||||
"Go back to the previous page.": "이전 페이지로 돌아가기",
|
||||
"Coming Soon": "Coming Soon",
|
||||
wayToGetClickSendSMSToken: "{0}에서 API 사용자 이름과 키를 얻을 수 있어요.",
|
||||
};
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Nåværende",
|
||||
Uptime: "Oppetid",
|
||||
"Cert Exp.": "Sertifikat utløper",
|
||||
days: "dager",
|
||||
day: "dag",
|
||||
day: "dag | dager",
|
||||
"-day": "-dag",
|
||||
hour: "time",
|
||||
"-hour": "-time",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "Oppdater passord",
|
||||
"Disable Auth": "Deaktiver autentisering",
|
||||
"Enable Auth": "Aktiver autentisering",
|
||||
"disableauth.message1": "Er du sikker på at du vil <strong>deaktiver autentisering</strong>?",
|
||||
"disableauth.message2": "Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.",
|
||||
"Please use this option carefully!": "Vennligst vær forsiktig.",
|
||||
Logout: "Logg ut",
|
||||
Leave: "Forlat",
|
||||
"I understand, please disable": "Jeg forstår, vennligst deaktiver",
|
||||
|
@@ -52,8 +52,7 @@ export default {
|
||||
Current: "Huidig",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Cert. verl.",
|
||||
days: "dagen",
|
||||
day: "dag",
|
||||
day: "dag | dagen",
|
||||
"-day": "-dag",
|
||||
hour: "uur",
|
||||
"-hour": "-uur",
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Aktualny",
|
||||
Uptime: "Czas pracy",
|
||||
"Cert Exp.": "Certyfikat wygasa",
|
||||
days: "dni",
|
||||
day: "dzień",
|
||||
day: "dzień | dni",
|
||||
"-day": " dni",
|
||||
hour: "godzina",
|
||||
"-hour": " godzin",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "Zaktualizuj hasło",
|
||||
"Disable Auth": "Wyłącz autoryzację",
|
||||
"Enable Auth": "Włącz autoryzację",
|
||||
"disableauth.message1": "Czy na pewno chcesz <strong>wyłączyć autoryzację</strong>?",
|
||||
"disableauth.message2": "Jest przeznaczony dla <strong>kogoś, kto ma autoryzację zewnętrzną</strong> przed Uptime Kuma, taką jak Cloudflare Access.",
|
||||
"Please use this option carefully!": "Proszę używać ostrożnie.",
|
||||
Logout: "Wyloguj",
|
||||
Leave: "Zostaw",
|
||||
"I understand, please disable": "Rozumiem, proszę wyłączyć",
|
||||
@@ -429,6 +431,7 @@ export default {
|
||||
Next: "Dalej",
|
||||
"The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.",
|
||||
"No Proxy": "Bez proxy",
|
||||
Authentication: "Uwierzytelnianie",
|
||||
"HTTP Basic Auth": "Podstawowa autoryzacja HTTP",
|
||||
"New Status Page": "Nowa strona statusu",
|
||||
"Page Not Found": "Strona nie została znaleziona",
|
||||
|
@@ -55,8 +55,7 @@ export default {
|
||||
Current: "Atual",
|
||||
Uptime: "Tempo de atividade",
|
||||
"Cert Exp.": "Cert Exp.",
|
||||
days: "dias",
|
||||
day: "dia",
|
||||
day: "dia | dias",
|
||||
"-day": "-dia",
|
||||
hour: "hora",
|
||||
"-hour": "-hora",
|
||||
@@ -97,6 +96,9 @@ export default {
|
||||
"Update Password": "Atualizar Senha",
|
||||
"Disable Auth": "Desativar Autenticação",
|
||||
"Enable Auth": "Ativar Autenticação",
|
||||
"disableauth.message1": "Você tem certeza que deseja <strong>desativar a autenticação</strong>?",
|
||||
"disableauth.message2": "Isso é para <strong>alguém que tem autenticação de terceiros</strong> na frente do 'UpTime Kuma' como o Cloudflare Access.",
|
||||
"Please use this option carefully!": "Por favor, utilize isso com cautela.",
|
||||
Logout: "Deslogar",
|
||||
Leave: "Sair",
|
||||
"I understand, please disable": "Eu entendo, por favor desative.",
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Текущий",
|
||||
Uptime: "Аптайм",
|
||||
"Cert Exp.": "Сертификат истекает",
|
||||
days: "дней",
|
||||
day: "день",
|
||||
day: "день | дней",
|
||||
"-day": " дней",
|
||||
hour: "час",
|
||||
"-hour": " часа",
|
||||
@@ -85,6 +84,9 @@ export default {
|
||||
"Update Password": "Обновить пароль",
|
||||
"Disable Auth": "Отключить авторизацию",
|
||||
"Enable Auth": "Включить авторизацию",
|
||||
"disableauth.message1": "Вы уверены, что хотите <strong>отключить авторизацию</strong>?",
|
||||
"disableauth.message2": "Это подходит для <strong>тех, у кого стоит другая авторизация</strong> перед открытием Uptime Kuma, например Cloudflare Access.",
|
||||
"Please use this option carefully!": "Пожалуйста, используйте с осторожностью.",
|
||||
Logout: "Выйти",
|
||||
Leave: "Отмена",
|
||||
"I understand, please disable": "Я понимаю, всё равно отключить",
|
||||
@@ -352,7 +354,8 @@ export default {
|
||||
"Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9",
|
||||
"No consecutive dashes --": "Запрещено использовать тире --",
|
||||
"HTTP Options": "HTTP Опции",
|
||||
"Basic Auth": "HTTP Авторизация",
|
||||
Authentication: "Аутентификация",
|
||||
"HTTP Basic Auth": "HTTP Авторизация",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (только Google Workspace)",
|
||||
|
@@ -56,8 +56,7 @@ export default {
|
||||
Current: "Trenutno",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Potek certifikata",
|
||||
days: "dni",
|
||||
day: "dan",
|
||||
day: "dan | dni",
|
||||
"-day": "-dni",
|
||||
hour: "ura",
|
||||
"-hour": "-ur",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "Posodobi geslo",
|
||||
"Disable Auth": "Onemogoči auth",
|
||||
"Enable Auth": "Omogoči auth",
|
||||
"disableauth.message1": "Ali ste prepričani, da želite onemogočiti <strong>avtentikacijo</strong>?",
|
||||
"disableauth.message2": "Namenjen je <strong>nekomu, ki ima pred programom Uptime Kuma vklopljeno zunanje preverjanje pristnosti</strong>, na primer Cloudflare Access.",
|
||||
"Please use this option carefully!": "Uporabljajte previdno.",
|
||||
Logout: "Odjava",
|
||||
Leave: "Zapusti",
|
||||
"I understand, please disable": "Razumem, prosim onemogočite",
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Trenutno",
|
||||
Uptime: "Vreme rada",
|
||||
"Cert Exp.": "Istek sert.",
|
||||
days: "dana",
|
||||
day: "dan",
|
||||
day: "dan | dana",
|
||||
"-day": "-dana",
|
||||
hour: "sat",
|
||||
"-hour": "-sata",
|
||||
@@ -85,6 +84,9 @@ export default {
|
||||
"Update Password": "Izmeni lozinku",
|
||||
"Disable Auth": "Isključi autentifikaciju",
|
||||
"Enable Auth": "Uključi autentifikaciju",
|
||||
"disableauth.message1": "Da li ste sigurni da želite da <strong>isključite autentifikaciju</strong>?",
|
||||
"disableauth.message2": "To je za <strong>one koji imaju dodatu autentifikaciju</strong> ispred Uptime Kuma kao na primer Cloudflare Access.",
|
||||
"Please use this option carefully!": "Molim Vas koristite ovo sa pažnjom.",
|
||||
Logout: "Odloguj se",
|
||||
Leave: "Izađi",
|
||||
"I understand, please disable": "Razumem, molim te isključi",
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Тренутно",
|
||||
Uptime: "Време рада",
|
||||
"Cert Exp.": "Истек серт.",
|
||||
days: "дана",
|
||||
day: "дан",
|
||||
day: "дан | дана",
|
||||
"-day": "-дана",
|
||||
hour: "сат",
|
||||
"-hour": "-сата",
|
||||
@@ -85,6 +84,9 @@ export default {
|
||||
"Update Password": "Измени лозинку",
|
||||
"Disable Auth": "Искључи аутентификацију",
|
||||
"Enable Auth": "Укључи аутентификацију",
|
||||
"disableauth.message1": "Да ли сте сигурни да желите да <strong>искључите аутентификацију</strong>?",
|
||||
"disableauth.message2": "То је за <strong>оне који имају додату аутентификацију</strong> испред Uptime Kuma као на пример Cloudflare Access.",
|
||||
"Please use this option carefully!": "Молим Вас користите ово са пажњом.",
|
||||
Logout: "Одлогуј се",
|
||||
Leave: "Изађи",
|
||||
"I understand, please disable": "Разумем, молим те искључи",
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Nuvarande",
|
||||
Uptime: "Drifttid",
|
||||
"Cert Exp.": "Certifikat utgår",
|
||||
days: "dagar",
|
||||
day: "dag",
|
||||
day: "dag | dagar",
|
||||
"-day": " dagar",
|
||||
hour: "timme",
|
||||
"-hour": " timmar",
|
||||
|
521
src/languages/th-TH.js
Normal file
521
src/languages/th-TH.js
Normal file
@@ -0,0 +1,521 @@
|
||||
export default {
|
||||
languageName: "ไทย",
|
||||
checkEverySecond: "ตรวจสอบทุก {0} วินาที",
|
||||
retryCheckEverySecond: "ลองใหม่ทุก {0} วินาที",
|
||||
retriesDescription: "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน",
|
||||
ignoreTLSError: "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS",
|
||||
upsideDownModeDescription: "กลับด้านสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้",
|
||||
maxRedirectDescription: "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตั่งเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง",
|
||||
acceptedStatusCodesDescription: "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ",
|
||||
passwordNotMatchMsg: "รหัสผ่านไม่ตรงกัน",
|
||||
notificationDescription: "การแจ้งเตือนต้องกำหนดให้มอนิเตอร์เพื่อให้สามารถใช้งานได้",
|
||||
keywordDescription: "ค้นหาคำสำคัญใน HTML หรือ JSON ของการตอบกลับ, คำสำคัญต้องคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่",
|
||||
pauseDashboardHome: "หยุดชั่วคราว",
|
||||
deleteMonitorMsg: "คุณแน่ใจหรือไม่ที่จะลบมอนิเตอร์?",
|
||||
deleteNotificationMsg: "คุณแน่ใจหรือไม่ที่จะลบการแจ้งเตือนสำหรับมอนิเตอร์ทั้งหมด?",
|
||||
resolverserverDescription: "Cloudflare เป็นเซิร์ฟเวอร์ค้นหาเริ่มต้น, คุณสามารถเปลี่ยนเซิร์ฟเวอร์ได้ตลอดเวลา",
|
||||
rrtypeDescription: "เลือกประเภท DNS Record ที่คุณต้องการจะมอนิเตอร์",
|
||||
pauseMonitorMsg: "คุณแน่ใจหรือไม่ที่จะหยุดมอนิเตอร์ชั่วคราว?",
|
||||
enableDefaultNotificationDescription: "การแจ้งเตือนนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้",
|
||||
clearEventsMsg: "คุณแน่ใจหรือไม่ที่จะลบเหตุการณ์ทั้งหมดสำหรับมอนิเตอร์นี้?",
|
||||
clearHeartbeatsMsg: "คุณแน่ใจหรือไม่ที่จะลบประวัติการตรวจสอบทั้งหมดสำหรับมอนิเตอร์นี้?",
|
||||
confirmClearStatisticsMsg: "คุณแน่ใจหรือไม่ที่จะลบสถิติทั้งหมด?",
|
||||
importHandleDescription: "เลือก \"ข้ามรายการที่มีอยู่แล้ว\" ถ้าคุณต้องการข้ามทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน, \"เขียนทับ\" จะลบทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน",
|
||||
confirmImportMsg: "คุณแน่ใจหรือไม่ที่จะนำเข้าข้อมูลสำรอง, กรุณาตรวจสอบว่าคุณเลือกข้อมูลที่ถูกต้อง",
|
||||
twoFAVerifyLabel: "โปรดกรอกกุญแจ 2FA ของคุณเพื่อยืนยัน:",
|
||||
tokenValidSettingsMsg: "กุญแจถูกต้อง, ตอนนี้คุณสามารถบันทึกการตั้งค่า 2FA ของคุณได้แล้ว",
|
||||
confirmEnableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะเปิดใช้งาน 2FA?",
|
||||
confirmDisableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะปิดใช้งาน 2FA?",
|
||||
Settings: "การตั้งค่า",
|
||||
Dashboard: "แผงควบคุม",
|
||||
"New Update": "อัพเดทใหม่",
|
||||
Language: "ภาษา",
|
||||
Appearance: "รูปร่าง",
|
||||
Theme: "หน้าตา",
|
||||
General: "ทั่วไป",
|
||||
"Primary Base URL": "URL หลัก",
|
||||
Version: "เวอร์ชั่น",
|
||||
"Check Update On GitHub": "ตรวจสอบการอัปเดตบน GitHub",
|
||||
List: "รายการ",
|
||||
Add: "เพิ่ม",
|
||||
"Add New Monitor": "เพิ่มมอนิเตอร์ใหม่",
|
||||
"Quick Stats": "สถิติด่วน",
|
||||
Up: "ใช้งานได้",
|
||||
Down: "ไม่สามารถใช้งานได้",
|
||||
Pending: "รอดำเนินการ",
|
||||
Unknown: "ไม่ทราบ",
|
||||
Pause: "หยุดชั่วคราว",
|
||||
Name: "ชื่อ",
|
||||
Status: "สถานะ",
|
||||
DateTime: "วันที่และเวลา",
|
||||
Message: "ข้อความ",
|
||||
"No important events": "ไม่มีกิจกรรมที่สำคัญ",
|
||||
Resume: "ดำเนินการต่อ",
|
||||
Edit: "แก้ไข",
|
||||
Delete: "ลบ",
|
||||
Current: "ปัจจุบัน",
|
||||
Uptime: "เวลาที่ใช้งาน",
|
||||
"Cert Exp.": "วันหมดอายุใบรับรอง",
|
||||
days: "วัน",
|
||||
day: "วัน",
|
||||
"-day": "-วัน",
|
||||
hour: "ชั่วโมง",
|
||||
"-hour": "-ชั่วโมง",
|
||||
Response: "การตอบสนอง",
|
||||
Ping: "การตอบสนอง",
|
||||
"Monitor Type": "ประเภทมอนิเตอร์",
|
||||
Keyword: "คำสำคัญ",
|
||||
"Friendly Name": "ชื่อที่เป็นมิตร",
|
||||
URL: "URL",
|
||||
Hostname: "ชื่อโฮสต์",
|
||||
Port: "พอร์ต",
|
||||
"Heartbeat Interval": "ระยะห่างระหว่างการทดสอบ",
|
||||
Retries: "จำนวนครั้งที่จะลองใหม่",
|
||||
"Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ",
|
||||
Advanced: "ขั้นสูง",
|
||||
"Upside Down Mode": "โหมดกลับด้าน",
|
||||
"Max. Redirects": "จำนวนการเปลี่ยนเส้นทางสูงสุด",
|
||||
"Accepted Status Codes": "รหัสสถานะที่ยอมรับ",
|
||||
"Push URL": "URL เป้าหมาย",
|
||||
needPushEvery: "คุณควรเรียก URL นี้ทุก {0} วินาที",
|
||||
pushOptionalParams: "ตัวแปรเสริม: {0}",
|
||||
Save: "บันทึก",
|
||||
Notifications: "การแจ้งเตือน",
|
||||
"Not available, please setup.": "ไม่พร้อมใช้งาน, กรุณาตั้งค่า",
|
||||
"Setup Notification": "ตั้งค่าการแจ้งเตือน",
|
||||
Light: "สว่าง",
|
||||
Dark: "มืด",
|
||||
Auto: "อัตโนมัติ",
|
||||
"Theme - Heartbeat Bar": "หน้าตา - แถบการตอบสนอง",
|
||||
Normal: "ปกติ",
|
||||
Bottom: "ด้านล่าง",
|
||||
None: "ไม่มี",
|
||||
Timezone: "เขตเวลา",
|
||||
"Search Engine Visibility": "การมองเห็นของเครื่องมือค้นหา",
|
||||
"Allow indexing": "อนุญาตให้สร้างดัชนี",
|
||||
"Discourage search engines from indexing site": "ปฏิเสธเครื่องมือค้นหาไม่ให้สร้างดัชนีของเว็บไซต์",
|
||||
"Change Password": "เปลี่ยนรหัสผ่าน",
|
||||
"Current Password": "รหัสผ่านปัจจุบัน",
|
||||
"New Password": "รหัสผ่านใหม่",
|
||||
"Repeat New Password": "ยืนยันรหัสผ่านใหม่",
|
||||
"Update Password": "อัพเดทรหัสผ่าน",
|
||||
"Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์",
|
||||
"Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์",
|
||||
"disableauth.message1": "คุณต้องการที่จะ <strong>ปิดใช้งานระบบรับรองความถูกต้องใช่หรือไม่</strong>?",
|
||||
"disableauth.message2": "ระบบนี้ถูกออกแบบมาเพื่อการใช้งานกับระบบรับรองความถูกต้องของบุคคลที่สามเช่น Cloudflare Access, Authelia หรือวิธีการอื่น ๆ",
|
||||
"Please use this option carefully!": "โปรดใช้ความระมัดระวังในการเลือกใช้งานระบบนี้ !",
|
||||
Logout: "ออกจากระบบ",
|
||||
Leave: "ออก",
|
||||
"I understand, please disable": "ฉันเข้าใจแล้ว, กรุณาปิดการใช้งาน",
|
||||
Confirm: "ยืนยัน",
|
||||
Yes: "ใช่",
|
||||
No: "ไม่",
|
||||
Username: "ชื่อผู้ใช้",
|
||||
Password: "รหัสผ่าน",
|
||||
"Remember me": "คงอยู่ในระบบ",
|
||||
Login: "เข้าสู่ระบบ",
|
||||
"No Monitors, please": "ไม่มีมอนิเตอร์, กรุณา",
|
||||
"add one": "สร้าง",
|
||||
"Notification Type": "ประเภทการแจ้งเตือน",
|
||||
Email: "อีเมล",
|
||||
Test: "ทดสอบ",
|
||||
"Certificate Info": "ข้อมูลใบรับรอง",
|
||||
"Resolver Server": "เซิร์ฟเวอร์ทีค้นหา",
|
||||
"Resource Record Type": "ประเภท DNS Record",
|
||||
"Last Result": "ผลล่าสุด",
|
||||
"Create your admin account": "สร้างบัญชีผู้ดูแลระบบ",
|
||||
"Repeat Password": "ยืนยันรหัสผ่าน",
|
||||
"Import Backup": "นำเข้าข้อมูลสำรอง",
|
||||
"Export Backup": "ส่งออกข้อมูลสำรอง",
|
||||
Export: "ส่งออก",
|
||||
Import: "นำเข้า",
|
||||
respTime: "ระยะเวลาการตอบสนอง (ms)",
|
||||
notAvailableShort: "ไม่สามารถใช้งานได้",
|
||||
"Default enabled": "เปิดใช้งานโดยค่าเริ่มต้น",
|
||||
"Apply on all existing monitors": "ใช้กับมอนิเตอร์ทั้งหมด",
|
||||
Create: "สร้าง",
|
||||
"Clear Data": "ล้างข้อมูล",
|
||||
Events: "เหตุการณ์",
|
||||
Heartbeats: "ประวัติการตรวจสอบ",
|
||||
"Auto Get": "ดึงอัตโนมัติ",
|
||||
backupDescription: "คุณสามารถสำรองข้อมูลการแจ้งเตือนและมอนิเตอร์ทั้งหมดได้ในไฟล์ JSON",
|
||||
backupDescription2: "หมายเหตุ : ประวัติและข้อมูลกิจกรรมจะไม่ถูกสำรอง",
|
||||
backupDescription3: "ข้อมูลที่ละเอียดอ่อนเช่นกุญแจการแจ้งเตือนจะรวมอยู่ในไฟล์ข้อมูลสำรอง, โปรดเก็บข้อมูลสำรองอย่างปลอดภัย",
|
||||
alertNoFile: "กรุณาเลือกไฟล์ที่จะใช้งาน",
|
||||
alertWrongFileType: "กรุณาเลือกไฟล์ที่เป็น JSON",
|
||||
"Clear all statistics": "ล้างข้อมูลสถิติทั้งหมด",
|
||||
"Skip existing": "ข้ามรายการที่มีอยู่แล้ว",
|
||||
Overwrite: "เขียนทับ",
|
||||
Options: "ตัวเลือก",
|
||||
"Keep both": "เก็บทั้งสอง",
|
||||
"Verify Token": "ยืนยันกุญแจ",
|
||||
"Setup 2FA": "ติดตั้ง 2FA",
|
||||
"Enable 2FA": "เปิดใช้งาน 2FA",
|
||||
"Disable 2FA": "ปิดใช้งาน 2FA",
|
||||
"2FA Settings": "ตั้งค่า 2FA",
|
||||
"Two Factor Authentication": "การตรวจสอบสิทธิ์สองปัจจัย",
|
||||
Active: "ใช้งาน",
|
||||
Inactive: "ไม่ใช้งาน",
|
||||
Token: "กุญแจ",
|
||||
"Show URI": "แสดง URI",
|
||||
Tags: "แท็ก",
|
||||
"Add New below or Select...": "เพิ่มใหม่ด้านล่างหรือเลือก...",
|
||||
"Tag with this name already exist.": "แท็กที่มีชื่อนี้มีอยู่แล้ว",
|
||||
"Tag with this value already exist.": "แท็กที่มีข้อมูลนี้มีอยู่แล้ว",
|
||||
color: "สี",
|
||||
"value (optional)": "ข้อมูล (ไม่จำเป็น)",
|
||||
Gray: "เทา",
|
||||
Red: "แดง",
|
||||
Orange: "ส้ม",
|
||||
Green: "เขียว",
|
||||
Blue: "น้ำเงิน",
|
||||
Indigo: "ม่วง",
|
||||
Purple: "ม่วง",
|
||||
Pink: "ชมพู",
|
||||
"Search...": "ค้นหา...",
|
||||
"Avg. Ping": "ค่า Ping เฉลี่ย",
|
||||
"Avg. Response": "ค่า Response เฉลี่ย",
|
||||
"Entry Page": "หน้าต้อนรับ",
|
||||
statusPageNothing: "ไม่มีอะไรตรงนี้ !, กรุณาเพิ่มกลุ่มหรือมอนิเตอร์",
|
||||
"No Services": "ไม่มีบริการ",
|
||||
"All Systems Operational": "บริการทั้งหมดทำงานได้ปกติ",
|
||||
"Partially Degraded Service": "บริการมีปัญหาบางส่วน",
|
||||
"Degraded Service": "บริการมีปัญหา",
|
||||
"Add Group": "เพิ่มกลุ่ม",
|
||||
"Add a monitor": "เพิ่มมอนิเตอร์",
|
||||
"Edit Status Page": "แก้ไขหน้าสถานะ",
|
||||
"Go to Dashboard": "ไปที่หน้าควบคุม",
|
||||
"Status Page": "หน้าสถานะ",
|
||||
"Status Pages": "หน้าสถานะ",
|
||||
defaultNotificationName: "การแจ้งเตือน {notification} ของฉัน ({number})",
|
||||
here: "ที่นี่",
|
||||
Required: "ต้องการ",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "กุญแจของบอท",
|
||||
wayToGetTelegramToken: "คุณสามารถรับกุญแจได้จาก {0}.",
|
||||
"Chat ID": "ไอดีแชท",
|
||||
supportTelegramChatID: "รองรับ แชทส่วนตัว, แชทกลุ่ม, ไอดีแชท",
|
||||
wayToGetTelegramChatID: "คุณสามารถรับ ID แชทของคุณได้โดยส่งข้อความไปยังบอทและไปที่ URL นี้เพื่อดู chat_id :",
|
||||
"YOUR BOT TOKEN HERE": "กุญแจของบอทของคุณที่นี่",
|
||||
chatIDNotFound: "ไม่พบไอดีแชท, กรุณาส่งข้อความไปที่บอท",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "URL โพสต์",
|
||||
"Content Type": "ประเภทเนื้อหา",
|
||||
webhookJsonDesc: "{0} ดีสำหรับเซิร์ฟเวอร์ HTTP สมัยใหม่เช่น Express.js",
|
||||
webhookFormDataDesc: "{multipart} ดีสำหรับ PHP, JSON จะต้องถูกประมวลผลด้วย {decodeFunction}",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignore TLS Error",
|
||||
"From Email": "From Email",
|
||||
emailCustomSubject: "Custom Subject",
|
||||
"To Email": "To Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "คุณสามารถรับได้โดยการไปที่ Server Settings -> Integrations -> Create Webhook",
|
||||
"Bot Display Name": "ชื่อบอท",
|
||||
"Prefix Custom Message": "คำนำหน้าข้อความที่กำหนดเอง",
|
||||
"Hello @everyone is...": "สวัสดี {'@'}everyone นี่...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "คุณสามารถเรียนรู้วิธีการสร้าง Webhook URL {0}",
|
||||
signal: "Signal",
|
||||
Number: "หมายเลข",
|
||||
Recipients: "ผู้รับ",
|
||||
needSignalAPI: "คุณต้องมี Signal Client ที่มี Rest APIl",
|
||||
wayToCheckSignalURL: "คุณสามารถตรวจสอบ URL นี้เพื่อดูวิธีตั้งค่า :",
|
||||
signalImportant: "สำคัญ: คุณไม่สามารถผสมกลุ่มและตัวเลขในผู้รับได้!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "กุญแจของแอพพลิเคชั่น",
|
||||
"Server URL": "Server URL",
|
||||
Priority: "ลำดับความสำคัญ",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "ชื่อห้อง",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "ข้อมูลเพิ่มเติมสำหรับ Webhooks : {0}",
|
||||
aboutChannelName: "ใส่ชื่อห้องบน {0} ในช่องชื่อห้องถ้าต้องการที่จะข้าม Webhook, เช่น: #ช่องอื่นๆ",
|
||||
aboutKumaURL: "ถ้าคุณไม่ใส่ข้อมูลในช่อง Uptime Kuma URL ค่าเริ่มต้นจะเป็นจะเป็น Uptime Kuma Github",
|
||||
emojiCheatSheet: "ตาราง Emoji : {0}",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (รองรับการแจ้งเตือนมากกว่า 50 บริการ)",
|
||||
GoogleChat: "Google Chat (Google Workspace only)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "กุญแจผู้ใช้งาน",
|
||||
Device: "อุปกรณ์",
|
||||
"Message Title": "หัวข้อข้อความ",
|
||||
"Notification Sound": "เสียงแจ้งเตือน",
|
||||
"More info on:": "ข้อมูลเพิ่มเติม : {0}",
|
||||
pushoverDesc1: "ลำดับความสำตคญฉุกเฉิน (2) มีการหมดเวลาเริ่มต้น 30 วินาทีระหว่างลองใหม่และจะหมดอายุหลังจาก 1 ชั่วโมง",
|
||||
pushoverDesc2: "ถ้าคุณต้องการจะส่งการแจ้งเตือนไปยังอุปกรณ์อื่น ๆ สามารถกำหนดได้ที่ช่องอุปกรณ์",
|
||||
"SMS Type": "ประเภท SMS",
|
||||
octopushTypePremium: "พรีเมี่ยม (เร็ว - แนะนำสำหรับการแจ้งเตือน)",
|
||||
octopushTypeLowCost: "ต้นทุนต่ำ (ช้า - บางครั้งจะถูกบล็อกโดยผู้ให้บริการ)",
|
||||
checkPrice: "ตรวจสอบราคาของ {0} :",
|
||||
apiCredentials: "ข้อมูลการตรวจสอบสิทธิ์ API",
|
||||
octopushLegacyHint: "คุณใช้เวอร์ชันดั้งเดิมของ Octopush (2011 - 2020) หรือเวอร์ชันใหม่หรือไม่?",
|
||||
"Check octopush prices": "ตรวจสอบราคาของ Octopush {0}",
|
||||
octopushPhoneNumber: "หมายเลขโทรศัพท์ (รูปแบบสากล เช่น +33612345678) ",
|
||||
octopushSMSSender: "ชื่อผู้ส่ง SMS : ความยาว 3 - 11 ตัวอักษร, ตัวเลข และช่องว่าง (a-zA-Z0-9 )",
|
||||
"LunaSea Device ID": "ไอดีอุปกรณ์ LunaSea",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "ตัวอย่าง : {0}",
|
||||
"Read more:": "อ่านเพิ่มเติม : {0}",
|
||||
"Status:": "สถานะ : {0}",
|
||||
"Read more": "อ่านเพิ่มเติม",
|
||||
appriseInstalled: "Apprise ถูกติดตั่งแล้ว",
|
||||
appriseNotInstalled: "Apprise ยังไม่ถูกติดตั่ง {0}",
|
||||
"Access Token": "กุญแจการเข้าถึง",
|
||||
"Channel access token": "กุญแจการเข้าถึงของช่อง",
|
||||
"Line Developers Console": "Line Developers Console",
|
||||
lineDevConsoleTo: "Line Developers Console - {0}",
|
||||
"Basic Settings": "การตั้งค่าพื้นฐาน",
|
||||
"User ID": "ไอดีผู้ใช้",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "ขั้นแรกให้เข้า {0} สร้างผู้ให้บริการและช่องทาง (Messaging API) จากนั้นคุณจะได้รับกุญแจการเข้าถึงช่องและไอดีผู้ใช้จากรายการเมนูที่กล่าวถึงข้างต้น",
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "คุณสามารถระบุลิงก์ไปยังรูปภาพใน \"URL ไอคอน\" เพื่อแทนที่รูปภาพโปรไฟล์เริ่มต้น จะไม่ถูกใช้หากมีการตั้งค่า Icon Emoji",
|
||||
aboutMattermostChannelName: "คุณลบล้างช่องเริ่มต้นที่ Webhook โพสต์ได้ด้วยการป้อนชื่อช่องลงในช่อง \"ชื่อช่อง\" ต้องเปิดใช้งานในการตั้งค่า Mattermost Webhook เช่น #ช่องอื่นๆ",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - ราคาถูก แต่ช้าและมักจะโอเวอร์โหลด จำกัดเฉพาะผู้รับโปแลนด์",
|
||||
promosmsTypeFlash: "SMS FLASH - ข้อความจะแสดงบนอุปกรณ์ของผู้รับโดยอัตโนมัติ จำกัดเฉพาะผู้รับโปแลนด์",
|
||||
promosmsTypeFull: "SMS FULL - SMS ระดับพรีเมียม คุณสามารถใช้ชื่อผู้ส่งของคุณได้ (คุณต้องลงทะเบียนชื่อก่อน) เชื่อถือได้สำหรับการแจ้งเตือน",
|
||||
promosmsTypeSpeed: "SMS SPEED - ลำดับความสำคัญสูงสุดในระบบ รวดเร็วและเชื่อถือได้ แต่มีค่าใช้จ่ายสูง (ประมาณสองเท่าของราคาเต็ม SMS)",
|
||||
promosmsPhoneNumber: "หมายเลขโทรศัพท์ (สำหรับผู้รับโปแลนด์ คุณสามารถข้ามรหัสพื้นที่ได้)",
|
||||
promosmsSMSSender: "ชื่อผู้ส่ง SMS : ชื่อที่ลงทะเบียนล่วงหน้าหรือหนึ่งในค่าเริ่มต้น: InfoSMS, ข้อมูล SMS, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "URL ของโฮมเซิร์ฟเวอร์ (พร้อม http(s):// และพอร์ตเสริม)",
|
||||
"Internal Room Id": "รหัสห้องภายใน",
|
||||
matrixDesc1: "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.",
|
||||
matrixDesc2: "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วมได้อย่างเต็มที่ ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}",
|
||||
Method: "วิธี",
|
||||
Body: "เนื้อหา",
|
||||
Headers: "ส่วนหัว",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "เนื้อหาคำขอส่วนหัวไม่ใช่ JSON ที่ถูกต้อง :",
|
||||
BodyInvalidFormat: "เนื้อหาคำขอไม่ใช่ JSON ที่ถูกต้อง : ",
|
||||
"Monitor History": "ประวัติมอนิเตอร์",
|
||||
clearDataOlderThan: "เก็บข้อมูลมอนิเตอร์ {0} วัน",
|
||||
PasswordsDoNotMatch: "รหัสผ่านไม่ตรงกัน",
|
||||
records: "บันทึก",
|
||||
"One record": "หนึ่งบันทึก",
|
||||
steamApiKeyDescription: "สำหรับการมอนิเตอร์ Steam Game Server คุณต้องมี Steam Web-API key, คุณสามารถรสมัครได้จากที่นี่ : ",
|
||||
"Current User": "ผู้ใช้ปัจจุบัน",
|
||||
topic: "หัวข้อ",
|
||||
topicExplanation: "MQTT หัวข้อที่จะมอนิเตอร์",
|
||||
successMessage: "ข้อความที่จะถือว่าประสบความสำเร็จ",
|
||||
successMessageExplanation: "MQTT ข้อความที่จะถือว่าประสบความสำเร็จ",
|
||||
recent: "ล่าสุด",
|
||||
Done: "สำเร็จ",
|
||||
Info: "ข้อมูล",
|
||||
Security: "ความปลอดภัย",
|
||||
"Steam API Key": "Steam API Key",
|
||||
"Shrink Database": "ย่อฐานข้อมูล",
|
||||
"Pick a RR-Type...": "เลือกชนิด DNS Record",
|
||||
"Pick Accepted Status Codes...": "เลือกสถานะที่ยอมรับ...",
|
||||
Default: "ค่าเริ่มต้น",
|
||||
"HTTP Options": "ตัวเลือก HTTP",
|
||||
"Create Incident": "สร้างเหตุการณ์",
|
||||
Title: "หัวข้อ",
|
||||
Content: "เนื้อหา",
|
||||
Style: "สไตล์",
|
||||
info: "ข้อมูล",
|
||||
warning: "แจ้งเตือน",
|
||||
danger: "อันตราย",
|
||||
primary: "หลัก",
|
||||
light: "สว่าง",
|
||||
dark: "มืด",
|
||||
Post: "โพสต์",
|
||||
"Please input title and content": "กรุณาใส่ชื่อและเนื้อหา",
|
||||
Created: "สร้าง",
|
||||
"Last Updated": "อัพเดทล่าสุด",
|
||||
Unpin: "เลิกตรึง",
|
||||
"Switch to Light Theme": "เปลี่ยนเป็นแบบสว่าง",
|
||||
"Switch to Dark Theme": "เปลี่ยนเป็นแบบมืด",
|
||||
"Show Tags": "แสดงแท็ก",
|
||||
"Hide Tags": "ซ่อนแท็ก",
|
||||
Description: "รายละเอียด",
|
||||
"No monitors available.": "ไม่มีมอนิเตอร์ที่สามารถใช้งานได้",
|
||||
"Add one": "เพิ่ม",
|
||||
"No Monitors": "ไม่มีมอนิเตอร์",
|
||||
"Untitled Group": "กลุ่มที่ไม่มีชื่อ",
|
||||
Services: "บริการ",
|
||||
Discard: "ทิ้ง",
|
||||
Cancel: "ยกเลิก",
|
||||
"Powered by": "ขับเคลื่อนโดย",
|
||||
shrinkDatabaseDescription: "ทริกเกอร์ฐานข้อมูล VACUUM สำหรับ SQLite หากฐานข้อมูลของคุณถูกสร้างขึ้นหลังจาก 1.10.0 แสดงว่า AUTO_VACUUM เปิดใช้งานอยู่แล้วและไม่จำเป็นต้องดำเนินการนี้",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API Username (incl. webapi_ prefix)",
|
||||
serwersmsAPIPassword: "API Password",
|
||||
serwersmsPhoneNumber: "หมายเลขโทรศัพท์",
|
||||
serwersmsSenderName: "ชื่อผู้ส่ง SMS (ลงทะเบียนผ่านหน้าควบคุม)",
|
||||
stackfield: "Stackfield",
|
||||
Customize: "ปรับแต่ง",
|
||||
"Custom Footer": "ส่วนท้ายที่กำหนดเอง",
|
||||
"Custom CSS": "CSS ที่กำหนดเอง",
|
||||
smtpDkimSettings: "ตั้งค่า DKIM",
|
||||
smtpDkimDesc: "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน",
|
||||
documentation: "เอกสาร",
|
||||
smtpDkimDomain: "ชื่อโดเมน",
|
||||
smtpDkimKeySelector: "Key Selector",
|
||||
smtpDkimPrivateKey: "Private Key",
|
||||
smtpDkimHashAlgo: "อัลกอริทึมแฮช (ไม่บังคับ)",
|
||||
smtpDkimheaderFieldNames: "คีย์ส่วนหัวเพื่อลงชื่อ (ไม่บังคับ)",
|
||||
smtpDkimskipFields: "Header Keys ไม่ต้องเซ็น (ไม่บังคับ)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "Environment",
|
||||
alertaApiKey: "กุญแจ API",
|
||||
alertaAlertState: "แจ้งเตือนสถานะ",
|
||||
alertaRecoverState: "กู้คืนสถานะ",
|
||||
deleteStatusPageMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้",
|
||||
Proxies: "พร็อกซี",
|
||||
default: "ค่าเริ่มต้น",
|
||||
enabled: "เปิดใช้งาน",
|
||||
setAsDefault: "ตั่งเป็นค่าเริ่มต้น",
|
||||
deleteProxyMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?",
|
||||
proxyDescription: "พร็อกซีจะต้องตั้งค่าให้มอนิเตอร์เพื่อให้ใช้งานได้",
|
||||
enableProxyDescription: "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้โดยสถานะการเปิดใช้งาน",
|
||||
setAsDefaultProxyDescription: "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้",
|
||||
"Certificate Chain": "ห่วงโซ่ใบรับรอง",
|
||||
Valid: "ถูกต้อง",
|
||||
Invalid: "ไม่ถูกต้อง",
|
||||
AccessKeyId: "กุญแจสิทธิ ID",
|
||||
SecretAccessKey: "กุญแจสิทธิ Secret",
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "รหัสเทมเพลต",
|
||||
SignName: "ป้ายชื่อ",
|
||||
"Sms template must contain parameters: ": "เทมเพลต SMS ต้องมีพารามิเตอร์ : ",
|
||||
"Bark Endpoint": "Bark Endpoint",
|
||||
WebHookUrl: "WebHookUrl",
|
||||
SecretKey: "SecretKey",
|
||||
"For safety, must use secret key": "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง",
|
||||
"Device Token": "Device Token",
|
||||
Platform: "แพลตฟอร์ม",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "สูง",
|
||||
Retry: "ลองใหม่",
|
||||
Topic: "หัวข้อ",
|
||||
"WeCom Bot Key": "WeCom Bot Key",
|
||||
"Setup Proxy": "ติดตั้งพร็อกซี่",
|
||||
"Proxy Protocol": "โปรโตคอลพร็อกซี่",
|
||||
"Proxy Server": "พร็อกซีเซิร์ฟ",
|
||||
"Proxy server has authentication": "พร็อกซีเซิร์ฟเวอร์มีการตรวจสอบสิทธิ์",
|
||||
User: "ผู้ใช้",
|
||||
Installed: "ติดตั้งแล้ว",
|
||||
"Not installed": "ไม่ได้ติดตั้ง",
|
||||
Running: "กำลังทำงาน",
|
||||
"Not running": "ไม่ได้ทำงาน",
|
||||
"Remove Token": "ลบกุญแจ",
|
||||
Start: "เริ่ม",
|
||||
Stop: "หยุด",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "เพิ่มหน้าสถานะใหม่",
|
||||
Slug: "ชื่อ",
|
||||
"Accept characters:": "ตัวอักษรที่ใช้งานได้ :",
|
||||
startOrEndWithOnly: "เริ่มหรือจบด้วย {0} เท่านั้น",
|
||||
"No consecutive dashes": "ไม่มีขีดกลางติดต่อกัน",
|
||||
Next: "ต่อไป",
|
||||
"The slug is already taken. Please choose another slug.": "ชื่อนี้ถูกใช้งานไปแล้ว กรุณาใช้ชื่ออื่น",
|
||||
"No Proxy": "ไม่มีพร็อกซี่",
|
||||
"HTTP Basic Auth": "HTTP Basic Auth",
|
||||
"New Status Page": "หน้าสถานะใหม่",
|
||||
"Page Not Found": "ไม่พบหน้านี้",
|
||||
"Reverse Proxy": "พร็อกซีย้อนกลับ",
|
||||
Backup: "สำรอง",
|
||||
About: "เกี่ยวกับ",
|
||||
wayToGetCloudflaredURL: "(ดาวโหลด cloudflared จาก {0})",
|
||||
cloudflareWebsite: "เว็บไซต์ Cloudflare",
|
||||
"Message:": "ข้อความ :",
|
||||
"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 Tunnel คุณแน่ใจหรือไม่ที่จะหยุด, พิมรหัสผ่านของคุณเพื่อยืนยัน",
|
||||
"Other Software": "ซอฟต์แวร์อื่น ๆ ",
|
||||
"For example: nginx, Apache and Traefik.": "เช่น: nginx, Apache และ Traefik",
|
||||
"Please read": "กรุณาอ่าน",
|
||||
"Subject:": "เรื่อง :",
|
||||
"Valid To:": "ถูกต้องถึง :",
|
||||
"Days Remaining:": "จำนวนวันที่เหลือ :",
|
||||
"Issuer:": "ผู้ออก :",
|
||||
"Fingerprint:": "ลายนิ้วมือ :",
|
||||
"No status pages": "ไม่มีหน้าสถานะ",
|
||||
"Domain Name Expiry Notification": "แจ้งเตือนการหมดอายุโดเมน",
|
||||
Proxy: "Proxy",
|
||||
"Date Created": "วันที่สร้าง",
|
||||
onebotHttpAddress: "ที่อยู่ HTTP OneBot ",
|
||||
onebotMessageType: "ชนิดข้อความ OneBot",
|
||||
onebotGroupMessage: "กลุ่ม",
|
||||
onebotPrivateMessage: "ส่วนตัว",
|
||||
onebotUserOrGroupId: "กลุ่ม / ไอดีผู้ใช้",
|
||||
onebotSafetyTips: "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง",
|
||||
"PushDeer Key": "กุญแจ PushDeer",
|
||||
"Footer Text": "ข้อความส่วนท้าย",
|
||||
"Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"",
|
||||
"Domain Names": "Domain Names",
|
||||
signedInDisp: "เข้าใช้งานในฐานะ {0}",
|
||||
signedInDispDisabled: "ปิดการตรวจสอบสิทธิ์",
|
||||
"Certificate Expiry Notification": "แจ้งเตือนการรับรองหมดอายุ",
|
||||
"API Username": "API Username",
|
||||
"API Key": "API Key",
|
||||
"Recipient Number": "หมายเลขผู้รับ",
|
||||
"From Name/Number": "จาก ชื่อ / หมายเลข",
|
||||
"Leave blank to use a shared sender number.": "ไม่ต้องกรอกเพื่อใช้ชื่อผู้ส่งร่วมกัน",
|
||||
"Octopush API Version": "Octopush API Version",
|
||||
"Legacy Octopush-DM": "Legacy Octopush-DM",
|
||||
endpoint: "endpoint",
|
||||
octopushAPIKey: "\"API key\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม",
|
||||
octopushLogin: "\"Login\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม",
|
||||
promosmsLogin: "API Login Name",
|
||||
promosmsPassword: "API Password",
|
||||
"pushoversounds pushover": "Pushover (default)",
|
||||
"pushoversounds bike": "Bike",
|
||||
"pushoversounds bugle": "Bugle",
|
||||
"pushoversounds cashregister": "Cash Register",
|
||||
"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",
|
||||
pushyToken: "Device token",
|
||||
"Show update if available": "แสดงการอัปเดตถ้ามี",
|
||||
"Also check beta release": "ตรวจสอบรุ่นเบต้า",
|
||||
"Using a Reverse Proxy?": "ใช้ Reverse Proxy?",
|
||||
"Check how to config it for WebSocket": "ตรวจสอบวิธีการตั้งค่าสำหรับ WebSocket",
|
||||
"Steam Game Server": "Steam Game Server",
|
||||
"Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :",
|
||||
"The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป",
|
||||
"There might be a typing error in the address.": "อาจมีข้อผิดพลาดในการพิมพ์ที่อยู่",
|
||||
"What you can try:": "สิ่งที่คุณสามารถลอง :",
|
||||
"Retype the address.": "พิมพ์ที่อยู่อีกครั้ง",
|
||||
"Go back to the previous page.": "กลับไปที่หน้าก่อนหน้า",
|
||||
"Coming Soon": "เร็ว ๆ นี้",
|
||||
wayToGetClickSendSMSToken: "คุณสามารถรับ API Username และ API Key ได้จาก {0}",
|
||||
};
|
@@ -1,6 +1,7 @@
|
||||
export default {
|
||||
languageName: "Türkçe",
|
||||
checkEverySecond: "{0} Saniyede bir kontrol et.",
|
||||
retryCheckEverySecond: "{0} Saniyede bir dene.",
|
||||
retriesDescription: "Servisin kapalı olarak işaretlenmeden ve bir bildirim gönderilmeden önce maksimum yeniden deneme sayısı",
|
||||
ignoreTLSError: "HTTPS web siteleri için TLS/SSL hatasını yoksay",
|
||||
upsideDownModeDescription: "Servisin durumunu tersine çevirir. Servis çalışıyorsa kapalı olarak işaretler.",
|
||||
@@ -12,12 +13,20 @@ export default {
|
||||
pauseDashboardHome: "Durdur",
|
||||
deleteMonitorMsg: "Servisi silmek istediğinden emin misin?",
|
||||
deleteNotificationMsg: "Bu bildirimi tüm servisler için silmek istediğinden emin misin?",
|
||||
dnsPortDescription: "DNS sunucusu bağlantı noktası. Varsayılan değer 53'tür. Bağlantı noktasını istediğiniz zaman değiştirebilirsiniz.",
|
||||
resolverserverDescription: "Cloudflare varsayılan sunucudur, çözümleyici sunucusunu istediğiniz zaman değiştirebilirsiniz.",
|
||||
rrtypeDescription: "İzlemek istediğiniz servisin RR-Tipini seçin",
|
||||
pauseMonitorMsg: "Durdurmak istediğinden emin misin?",
|
||||
enableDefaultNotificationDescription: "Bu bildirim her yeni serviste aktif olacaktır. Bildirimi servisler için ayrı ayrı deaktive edebilirsiniz. ",
|
||||
clearEventsMsg: "Bu servisin bütün kayıtlarını silmek istediğinden emin misin?",
|
||||
clearHeartbeatsMsg: "Bu servis için tüm sağlık durumunu silmek istediğinden emin misin?",
|
||||
confirmClearStatisticsMsg: "Tüm istatistikleri silmek istediğinden emin misin?",
|
||||
importHandleDescription: "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir. ",
|
||||
confirmImportMsg: "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz. ",
|
||||
twoFAVerifyLabel: "Lütfen tokeni yazarak 2FA doğrulamanın çalıştığından emin olunuz.",
|
||||
tokenValidSettingsMsg: "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz. ",
|
||||
confirmEnableTwoFAMsg: "2FA'ı etkinleştirmek istediğinizden emin misiniz?",
|
||||
confirmDisableTwoFAMsg: "2FA'ı devre dışı bırakmak istediğinize emin misiniz?",
|
||||
Settings: "Ayarlar",
|
||||
Dashboard: "Panel",
|
||||
"New Update": "Yeni Güncelleme",
|
||||
@@ -25,6 +34,7 @@ export default {
|
||||
Appearance: "Görünüm",
|
||||
Theme: "Tema",
|
||||
General: "Genel",
|
||||
"Primary Base URL": "Birincil Temel URL",
|
||||
Version: "Versiyon",
|
||||
"Check Update On GitHub": "GitHub'da Güncellemeyi Kontrol Edin",
|
||||
List: "Liste",
|
||||
@@ -47,8 +57,7 @@ export default {
|
||||
Current: "Şu anda",
|
||||
Uptime: "Çalışma zamanı",
|
||||
"Cert Exp.": "Sertifika Süresi",
|
||||
days: "günler",
|
||||
day: "gün",
|
||||
day: "gün | günler",
|
||||
"-day": "-gün",
|
||||
hour: "saat",
|
||||
"-hour": "-saat",
|
||||
@@ -62,10 +71,14 @@ export default {
|
||||
Port: "Port",
|
||||
"Heartbeat Interval": "Servis Test Aralığı",
|
||||
Retries: "Yeniden deneme",
|
||||
"Heartbeat Retry Interval": "Sağlık Durumları Tekrar Deneme Sıklığı",
|
||||
Advanced: "Gelişmiş",
|
||||
"Upside Down Mode": "Ters/Düz Modu",
|
||||
"Max. Redirects": "Maksimum Yönlendirme",
|
||||
"Accepted Status Codes": "Kabul Edilen Durum Kodları",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Bu URL'yi her {0} saniyede bir aramalısınız.",
|
||||
pushOptionalParams: "İsteğe bağlı parametreler: {0}",
|
||||
Save: "Kaydet",
|
||||
Notifications: "Bildirimler",
|
||||
"Not available, please setup.": "Atanmış bildirim yöntemi yok. Ayarlardan belirleyebilirsiniz.",
|
||||
@@ -88,6 +101,9 @@ export default {
|
||||
"Update Password": "Şifreyi Değiştir",
|
||||
"Disable Auth": "Şifreli girişi iptal et.",
|
||||
"Enable Auth": "Şifreli girişi aktif et.",
|
||||
"disableauth.message1": "<strong>Şifreli girişi devre dışı bırakmak istediğinizden</strong>emin misiniz?",
|
||||
"disableauth.message2": "Bu, Uptime Kuma'nın önünde Cloudflare Access gibi <strong>üçüncü taraf yetkilendirmesi olan</strong> kişiler içindir.",
|
||||
"Please use this option carefully!": "Lütfen dikkatli kullanın.",
|
||||
Logout: "Çıkış yap",
|
||||
Leave: "Ayrıl",
|
||||
"I understand, please disable": "Evet farkındayım, iptal et",
|
||||
@@ -109,28 +125,19 @@ export default {
|
||||
"Last Result": "En son sonuçlar",
|
||||
"Create your admin account": "Yönetici hesabınızı oluşturun",
|
||||
"Repeat Password": "Şifrenizi tekrar girin",
|
||||
respTime: "Cevap Süresi (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
Create: "Yarat",
|
||||
"Clear Data": "Verileri Temizle",
|
||||
Events: "Olaylar",
|
||||
Heartbeats: "Sağlık Durumları",
|
||||
"Auto Get": "Otomatik Al",
|
||||
retryCheckEverySecond: "{0} Saniyede bir dene.",
|
||||
enableDefaultNotificationDescription: "Bu bildirim her yeni serviste aktif olacaktır. Bildirimi servisler için ayrı ayrı deaktive edebilirsiniz. ",
|
||||
importHandleDescription: "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir. ",
|
||||
confirmImportMsg: "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz. ",
|
||||
twoFAVerifyLabel: "Lütfen tokeni yazarak 2FA doğrulamanın çalıştığından emin olunuz.",
|
||||
tokenValidSettingsMsg: "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz. ",
|
||||
confirmEnableTwoFAMsg: "2FA'ı etkinleştirmek istediğinizden emin misiniz?",
|
||||
confirmDisableTwoFAMsg: "2FA'ı devre dışı bırakmak istediğinize emin misiniz?",
|
||||
"Heartbeat Retry Interval": "Sağlık Durumları Tekrar Deneme Sıklığı",
|
||||
"Import Backup": "Yedeği içe aktar",
|
||||
"Export Backup": "Yedeği dışa aktar",
|
||||
Export: "Dışa aktar",
|
||||
Import: "İçe aktar",
|
||||
respTime: "Cevap Süresi (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
"Default enabled": "Varsayılan etkinleştirilmiş",
|
||||
"Apply on all existing monitors": "Var olan bütün servislere uygula",
|
||||
Create: "Oluştur",
|
||||
"Clear Data": "Verileri Temizle",
|
||||
Events: "Olaylar",
|
||||
Heartbeats: "Sağlık Durumları",
|
||||
"Auto Get": "Otomatik Al",
|
||||
backupDescription: "Bütün servisleri ve bildirimleri JSON dosyasına yedekleyebilirsiniz.",
|
||||
backupDescription2: "Not: Geçmiş ve etkinlik verileri içinde değildir.",
|
||||
backupDescription3: "Dışa aktarma dosyasında bildirim tokeni gibi hassas veriler bulunur, dikkatli bir şekilde saklayınız.",
|
||||
@@ -149,4 +156,375 @@ export default {
|
||||
"Two Factor Authentication": "İki Faktörlü Kimlik Doğrulama (2FA)",
|
||||
Active: "Aktif",
|
||||
Inactive: "İnaktif",
|
||||
Token: "Token",
|
||||
"Show URI": "URI'yi göster",
|
||||
Tags: "Etiketler",
|
||||
"Add New below or Select...": "Aşağıya Yeni Ekle veya Seç...",
|
||||
"Tag with this name already exist.": "Bu ada sahip etiket zaten var.",
|
||||
"Tag with this value already exist.": "Bu değere sahip etiket zaten var.",
|
||||
color: "renk",
|
||||
"value (optional)": "değer (isteğe bağlı)",
|
||||
Gray: "Gri",
|
||||
Red: "Kırmızı",
|
||||
Orange: "Turuncu",
|
||||
Green: "Yeşil",
|
||||
Blue: "Mavi",
|
||||
Indigo: "Çivit mavisi",
|
||||
Purple: "Mor",
|
||||
Pink: "Pembe",
|
||||
"Search...": "Ara...",
|
||||
"Avg. Ping": "Ortalama Ping",
|
||||
"Avg. Response": "Ortalama Cevap Süresi",
|
||||
"Entry Page": "Giriş Sayfası",
|
||||
statusPageNothing: "Burada hiçbir şey yok, lütfen bir grup veya servis ekleyin.",
|
||||
"No Services": "Hizmet Yok",
|
||||
"All Systems Operational": "Tüm Sistemler Operasyonel",
|
||||
"Partially Degraded Service": "Kısmen Bozulmuş Hizmet",
|
||||
"Degraded Service": "Bozulmuş Hizmet",
|
||||
"Add Group": "Grup Ekle",
|
||||
"Add a monitor": "Servis Ekle",
|
||||
"Edit Status Page": "Durum Sayfasını Düzenle",
|
||||
"Go to Dashboard": "Panele Git",
|
||||
"Status Page": "Durum Sayfası",
|
||||
"Status Pages": "Durum Sayfaları",
|
||||
defaultNotificationName: "My {notification} Alert ({number})",
|
||||
here: "burada",
|
||||
Required: "Gerekli",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "{0} adresinden bir token alabilirsiniz.",
|
||||
"Chat ID": "Chat ID",
|
||||
supportTelegramChatID: "Doğrudan Sohbet / Grup / Kanalın Sohbet Kimliğini Destekleyin",
|
||||
wayToGetTelegramChatID: "Bot'a bir mesaj göndererek ve chat_id'yi görüntülemek için bu URL'ye giderek sohbet kimliğinizi alabilirsiniz:",
|
||||
"YOUR BOT TOKEN HERE": "BOT TOKENİNİZ BURADA",
|
||||
chatIDNotFound: "Chat ID bulunamadı; lütfen önce bu bota bir mesaj gönderin",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0}, Express.js gibi tüm modern HTTP sunucuları için iyidir",
|
||||
webhookFormDataDesc: "{multipart} PHP için iyidir. JSON'un {decodeFunction} ile ayrıştırılması gerekecek",
|
||||
smtp: "E-mail (SMTP)",
|
||||
secureOptionNone: "Hiçbiri / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "TLS Hatasını Yoksay",
|
||||
"From Email": "E-postadan",
|
||||
emailCustomSubject: "Özel Konu",
|
||||
"To Email": "E-postaya",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Bunu Sunucu Ayarları -> Entegrasyonlar -> Webhook Oluştur'a giderek alabilirsiniz.",
|
||||
"Bot Display Name": "Botun Görünecek Adı",
|
||||
"Prefix Custom Message": "Önek Özel Mesaj",
|
||||
"Hello @everyone is...": "Merhaba {'@'}everyone ...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Bir webhook URL'sinin nasıl oluşturulacağını öğrenebilirsiniz {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Numara",
|
||||
Recipients: "Alıcılar",
|
||||
needSignalAPI: "REST API ile bir signal istemciniz olması gerekiyor.",
|
||||
wayToCheckSignalURL: "Nasıl kurulacağını görmek için bu URL'yi kontrol edebilirsiniz:",
|
||||
signalImportant: "ÖNEMLİ: Alıcılarda grupları ve sayıları karıştıramazsınız!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Uygulama Tokeni",
|
||||
"Server URL": "Sunucu URL",
|
||||
Priority: "Öncelik",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "İkon Emoji",
|
||||
"Channel Name": "Kanal Adı",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Webhook hakkında daha fazla bilgi: {0}",
|
||||
aboutChannelName: "Webhook kanalını atlamak istiyorsanız, {0} Kanal Adı alanına kanal adını girin. Ör: #diğer-kanal",
|
||||
aboutKumaURL: "Uptime Kuma URL alanını boş bırakırsanız, varsayılan olarak Project GitHub sayfası olur.",
|
||||
emojiCheatSheet: "Emoji cheat sheet: {0}",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (50'den fazla Bildirim hizmetini destekler)",
|
||||
GoogleChat: "Google Chat (sadece Google Workspace)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Kullancı Anahtarı",
|
||||
Device: "Cihaz",
|
||||
"Message Title": "Mesaj Başlığı",
|
||||
"Notification Sound": "Bilgilendirme sesi",
|
||||
"More info on:": "Daha fazla bilgi: {0}",
|
||||
pushoverDesc1: "Acil durum önceliği (2), yeniden denemeler arasında varsayılan olarak 30 saniyelik bir zaman aşımına sahiptir ve 1 saat sonra sona erecektir.",
|
||||
pushoverDesc2: "Farklı cihazlara bildirim göndermek istiyorsanız Cihaz alanını doldurunuz.",
|
||||
"SMS Type": "SMS Tipi",
|
||||
octopushTypePremium: "Premium (Hızlı - uyarı için önerilir)",
|
||||
octopushTypeLowCost: "Düşük Maliyet (Yavaş - bazen operatör tarafından engellenir)",
|
||||
checkPrice: "{0} fiyatlarını kontrol edin:",
|
||||
apiCredentials: "API kimlik bilgileri",
|
||||
octopushLegacyHint: "Octopush'un (2011-2020) eski sürümünü mü yoksa yeni sürümünü mü kullanıyorsunuz?",
|
||||
"Check octopush prices": "Octopush fiyatlarını kontrol edin {0}.",
|
||||
octopushPhoneNumber: "Telefon numarası (uluslararası biçim, örneğin: +33612345678) ",
|
||||
octopushSMSSender: "SMS Gönderici Adı : 3-11 alfanümerik karakter ve boşluk (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea Cihaz ID",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "Örnek: {0}",
|
||||
"Read more:": "Daha fazla oku: {0}",
|
||||
"Status:": "Durum: {0}",
|
||||
"Read more": "Daha fazla oku",
|
||||
appriseInstalled: "Apprise yüklendi.",
|
||||
appriseNotInstalled: "Appris yüklü değil. {0}",
|
||||
"Access Token": "Erişim Tokeni",
|
||||
"Channel access token": "Kanal erişim tokeni",
|
||||
"Line Developers Console": "Line Geliştirici Konsolu",
|
||||
lineDevConsoleTo: "Line Geliştirici Konsolu - {0}",
|
||||
"Basic Settings": "Temel Ayarlar",
|
||||
"User ID": "Kullanıcı ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "Önce {0}'e erişin, bir sağlayıcı ve kanal (Messaging API) oluşturun, ardından yukarıda belirtilen menü öğelerinden kanal erişim tokenini ve kullanıcı id alabilirsiniz.",
|
||||
"Icon URL": "Simge URL",
|
||||
aboutIconURL: "Varsayılan profil resmini geçersiz kılmak için \"Simge URL\" bölümünde bir resme bağlantı sağlayabilirsiniz. Simge Emojisi ayarlanmışsa kullanılmayacaktır.",
|
||||
aboutMattermostChannelName: "Kanal adını \"Kanal Adı\" alanına girerek Webhook'un gönderi yaptığı varsayılan kanalı geçersiz kılabilirsiniz. Bunun Mattermost Webhook ayarlarında etkinleştirilmesi gerekir. Ör: #diğer-kanal",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - ucuz ama yavaş ve genellikle aşırı yüklü. Yalnızca Polonyalı alıcılarla sınırlıdır.",
|
||||
promosmsTypeFlash: "SMS FLASH - Mesaj, alıcı cihazda otomatik olarak gösterilecektir. Yalnızca Polonyalı alıcılarla sınırlıdır.",
|
||||
promosmsTypeFull: "SMS FULL - Premium SMS katmanı, Gönderici Adınızı kullanabilirsiniz (Önce adınızı kaydetmeniz gerekir). Uyarılar için güvenilir.",
|
||||
promosmsTypeSpeed: "SMS HIZI - Sistemde en yüksek öncelik. Çok hızlı ve güvenilir ancak maliyetli (SMS FULL fiyatının yaklaşık iki katı).",
|
||||
promosmsPhoneNumber: "Telefon numarası (Polonyalı alıcı için Alan kodlarını atlayabilirsiniz)",
|
||||
promosmsSMSSender: "SMS Gönderici Adı : Ön kayıtlı ad veya varsayılanlardan biri: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "Homeserver URL (http(s):// ve isteğe bağlı olarak bağlantı noktası ile)",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
matrixDesc1: "Internal Room ID'sini, Matrix istemcinizdeki oda ayarlarının gelişmiş bölümüne bakarak bulabilirsiniz. !QMdRCpUIfLwsfjxye6:home.server gibi görünmelidir.",
|
||||
matrixDesc2: "Hesabınıza ve katıldığınız tüm odalara tam erişime izin vereceğinden, yeni bir kullanıcı oluşturmanız ve kendi Matrix kullanıcınızın erişim belirtecini kullanmamanız şiddetle tavsiye edilir. Bunun yerine, yeni bir kullanıcı oluşturun ve onu yalnızca bildirimi almak istediğiniz odaya davet edin. {0} komutunu çalıştırarak erişim tokenini alabilirsiniz.",
|
||||
Method: "Yöntem",
|
||||
Body: "Gövde",
|
||||
Headers: "Başlıklar",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "İstek başlıkları geçerli JSON değil:",
|
||||
BodyInvalidFormat: "İstek gövdesi geçerli JSON değil:",
|
||||
"Monitor History": "Servis Geçmişi",
|
||||
clearDataOlderThan: "{0} gün boyunca izleme geçmişi verilerini saklayın.",
|
||||
PasswordsDoNotMatch: "Parolalar uyuşmuyor.",
|
||||
records: "kayıtlar",
|
||||
"One record": "Bir Kayıt",
|
||||
steamApiKeyDescription: "Bir Steam Oyun Sunucusunu izlemek için bir Steam Web-API anahtarına ihtiyacınız vardır. API anahtarınızı buradan kaydedebilirsiniz: ",
|
||||
"Current User": "Şu anki kullanıcı",
|
||||
topic: "Başlık",
|
||||
topicExplanation: "İzlenecek MQTT servisi",
|
||||
successMessage: "Başarılı Mesaj",
|
||||
successMessageExplanation: "Başarılı olarak kabul edilecek MQTT mesajı",
|
||||
recent: "Son",
|
||||
Done: "Tamamlandı",
|
||||
Info: "Bilgi",
|
||||
Security: "Güvenlik",
|
||||
"Steam API Key": "Steam API Anahtarı",
|
||||
"Shrink Database": "Veritabanını Küçült",
|
||||
"Pick a RR-Type...": "Bir RR-Tipi seçin...",
|
||||
"Pick Accepted Status Codes...": "Kabul Edilen Durum Kodlarını Seçin...",
|
||||
Default: "Varsayılan",
|
||||
"HTTP Options": "HTTP Ayarları",
|
||||
"Create Incident": "Olay Oluştur",
|
||||
Title: "Başlık",
|
||||
Content: "İçerik",
|
||||
Style: "Stil",
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
danger: "danger",
|
||||
primary: "primary",
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
Post: "Post",
|
||||
"Please input title and content": "Lütfen başlık ve içerik girin",
|
||||
Created: "Oluşturuldu",
|
||||
"Last Updated": "Son Güncelleme",
|
||||
Unpin: "Unpin",
|
||||
"Switch to Light Theme": "Açık Temaya Geç",
|
||||
"Switch to Dark Theme": "Karanlık Temaya Geç",
|
||||
"Show Tags": "Etiketleri Göster",
|
||||
"Hide Tags": "Etiketleri Gizle",
|
||||
Description: "Açıklama",
|
||||
"No monitors available.": "Kullanılabilir servis yok.",
|
||||
"Add one": "Bir tane ekle",
|
||||
"No Monitors": "Servis Yok",
|
||||
"Untitled Group": "Adsız Grup",
|
||||
Services: "Hizmetler",
|
||||
Discard: "İptal Et",
|
||||
Cancel: "İptal Et",
|
||||
"Powered by": "Powered by",
|
||||
shrinkDatabaseDescription: "SQLite için veritabanı VACUUM'unu tetikleyin. Veritabanınız 1.10.0'dan sonra oluşturulduysa, AUTO_VACUUM zaten etkinleştirilmiştir ve bu eyleme gerek yoktur.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API Kullanıcı Adı (webapi_ öneki dahil)",
|
||||
serwersmsAPIPassword: "API Şifre",
|
||||
serwersmsPhoneNumber: "Telefon numarası",
|
||||
serwersmsSenderName: "SMS Gönderici Adı (müşteri portalı üzerinden kayıtlı)",
|
||||
stackfield: "Stackfield",
|
||||
Customize: "Özelleştirme",
|
||||
"Custom Footer": "Özel Altbilgi",
|
||||
"Custom CSS": "Özel CSS",
|
||||
smtpDkimSettings: "DKIM Ayarları",
|
||||
smtpDkimDesc: "Kullanım için lütfen Nodemailer DKIM'e {0} bakın.",
|
||||
documentation: "belgeler",
|
||||
smtpDkimDomain: "Alan adı",
|
||||
smtpDkimKeySelector: "Anahtar Seçici",
|
||||
smtpDkimPrivateKey: "Özel anahtar",
|
||||
smtpDkimHashAlgo: "Hash Algoritması (Opsiyonel)",
|
||||
smtpDkimheaderFieldNames: "İmzalanacak Başlık Anahtarları (Opsiyonel)",
|
||||
smtpDkimskipFields: "İmzalamayacak Başlık Anahtarları (Opsiyonel)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "Environment",
|
||||
alertaApiKey: "API Key",
|
||||
alertaAlertState: "Uyarı Durumu",
|
||||
alertaRecoverState: "Kurtarma Durumu",
|
||||
deleteStatusPageMsg: "Bu durum sayfasını silmek istediğinizden emin misiniz?",
|
||||
Proxies: "Proxy'ler",
|
||||
default: "Varsayılan",
|
||||
enabled: "Etkinleştirilmiş",
|
||||
setAsDefault: "Varsayılan Olarak Ayarla",
|
||||
deleteProxyMsg: "Bu proxy'yi tüm servisler için silmek istediğinizden emin misiniz?",
|
||||
proxyDescription: "Proxy'lerin çalışması için bir servise atanması gerekir.",
|
||||
enableProxyDescription: "Bu proxy, etkinleştirilene kadar izleme isteklerini etkilemeyecektir. Aktivasyon durumuna göre proxy'yi tüm servislerden geçici olarak devre dışı bırakabilirsiniz.",
|
||||
setAsDefaultProxyDescription: "Bu proxy, yeni servisler için varsayılan olarak etkinleştirilecektir. Yine de proxy'yi her servis için ayrı ayrı devre dışı bırakabilirsiniz.",
|
||||
"Certificate Chain": "Sertifika Zinciri",
|
||||
Valid: "Geçerli",
|
||||
Invalid: "Geçersiz",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "SignName",
|
||||
"Sms template must contain parameters: ": "Sms şablonu parametreleri içermelidir:",
|
||||
"Bark Endpoint": "Bark Endpoint",
|
||||
WebHookUrl: "WebHookUrl",
|
||||
SecretKey: "SecretKey",
|
||||
"For safety, must use secret key": "Güvenlik için gizli anahtar kullanılmalıdır",
|
||||
"Device Token": "Cihaz Tokeni",
|
||||
Platform: "Platform",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "High",
|
||||
Retry: "Retry",
|
||||
Topic: "Topic",
|
||||
"WeCom Bot Key": "WeCom Bot Key",
|
||||
"Setup Proxy": "Proxy kur",
|
||||
"Proxy Protocol": "Proxy Protokolü",
|
||||
"Proxy Server": "Proxy Sunucusu",
|
||||
"Proxy server has authentication": "Proxy sunucusunun kimlik doğrulaması var",
|
||||
User: "Kullanıcı",
|
||||
Installed: "Yüklenmiş",
|
||||
"Not installed": "Yüklü değil",
|
||||
Running: "Çalışıyor",
|
||||
"Not running": "Çalışmıyor",
|
||||
"Remove Token": "Tokeni Kaldır",
|
||||
Start: "Başlat",
|
||||
Stop: "Durdur",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "Yeni Durum Sayfası Ekle",
|
||||
Slug: "Slug",
|
||||
"Accept characters:": "Kabul edilen karakterler:",
|
||||
startOrEndWithOnly: "Yalnızca {0} ile başlayın veya bitirin",
|
||||
"No consecutive dashes": "Ardışık tire yok",
|
||||
Next: "Sonraki",
|
||||
"The slug is already taken. Please choose another slug.": "Slug zaten alındı. Lütfen başka bir slug seçin.",
|
||||
"No Proxy": "Proxy Yok",
|
||||
"HTTP Basic Auth": "HTTP Temel Yetkilendirme",
|
||||
"New Status Page": "Yeni Durum Sayfası",
|
||||
"Page Not Found": "Sayfa bulunamadı",
|
||||
"Reverse Proxy": "Ters Proxy",
|
||||
Backup: "Yedek",
|
||||
About: "Hakkında",
|
||||
wayToGetCloudflaredURL: "(Cloudflared'i {0} adresinden indirin)",
|
||||
cloudflareWebsite: "Cloudflare Website",
|
||||
"Message:": "Mesaj:",
|
||||
"Don't know how to get the token? Please read the guide:": "Tokeni nasıl alacağınızı bilmiyor musunuz? Lütfen kılavuzu okuyun:",
|
||||
"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.": "Halihazırda Cloudflare Tüneli üzerinden bağlanıyorsanız mevcut bağlantı kesilebilir. Durdurmak istediğinden emin misin? Onaylamak için mevcut şifrenizi yazın.",
|
||||
"Other Software": "Diğer Yazılımlar",
|
||||
"For example: nginx, Apache and Traefik.": "Örneğin: nginx, Apache ve Traefik.",
|
||||
"Please read": "Lütfen oku",
|
||||
"Subject:": "Başlık:",
|
||||
"Valid To:": "Geçerlilik:",
|
||||
"Days Remaining:": "Kalan günler:",
|
||||
"Issuer:": "Veren:",
|
||||
"Fingerprint:": "Parmak izi:",
|
||||
"No status pages": "Durum sayfası yok",
|
||||
"Domain Name Expiry Notification": "Alan Adı Sona Erme Bildirimi",
|
||||
Proxy: "Proxy",
|
||||
"Date Created": "Tarih Oluşturuldu",
|
||||
onebotHttpAddress: "OneBot HTTP Adresi",
|
||||
onebotMessageType: "OneBot Mesaj Türü",
|
||||
onebotGroupMessage: "Grup",
|
||||
onebotPrivateMessage: "Özel",
|
||||
onebotUserOrGroupId: "Grup/Kullanıcı Kimliği",
|
||||
onebotSafetyTips: "Güvenlik için erişim tokeni ayarlamalısınız",
|
||||
"PushDeer Key": "PushDeer Anahtarı",
|
||||
"Footer Text": "Altbilgi metni",
|
||||
"Show Powered By": "\"Powered by\" kısmını göster",
|
||||
"Domain Names": "Alan isimleri",
|
||||
signedInDisp: "{0} olarak oturum açıldı",
|
||||
signedInDispDisabled: "Yetkilendirme Devre Dışı.",
|
||||
"Certificate Expiry Notification": "Sertifika Sona Erme Bildirimi",
|
||||
"API Username": "API Kullanıc Adı",
|
||||
"API Key": "API Anahtarı",
|
||||
"Recipient Number": "Alıcı Numarası",
|
||||
"From Name/Number": "İsimden/Numaradan",
|
||||
"Leave blank to use a shared sender number.": "Paylaşılan bir gönderen numarası kullanmak için boş bırakın.",
|
||||
"Octopush API Version": "Octopush API Sürümü",
|
||||
"Legacy Octopush-DM": "Eski Octopush-DM",
|
||||
"endpoint": "endpoint",
|
||||
octopushAPIKey: "Kontrol panelindeki HTTP API kimlik bilgilerinden \"API Key\"",
|
||||
octopushLogin: "Kontrol panelindeki HTTP API kimlik bilgilerinden \"Login\"",
|
||||
promosmsLogin: "API Oturum Açma Adı",
|
||||
promosmsPassword: "API Şifresi",
|
||||
"pushoversounds pushover": "Pushover (varsayılan)",
|
||||
"pushoversounds bike": "Bisiklet",
|
||||
"pushoversounds bugle": "Boru",
|
||||
"pushoversounds cashregister": "Yazar kasa",
|
||||
"pushoversounds classical": "Klasik",
|
||||
"pushoversounds cosmic": "Kozmik",
|
||||
"pushoversounds falling": "Düşme",
|
||||
"pushoversounds gamelan": "Oyun Alanı",
|
||||
"pushoversounds incoming": "Gelen",
|
||||
"pushoversounds intermission": "Ara",
|
||||
"pushoversounds magic": "Büyü",
|
||||
"pushoversounds mechanical": "Mekanik",
|
||||
"pushoversounds pianobar": "Piano",
|
||||
"pushoversounds siren": "Siren",
|
||||
"pushoversounds spacealarm": "Uzay Alarmı",
|
||||
"pushoversounds tugboat": "Römorkör",
|
||||
"pushoversounds alien": "Uzaylı Alarmı (uzun)",
|
||||
"pushoversounds climb": "Tırmanış (uzun)",
|
||||
"pushoversounds persistent": "Sürekli (uzun)",
|
||||
"pushoversounds echo": "Pushover Yankı (uzun)",
|
||||
"pushoversounds updown": "Yukarı Aşağı (uzun)",
|
||||
"pushoversounds vibrate": "Sadece titreşim",
|
||||
"pushoversounds none": "Yok (sessiz)",
|
||||
pushyAPIKey: "Gizli API Anahtarı",
|
||||
pushyToken: "Cihaz tokeni",
|
||||
"Show update if available": "Varsa güncellemeyi göster",
|
||||
"Also check beta release": "Ayrıca beta sürümünü kontrol edin",
|
||||
"Using a Reverse Proxy?": "Ters Proxy mi Kullanıyorsunuz?",
|
||||
"Check how to config it for WebSocket": "WebSocket için nasıl yapılandırılacağını kontrol edin",
|
||||
"Steam Game Server": "Steam Oyun Sunucusu",
|
||||
"Most likely causes:": "En olası nedenler:",
|
||||
"The resource is no longer available.": "Kaynak artık mevcut değil.",
|
||||
"There might be a typing error in the address.": "Adreste bir yazım hatası olabilir.",
|
||||
"What you can try:": "Ne deneyebilirsin:",
|
||||
"Retype the address.": "Adresi tekrar yazın.",
|
||||
"Go back to the previous page.": "Bir önceki sayfaya geri git.",
|
||||
"Coming Soon": "Yakında gelecek",
|
||||
wayToGetClickSendSMSToken: "API Kullanıcı Adı ve API Anahtarını {0} adresinden alabilirsiniz.",
|
||||
error: "hata",
|
||||
critical: "kritik",
|
||||
wayToGetPagerDutyKey: "Bunu şuraya giderek alabilirsiniz: Servis -> Servis Dizini -> (Bir servis seçin) -> Entegrasyonlar -> Entegrasyon ekle. Burada \"Events API V2\" için arama yapabilirsiniz. Daha fazla bilgi {0}",
|
||||
"Integration Key": "Entegrasyon Anahtarı",
|
||||
"Integration URL": "Entegrasyon URL",
|
||||
"Auto resolve or acknowledged": "Otomatik çözümleme veya onaylama",
|
||||
"do nothing": "hiçbir şey yapma",
|
||||
"auto acknowledged": "otomatik onaylama",
|
||||
"auto resolve": "otomatik çözümleme",
|
||||
};
|
||||
|
@@ -44,8 +44,7 @@ export default {
|
||||
Current: "Поточний",
|
||||
Uptime: "Аптайм",
|
||||
"Cert Exp.": "Сертифікат спливає",
|
||||
days: "днів",
|
||||
day: "день",
|
||||
day: "день | днів",
|
||||
"-day": " днів",
|
||||
hour: "година",
|
||||
"-hour": " години",
|
||||
@@ -85,6 +84,9 @@ export default {
|
||||
"Update Password": "Оновити пароль",
|
||||
"Disable Auth": "Вимкнути авторизацію",
|
||||
"Enable Auth": "Увімкнути авторизацію",
|
||||
"disableauth.message1": "Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?",
|
||||
"disableauth.message2": "Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.",
|
||||
"Please use this option carefully!": "Будь ласка, використовуйте з обережністю.",
|
||||
Logout: "Вийти",
|
||||
Leave: "Відміна",
|
||||
"I understand, please disable": "Я розумію, все одно відключити",
|
||||
@@ -351,7 +353,8 @@ export default {
|
||||
"Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9",
|
||||
"No consecutive dashes --": "Заборонено використовувати тире --",
|
||||
"HTTP Options": "HTTP Опції",
|
||||
"Basic Auth": "HTTP Авторизація",
|
||||
Authentication: "Аутентифікація",
|
||||
"HTTP Basic Auth": "HTTP Авторизація",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (тільки Google Workspace)",
|
||||
|
@@ -56,7 +56,6 @@ export default {
|
||||
Current: "Hiện tại",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Cert hết hạn",
|
||||
days: "ngày",
|
||||
day: "ngày",
|
||||
"-day": "-ngày",
|
||||
hour: "giờ",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "Cập nhật mật khẩu",
|
||||
"Disable Auth": "Tắt xác minh",
|
||||
"Enable Auth": "Bật xác minh",
|
||||
"disableauth.message1": "Bạn có muốn <strong>TẮT XÁC THỰC</strong> không?",
|
||||
"disableauth.message2": "Điều này rất nguy hiểm<strong>BẤT KỲ AI</strong> cũng có thể truy cập và cướp quyền điều khiển.",
|
||||
"Please use this option carefully!": "Vui lòng <strong>cẩn thận</strong>.",
|
||||
Logout: "Đăng xuất",
|
||||
Leave: "Rời",
|
||||
"I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!",
|
||||
|
@@ -57,7 +57,6 @@ export default {
|
||||
Current: "当前",
|
||||
Uptime: "在线时间",
|
||||
"Cert Exp.": "证书有效期",
|
||||
days: "天",
|
||||
day: "天",
|
||||
"-day": " 天",
|
||||
hour: "小时",
|
||||
@@ -102,6 +101,9 @@ export default {
|
||||
"Update Password": "更新密码",
|
||||
"Disable Auth": "禁用身份验证",
|
||||
"Enable Auth": "启用身份验证",
|
||||
"disableauth.message1": "是否确定 <strong>取消登录验证</strong>?",
|
||||
"disableauth.message2": "这是为 <strong>有第三方认证</strong> 的用户提供的功能,如 Cloudflare Access",
|
||||
"Please use this option carefully!": "请谨慎使用!",
|
||||
Logout: "退出",
|
||||
Leave: "离开",
|
||||
"I understand, please disable": "我已了解,继续禁用",
|
||||
@@ -437,6 +439,7 @@ export default {
|
||||
Next: "下一步",
|
||||
"The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。",
|
||||
"No Proxy": "无代理",
|
||||
Authentication: "验证",
|
||||
"HTTP Basic Auth": "HTTP 基础身份验证",
|
||||
"New Status Page": "新的状态页",
|
||||
"Page Not Found": "未找到该页面",
|
||||
@@ -520,4 +523,21 @@ export default {
|
||||
wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。",
|
||||
signedInDisp: "当前用户: {0}",
|
||||
signedInDispDisabled: "已禁用身份验证",
|
||||
dnsPortDescription: "DNS 服务器端口,默认为 53,你可以在任何时候更改此端口.",
|
||||
error: "错误",
|
||||
critical: "关键",
|
||||
wayToGetPagerDutyKey: "你可以在 Service -> Service Directory -> (选择一个 Service) -> Integrations -> Add integration 页面中搜索 \"Events API V2\" 以获取此 Integration Key,更多信息请参见 {0}",
|
||||
"Integration Key": "Integration Key",
|
||||
"Integration URL": "Integration URL",
|
||||
"Auto resolve or acknowledged": "自动标记为已解决或已读",
|
||||
"do nothing": "不做任何操作",
|
||||
"auto acknowledged": "自动标记为已读",
|
||||
"auto resolve": "自动标记为已解决",
|
||||
"Connection String": "连接字符串",
|
||||
Query: "查询语句",
|
||||
settingsCertificateExpiry: "TLS 证书过期通知",
|
||||
certificationExpiryDescription: "HTTPS 监控项发现被监控目标的 TLS 证书剩余有效期少于以下天数时将发出通知:",
|
||||
"ntfy Topic": "ntfy 主题",
|
||||
"Domain": "域名",
|
||||
"Workstation": "工作站",
|
||||
};
|
||||
|
@@ -30,7 +30,6 @@ export default {
|
||||
Current: "目前",
|
||||
Uptime: "上線率",
|
||||
"Cert Exp.": "証書期限",
|
||||
days: "日",
|
||||
day: "日",
|
||||
"-day": "日",
|
||||
hour: "小時",
|
||||
@@ -78,6 +77,9 @@ export default {
|
||||
"Update Password": "更新密碼",
|
||||
"Disable Auth": "取消登入認証",
|
||||
"Enable Auth": "開啟登入認証",
|
||||
"disableauth.message1": "你是否確認<strong>取消登入認証</strong>?",
|
||||
"disableauth.message2": "這個功能是設計給已有<strong>第三方認証</strong>的用家,例如 Cloudflare Access。",
|
||||
"Please use this option carefully!": "請小心使用。",
|
||||
Logout: "登出",
|
||||
notificationDescription: "新增後,你需要在監測器裡啟用。",
|
||||
Leave: "離開",
|
||||
|
@@ -56,7 +56,6 @@ export default {
|
||||
Current: "目前",
|
||||
Uptime: "運作率",
|
||||
"Cert Exp.": "憑證期限",
|
||||
days: "天",
|
||||
day: "天",
|
||||
"-day": "天",
|
||||
hour: "小時",
|
||||
@@ -101,6 +100,9 @@ export default {
|
||||
"Update Password": "更新密碼",
|
||||
"Disable Auth": "停用驗證",
|
||||
"Enable Auth": "啟用驗證",
|
||||
"disableauth.message1": ">你是否要<strong>取消登入驗證</strong>?",
|
||||
"disableauth.message2": "此功能是設計給已有<strong>第三方認證</strong>的使用者,例如 Cloudflare Access。",
|
||||
"Please use this option carefully!": "請謹慎使用。",
|
||||
Logout: "登出",
|
||||
Leave: "離開",
|
||||
"I understand, please disable": "我了解了,請停用",
|
||||
@@ -429,6 +431,7 @@ export default {
|
||||
Next: "下一步",
|
||||
"The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。",
|
||||
"No Proxy": "無 Proxy",
|
||||
Authentication: "驗證",
|
||||
"HTTP Basic Auth": "HTTP 基本驗證",
|
||||
"New Status Page": "新狀態頁",
|
||||
"Page Not Found": "找不到頁面",
|
||||
|
@@ -18,14 +18,31 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Return a given value in the format YYYY-MM-DD HH:mm:ss
|
||||
* @param {any} value Value to format as date time
|
||||
* @returns {string}
|
||||
*/
|
||||
datetime(value) {
|
||||
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a given value in the format YYYY-MM-DD
|
||||
* @param {any} value Value to format as date
|
||||
* @returns {string}
|
||||
*/
|
||||
date(value) {
|
||||
return this.datetimeFormat(value, "YYYY-MM-DD");
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a given value in the format HH:mm or if second is set
|
||||
* to true, HH:mm:ss
|
||||
* @param {any} value Value to format
|
||||
* @param {boolean} second Should seconds be included?
|
||||
* @returns {string}
|
||||
*/
|
||||
time(value, second = true) {
|
||||
let secondString;
|
||||
if (second) {
|
||||
@@ -36,6 +53,12 @@ export default {
|
||||
return this.datetimeFormat(value, "HH:mm" + secondString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a value in a custom format
|
||||
* @param {any} value Value to format
|
||||
* @param {any} format Format to return value in
|
||||
* @returns {string}
|
||||
*/
|
||||
datetimeFormat(value, format) {
|
||||
if (value !== undefined && value !== "") {
|
||||
return dayjs.utc(value).tz(this.timezone).format(format);
|
||||
|
@@ -22,6 +22,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Change the application language */
|
||||
async changeLang(lang) {
|
||||
let message = (await langModules["../languages/" + lang + ".js"]()).default;
|
||||
this.$i18n.setLocaleMessage(lang, message);
|
||||
|
@@ -12,11 +12,13 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Handle screen resize */
|
||||
onResize() {
|
||||
this.windowWidth = window.innerWidth;
|
||||
this.updateBody();
|
||||
},
|
||||
|
||||
/** Add css-class "mobile" to body if needed */
|
||||
updateBody() {
|
||||
if (this.isMobile) {
|
||||
document.body.classList.add("mobile");
|
||||
|
@@ -62,6 +62,12 @@ export default {
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* Initialize connection to socket server
|
||||
* @param {boolean} [bypass = false] Should the check for if we
|
||||
* are on a status page be bypassed?
|
||||
* @returns {(void|null)}
|
||||
*/
|
||||
initSocketIO(bypass = false) {
|
||||
// No need to re-init
|
||||
if (this.socket.initedSocketIO) {
|
||||
@@ -258,10 +264,18 @@ export default {
|
||||
socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
|
||||
},
|
||||
|
||||
/**
|
||||
* The storage currently in use
|
||||
* @returns {Storage}
|
||||
*/
|
||||
storage() {
|
||||
return (this.remember) ? localStorage : sessionStorage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get payload of JWT cookie
|
||||
* @returns {(Object|undefined)}
|
||||
*/
|
||||
getJWTPayload() {
|
||||
const jwtToken = this.$root.storage().token;
|
||||
|
||||
@@ -271,10 +285,18 @@ export default {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current socket
|
||||
* @returns {Socket}
|
||||
*/
|
||||
getSocket() {
|
||||
return socket;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show success or error toast dependant on response status code
|
||||
* @param {Object} res Response object
|
||||
*/
|
||||
toastRes(res) {
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
@@ -283,14 +305,35 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a success toast
|
||||
* @param {string} msg Message to show
|
||||
*/
|
||||
toastSuccess(msg) {
|
||||
toast.success(msg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show an error toast
|
||||
* @param {string} msg Message to show
|
||||
*/
|
||||
toastError(msg) {
|
||||
toast.error(msg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for login
|
||||
* @callback loginCB
|
||||
* @param {Object} res Response object
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send request to log user in
|
||||
* @param {string} username Username to log in with
|
||||
* @param {string} password Password to log in with
|
||||
* @param {string} token User token
|
||||
* @param {loginCB} callback Callback to call with result
|
||||
*/
|
||||
login(username, password, token, callback) {
|
||||
socket.emit("login", {
|
||||
username,
|
||||
@@ -315,6 +358,10 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Log in using a token
|
||||
* @param {string} token Token to log in with
|
||||
*/
|
||||
loginByToken(token) {
|
||||
socket.emit("loginByToken", token, (res) => {
|
||||
this.allowLoginDialog = true;
|
||||
@@ -328,6 +375,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Log out of the web application */
|
||||
logout() {
|
||||
socket.emit("logout", () => { });
|
||||
this.storage().removeItem("token");
|
||||
@@ -337,26 +385,54 @@ export default {
|
||||
this.clearData();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for general socket requests
|
||||
* @callback socketCB
|
||||
* @param {Object} res Result of operation
|
||||
*/
|
||||
/** Prepare 2FA configuration */
|
||||
prepare2FA(callback) {
|
||||
socket.emit("prepare2FA", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the current 2FA configuration
|
||||
* @param {any} secret Unused
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
save2FA(secret, callback) {
|
||||
socket.emit("save2FA", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable 2FA for this user
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
disable2FA(callback) {
|
||||
socket.emit("disable2FA", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify the provided 2FA token
|
||||
* @param {string} token Token to verify
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
verifyToken(token, callback) {
|
||||
socket.emit("verifyToken", token, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current 2FA status
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
twoFAStatus(callback) {
|
||||
socket.emit("twoFAStatus", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get list of monitors
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
getMonitorList(callback) {
|
||||
if (! callback) {
|
||||
callback = () => { };
|
||||
@@ -364,36 +440,74 @@ export default {
|
||||
socket.emit("getMonitorList", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a monitor
|
||||
* @param {Object} monitor Object representing monitor to add
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
add(monitor, callback) {
|
||||
socket.emit("add", monitor, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete monitor by ID
|
||||
* @param {number} monitorID ID of monitor to delete
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
deleteMonitor(monitorID, callback) {
|
||||
socket.emit("deleteMonitor", monitorID, callback);
|
||||
},
|
||||
|
||||
/** Clear the hearbeat list */
|
||||
clearData() {
|
||||
console.log("reset heartbeat list");
|
||||
this.heartbeatList = {};
|
||||
this.importantHeartbeatList = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload the provided backup
|
||||
* @param {string} uploadedJSON JSON to upload
|
||||
* @param {string} importHandle Type of import. If set to
|
||||
* most data in database will be replaced
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
uploadBackup(uploadedJSON, importHandle, callback) {
|
||||
socket.emit("uploadBackup", uploadedJSON, importHandle, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear events for a specified monitor
|
||||
* @param {number} monitorID ID of monitor to clear
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
clearEvents(monitorID, callback) {
|
||||
socket.emit("clearEvents", monitorID, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the heartbeats of a specified monitor
|
||||
* @param {number} monitorID Id of monitor to clear
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
clearHeartbeats(monitorID, callback) {
|
||||
socket.emit("clearHeartbeats", monitorID, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all statistics
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
clearStatistics(callback) {
|
||||
socket.emit("clearStatistics", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get monitor beats for a specific monitor in a time range
|
||||
* @param {number} monitorID ID of monitor to fetch
|
||||
* @param {number} period Time in hours from now
|
||||
* @param {socketCB} callback
|
||||
*/
|
||||
getMonitorBeats(monitorID, period, callback) {
|
||||
socket.emit("getMonitorBeats", monitorID, period, callback);
|
||||
}
|
||||
|
@@ -75,6 +75,7 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Update the theme color meta tag */
|
||||
updateThemeColorMeta() {
|
||||
if (this.theme === "dark") {
|
||||
document.querySelector("#theme-color").setAttribute("content", "#161B22");
|
||||
|
@@ -51,6 +51,7 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** Submit form data to add new status page */
|
||||
async submit() {
|
||||
this.processing = true;
|
||||
|
||||
|
@@ -77,7 +77,7 @@
|
||||
<h4>{{ $t("Cert Exp.") }}</h4>
|
||||
<p>(<Datetime :value="tlsInfo.certInfo.validTo" date-only />)</p>
|
||||
<span class="num">
|
||||
<a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $t("days") }}</a>
|
||||
<a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $tc("day", tlsInfo.certInfo.daysRemaining) }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,39 +289,47 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/** Request a test notification be sent for this monitor */
|
||||
testNotification() {
|
||||
this.$root.getSocket().emit("testNotification", this.monitor.id);
|
||||
toast.success("Test notification is requested.");
|
||||
},
|
||||
|
||||
/** Show dialog to confirm pause */
|
||||
pauseDialog() {
|
||||
this.$refs.confirmPause.show();
|
||||
},
|
||||
|
||||
/** Resume this monitor */
|
||||
resumeMonitor() {
|
||||
this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
},
|
||||
|
||||
/** Request that this monitor is paused */
|
||||
pauseMonitor() {
|
||||
this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
},
|
||||
|
||||
/** Show dialog to confirm deletion */
|
||||
deleteDialog() {
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
/** Show dialog to confirm clearing events */
|
||||
clearEventsDialog() {
|
||||
this.$refs.confirmClearEvents.show();
|
||||
},
|
||||
|
||||
/** Show dialog to confirm clearing heartbeats */
|
||||
clearHeartbeatsDialog() {
|
||||
this.$refs.confirmClearHeartbeats.show();
|
||||
},
|
||||
|
||||
/** Request that this monitor is deleted */
|
||||
deleteMonitor() {
|
||||
this.$root.deleteMonitor(this.monitor.id, (res) => {
|
||||
if (res.ok) {
|
||||
@@ -333,6 +341,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Request that this monitors events are cleared */
|
||||
clearEvents() {
|
||||
this.$root.clearEvents(this.monitor.id, (res) => {
|
||||
if (! res.ok) {
|
||||
@@ -341,6 +350,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** Request that this monitors heartbeats are cleared */
|
||||
clearHeartbeats() {
|
||||
this.$root.clearHeartbeats(this.monitor.id, (res) => {
|
||||
if (! res.ok) {
|
||||
@@ -349,6 +359,11 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the correct title for the ping stat
|
||||
* @param {boolean} [average=false] Is the statistic an average?
|
||||
* @returns {string} Title formated dependant on monitor type
|
||||
*/
|
||||
pingTitle(average = false) {
|
||||
let translationPrefix = "";
|
||||
if (average) {
|
||||
|
@@ -11,30 +11,41 @@
|
||||
<div class="my-3">
|
||||
<label for="type" class="form-label">{{ $t("Monitor Type") }}</label>
|
||||
<select id="type" v-model="monitor.type" class="form-select">
|
||||
<option value="http">
|
||||
HTTP(s)
|
||||
</option>
|
||||
<option value="port">
|
||||
TCP Port
|
||||
</option>
|
||||
<option value="ping">
|
||||
Ping
|
||||
</option>
|
||||
<option value="keyword">
|
||||
HTTP(s) - {{ $t("Keyword") }}
|
||||
</option>
|
||||
<option value="dns">
|
||||
DNS
|
||||
</option>
|
||||
<option value="push">
|
||||
Push
|
||||
</option>
|
||||
<option value="steam">
|
||||
{{ $t("Steam Game Server") }}
|
||||
</option>
|
||||
<option value="mqtt">
|
||||
MQTT
|
||||
</option>
|
||||
<optgroup label="General Monitor Type">
|
||||
<option value="http">
|
||||
HTTP(s)
|
||||
</option>
|
||||
<option value="port">
|
||||
TCP Port
|
||||
</option>
|
||||
<option value="ping">
|
||||
Ping
|
||||
</option>
|
||||
<option value="keyword">
|
||||
HTTP(s) - {{ $t("Keyword") }}
|
||||
</option>
|
||||
<option value="dns">
|
||||
DNS
|
||||
</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Passive Monitor Type">
|
||||
<option value="push">
|
||||
Push
|
||||
</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Specific Monitor Type">
|
||||
<option value="steam">
|
||||
{{ $t("Steam Game Server") }}
|
||||
</option>
|
||||
<option value="mqtt">
|
||||
MQTT
|
||||
</option>
|
||||
<option value="sqlserver">
|
||||
SQL Server
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -94,6 +105,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Port -->
|
||||
<div class="my-3">
|
||||
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
||||
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
|
||||
<div class="form-text">
|
||||
{{ $t("dnsPortDescription") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="dns_resolve_type" class="form-label">{{ $t("Resource Record Type") }}</label>
|
||||
|
||||
@@ -148,6 +168,18 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- SQL Server -->
|
||||
<template v-if="monitor.type === 'sqlserver'">
|
||||
<div class="my-3">
|
||||
<label for="sqlserverConnectionString" class="form-label">SQL Server {{ $t("Connection String") }}</label>
|
||||
<input id="sqlserverConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control">
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="sqlserverQuery" class="form-label">SQL Server {{ $t("Query") }}</label>
|
||||
<textarea id="sqlserverQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Interval -->
|
||||
<div class="my-3">
|
||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
||||
@@ -336,18 +368,46 @@
|
||||
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- HTTP Basic Auth -->
|
||||
<h4 class="mt-5 mb-2">{{ $t("HTTP Basic Auth") }}</h4>
|
||||
<!-- HTTP Auth -->
|
||||
<h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4>
|
||||
|
||||
<!-- Method -->
|
||||
<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')">
|
||||
<label for="method" class="form-label">{{ $t("Method") }}</label>
|
||||
<select id="method" v-model="monitor.authMethod" class="form-select">
|
||||
<option :value="null">
|
||||
{{ $t("None") }}
|
||||
</option>
|
||||
<option value="basic">
|
||||
{{ $t("HTTP Basic Auth") }}
|
||||
</option>
|
||||
<option value="ntlm">
|
||||
NTLM
|
||||
</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>
|
||||
<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' ">
|
||||
<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')">
|
||||
</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')">
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -469,6 +529,15 @@ export default {
|
||||
this.monitor.pushToken = genSecret(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Set default port for DNS if not already defined
|
||||
if (! this.monitor.port || this.monitor.port === "53") {
|
||||
if (this.monitor.type === "dns") {
|
||||
this.monitor.port = "53";
|
||||
} else {
|
||||
this.monitor.port = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
@@ -504,6 +573,7 @@ export default {
|
||||
this.dnsresolvetypeOptions = dnsresolvetypeOptions;
|
||||
},
|
||||
methods: {
|
||||
/** Initialize the edit monitor form */
|
||||
init() {
|
||||
if (this.isAdd) {
|
||||
|
||||
@@ -514,6 +584,7 @@ export default {
|
||||
method: "GET",
|
||||
interval: 60,
|
||||
retryInterval: this.interval,
|
||||
databaseConnectionString: "Server=<hostname>,<port>;Database=<your database>;User Id=<your user id>;Password=<your password>;Encrypt=<true/false>;TrustServerCertificate=<Yes/No>;Connection Timeout=<int>",
|
||||
maxretries: 0,
|
||||
notificationIDList: {},
|
||||
ignoreTls: false,
|
||||
@@ -528,6 +599,7 @@ export default {
|
||||
mqttPassword: "",
|
||||
mqttTopic: "",
|
||||
mqttSuccessMessage: "",
|
||||
authMethod: null,
|
||||
};
|
||||
|
||||
if (this.$root.proxyList && !this.monitor.proxyId) {
|
||||
@@ -560,6 +632,10 @@ export default {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate form input
|
||||
* @returns {boolean} Is the form input valid?
|
||||
*/
|
||||
isInputValid() {
|
||||
if (this.monitor.body) {
|
||||
try {
|
||||
@@ -580,6 +656,10 @@ export default {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit the form data for processing
|
||||
* @returns {void}
|
||||
*/
|
||||
async submit() {
|
||||
this.processing = true;
|
||||
|
||||
@@ -624,14 +704,20 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// Added a Notification Event
|
||||
// Enable it if the notification is added in EditMonitor.vue
|
||||
/**
|
||||
* Added a Notification Event
|
||||
* Enable it if the notification is added in EditMonitor.vue
|
||||
* @param {number} id ID of notification to add
|
||||
*/
|
||||
addedNotification(id) {
|
||||
this.monitor.notificationIDList[id] = true;
|
||||
},
|
||||
|
||||
// Added a Proxy Event
|
||||
// Enable it if the proxy is added in EditMonitor.vue
|
||||
/**
|
||||
* Added a Proxy Event
|
||||
* Enable it if the proxy is added in EditMonitor.vue
|
||||
* @param {number} id ID of proxy to add
|
||||
*/
|
||||
addedProxy(id) {
|
||||
this.monitor.proxyId = id;
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<transition name="slide-fade" appear>
|
||||
<MonitorList />
|
||||
<MonitorList :scrollbar="true" />
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
@@ -14,3 +14,11 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars";
|
||||
|
||||
.shadow-box {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -51,6 +51,11 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Get the correct URL for the icon
|
||||
* @param {string} icon Path for icon
|
||||
* @returns {string} Correctly formatted path including port numbers
|
||||
*/
|
||||
icon(icon) {
|
||||
if (icon === "/icon.svg") {
|
||||
return icon;
|
||||
@@ -86,6 +91,7 @@ export default {
|
||||
|
||||
.logo {
|
||||
width: $logo-width;
|
||||
height: $logo-width;
|
||||
|
||||
// Better when the image is loading
|
||||
min-height: 1px;
|
||||
|
@@ -32,6 +32,7 @@
|
||||
<ul>
|
||||
<li>{{ $t("Retype the address.") }}</li>
|
||||
<li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li>
|
||||
<li><a href="/" class="go-back">Go back to home page.</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,6 +45,7 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/** Go back 1 in browser history */
|
||||
goBack() {
|
||||
history.back();
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user