mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-12 06:26:59 +08:00
Compare commits
371 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7899707582 | ||
|
d4bfe57b79 | ||
|
dcc91d6c72 | ||
|
76611ecaca | ||
|
f802154456 | ||
|
9fb461976d | ||
|
c8e364911f | ||
|
88bc08e7b7 | ||
|
03aeab0421 | ||
|
f331f1a63e | ||
|
d645e29455 | ||
|
b4507f9706 | ||
|
fc6d0d1fca | ||
|
b62f1475ee | ||
|
d47d8517a8 | ||
|
19d2db6c8c | ||
|
5a8162747c | ||
|
220108ebc6 | ||
|
984a3704e0 | ||
|
909412c87e | ||
|
481fd3a05f | ||
|
5434e2da4f | ||
|
b3d348dcea | ||
|
0aca0455ab | ||
|
8f3ef734bc | ||
|
120eb0d85f | ||
|
4aaed0837e | ||
|
60657132c0 | ||
|
76cbef85d5 | ||
|
e17ef02008 | ||
|
f33d55c92d | ||
|
67849a9e84 | ||
|
ee79a34148 | ||
|
d2f0480889 | ||
|
c36190bba6 | ||
|
4b3fae53d4 | ||
|
4dd60cba3d | ||
|
4bc84d2122 | ||
|
a796f80018 | ||
|
40cb22e671 | ||
|
d95258e7db | ||
|
baae4b5a5e | ||
|
c1b118a0f6 | ||
|
9c5466890e | ||
|
bf8dbd78b3 | ||
|
6cd130de38 | ||
|
a864b72e03 | ||
|
5070927478 | ||
|
bedc1f8617 | ||
|
077f3837d9 | ||
|
aea128a85b | ||
|
c50b2b636a | ||
|
a284703d9e | ||
|
64ec766423 | ||
|
186c11540f | ||
|
4d947d9374 | ||
|
4888c97d86 | ||
|
50593f3edf | ||
|
c1267e9b3b | ||
|
2ca7a5b962 | ||
|
9f0c66d775 | ||
|
a1f9a82537 | ||
|
37e6ca8d77 | ||
|
0b0fd6609d | ||
|
3a32fd6f42 | ||
|
97cb060cf5 | ||
|
5afb29f8f9 | ||
|
f9b8dbf4db | ||
|
92a5f18bf5 | ||
|
dce908a07b | ||
|
4155f84eec | ||
|
94ffeeeab6 | ||
|
3d222ac5f5 | ||
|
c811c1ccde | ||
|
bd3d34400d | ||
|
5d3bf68123 | ||
|
1f77526210 | ||
|
88ed965d69 | ||
|
7f4d5a0f76 | ||
|
df813fbdee | ||
|
07742799ed | ||
|
f65cc655c0 | ||
|
1a218aaa17 | ||
|
369cad90c1 | ||
|
f9bb48de13 | ||
|
74d2b38cb6 | ||
|
7bba4fe2d0 | ||
|
be3a791e6e | ||
|
9747048890 | ||
|
d5d957b748 | ||
|
5cdb5edeb3 | ||
|
73c18b6ff0 | ||
|
567ea346fe | ||
|
453f6fbadf | ||
|
dd79042128 | ||
|
583e6bf978 | ||
|
b1fca7c1a7 | ||
|
19dd11d624 | ||
|
42ce34b6c7 | ||
|
b7a9d1474f | ||
|
31fa67452e | ||
|
9ef3727c91 | ||
|
ed39485af9 | ||
|
daef238a70 | ||
|
4cc433166e | ||
|
28f530394e | ||
|
b0615d347b | ||
|
be19336149 | ||
|
94508cae2f | ||
|
265cca9ed1 | ||
|
267654c987 | ||
|
2c85491ee0 | ||
|
5d836cf05d | ||
|
ba46fb6b1c | ||
|
5df34cd137 | ||
|
bf64095cea | ||
|
2333d1c7a7 | ||
|
95bae8289d | ||
|
45f7c647a6 | ||
|
dff1056bb1 | ||
|
62222c0336 | ||
|
733d0af75f | ||
|
b88e74fad8 | ||
|
734762b773 | ||
|
0275d7a42b | ||
|
41a6d1b701 | ||
|
34d8984e3a | ||
|
c92153c97e | ||
|
ad82ab0305 | ||
|
f952d283c6 | ||
|
e164fabf81 | ||
|
bc69a331ee | ||
|
e4506963d9 | ||
|
222540898b | ||
|
baf3612ece | ||
|
8f44b9f618 | ||
|
210566c7af | ||
|
0481a241f3 | ||
|
179ca232bc | ||
|
0dcb7aed21 | ||
|
23736549f9 | ||
|
665c263c03 | ||
|
c5e6628803 | ||
|
3a1d8ddc11 | ||
|
bc5f61b3ec | ||
|
314fa18bdc | ||
|
57389fab2c | ||
|
ee2c54cfd1 | ||
|
82cde7c847 | ||
|
1ba2034701 | ||
|
dee131c25d | ||
|
e5d6410caf | ||
|
e496c3b3be | ||
|
69f5112b38 | ||
|
c094dc0c5b | ||
|
1fb9b25d13 | ||
|
9a135deac2 | ||
|
8e6173c05e | ||
|
dec84282ed | ||
|
df80f413b5 | ||
|
17e59f1d8d | ||
|
973c2bb429 | ||
|
da0eaddeb8 | ||
|
b2bc8d9db9 | ||
|
541068ff3b | ||
|
83ee46454a | ||
|
75b21c905f | ||
|
60e12f4bfa | ||
|
191b81ee07 | ||
|
f3651a1219 | ||
|
12ef9f39c5 | ||
|
4004926e64 | ||
|
4d3d6d6e25 | ||
|
d06e5ef6fa | ||
|
b12b848d97 | ||
|
bb96a577ca | ||
|
8840ca618b | ||
|
8ec858fd14 | ||
|
124c98ce76 | ||
|
61135e8500 | ||
|
08a58dec2b | ||
|
741ed548da | ||
|
52d80d3a5d | ||
|
586c748d44 | ||
|
b5d6e96b1d | ||
|
68b74f07e4 | ||
|
bc615c2dd8 | ||
|
e7104737e7 | ||
|
1dbf1c3dea | ||
|
74688e69aa | ||
|
b32bfb3ff1 | ||
|
24664cde2c | ||
|
348c5ec995 | ||
|
9143b73f84 | ||
|
5e6d945095 | ||
|
ba93129b18 | ||
|
69aa60d1fb | ||
|
eaecd6e571 | ||
|
f2a27a2cf1 | ||
|
d4c9431142 | ||
|
d7f7dba13f | ||
|
3e5ae00d25 | ||
|
5311bef3eb | ||
|
c400595f67 | ||
|
e84f7dac60 | ||
|
67a22399bc | ||
|
947fc6001e | ||
|
c87e67ad1b | ||
|
6f92774a8f | ||
|
6e76ab7426 | ||
|
b2fbd7e263 | ||
|
199e6ec82b | ||
|
18a99c2016 | ||
|
e261a27ebe | ||
|
de5cce9d90 | ||
|
b85c9186f9 | ||
|
eb22ad5ffe | ||
|
f5f4835b74 | ||
|
44c1b336dc | ||
|
110ec491ee | ||
|
640b6e5b1c | ||
|
234fba3978 | ||
|
1285ccb537 | ||
|
6c542edfc9 | ||
|
767807dd22 | ||
|
546402f3d2 | ||
|
698a38e773 | ||
|
71884cf42a | ||
|
d676c782bb | ||
|
dd773aa5a2 | ||
|
2852e59ffb | ||
|
cb3da50e7e | ||
|
f25653d778 | ||
|
2e7ad1b7b2 | ||
|
e0e1ab6fa6 | ||
|
8d984881c9 | ||
|
955f9ae20a | ||
|
a9e319517a | ||
|
39ad8b4bb7 | ||
|
8fb8cbdaf3 | ||
|
3f3d8b4eb3 | ||
|
9123e9461f | ||
|
77addfebc8 | ||
|
16846c7c6d | ||
|
d1c4d13903 | ||
|
7cd4bfc11d | ||
|
1d5c0502ab | ||
|
a1cda93ad5 | ||
|
3bd420f0e0 | ||
|
78424b4f2d | ||
|
f8055ed03d | ||
|
fe4724fc53 | ||
|
7f0dda6a44 | ||
|
43791ee97e | ||
|
6362ef6a9c | ||
|
9d3a4e9d1e | ||
|
6c60096f56 | ||
|
ba1e025353 | ||
|
a41a081727 | ||
|
a5f15f2319 | ||
|
e69799f613 | ||
|
3c795bebe3 | ||
|
3a43fec666 | ||
|
24c645e437 | ||
|
9d31da1fe8 | ||
|
2e4c42941a | ||
|
4fc2603818 | ||
|
7bc38d4231 | ||
|
daad63d70b | ||
|
9ddd2c7365 | ||
|
fa5ba12e14 | ||
|
85053f865e | ||
|
1239f6d1a2 | ||
|
fed611d1b9 | ||
|
bc68088350 | ||
|
90200958cd | ||
|
aa13d74d7a | ||
|
d82f305f6e | ||
|
7c63cbfd84 | ||
|
c7e1267779 | ||
|
5d0b54c292 | ||
|
b50b390048 | ||
|
65158cb06b | ||
|
8fe5e4e605 | ||
|
ab5ddae2ee | ||
|
89c64f4ea2 | ||
|
40a1ebecc5 | ||
|
e1793596fe | ||
|
c489058a57 | ||
|
95342ec006 | ||
|
bdebbf8e40 | ||
|
9a9fca67d5 | ||
|
665bae0806 | ||
|
e4be28a9e7 | ||
|
445674aacb | ||
|
2f7b60f5e5 | ||
|
b83c59e308 | ||
|
ce852dfa02 | ||
|
c9549c0de2 | ||
|
957c292307 | ||
|
b5eb17ed93 | ||
|
d578300104 | ||
|
b77b33e790 | ||
|
4becb97a5d | ||
|
85e2b36424 | ||
|
abdf1ae90a | ||
|
606c967985 | ||
|
93c231b4d9 | ||
|
9ad8e5f56a | ||
|
8a481a1be0 | ||
|
657987a013 | ||
|
d74577608b | ||
|
20a399c557 | ||
|
060dde9827 | ||
|
1d1601cf24 | ||
|
ff5f2e8dfb | ||
|
5451fb7672 | ||
|
56094a43d7 | ||
|
68bbe8944a | ||
|
8f1da6aa22 | ||
|
c0d6fe0d76 | ||
|
29e4e41215 | ||
|
7a1bb964e9 | ||
|
3fe0e9bf1e | ||
|
9982887783 | ||
|
c31efc0ef4 | ||
|
6463d4b209 | ||
|
acada8028a | ||
|
d0b0c64b81 | ||
|
cd04ac4557 | ||
|
d7d2f7b7fc | ||
|
5a05d135b8 | ||
|
e03ee593e2 | ||
|
6c1ee70e15 | ||
|
5c3892313e | ||
|
c57c94642c | ||
|
62f168a2a5 | ||
|
c808f78f09 | ||
|
9c80e1c732 | ||
|
acc2995d86 | ||
|
7def9dcec7 | ||
|
a35569481d | ||
|
9ddffc0f7f | ||
|
76e7c8b276 | ||
|
572a5300aa | ||
|
e1f1d4a959 | ||
|
b5857f7c0c | ||
|
6277babf25 | ||
|
300a95d779 | ||
|
23714ab688 | ||
|
16b44001e7 | ||
|
f2f8f33b86 | ||
|
df4682d19b | ||
|
11a1f35cc5 | ||
|
2a3ce15328 | ||
|
7cb25255bf | ||
|
c622f7958f | ||
|
8cb26d2b31 | ||
|
bda481c61e | ||
|
86dcc9bc8f | ||
|
145b722aec | ||
|
79c81395bc | ||
|
89c0f8b734 | ||
|
dc805cff97 | ||
|
dc1de50a02 | ||
|
0e6d7694ce | ||
|
11bcd1e2ed | ||
|
06310423f4 | ||
|
e127e168b6 | ||
|
075535ba46 | ||
|
13cf6891ac |
@@ -1,5 +1,4 @@
|
||||
/.idea
|
||||
/dist
|
||||
/node_modules
|
||||
/data
|
||||
/out
|
||||
|
21
.github/ISSUE_TEMPLATE/ask-for-help.md
vendored
21
.github/ISSUE_TEMPLATE/ask-for-help.md
vendored
@@ -1,21 +0,0 @@
|
||||
---
|
||||
name: Ask for help
|
||||
about: You can ask any question related to Uptime Kuma.
|
||||
title: ''
|
||||
labels: help
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Is it a duplicate question?**
|
||||
Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q=
|
||||
|
||||
**Describe your problem**
|
||||
Please describe what you are asking for
|
||||
|
||||
**Info**
|
||||
Uptime Kuma Version:
|
||||
Using Docker?: Yes/No
|
||||
Docker Version:
|
||||
Node.js Version (Without Docker only):
|
||||
OS:
|
||||
Browser:
|
68
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
Normal file
68
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: "❓ Ask for help"
|
||||
description: "Submit any question related to Uptime Kuma"
|
||||
#title: "[Help] "
|
||||
labels: [help]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "⚠️ Please verify that this bug has NOT been raised before."
|
||||
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
|
||||
options:
|
||||
- label: "I checked and didn't find similar issue"
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "🛡️ Security Policy"
|
||||
description: Please review the security policy before reporting security related issues/bugs.
|
||||
options:
|
||||
- label: I agree to have read this project [Security Policy](https://github.com/louislam/uptime-kuma/security/policy)
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "📝 Describe your problem"
|
||||
description: "Please walk us through it step by step."
|
||||
placeholder: "Describe what are you asking for..."
|
||||
- type: input
|
||||
id: uptime-kuma-version
|
||||
attributes:
|
||||
label: "🐻 Uptime-Kuma Version"
|
||||
description: "Which version of Uptime-Kuma are you running? Please do NOT provide the docker tag such as latest or 1"
|
||||
placeholder: "Ex. 1.10.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: operating-system
|
||||
attributes:
|
||||
label: "💻 Operating System and Arch"
|
||||
description: "Which OS is your server/device running on?"
|
||||
placeholder: "Ex. Ubuntu 20.04 x86"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser-vendor
|
||||
attributes:
|
||||
label: "🌐 Browser"
|
||||
description: "Which browser are you running on?"
|
||||
placeholder: "Ex. Google Chrome 95.0.4638.69"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: docker-version
|
||||
attributes:
|
||||
label: "🐋 Docker Version"
|
||||
description: "If running with Docker, which version are you running?"
|
||||
placeholder: "Ex. Docker 20.10.9 / K8S / Podman"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: nodejs-version
|
||||
attributes:
|
||||
label: "🟩 NodeJS Version"
|
||||
description: "If running with Node.js? which version are you running?"
|
||||
placeholder: "Ex. 14.18.0"
|
||||
validations:
|
||||
required: false
|
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,42 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is it a duplicate question?**
|
||||
Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q=
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Info**
|
||||
Uptime Kuma Version:
|
||||
Using Docker?: Yes/No
|
||||
Docker Version:
|
||||
Node.js Version (Without Docker only):
|
||||
OS:
|
||||
Browser:
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Error Log**
|
||||
It is easier for us to find out the problem.
|
||||
|
||||
Docker: `docker logs <container id>`
|
||||
PM2: `~/.pm2/logs/` (e.g. `/home/ubuntu/.pm2/logs`)
|
99
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
99
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: "🐛 Bug Report"
|
||||
description: "Submit a bug report to help us improve"
|
||||
#title: "[Bug] "
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "⚠️ Please verify that this bug has NOT been raised before."
|
||||
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
|
||||
options:
|
||||
- label: "I checked and didn't find similar issue"
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: "🛡️ Security Policy"
|
||||
description: Please review the security policy before reporting security related issues/bugs.
|
||||
options:
|
||||
- label: I agree to have read this project [Security Policy](https://github.com/louislam/uptime-kuma/security/policy)
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "Description"
|
||||
description: "You could also upload screenshots"
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "👟 Reproduction steps"
|
||||
description: "How do you trigger this bug? Please walk us through it step by step."
|
||||
placeholder: "..."
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "👀 Expected behavior"
|
||||
description: "What did you think would happen?"
|
||||
placeholder: "..."
|
||||
- type: textarea
|
||||
id: actual-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "😓 Actual Behavior"
|
||||
description: "What actually happen?"
|
||||
placeholder: "..."
|
||||
- type: input
|
||||
id: uptime-kuma-version
|
||||
attributes:
|
||||
label: "🐻 Uptime-Kuma Version"
|
||||
description: "Which version of Uptime-Kuma are you running? Please do NOT provide the docker tag such as latest or 1"
|
||||
placeholder: "Ex. 1.10.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: operating-system
|
||||
attributes:
|
||||
label: "💻 Operating System and Arch"
|
||||
description: "Which OS is your server/device running on?"
|
||||
placeholder: "Ex. Ubuntu 20.04 x86"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser-vendor
|
||||
attributes:
|
||||
label: "🌐 Browser"
|
||||
description: "Which browser are you running on?"
|
||||
placeholder: "Ex. Google Chrome 95.0.4638.69"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: docker-version
|
||||
attributes:
|
||||
label: "🐋 Docker Version"
|
||||
description: "If running with Docker, which version are you running?"
|
||||
placeholder: "Ex. Docker 20.10.9 / K8S / Podman"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: nodejs-version
|
||||
attributes:
|
||||
label: "🟩 NodeJS Version"
|
||||
description: "If running with Node.js? which version are you running?"
|
||||
placeholder: "Ex. 14.18.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: "📝 Relevant log output"
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Is it a duplicate question?**
|
||||
Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q=
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
59
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
59
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: 🚀 Feature Request
|
||||
description: "Submit a proposal for a new feature"
|
||||
#title: "[Feature] "
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "⚠️ Please verify that this feature request has NOT been suggested before."
|
||||
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
|
||||
options:
|
||||
- label: "I checked and didn't find similar feature request"
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: feature-area
|
||||
attributes:
|
||||
label: "🏷️ Feature Request Type"
|
||||
description: "What kind of feature request is this?"
|
||||
multiple: true
|
||||
options:
|
||||
- API
|
||||
- New Notification
|
||||
- New Monitor
|
||||
- UI Feature
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "🔖 Feature description"
|
||||
description: "A clear and concise description of what the feature request is."
|
||||
placeholder: "You should add ..."
|
||||
- type: textarea
|
||||
id: solution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "✔️ Solution"
|
||||
description: "A clear and concise description of what you want to happen."
|
||||
placeholder: "In my use-case, ..."
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "❓ Alternatives"
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
placeholder: "I have considered ..."
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "📝 Additional Context"
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
placeholder: "..."
|
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Description
|
||||
|
||||
Fixes #(issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- Bug fix (non-breaking change which fixes an issue)
|
||||
- User Interface
|
||||
- New feature (non-breaking change which adds functionality)
|
||||
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- Translation update
|
||||
- Other
|
||||
- This change requires a documentation update
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I ran ESLint and other linters for modified files
|
||||
- [ ] I have performed a self-review of my own code and test it
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] My code needed automated testing. I have added them (this is optional task)
|
||||
|
||||
## Screenshots (if any)
|
||||
|
||||
Please do not use any external image service. Instead, just paste in or drag and drop the image here, and it will be uploaded automatically.
|
2
.github/workflows/auto-test.yml
vendored
2
.github/workflows/auto-test.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
node-version: [14.x, 16.x]
|
||||
node-version: [14.x, 16.x, 17.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
|
22
.github/workflows/stale-bot.yml
vendored
Normal file
22
.github/workflows/stale-bot.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: 'Automatically close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
#Run once a day at midnight
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
stale-pr-message: 'We are clearing up our old Pull Requests and yours has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
|
||||
days-before-stale: 180
|
||||
days-before-close: 7
|
||||
exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,'
|
||||
exempt-pr-labels: 'awaiting-approval,work-in-progress,enhancement,'
|
||||
exempt-issue-assignees: 'louislam'
|
||||
exempt-pr-assignees: 'louislam'
|
@@ -1,8 +1,8 @@
|
||||
# Project Info
|
||||
|
||||
First of all, thank you everyone who made pull requests for Uptime Kuma, I never thought GitHub Community can be that nice! And also because of this, I also never thought other people actually read my code and edit my code. It is not structed and commented so well, lol. Sorry about that.
|
||||
First of all, thank you everyone who made pull requests for Uptime Kuma, I never thought GitHub Community can be that nice! And also because of this, I also never thought other people actually read my code and edit my code. It is not structured and commented so well, lol. Sorry about that.
|
||||
|
||||
The project was created with vite.js (vue3). Then I created a sub-directory called "server" for server part. Both frontend and backend share the same package.json.
|
||||
The project was created with vite.js (vue3). Then I created a subdirectory called "server" for server part. Both frontend and backend share the same package.json.
|
||||
|
||||
The frontend code build into "dist" directory. The server (express.js) exposes the "dist" directory as root of the endpoint. This is how production is working.
|
||||
|
||||
@@ -27,18 +27,35 @@ The frontend code build into "dist" directory. The server (express.js) exposes t
|
||||
|
||||
## Can I create a pull request for Uptime Kuma?
|
||||
|
||||
Generally, if the pull request is working fine and it do not affect any existing logic, workflow and perfomance, I will merge into the master branch once it is tested.
|
||||
Generally, if the pull request is working fine, and it does not affect any existing logic, workflow and performance, I will merge into the master branch once it is tested.
|
||||
|
||||
If you are not sure, feel free to create an empty pull request draft first.
|
||||
If you are not sure whether I will accept your pull request, feel free to create an empty pull request draft first.
|
||||
|
||||
### Recommended Pull Request Guideline
|
||||
|
||||
1. Fork the project
|
||||
1. Clone your fork repo to local
|
||||
1. Create a new branch
|
||||
1. Create an empty commit
|
||||
`git commit -m "[empty commit] pull request for <YOUR TASK NAME>" --allow-empty`
|
||||
1. Push to your fork repo
|
||||
1. Create a pull request: https://github.com/louislam/uptime-kuma/compare
|
||||
1. Write a proper description
|
||||
1. Click "Change to draft"
|
||||
|
||||
### Pull Request Examples
|
||||
|
||||
Here are some example situations in the past.
|
||||
|
||||
#### ✅ High - Medium Priority
|
||||
|
||||
Easy to review, no breaking change and not touching the existing code
|
||||
|
||||
- Add a new notification
|
||||
- Add a chart
|
||||
- Fix a bug
|
||||
- Translations
|
||||
- Add a independent new feature
|
||||
|
||||
#### *️⃣ Requires one more reviewer
|
||||
|
||||
@@ -46,6 +63,16 @@ I do not have such knowledge to test it.
|
||||
|
||||
- Add k8s supports
|
||||
|
||||
#### ⚠ Low Priority - Harsh Mode
|
||||
|
||||
Some pull requests are required to modify the core. To be honest, I do not want anyone to try to do that, because it would spend a lot of your time. I will review your pull request harshly. Also, you may need to write a lot of unit tests to ensure that there is no breaking change.
|
||||
|
||||
- Touch large parts of code of any very important features
|
||||
- Touch monitoring logic
|
||||
- Drop a table or drop a column for any reason
|
||||
- Touch the entry point of Docker or Node.js
|
||||
- Modify auth
|
||||
|
||||
#### *️⃣ Low Priority
|
||||
|
||||
It changed my current workflow and require further studies.
|
||||
@@ -54,6 +81,7 @@ It changed my current workflow and require further studies.
|
||||
|
||||
#### ❌ Won't Merge
|
||||
|
||||
- Any breaking changes
|
||||
- Duplicated pull request
|
||||
- Buggy
|
||||
- Existing logic is completely modified or deleted
|
||||
@@ -84,7 +112,7 @@ I personally do not like something need to learn so much and need to config so m
|
||||
|
||||
- Node.js >= 14
|
||||
- Git
|
||||
- IDE that supports ESLint and EditorConfig (I am using Intellji Idea)
|
||||
- IDE that supports ESLint and EditorConfig (I am using IntelliJ IDEA)
|
||||
- A SQLite tool (SQLite Expert Personal is suggested)
|
||||
|
||||
## Install dependencies
|
||||
@@ -111,9 +139,9 @@ express.js is just used for serving the frontend built files (index.html, .js an
|
||||
|
||||
- model/ (Object model, auto mapping to the database table name)
|
||||
- modules/ (Modified 3rd-party modules)
|
||||
- notification-providers/ (indivdual notification logic)
|
||||
- notification-providers/ (individual notification logic)
|
||||
- routers/ (Express Routers)
|
||||
- scoket-handler (Socket.io Handlers)
|
||||
- socket-handler (Socket.io Handlers)
|
||||
- server.js (Server main logic)
|
||||
|
||||
## How to start the Frontend Dev Server
|
||||
@@ -171,10 +199,56 @@ ncu -u -t patch
|
||||
npm install
|
||||
```
|
||||
|
||||
Since previously updating vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only.
|
||||
Since previously updating Vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only.
|
||||
|
||||
Patch release = the third digit ([Semantic Versioning](https://semver.org/))
|
||||
|
||||
## Translations
|
||||
|
||||
Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages
|
||||
|
||||
## Wiki
|
||||
|
||||
Since there is no way to make a pull request to wiki's repo, I have set up another repo to do that.
|
||||
|
||||
https://github.com/louislam/uptime-kuma-wiki
|
||||
|
||||
## Maintainer
|
||||
|
||||
Check the latest issues and pull requests:
|
||||
https://github.com/louislam/uptime-kuma/issues?q=sort%3Aupdated-desc
|
||||
|
||||
### Release Procedures
|
||||
|
||||
1. Draft a release note
|
||||
1. Make sure the repo is cleared
|
||||
1. `npm run update-version 1.X.X`
|
||||
1. `npm run build`
|
||||
1. `npm run build-docker`
|
||||
1. `git push`
|
||||
1. Publish the release note as 1.X.X
|
||||
1. `npm run upload-artifacts`
|
||||
1. SSH to demo site server and update to 1.X.X
|
||||
|
||||
Checking:
|
||||
|
||||
- Check all tags is fine on https://hub.docker.com/r/louislam/uptime-kuma/tags
|
||||
- Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 / armv7)
|
||||
- Try clean installation with Node.js
|
||||
|
||||
### Release Wiki
|
||||
|
||||
#### Setup Repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/louislam/uptime-kuma-wiki.git
|
||||
cd uptime-kuma-wiki
|
||||
git remote add production https://github.com/louislam/uptime-kuma.wiki.git
|
||||
```
|
||||
|
||||
#### Push to Production Wiki
|
||||
|
||||
```bash
|
||||
git pull
|
||||
git push production master
|
||||
```
|
||||
|
17
README.md
17
README.md
@@ -1,6 +1,7 @@
|
||||
# Uptime Kuma
|
||||
|
||||
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Backers&color=brightgreen" /></a>
|
||||
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a>
|
||||
[](https://github.com/sponsors/louislam)
|
||||
|
||||
<div align="center" width="100%">
|
||||
<img src="./public/icon.svg" width="128" alt="" />
|
||||
@@ -16,13 +17,13 @@ Try it!
|
||||
|
||||
https://demo.uptime.kuma.pet
|
||||
|
||||
It is a temporary live demo, all data will be deleted after 10 minutes. The server is located at Tokyo, so if you live far from there it may affect your experience. I suggest that you should install and try it out for the best demo experience.
|
||||
It is a temporary live demo, all data will be deleted after 10 minutes. The server is located in Tokyo, so if you live far from there, it may affect your experience. I suggest that you should install and try it out for the best demo experience.
|
||||
|
||||
VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollective.com/uptime-kuma)! Thank you so much!
|
||||
|
||||
## ⭐ Features
|
||||
|
||||
* Monitoring uptime for HTTP(s) / TCP / Ping / DNS Record / Push.
|
||||
* Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / Ping / DNS Record / Push / Steam Game Server.
|
||||
* Fancy, Reactive, Fast UI/UX.
|
||||
* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [70+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications).
|
||||
* 20 second intervals.
|
||||
@@ -42,7 +43,7 @@ docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name upti
|
||||
|
||||
Browse to http://localhost:3001 after starting.
|
||||
|
||||
### 💪🏻 Without Docker
|
||||
### 💪🏻 Non-Docker
|
||||
|
||||
Required Tools: Node.js >= 14, git and pm2.
|
||||
|
||||
@@ -66,7 +67,7 @@ Browse to http://localhost:3001 after starting.
|
||||
|
||||
### Advanced Installation
|
||||
|
||||
If you need more options or need to browse via a reserve proxy, please read:
|
||||
If you need more options or need to browse via a reverse proxy, please read:
|
||||
|
||||
https://github.com/louislam/uptime-kuma/wiki/%F0%9F%94%A7-How-to-Install
|
||||
|
||||
@@ -119,7 +120,7 @@ If you love this project, please consider giving me a ⭐.
|
||||
|
||||
### Issues Page
|
||||
|
||||
You can discuss or ask for help in [Issues](https://github.com/louislam/uptime-kuma/issues).
|
||||
You can discuss or ask for help in [issues](https://github.com/louislam/uptime-kuma/issues).
|
||||
|
||||
### Subreddit
|
||||
|
||||
@@ -131,8 +132,8 @@ https://www.reddit.com/r/UptimeKuma/
|
||||
|
||||
If you want to report a bug or request a new feature. Free feel to open a [new issue](https://github.com/louislam/uptime-kuma/issues).
|
||||
|
||||
If you want to translate Uptime Kuma into your langauge, please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages
|
||||
If you want to translate Uptime Kuma into your language, please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages
|
||||
|
||||
If you want to modify Uptime Kuma, this guideline may be useful for you: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md
|
||||
|
||||
English proofreading is needed too because my grammar is not that great sadly. Feel free to correct my grammar in this readme, source code, or wiki.
|
||||
English proofreading is needed too because my grammar is not that great, sadly. Feel free to correct my grammar in this README, source code, or wiki.
|
||||
|
16
SECURITY.md
16
SECURITY.md
@@ -1,5 +1,11 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to uptime@kuma.pet.
|
||||
|
||||
Do not use the issue tracker or discuss it in the public as it will cause more damage.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
@@ -9,8 +15,8 @@ currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.8.X | :white_check_mark: |
|
||||
| <= 1.7.X | ❌ |
|
||||
| 1.9.X | :white_check_mark: |
|
||||
| <= 1.8.X | ❌ |
|
||||
|
||||
### Upgradable Docker Tags
|
||||
|
||||
@@ -23,9 +29,3 @@ currently being supported with security updates.
|
||||
| debian | :white_check_mark: |
|
||||
| alpine | :white_check_mark: |
|
||||
| All other tags | ❌ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to uptime@kuma.pet.
|
||||
|
||||
Do not use the issue tracker or discuss it in the public as it will cause more damage.
|
||||
|
33
config/jest-debug-env.js
Normal file
33
config/jest-debug-env.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const PuppeteerEnvironment = require("jest-environment-puppeteer");
|
||||
const util = require("util");
|
||||
|
||||
class DebugEnv extends PuppeteerEnvironment {
|
||||
async handleTestEvent(event, state) {
|
||||
const ignoredEvents = [
|
||||
"setup",
|
||||
"add_hook",
|
||||
"start_describe_definition",
|
||||
"add_test",
|
||||
"finish_describe_definition",
|
||||
"run_start",
|
||||
"run_describe_start",
|
||||
"test_start",
|
||||
"hook_start",
|
||||
"hook_success",
|
||||
"test_fn_start",
|
||||
"test_fn_success",
|
||||
"test_done",
|
||||
"run_describe_finish",
|
||||
"run_finish",
|
||||
"teardown",
|
||||
"test_fn_failure",
|
||||
];
|
||||
if (!ignoredEvents.includes(event.name)) {
|
||||
console.log(
|
||||
new Date().toString() + ` Unhandled event [${event.name}] ` + util.inspect(event)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DebugEnv;
|
@@ -1,6 +1,20 @@
|
||||
module.exports = {
|
||||
"launch": {
|
||||
"dumpio": true,
|
||||
"slowMo": 500,
|
||||
"headless": process.env.HEADLESS_TEST || false,
|
||||
"userDataDir": "./data/test-chrome-profile",
|
||||
args: [
|
||||
"--disable-setuid-sandbox",
|
||||
"--disable-gpu",
|
||||
"--disable-dev-shm-usage",
|
||||
"--no-default-browser-check",
|
||||
"--no-experiments",
|
||||
"--no-first-run",
|
||||
"--no-pings",
|
||||
"--no-sandbox",
|
||||
"--no-zygote",
|
||||
"--single-process",
|
||||
],
|
||||
}
|
||||
};
|
||||
|
@@ -5,6 +5,7 @@ module.exports = {
|
||||
"__DEV__": true
|
||||
},
|
||||
"testRegex": "./test/e2e.spec.js",
|
||||
"testEnvironment": "./config/jest-debug-env.js",
|
||||
"rootDir": "..",
|
||||
"testTimeout": 30000,
|
||||
};
|
||||
|
7
db/patch-2fa-invalidate-used-token.sql
Normal file
7
db/patch-2fa-invalidate-used-token.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE user
|
||||
ADD twofa_last_token VARCHAR(6);
|
||||
|
||||
COMMIT;
|
10
db/patch-monitor-basic-auth.sql
Normal file
10
db/patch-monitor-basic-auth.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD basic_auth_user TEXT default null;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD basic_auth_pass TEXT default null;
|
||||
|
||||
COMMIT;
|
18
db/patch-notification_sent_history.sql
Normal file
18
db/patch-notification_sent_history.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
CREATE TABLE [notification_sent_history] (
|
||||
[id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
[type] VARCHAR(50) NOT NULL,
|
||||
[monitor_id] INTEGER NOT NULL,
|
||||
[days] INTEGER NOT NULL,
|
||||
UNIQUE([type], [monitor_id], [days])
|
||||
);
|
||||
|
||||
CREATE INDEX [good_index] ON [notification_sent_history] (
|
||||
[type],
|
||||
[monitor_id],
|
||||
[days]
|
||||
);
|
||||
|
||||
COMMIT;
|
@@ -4,5 +4,5 @@ WORKDIR /app
|
||||
|
||||
# Install apprise, iputils for non-root ping, setpriv
|
||||
RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \
|
||||
pip3 --no-cache-dir install apprise && \
|
||||
pip3 --no-cache-dir install apprise==0.9.6 && \
|
||||
rm -rf /root/.cache
|
||||
|
@@ -4,9 +4,9 @@ FROM node:14-buster-slim
|
||||
WORKDIR /app
|
||||
|
||||
# Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv
|
||||
# Stupid python3 and python3-pip actually install a lot of useless things into Debian, specific --no-install-recommends to skip them, make the base even smaller than alpine!
|
||||
# Stupid python3 and python3-pip actually install a lot of useless things into Debian, specify --no-install-recommends to skip them, make the base even smaller than alpine!
|
||||
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 && \
|
||||
pip3 --no-cache-dir install apprise==0.9.6 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
@@ -4,9 +4,7 @@ WORKDIR /app
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
||||
|
||||
COPY . .
|
||||
RUN npm ci && \
|
||||
npm run build && \
|
||||
npm ci --production && \
|
||||
RUN npm ci --production && \
|
||||
chmod +x /app/extra/entrypoint.sh
|
||||
|
||||
|
||||
@@ -22,23 +20,26 @@ HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD nod
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"]
|
||||
CMD ["node", "server/server.js"]
|
||||
|
||||
|
||||
FROM release AS nightly
|
||||
RUN npm run mark-as-nightly
|
||||
|
||||
|
||||
# Upload the artifact to Github
|
||||
FROM louislam/uptime-kuma:base-debian AS upload-artifact
|
||||
WORKDIR /
|
||||
RUN apt update && \
|
||||
apt --yes install curl file
|
||||
|
||||
COPY --from=build /app /app
|
||||
|
||||
ARG VERSION=1.9.1
|
||||
ARG GITHUB_TOKEN
|
||||
ARG TARGETARCH
|
||||
ARG PLATFORM=debian
|
||||
ARG VERSION=1.9.0
|
||||
ARG FILE=$PLATFORM-$TARGETARCH-$VERSION.tar.gz
|
||||
ARG DIST=dist.tar.gz
|
||||
|
||||
COPY --from=build /app /app
|
||||
RUN chmod +x /app/extra/upload-github-release-asset.sh
|
||||
|
||||
# Full Build
|
||||
|
@@ -4,9 +4,7 @@ WORKDIR /app
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
||||
|
||||
COPY . .
|
||||
RUN npm ci && \
|
||||
npm run build && \
|
||||
npm ci --production && \
|
||||
RUN npm ci --production && \
|
||||
chmod +x /app/extra/entrypoint.sh
|
||||
|
||||
|
||||
@@ -22,5 +20,6 @@ HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD nod
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"]
|
||||
CMD ["node", "server/server.js"]
|
||||
|
||||
|
||||
FROM release AS nightly
|
||||
RUN npm run mark-as-nightly
|
||||
|
@@ -34,9 +34,11 @@ function download(url) {
|
||||
});
|
||||
|
||||
tarStream.on("close", () => {
|
||||
fs.rmdirSync("./dist-backup", {
|
||||
recursive: true
|
||||
});
|
||||
if (fs.existsSync("./dist-backup")) {
|
||||
fs.rmdirSync("./dist-backup", {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
console.log("Done");
|
||||
});
|
||||
|
||||
@@ -44,7 +46,7 @@ function download(url) {
|
||||
if (fs.existsSync("./dist-backup")) {
|
||||
fs.renameSync("./dist-backup", "./dist");
|
||||
}
|
||||
console.log("Done");
|
||||
console.error("Error from tarStream");
|
||||
});
|
||||
|
||||
response.pipe(tarStream);
|
||||
|
@@ -1,25 +1,41 @@
|
||||
/*
|
||||
* This script should be run after a period of time (180s), because the server may need some time to prepare.
|
||||
*/
|
||||
const { FBSD } = require("../server/util-server");
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
|
||||
let client;
|
||||
|
||||
if (process.env.SSL_KEY && process.env.SSL_CERT) {
|
||||
const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
|
||||
const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
|
||||
|
||||
if (sslKey && sslCert) {
|
||||
client = require("https");
|
||||
} else {
|
||||
client = require("http");
|
||||
}
|
||||
|
||||
// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
|
||||
// Dual-stack support for (::)
|
||||
let hostname = process.env.UPTIME_KUMA_HOST;
|
||||
|
||||
// Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
|
||||
if (!hostname && !FBSD) {
|
||||
hostname = process.env.HOST;
|
||||
}
|
||||
|
||||
const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || 3001);
|
||||
|
||||
let options = {
|
||||
host: process.env.HOST || "127.0.0.1",
|
||||
port: parseInt(process.env.PORT) || 3001,
|
||||
host: hostname || "127.0.0.1",
|
||||
port: port,
|
||||
timeout: 28 * 1000,
|
||||
};
|
||||
|
||||
let request = client.request(options, (res) => {
|
||||
console.log(`Health Check OK [Res Code: ${res.statusCode}]`);
|
||||
if (res.statusCode === 200) {
|
||||
if (res.statusCode === 302) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(1);
|
||||
|
60
extra/remove-2fa.js
Normal file
60
extra/remove-2fa.js
Normal file
@@ -0,0 +1,60 @@
|
||||
console.log("== Uptime Kuma Remove 2FA Tool ==");
|
||||
console.log("Loading the database");
|
||||
|
||||
const Database = require("../server/database");
|
||||
const { R } = require("redbean-node");
|
||||
const readline = require("readline");
|
||||
const TwoFA = require("../server/2fa");
|
||||
const args = require("args-parser")(process.argv);
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
Database.init(args);
|
||||
await Database.connect();
|
||||
|
||||
try {
|
||||
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
|
||||
if (!process.env.TEST_BACKEND) {
|
||||
const user = await R.findOne("user");
|
||||
if (! user) {
|
||||
throw new Error("user not found, have you installed?");
|
||||
}
|
||||
|
||||
console.log("Found user: " + user.username);
|
||||
|
||||
let ans = await question("Are you sure want to remove 2FA? [y/N]");
|
||||
|
||||
if (ans.toLowerCase() === "y") {
|
||||
await TwoFA.disable2FA(user.id);
|
||||
console.log("2FA has been removed successfully.");
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error: " + e.message);
|
||||
}
|
||||
|
||||
await Database.close();
|
||||
rl.close();
|
||||
|
||||
console.log("Finished.");
|
||||
};
|
||||
|
||||
function question(question) {
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!process.env.TEST_BACKEND) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
main,
|
||||
};
|
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" id="theme-color" content="" />
|
||||
<meta name="description" content="Uptime Kuma monitoring tool" />
|
||||
<title>Uptime Kuma</title>
|
||||
|
11569
package-lock.json
generated
11569
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
61
package.json
61
package.json
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "1.9.2",
|
||||
"version": "1.11.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/louislam/uptime-kuma.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.*"
|
||||
"node": "14.* || >=16.*"
|
||||
},
|
||||
"scripts": {
|
||||
"install-legacy": "npm install --legacy-peer-deps",
|
||||
@@ -22,7 +22,7 @@
|
||||
"build": "vite build --config ./config/vite.config.js",
|
||||
"test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
|
||||
"test-with-build": "npm run build && npm test",
|
||||
"jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend && jest --config=./config/jest.config.js",
|
||||
"jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend",
|
||||
"jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js",
|
||||
"jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js",
|
||||
"tsc": "tsc",
|
||||
@@ -30,17 +30,18 @@
|
||||
"build-docker": "npm run build-docker-debian && npm run build-docker-alpine",
|
||||
"build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
|
||||
"build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
|
||||
"build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.9.2-alpine --target release . --push",
|
||||
"build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.9.2 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.9.2-debian --target release . --push",
|
||||
"build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.11.0-alpine --target release . --push",
|
||||
"build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.0 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.0-debian --target release . --push",
|
||||
"build-docker-nightly": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
|
||||
"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 GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||
"setup": "git checkout 1.9.2 && npm ci --production && npm run download-dist",
|
||||
"setup": "git checkout 1.11.0 && npm ci --production && npm run download-dist",
|
||||
"download-dist": "node extra/download-dist.js",
|
||||
"update-version": "node extra/update-version.js",
|
||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||
"reset-password": "node extra/reset-password.js",
|
||||
"remove-2fa": "node extra/remove-2fa.js",
|
||||
"compile-install-script": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./extra/compile-install-script.ps1",
|
||||
"test-install-script-centos7": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/centos7.dockerfile .",
|
||||
"test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .",
|
||||
@@ -49,80 +50,84 @@
|
||||
"test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
|
||||
"simple-dns-server": "node extra/simple-dns-server.js",
|
||||
"update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
|
||||
"update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix"
|
||||
"update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix",
|
||||
"ncu-patch": "ncu -u -t patch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "~1.2.36",
|
||||
"@fortawesome/free-regular-svg-icons": "~5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "~5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "~3.0.0-4",
|
||||
"@louislam/sqlite3": "~6.0.0",
|
||||
"@fortawesome/vue-fontawesome": "~3.0.0-5",
|
||||
"@louislam/sqlite3": "~6.0.1",
|
||||
"@popperjs/core": "~2.10.2",
|
||||
"args-parser": "~1.3.0",
|
||||
"axios": "~0.21.4",
|
||||
"bcryptjs": "~2.4.3",
|
||||
"bootstrap": "~5.1.1",
|
||||
"bootstrap": "5.1.3",
|
||||
"bree": "~7.1.0",
|
||||
"chardet": "^1.3.0",
|
||||
"bree": "~6.3.1",
|
||||
"chart.js": "~3.5.1",
|
||||
"chart.js": "~3.6.0",
|
||||
"chartjs-adapter-dayjs": "~1.0.0",
|
||||
"check-password-strength": "^2.0.3",
|
||||
"command-exists": "~1.2.9",
|
||||
"compare-versions": "~3.6.0",
|
||||
"dayjs": "~1.10.7",
|
||||
"express": "~4.17.1",
|
||||
"express-basic-auth": "~1.2.0",
|
||||
"form-data": "~4.0.0",
|
||||
"http-graceful-shutdown": "~3.1.4",
|
||||
"http-graceful-shutdown": "~3.1.5",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"limiter": "^2.1.0",
|
||||
"nodemailer": "~6.6.5",
|
||||
"notp": "~2.0.3",
|
||||
"password-hash": "~1.2.2",
|
||||
"postcss-rtlcss": "~3.4.1",
|
||||
"postcss-scss": "~4.0.1",
|
||||
"postcss-scss": "~4.0.2",
|
||||
"prom-client": "~13.2.0",
|
||||
"prometheus-api-metrics": "~3.2.0",
|
||||
"qrcode": "~1.4.4",
|
||||
"redbean-node": "0.1.2",
|
||||
"qrcode": "~1.5.0",
|
||||
"redbean-node": "0.1.3",
|
||||
"socket.io": "~4.2.0",
|
||||
"socket.io-client": "~4.2.0",
|
||||
"tar": "^6.1.11",
|
||||
"tcp-ping": "~0.1.1",
|
||||
"thirty-two": "~1.0.2",
|
||||
"timezones-list": "~3.0.1",
|
||||
"v-pagination-3": "~0.1.6",
|
||||
"v-pagination-3": "~0.1.7",
|
||||
"vue": "next",
|
||||
"vue-chart-3": "~0.5.8",
|
||||
"vue-chart-3": "~0.5.11",
|
||||
"vue-confirm-dialog": "~1.0.2",
|
||||
"vue-contenteditable": "~3.0.4",
|
||||
"vue-i18n": "~9.1.9",
|
||||
"vue-image-crop-upload": "~3.0.3",
|
||||
"vue-multiselect": "~3.0.0-alpha.2",
|
||||
"vue-qrcode": "~1.0.0",
|
||||
"vue-router": "~4.0.11",
|
||||
"vue-toastification": "~2.0.0-rc.1",
|
||||
"vue-router": "~4.0.12",
|
||||
"vue-toastification": "~2.0.0-rc.5",
|
||||
"vuedraggable": "~4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "~7.15.7",
|
||||
"@babel/eslint-parser": "~7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@types/bootstrap": "~5.1.6",
|
||||
"@vitejs/plugin-legacy": "~1.6.1",
|
||||
"@vitejs/plugin-vue": "~1.9.2",
|
||||
"@vue/compiler-sfc": "~3.2.19",
|
||||
"@vitejs/plugin-legacy": "~1.6.3",
|
||||
"@vitejs/plugin-vue": "~1.9.4",
|
||||
"@vue/compiler-sfc": "~3.2.22",
|
||||
"babel-plugin-rewire": "~1.2.0",
|
||||
"core-js": "~3.18.1",
|
||||
"core-js": "~3.18.3",
|
||||
"cross-env": "~7.0.3",
|
||||
"dns2": "~2.0.1",
|
||||
"eslint": "~7.32.0",
|
||||
"eslint-plugin-vue": "~7.18.0",
|
||||
"jest": "~27.2.4",
|
||||
"jest": "~27.2.5",
|
||||
"jest-puppeteer": "~6.0.0",
|
||||
"puppeteer": "~10.4.0",
|
||||
"sass": "~1.42.1",
|
||||
"stylelint": "~13.13.1",
|
||||
"stylelint-config-standard": "~22.0.0",
|
||||
"typescript": "~4.4.3",
|
||||
"vite": "~2.6.4"
|
||||
"typescript": "~4.4.4",
|
||||
"vite": "~2.6.14"
|
||||
}
|
||||
}
|
||||
|
14
server/2fa.js
Normal file
14
server/2fa.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const { checkLogin } = require("./util-server");
|
||||
const { R } = require("redbean-node");
|
||||
|
||||
class TwoFA {
|
||||
|
||||
static async disable2FA(userID) {
|
||||
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
|
||||
userID,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = TwoFA;
|
@@ -1,8 +1,9 @@
|
||||
const basicAuth = require("express-basic-auth")
|
||||
const basicAuth = require("express-basic-auth");
|
||||
const passwordHash = require("./password-hash");
|
||||
const { R } = require("redbean-node");
|
||||
const { setting } = require("./util-server");
|
||||
const { debug } = require("../src/util");
|
||||
const { loginRateLimiter } = require("./rate-limiter");
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -13,7 +14,7 @@ const { debug } = require("../src/util");
|
||||
exports.login = async function (username, password) {
|
||||
let user = await R.findOne("user", " username = ? AND active = 1 ", [
|
||||
username,
|
||||
])
|
||||
]);
|
||||
|
||||
if (user && passwordHash.verify(password, user.password)) {
|
||||
// Upgrade the hash to bcrypt
|
||||
@@ -27,21 +28,30 @@ exports.login = async function (username, password) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
function myAuthorizer(username, password, callback) {
|
||||
|
||||
setting("disableAuth").then((result) => {
|
||||
|
||||
if (result) {
|
||||
callback(null, true)
|
||||
callback(null, true);
|
||||
} else {
|
||||
exports.login(username, password).then((user) => {
|
||||
callback(null, user != null)
|
||||
})
|
||||
}
|
||||
})
|
||||
// Login Rate Limit
|
||||
loginRateLimiter.pass(null, 0).then((pass) => {
|
||||
if (pass) {
|
||||
exports.login(username, password).then((user) => {
|
||||
callback(null, user != null);
|
||||
|
||||
if (user == null) {
|
||||
loginRateLimiter.removeTokens(1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.basicAuth = basicAuth({
|
||||
|
@@ -9,18 +9,17 @@ let interval;
|
||||
exports.startInterval = () => {
|
||||
let check = async () => {
|
||||
try {
|
||||
const res = await axios.get("https://raw.githubusercontent.com/louislam/uptime-kuma/master/package.json");
|
||||
|
||||
if (typeof res.data === "string") {
|
||||
res.data = JSON.parse(res.data);
|
||||
}
|
||||
const res = await axios.get("https://uptime.kuma.pet/version");
|
||||
|
||||
// For debug
|
||||
if (process.env.TEST_CHECK_VERSION === "1") {
|
||||
res.data.version = "1000.0.0";
|
||||
res.data.slow = "1000.0.0";
|
||||
}
|
||||
|
||||
if (res.data.slow) {
|
||||
exports.latestVersion = res.data.slow;
|
||||
}
|
||||
|
||||
exports.latestVersion = res.data.version;
|
||||
} catch (_) { }
|
||||
|
||||
};
|
||||
|
@@ -50,6 +50,9 @@ class Database {
|
||||
"patch-group-table.sql": true,
|
||||
"patch-monitor-push_token.sql": true,
|
||||
"patch-http-monitor-method-body-and-headers.sql": true,
|
||||
"patch-2fa-invalidate-used-token.sql": true,
|
||||
"patch-notification_sent_history.sql": true,
|
||||
"patch-monitor-basic-auth.sql": true,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +80,7 @@ class Database {
|
||||
console.log(`Data Dir: ${Database.dataDir}`);
|
||||
}
|
||||
|
||||
static async connect() {
|
||||
static async connect(testMode = false) {
|
||||
const acquireConnectionTimeout = 120 * 1000;
|
||||
|
||||
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
|
||||
@@ -110,9 +113,15 @@ class Database {
|
||||
await R.autoloadModels("./server/model");
|
||||
|
||||
await R.exec("PRAGMA foreign_keys = ON");
|
||||
// Change to WAL
|
||||
await R.exec("PRAGMA journal_mode = WAL");
|
||||
if (testMode) {
|
||||
// Change to MEMORY
|
||||
await R.exec("PRAGMA journal_mode = MEMORY");
|
||||
} else {
|
||||
// Change to WAL
|
||||
await R.exec("PRAGMA journal_mode = WAL");
|
||||
}
|
||||
await R.exec("PRAGMA cache_size = -12000");
|
||||
await R.exec("PRAGMA auto_vacuum = FULL");
|
||||
|
||||
console.log("SQLite config:");
|
||||
console.log(await R.getAll("PRAGMA journal_mode"));
|
||||
@@ -131,7 +140,7 @@ class Database {
|
||||
console.info("Latest database version: " + this.latestVersion);
|
||||
|
||||
if (version === this.latestVersion) {
|
||||
console.info("Database no need to patch");
|
||||
console.info("Database patch not needed");
|
||||
} else if (version > this.latestVersion) {
|
||||
console.info("Warning: Database version is newer than expected");
|
||||
} else {
|
||||
@@ -152,8 +161,8 @@ class Database {
|
||||
await Database.close();
|
||||
|
||||
console.error(ex);
|
||||
console.error("Start Uptime-Kuma failed due to patch db failed");
|
||||
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
|
||||
console.error("Start Uptime-Kuma failed due to issue patching the database");
|
||||
console.error("Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
|
||||
|
||||
this.restore();
|
||||
process.exit(1);
|
||||
@@ -191,7 +200,7 @@ class Database {
|
||||
await Database.close();
|
||||
|
||||
console.error(ex);
|
||||
console.error("Start Uptime-Kuma failed due to patch db failed");
|
||||
console.error("Start Uptime-Kuma failed due to issue patching the database");
|
||||
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
|
||||
|
||||
this.restore();
|
||||
@@ -232,7 +241,7 @@ class Database {
|
||||
this.patched = true;
|
||||
await this.importSQLFile("./db/" + sqlFilename);
|
||||
databasePatchedFiles[sqlFilename] = true;
|
||||
console.log(sqlFilename + " is patched successfully");
|
||||
console.log(sqlFilename + " was patched successfully");
|
||||
|
||||
} else {
|
||||
debug(sqlFilename + " is already patched, skip");
|
||||
@@ -287,7 +296,7 @@ class Database {
|
||||
};
|
||||
process.addListener("unhandledRejection", listener);
|
||||
|
||||
console.log("Closing DB");
|
||||
console.log("Closing the database");
|
||||
|
||||
while (true) {
|
||||
Database.noReject = true;
|
||||
@@ -297,7 +306,7 @@ class Database {
|
||||
if (Database.noReject) {
|
||||
break;
|
||||
} else {
|
||||
console.log("Waiting to close the db");
|
||||
console.log("Waiting to close the database");
|
||||
}
|
||||
}
|
||||
console.log("SQLite closed");
|
||||
@@ -312,7 +321,7 @@ class Database {
|
||||
*/
|
||||
static backup(version) {
|
||||
if (! this.backupPath) {
|
||||
console.info("Backup the db");
|
||||
console.info("Backing up the database");
|
||||
this.backupPath = this.dataDir + "kuma.db.bak" + version;
|
||||
fs.copyFileSync(Database.path, this.backupPath);
|
||||
|
||||
@@ -335,7 +344,7 @@ class Database {
|
||||
*/
|
||||
static restore() {
|
||||
if (this.backupPath) {
|
||||
console.error("Patch db failed!!! Restoring the backup");
|
||||
console.error("Patching the database failed!!! Restoring the backup");
|
||||
|
||||
const shmPath = Database.path + "-shm";
|
||||
const walPath = Database.path + "-wal";
|
||||
@@ -354,7 +363,7 @@ class Database {
|
||||
fs.unlinkSync(walPath);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Restore failed, you may need to restore the backup manually");
|
||||
console.log("Restore failed; you may need to restore the backup manually");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -373,6 +382,17 @@ class Database {
|
||||
console.log("Nothing to restore");
|
||||
}
|
||||
}
|
||||
|
||||
static getSize() {
|
||||
debug("Database.getSize()");
|
||||
let stats = fs.statSync(Database.path);
|
||||
debug(stats);
|
||||
return stats.size;
|
||||
}
|
||||
|
||||
static async shrink() {
|
||||
await R.exec("VACUUM");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Database;
|
||||
|
@@ -6,7 +6,7 @@ const jobs = [
|
||||
{
|
||||
name: "clear-old-data",
|
||||
interval: "at 03:14",
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const initBackgroundJobs = function (args) {
|
||||
|
@@ -7,7 +7,7 @@ dayjs.extend(timezone);
|
||||
const axios = require("axios");
|
||||
const { Prometheus } = require("../prometheus");
|
||||
const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting } = require("../util-server");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { Notification } = require("../notification");
|
||||
@@ -58,6 +58,8 @@ class Monitor extends BeanModel {
|
||||
method: this.method,
|
||||
body: this.body,
|
||||
headers: this.headers,
|
||||
basic_auth_user: this.basic_auth_user,
|
||||
basic_auth_pass: this.basic_auth_pass,
|
||||
hostname: this.hostname,
|
||||
port: this.port,
|
||||
maxretries: this.maxretries,
|
||||
@@ -80,6 +82,15 @@ class Monitor extends BeanModel {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode user and password to Base64 encoding
|
||||
* for HTTP "basic" auth, as per RFC-7617
|
||||
* @returns {string}
|
||||
*/
|
||||
encodeBase64(user, pass) {
|
||||
return Buffer.from(user + ":" + pass).toString("base64");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse to boolean
|
||||
* @returns {boolean}
|
||||
@@ -141,6 +152,16 @@ class Monitor extends BeanModel {
|
||||
// Do not do any queries/high loading things before the "bean.ping"
|
||||
let startTime = dayjs().valueOf();
|
||||
|
||||
// HTTP basic auth
|
||||
let basicAuthHeader = {};
|
||||
if (this.basic_auth_user) {
|
||||
basicAuthHeader = {
|
||||
"Authorization": "Basic " + this.encodeBase64(this.basic_auth_user, this.basic_auth_pass),
|
||||
};
|
||||
}
|
||||
|
||||
debug(`[${this.name}] Prepare Options for axios`);
|
||||
|
||||
const options = {
|
||||
url: this.url,
|
||||
method: (this.method || "get").toLowerCase(),
|
||||
@@ -150,6 +171,7 @@ class Monitor extends BeanModel {
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "Uptime-Kuma/" + version,
|
||||
...(this.headers ? JSON.parse(this.headers) : {}),
|
||||
...(basicAuthHeader),
|
||||
},
|
||||
httpsAgent: new https.Agent({
|
||||
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
||||
@@ -160,6 +182,8 @@ class Monitor extends BeanModel {
|
||||
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
||||
},
|
||||
};
|
||||
|
||||
debug(`[${this.name}] Axios Request`);
|
||||
let res = await axios.request(options);
|
||||
bean.msg = `${res.status} - ${res.statusText}`;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
@@ -167,8 +191,16 @@ class Monitor extends BeanModel {
|
||||
// Check certificate if https is used
|
||||
let certInfoStartTime = dayjs().valueOf();
|
||||
if (this.getUrl()?.protocol === "https:") {
|
||||
debug(`[${this.name}] Check cert`);
|
||||
try {
|
||||
tlsInfo = await this.updateTlsInfo(checkCertificate(res));
|
||||
let tlsInfoObject = checkCertificate(res);
|
||||
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
||||
|
||||
if (!this.getIgnoreTls()) {
|
||||
debug(`[${this.name}] call sendCertNotification`);
|
||||
await this.sendCertNotification(tlsInfoObject);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (e.message !== "No TLS certificate in response") {
|
||||
console.error(e.message);
|
||||
@@ -264,6 +296,9 @@ class Monitor extends BeanModel {
|
||||
debug("heartbeatCount" + heartbeatCount + " " + time);
|
||||
|
||||
if (heartbeatCount <= 0) {
|
||||
// Fix #922, since previous heartbeat could be inserted by api, it should get from database
|
||||
previousBeat = await Monitor.getPreviousHeartbeat(this.id);
|
||||
|
||||
throw new Error("No heartbeat in the time window");
|
||||
} else {
|
||||
// No need to insert successful heartbeat for push type, so end here
|
||||
@@ -344,13 +379,21 @@ class Monitor extends BeanModel {
|
||||
|
||||
let beatInterval = this.interval;
|
||||
|
||||
debug(`[${this.name}] Check isImportant`);
|
||||
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
|
||||
|
||||
// Mark as important if status changed, ignore pending pings,
|
||||
// Don't notify if disrupted changes to up
|
||||
if (isImportant) {
|
||||
bean.important = true;
|
||||
|
||||
debug(`[${this.name}] sendNotification`);
|
||||
await Monitor.sendNotification(isFirstBeat, this, bean);
|
||||
|
||||
// Clear Status Page Cache
|
||||
debug(`[${this.name}] apicache clear`);
|
||||
apicache.clear();
|
||||
|
||||
} else {
|
||||
bean.important = false;
|
||||
}
|
||||
@@ -366,10 +409,14 @@ class Monitor extends BeanModel {
|
||||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
}
|
||||
|
||||
debug(`[${this.name}] Send to socket`);
|
||||
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
||||
Monitor.sendStats(io, this.id, this.user_id);
|
||||
|
||||
debug(`[${this.name}] Store`);
|
||||
await R.store(bean);
|
||||
|
||||
debug(`[${this.name}] prometheus.update`);
|
||||
prometheus.update(bean, tlsInfo);
|
||||
|
||||
previousBeat = bean;
|
||||
@@ -383,18 +430,36 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
}
|
||||
|
||||
this.heartbeatInterval = setTimeout(beat, beatInterval * 1000);
|
||||
debug(`[${this.name}] SetTimeout for next check.`);
|
||||
this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000);
|
||||
} else {
|
||||
console.log(`[${this.name}] isStop = true, no next check.`);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const safeBeat = async () => {
|
||||
try {
|
||||
await beat();
|
||||
} catch (e) {
|
||||
console.trace(e);
|
||||
errorLog(e, false);
|
||||
console.error("Please report to https://github.com/louislam/uptime-kuma/issues");
|
||||
|
||||
if (! this.isStop) {
|
||||
console.log("Try to restart the monitor");
|
||||
this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Delay Push Type
|
||||
if (this.type === "push") {
|
||||
setTimeout(() => {
|
||||
beat();
|
||||
safeBeat();
|
||||
}, this.interval * 1000);
|
||||
} else {
|
||||
beat();
|
||||
safeBeat();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,10 +491,36 @@ class Monitor extends BeanModel {
|
||||
let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
|
||||
this.id,
|
||||
]);
|
||||
|
||||
if (tls_info_bean == null) {
|
||||
tls_info_bean = R.dispense("monitor_tls_info");
|
||||
tls_info_bean.monitor_id = this.id;
|
||||
} else {
|
||||
|
||||
// Clear sent history if the cert changed.
|
||||
try {
|
||||
let oldCertInfo = JSON.parse(tls_info_bean.info_json);
|
||||
|
||||
let isValidObjects = oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo;
|
||||
|
||||
if (isValidObjects) {
|
||||
if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
|
||||
debug("Resetting sent_history");
|
||||
await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
|
||||
this.id
|
||||
]);
|
||||
} else {
|
||||
debug("No need to reset sent_history");
|
||||
debug(oldCertInfo.certInfo.fingerprint256);
|
||||
debug(checkCertificateResult.certInfo.fingerprint256);
|
||||
}
|
||||
} else {
|
||||
debug("Not valid object");
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
}
|
||||
|
||||
tls_info_bean.info_json = JSON.stringify(checkCertificateResult);
|
||||
await R.store(tls_info_bean);
|
||||
|
||||
@@ -577,9 +668,7 @@ class Monitor extends BeanModel {
|
||||
|
||||
static async sendNotification(isFirstBeat, monitor, bean) {
|
||||
if (!isFirstBeat || bean.status === DOWN) {
|
||||
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
|
||||
monitor.id,
|
||||
]);
|
||||
const notificationList = await Monitor.getNotificationList(monitor);
|
||||
|
||||
let text;
|
||||
if (bean.status === UP) {
|
||||
@@ -598,12 +687,82 @@ class Monitor extends BeanModel {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear Status Page Cache
|
||||
apicache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static async getNotificationList(monitor) {
|
||||
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
|
||||
monitor.id,
|
||||
]);
|
||||
return notificationList;
|
||||
}
|
||||
|
||||
async sendCertNotification(tlsInfoObject) {
|
||||
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
||||
const notificationList = await Monitor.getNotificationList(this);
|
||||
|
||||
debug("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);
|
||||
}
|
||||
}
|
||||
|
||||
async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
|
||||
|
||||
if (daysRemaining > targetDays) {
|
||||
debug(`No need to send cert notification. ${daysRemaining} > ${targetDays}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (notificationList.length > 0) {
|
||||
|
||||
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days = ?", [
|
||||
"certificate",
|
||||
this.id,
|
||||
targetDays,
|
||||
]);
|
||||
|
||||
// Sent already, no need to send again
|
||||
if (row) {
|
||||
debug("Sent already, no need to send again");
|
||||
return;
|
||||
}
|
||||
|
||||
let sent = false;
|
||||
debug("Send certificate notification");
|
||||
|
||||
for (let notification of notificationList) {
|
||||
try {
|
||||
debug("Sending to " + notification.name);
|
||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`);
|
||||
sent = true;
|
||||
} catch (e) {
|
||||
console.error("Cannot send cert notification to " + notification.name);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (sent) {
|
||||
await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
|
||||
"certificate",
|
||||
this.id,
|
||||
targetDays,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
debug("No notification, no need to send cert notification");
|
||||
}
|
||||
}
|
||||
|
||||
static async getPreviousHeartbeat(monitorID) {
|
||||
return await R.getRow(`
|
||||
SELECT status, time FROM heartbeat
|
||||
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
|
||||
`, [
|
||||
monitorID
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Monitor;
|
||||
|
89
server/notification-providers/bark.js
Normal file
89
server/notification-providers/bark.js
Normal file
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// bark.js
|
||||
// UptimeKuma
|
||||
//
|
||||
// Created by Lakr Aream on 2021/10/24.
|
||||
// Copyright © 2021 Lakr Aream. All rights reserved.
|
||||
//
|
||||
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const { DOWN, UP } = require("../../src/util");
|
||||
const { default: axios } = require("axios");
|
||||
|
||||
// bark is an APN bridge that sends notifications to Apple devices.
|
||||
|
||||
const barkNotificationGroup = "UptimeKuma";
|
||||
const barkNotificationAvatar = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png";
|
||||
const barkNotificationSound = "telegraph";
|
||||
const successMessage = "Successes!";
|
||||
|
||||
class Bark extends NotificationProvider {
|
||||
name = "Bark";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
try {
|
||||
var barkEndpoint = notification.barkEndpoint;
|
||||
|
||||
// check if the endpoint has a "/" suffix, if so, delete it first
|
||||
if (barkEndpoint.endsWith("/")) {
|
||||
barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1);
|
||||
}
|
||||
|
||||
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) {
|
||||
let title = "UptimeKuma Monitor Up";
|
||||
return await this.postNotification(title, msg, barkEndpoint);
|
||||
}
|
||||
|
||||
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) {
|
||||
let title = "UptimeKuma Monitor Down";
|
||||
return await this.postNotification(title, msg, barkEndpoint);
|
||||
}
|
||||
|
||||
if (msg != null) {
|
||||
let title = "UptimeKuma Message";
|
||||
return await this.postNotification(title, msg, barkEndpoint);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// add additional parameter for better on device styles (iOS 15 optimized)
|
||||
appendAdditionalParameters(postUrl) {
|
||||
// grouping all our notifications
|
||||
postUrl += "?group=" + barkNotificationGroup;
|
||||
// set icon to uptime kuma icon, 11kb should be fine
|
||||
postUrl += "&icon=" + barkNotificationAvatar;
|
||||
// picked a sound, this should follow system's mute status when arrival
|
||||
postUrl += "&sound=" + barkNotificationSound;
|
||||
return postUrl;
|
||||
}
|
||||
|
||||
// thrown if failed to check result, result code should be in range 2xx
|
||||
checkResult(result) {
|
||||
if (result.status == null) {
|
||||
throw new Error("Bark notification failed with invalid response!");
|
||||
}
|
||||
if (result.status < 200 || result.status >= 300) {
|
||||
throw new Error("Bark notification failed with status code " + result.status);
|
||||
}
|
||||
}
|
||||
|
||||
async postNotification(title, subtitle, endpoint) {
|
||||
// url encode title and subtitle
|
||||
title = encodeURIComponent(title);
|
||||
subtitle = encodeURIComponent(subtitle);
|
||||
let postUrl = endpoint + "/" + title + "/" + subtitle;
|
||||
postUrl = this.appendAdditionalParameters(postUrl);
|
||||
let result = await axios.get(postUrl);
|
||||
this.checkResult(result);
|
||||
if (result.statusText != null) {
|
||||
return "Bark notification succeed: " + result.statusText;
|
||||
}
|
||||
// because returned in range 200 ..< 300
|
||||
return successMessage;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Bark;
|
42
server/notification-providers/clicksendsms.js
Normal file
42
server/notification-providers/clicksendsms.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
|
||||
class ClickSendSMS extends NotificationProvider {
|
||||
|
||||
name = "clicksendsms";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
try {
|
||||
console.log({ notification });
|
||||
let config = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Basic " + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString('base64'),
|
||||
"Accept": "text/json",
|
||||
}
|
||||
};
|
||||
let data = {
|
||||
messages: [
|
||||
{
|
||||
"body": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||
"to": notification.clicksendsmsToNumber,
|
||||
"source": "uptime-kuma",
|
||||
"from": notification.clicksendsmsSenderName,
|
||||
}
|
||||
]
|
||||
};
|
||||
let resp = await axios.post("https://rest.clicksend.com/v3/sms/send", data, config);
|
||||
if (resp.data.data.messages[0].status !== "SUCCESS") {
|
||||
let error = "Something gone wrong. Api returned " + resp.data.data.messages[0].status + ".";
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClickSendSMS;
|
@@ -14,8 +14,8 @@ class DingDing extends NotificationProvider {
|
||||
let params = {
|
||||
msgtype: "markdown",
|
||||
markdown: {
|
||||
title: monitorJSON["name"],
|
||||
text: `## [${this.statusToString(heartbeatJSON["status"])}] \n > ${heartbeatJSON["msg"]} \n > Time(UTC):${heartbeatJSON["time"]}`,
|
||||
title: `[${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]}`,
|
||||
text: `## [${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]} \n > ${heartbeatJSON["msg"]} \n > Time(UTC):${heartbeatJSON["time"]}`,
|
||||
}
|
||||
};
|
||||
if (this.sendToDingDing(notification, params)) {
|
||||
|
@@ -27,7 +27,7 @@ class Feishu extends NotificationProvider {
|
||||
content: {
|
||||
post: {
|
||||
zh_cn: {
|
||||
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
||||
title: "UptimeKuma Alert: [Down] " + monitorJSON["name"],
|
||||
content: [
|
||||
[
|
||||
{
|
||||
@@ -54,7 +54,7 @@ class Feishu extends NotificationProvider {
|
||||
content: {
|
||||
post: {
|
||||
zh_cn: {
|
||||
title: "UptimeKuma Alert: " + monitorJSON["name"],
|
||||
title: "UptimeKuma Alert: [Up] " + monitorJSON["name"],
|
||||
content: [
|
||||
[
|
||||
{
|
||||
|
@@ -7,12 +7,12 @@ class Pushover extends NotificationProvider {
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
let pushoverlink = "https://api.pushover.net/1/messages.json"
|
||||
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
||||
|
||||
try {
|
||||
if (heartbeatJSON == null) {
|
||||
let data = {
|
||||
"message": "<b>Uptime Kuma Pushover testing successful.</b>",
|
||||
"message": msg,
|
||||
"user": notification.pushoveruserkey,
|
||||
"token": notification.pushoverapptoken,
|
||||
"sound": notification.pushoversounds,
|
||||
@@ -21,8 +21,8 @@ class Pushover extends NotificationProvider {
|
||||
"retry": "30",
|
||||
"expire": "3600",
|
||||
"html": 1,
|
||||
}
|
||||
await axios.post(pushoverlink, data)
|
||||
};
|
||||
await axios.post(pushoverlink, data);
|
||||
return okMsg;
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ class Pushover extends NotificationProvider {
|
||||
"retry": "30",
|
||||
"expire": "3600",
|
||||
"html": 1,
|
||||
}
|
||||
await axios.post(pushoverlink, data)
|
||||
};
|
||||
await axios.post(pushoverlink, data);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error)
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
44
server/notification-providers/serwersms.js
Normal file
44
server/notification-providers/serwersms.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
|
||||
class SerwerSMS extends NotificationProvider {
|
||||
|
||||
name = "serwersms";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
|
||||
try {
|
||||
let config = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
let data = {
|
||||
"username": notification.serwersmsUsername,
|
||||
"password": notification.serwersmsPassword,
|
||||
"phone": notification.serwersmsPhoneNumber,
|
||||
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||
"sender": notification.serwersmsSenderName,
|
||||
};
|
||||
|
||||
let resp = await axios.post("https://api2.serwersms.pl/messages/send_sms", data, config);
|
||||
|
||||
if (!resp.data.success) {
|
||||
if (resp.data.error) {
|
||||
let error = `SerwerSMS.pl API returned error code ${resp.data.error.code} (${resp.data.error.type}) with error message: ${resp.data.error.message}`;
|
||||
this.throwGeneralAxiosError(error);
|
||||
} else {
|
||||
let error = "SerwerSMS.pl API returned an unexpected response";
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SerwerSMS;
|
@@ -12,6 +12,9 @@ class SMTP extends NotificationProvider {
|
||||
host: notification.smtpHost,
|
||||
port: notification.smtpPort,
|
||||
secure: notification.smtpSecure,
|
||||
tls: {
|
||||
rejectUnauthorized: notification.smtpIgnoreTLSError || false,
|
||||
},
|
||||
};
|
||||
|
||||
// Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904
|
||||
@@ -87,9 +90,6 @@ class SMTP extends NotificationProvider {
|
||||
to: notification.smtpTo,
|
||||
subject: subject,
|
||||
text: bodyTextContent,
|
||||
tls: {
|
||||
rejectUnauthorized: notification.smtpIgnoreTLSError || false,
|
||||
},
|
||||
});
|
||||
|
||||
return "Sent Successfully.";
|
||||
|
41
server/notification-providers/stackfield.js
Normal file
41
server/notification-providers/stackfield.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { setting } = require("../util-server");
|
||||
const { getMonitorRelativeURL } = require("../../src/util");
|
||||
|
||||
class Stackfield extends NotificationProvider {
|
||||
|
||||
name = "stackfield";
|
||||
|
||||
async send(notification, msg, monitorJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
try {
|
||||
// Stackfield message formatting: https://www.stackfield.com/help/formatting-messages-2001
|
||||
|
||||
let textMsg = "+Uptime Kuma Alert+";
|
||||
|
||||
if (monitorJSON && monitorJSON.name) {
|
||||
textMsg += `\n*${monitorJSON.name}*`;
|
||||
}
|
||||
|
||||
textMsg += `\n${msg}`;
|
||||
|
||||
const baseURL = await setting("primaryBaseURL");
|
||||
if (baseURL) {
|
||||
textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
|
||||
}
|
||||
|
||||
const data = {
|
||||
"Title": textMsg,
|
||||
};
|
||||
|
||||
await axios.post(notification.stackfieldwebhookURL, data);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Stackfield;
|
@@ -8,6 +8,7 @@ const Mattermost = require("./notification-providers/mattermost");
|
||||
const Matrix = require("./notification-providers/matrix");
|
||||
const Octopush = require("./notification-providers/octopush");
|
||||
const PromoSMS = require("./notification-providers/promosms");
|
||||
const ClickSendSMS = require("./notification-providers/clicksendsms");
|
||||
const Pushbullet = require("./notification-providers/pushbullet");
|
||||
const Pushover = require("./notification-providers/pushover");
|
||||
const Pushy = require("./notification-providers/pushy");
|
||||
@@ -21,6 +22,9 @@ const Webhook = require("./notification-providers/webhook");
|
||||
const Feishu = require("./notification-providers/feishu");
|
||||
const AliyunSms = require("./notification-providers/aliyun-sms");
|
||||
const DingDing = require("./notification-providers/dingding");
|
||||
const Bark = require("./notification-providers/bark");
|
||||
const SerwerSMS = require("./notification-providers/serwersms");
|
||||
const Stackfield = require("./notification-providers/stackfield");
|
||||
|
||||
class Notification {
|
||||
|
||||
@@ -45,6 +49,7 @@ class Notification {
|
||||
new Matrix(),
|
||||
new Octopush(),
|
||||
new PromoSMS(),
|
||||
new ClickSendSMS(),
|
||||
new Pushbullet(),
|
||||
new Pushover(),
|
||||
new Pushy(),
|
||||
@@ -54,6 +59,9 @@ class Notification {
|
||||
new SMTP(),
|
||||
new Telegram(),
|
||||
new Webhook(),
|
||||
new Bark(),
|
||||
new SerwerSMS(),
|
||||
new Stackfield(),
|
||||
];
|
||||
|
||||
for (let item of list) {
|
||||
|
39
server/rate-limiter.js
Normal file
39
server/rate-limiter.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { RateLimiter } = require("limiter");
|
||||
const { debug } = require("../src/util");
|
||||
|
||||
class KumaRateLimiter {
|
||||
constructor(config) {
|
||||
this.errorMessage = config.errorMessage;
|
||||
this.rateLimiter = new RateLimiter(config);
|
||||
}
|
||||
|
||||
async pass(callback, num = 1) {
|
||||
const remainingRequests = await this.removeTokens(num);
|
||||
debug("Rate Limit (remainingRequests):" + remainingRequests);
|
||||
if (remainingRequests < 0) {
|
||||
if (callback) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: this.errorMessage,
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async removeTokens(num = 1) {
|
||||
return await this.rateLimiter.removeTokens(num);
|
||||
}
|
||||
}
|
||||
|
||||
const loginRateLimiter = new KumaRateLimiter({
|
||||
tokensPerInterval: 20,
|
||||
interval: "minute",
|
||||
fireImmediately: true,
|
||||
errorMessage: "Too frequently, try again later."
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
loginRateLimiter
|
||||
};
|
@@ -31,12 +31,7 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
throw new Error("Monitor not found or not active.");
|
||||
}
|
||||
|
||||
const previousHeartbeat = await R.getRow(`
|
||||
SELECT status, time FROM heartbeat
|
||||
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
|
||||
`, [
|
||||
monitor.id
|
||||
]);
|
||||
const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id);
|
||||
|
||||
let status = UP;
|
||||
if (monitor.isUpsideDown()) {
|
||||
@@ -101,6 +96,10 @@ router.get("/api/status-page/config", async (_request, response) => {
|
||||
config.statusPagePublished = true;
|
||||
}
|
||||
|
||||
if (! config.statusPageTags) {
|
||||
config.statusPageTags = false;
|
||||
}
|
||||
|
||||
if (! config.title) {
|
||||
config.title = "Uptime Kuma";
|
||||
}
|
||||
@@ -140,10 +139,28 @@ router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request,
|
||||
try {
|
||||
await checkPublished();
|
||||
const publicGroupList = [];
|
||||
let list = await R.find("group", " public = 1 ORDER BY weight ");
|
||||
|
||||
const tagsVisible = (await getSettings("statusPage")).statusPageTags;
|
||||
const list = await R.find("group", " public = 1 ORDER BY weight ");
|
||||
for (let groupBean of list) {
|
||||
publicGroupList.push(await groupBean.toPublicJSON());
|
||||
let monitorGroup = await groupBean.toPublicJSON();
|
||||
if (tagsVisible) {
|
||||
monitorGroup.monitorList = await Promise.all(monitorGroup.monitorList.map(async (monitor) => {
|
||||
// Includes tags as an array in response, allows for tags to be displayed on public status page
|
||||
const tags = await R.getAll(
|
||||
`SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color
|
||||
FROM monitor_tag
|
||||
JOIN tag
|
||||
ON monitor_tag.tag_id = tag.id
|
||||
WHERE monitor_tag.monitor_id = ?`, [monitor.id]
|
||||
);
|
||||
return {
|
||||
...monitor,
|
||||
tags: tags
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
publicGroupList.push(monitorGroup);
|
||||
}
|
||||
|
||||
response.json(publicGroupList);
|
||||
|
127
server/server.js
127
server/server.js
@@ -31,6 +31,7 @@ debug("Importing prometheus-api-metrics");
|
||||
const prometheusAPIMetrics = require("prometheus-api-metrics");
|
||||
debug("Importing compare-versions");
|
||||
const compareVersions = require("compare-versions");
|
||||
const { passwordStrength } = require("check-password-strength");
|
||||
|
||||
debug("Importing 2FA Modules");
|
||||
const notp = require("notp");
|
||||
@@ -40,7 +41,7 @@ console.log("Importing this project modules");
|
||||
debug("Importing Monitor");
|
||||
const Monitor = require("./model/monitor");
|
||||
debug("Importing Settings");
|
||||
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD } = require("./util-server");
|
||||
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog } = require("./util-server");
|
||||
|
||||
debug("Importing Notification");
|
||||
const { Notification } = require("./notification");
|
||||
@@ -51,6 +52,7 @@ const Database = require("./database");
|
||||
|
||||
debug("Importing Background Jobs");
|
||||
const { initBackgroundJobs } = require("./jobs");
|
||||
const { loginRateLimiter } = require("./rate-limiter");
|
||||
|
||||
const { basicAuth } = require("./auth");
|
||||
const { login } = require("./auth");
|
||||
@@ -77,6 +79,7 @@ const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.p
|
||||
// SSL
|
||||
const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined;
|
||||
const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined;
|
||||
const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false;
|
||||
|
||||
// 2FA / notp verification defaults
|
||||
const twofa_verification_opts = {
|
||||
@@ -116,9 +119,20 @@ module.exports.io = io;
|
||||
// Must be after io instantiation
|
||||
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo } = require("./client");
|
||||
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
|
||||
const databaseSocketHandler = require("./socket-handlers/database-socket-handler");
|
||||
const TwoFA = require("./2fa");
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// Global Middleware
|
||||
app.use(function (req, res, next) {
|
||||
if (!disableFrameSameOrigin) {
|
||||
res.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
res.removeHeader("X-Powered-By");
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Total WebSocket client connected to server currently, no actual use
|
||||
* @type {number}
|
||||
@@ -147,13 +161,23 @@ let needSetup = false;
|
||||
* Cache Index HTML
|
||||
* @type {string}
|
||||
*/
|
||||
let indexHTML = fs.readFileSync("./dist/index.html").toString();
|
||||
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") {
|
||||
console.error("Error: Cannot find 'dist/index.html', did you install correctly?");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
exports.entryPage = "dashboard";
|
||||
|
||||
(async () => {
|
||||
Database.init(args);
|
||||
await initDatabase();
|
||||
await initDatabase(testMode);
|
||||
|
||||
exports.entryPage = await setting("entryPage");
|
||||
|
||||
@@ -163,6 +187,15 @@ exports.entryPage = "dashboard";
|
||||
// Normal Router here
|
||||
// ***************************
|
||||
|
||||
// Entry Page
|
||||
app.get("/", async (_request, response) => {
|
||||
if (exports.entryPage === "statusPage") {
|
||||
response.redirect("/status");
|
||||
} else {
|
||||
response.redirect("/dashboard");
|
||||
}
|
||||
});
|
||||
|
||||
// Robots.txt
|
||||
app.get("/robots.txt", async (_request, response) => {
|
||||
let txt = "User-agent: *\nDisallow:";
|
||||
@@ -192,7 +225,7 @@ exports.entryPage = "dashboard";
|
||||
const apiRouter = require("./routers/api-router");
|
||||
app.use(apiRouter);
|
||||
|
||||
// Universal Route Handler, must be at the end of all express route.
|
||||
// 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.");
|
||||
@@ -260,10 +293,15 @@ exports.entryPage = "dashboard";
|
||||
socket.on("login", async (data, callback) => {
|
||||
console.log("Login");
|
||||
|
||||
// Login Rate Limit
|
||||
if (! await loginRateLimiter.pass(callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let user = await login(data.username, data.password);
|
||||
|
||||
if (user) {
|
||||
if (user.twofaStatus == 0) {
|
||||
if (user.twofa_status == 0) {
|
||||
afterLogin(socket, user);
|
||||
callback({
|
||||
ok: true,
|
||||
@@ -273,7 +311,7 @@ exports.entryPage = "dashboard";
|
||||
});
|
||||
}
|
||||
|
||||
if (user.twofaStatus == 1 && !data.token) {
|
||||
if (user.twofa_status == 1 && !data.token) {
|
||||
callback({
|
||||
tokenRequired: true,
|
||||
});
|
||||
@@ -282,8 +320,14 @@ exports.entryPage = "dashboard";
|
||||
if (data.token) {
|
||||
let verify = notp.totp.verify(data.token, user.twofa_secret, twofa_verification_opts);
|
||||
|
||||
if (verify && verify.delta == 0) {
|
||||
if (user.twofa_last_token !== data.token && verify) {
|
||||
afterLogin(socket, user);
|
||||
|
||||
await R.exec("UPDATE `user` SET twofa_last_token = ? WHERE id = ? ", [
|
||||
data.token,
|
||||
socket.userID,
|
||||
]);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
token: jwt.sign({
|
||||
@@ -321,7 +365,7 @@ exports.entryPage = "dashboard";
|
||||
]);
|
||||
|
||||
if (user.twofa_status == 0) {
|
||||
let newSecret = await genSecret();
|
||||
let newSecret = genSecret();
|
||||
let encodedSecret = base32.encode(newSecret);
|
||||
|
||||
// Google authenticator doesn't like equal signs
|
||||
@@ -377,10 +421,7 @@ exports.entryPage = "dashboard";
|
||||
socket.on("disable2FA", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
|
||||
socket.userID,
|
||||
]);
|
||||
await TwoFA.disable2FA(socket.userID);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
@@ -401,7 +442,7 @@ exports.entryPage = "dashboard";
|
||||
|
||||
let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts);
|
||||
|
||||
if (verify && verify.delta == 0) {
|
||||
if (user.twofa_last_token !== token && verify) {
|
||||
callback({
|
||||
ok: true,
|
||||
valid: true,
|
||||
@@ -448,8 +489,12 @@ exports.entryPage = "dashboard";
|
||||
|
||||
socket.on("setup", async (username, password, callback) => {
|
||||
try {
|
||||
if (passwordStrength(password).value === "Too weak") {
|
||||
throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.");
|
||||
}
|
||||
|
||||
if ((await R.count("user")) !== 0) {
|
||||
throw new Error("Uptime Kuma has been setup. If you want to setup again, please delete the database.");
|
||||
throw new Error("Uptime Kuma has been initialized. If you want to run setup again, please delete the database.");
|
||||
}
|
||||
|
||||
let user = R.dispense("user");
|
||||
@@ -494,8 +539,8 @@ exports.entryPage = "dashboard";
|
||||
|
||||
await updateMonitorNotification(bean.id, notificationIDList);
|
||||
|
||||
await startMonitor(socket.userID, bean.id);
|
||||
await sendMonitorList(socket);
|
||||
await startMonitor(socket.userID, bean.id);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
@@ -528,6 +573,8 @@ exports.entryPage = "dashboard";
|
||||
bean.method = monitor.method;
|
||||
bean.body = monitor.body;
|
||||
bean.headers = monitor.headers;
|
||||
bean.basic_auth_user = monitor.basic_auth_user;
|
||||
bean.basic_auth_pass = monitor.basic_auth_pass;
|
||||
bean.interval = monitor.interval;
|
||||
bean.retryInterval = monitor.retryInterval;
|
||||
bean.hostname = monitor.hostname;
|
||||
@@ -607,6 +654,38 @@ exports.entryPage = "dashboard";
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("getMonitorBeats", async (monitorID, period, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
console.log(`Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`);
|
||||
|
||||
if (period == null) {
|
||||
throw new Error("Invalid period.");
|
||||
}
|
||||
|
||||
let list = await R.getAll(`
|
||||
SELECT * FROM heartbeat
|
||||
WHERE monitor_id = ? AND
|
||||
time > DATETIME('now', '-' || ? || ' hours')
|
||||
ORDER BY time ASC
|
||||
`, [
|
||||
monitorID,
|
||||
period,
|
||||
]);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
data: list,
|
||||
});
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Start or Resume the monitor
|
||||
socket.on("resumeMonitor", async (monitorID, callback) => {
|
||||
try {
|
||||
@@ -837,10 +916,14 @@ exports.entryPage = "dashboard";
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
if (! password.currentPassword) {
|
||||
if (! password.newPassword) {
|
||||
throw new Error("Invalid new password");
|
||||
}
|
||||
|
||||
if (passwordStrength(password.newPassword).value === "Too weak") {
|
||||
throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.");
|
||||
}
|
||||
|
||||
let user = await R.findOne("user", " id = ? AND active = 1 ", [
|
||||
socket.userID,
|
||||
]);
|
||||
@@ -1056,6 +1139,8 @@ exports.entryPage = "dashboard";
|
||||
method: monitorListData[i].method || "GET",
|
||||
body: monitorListData[i].body,
|
||||
headers: monitorListData[i].headers,
|
||||
basic_auth_user: monitorListData[i].basic_auth_user,
|
||||
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
||||
interval: monitorListData[i].interval,
|
||||
retryInterval: retryInterval,
|
||||
hostname: monitorListData[i].hostname,
|
||||
@@ -1222,6 +1307,7 @@ exports.entryPage = "dashboard";
|
||||
|
||||
// Status Page Socket Handler for admin only
|
||||
statusPageSocketHandler(socket);
|
||||
databaseSocketHandler(socket);
|
||||
|
||||
debug("added all socket handlers");
|
||||
|
||||
@@ -1333,14 +1419,14 @@ async function getMonitorJSONList(userID) {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function initDatabase() {
|
||||
async function initDatabase(testMode = false) {
|
||||
if (! fs.existsSync(Database.path)) {
|
||||
console.log("Copying Database");
|
||||
fs.copyFileSync(Database.templatePath, Database.path);
|
||||
}
|
||||
|
||||
console.log("Connecting to Database");
|
||||
await Database.connect();
|
||||
console.log("Connecting to the Database");
|
||||
await Database.connect(testMode);
|
||||
console.log("Connected");
|
||||
|
||||
// Patch the database
|
||||
@@ -1439,7 +1525,7 @@ async function shutdownFunction(signal) {
|
||||
}
|
||||
|
||||
function finalFunction() {
|
||||
console.log("Graceful shutdown successfully!");
|
||||
console.log("Graceful shutdown successful!");
|
||||
}
|
||||
|
||||
gracefulShutdown(server, {
|
||||
@@ -1454,5 +1540,6 @@ gracefulShutdown(server, {
|
||||
// Catch unexpected errors here
|
||||
process.addListener("unhandledRejection", (error, promise) => {
|
||||
console.trace(error);
|
||||
errorLog(error, false);
|
||||
console.error("If you keep encountering errors, please report to https://github.com/louislam/uptime-kuma/issues");
|
||||
});
|
||||
|
37
server/socket-handlers/database-socket-handler.js
Normal file
37
server/socket-handlers/database-socket-handler.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const { checkLogin } = require("../util-server");
|
||||
const Database = require("../database");
|
||||
|
||||
module.exports = (socket) => {
|
||||
|
||||
// Post or edit incident
|
||||
socket.on("getDatabaseSize", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
callback({
|
||||
ok: true,
|
||||
size: Database.getSize(),
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("shrinkDatabase", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
Database.shrink();
|
||||
callback({
|
||||
ok: true,
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
@@ -8,6 +8,8 @@ const { Resolver } = require("dns");
|
||||
const child_process = require("child_process");
|
||||
const iconv = require("iconv-lite");
|
||||
const chardet = require("chardet");
|
||||
const fs = require("fs");
|
||||
const nodeJsUtil = require("util");
|
||||
|
||||
// From ping-lite
|
||||
exports.WIN = /^win/.test(process.platform);
|
||||
@@ -199,8 +201,13 @@ const getDaysRemaining = (validFrom, validTo) => {
|
||||
// param: info - the chain obtained from getPeerCertificate()
|
||||
const parseCertificateInfo = function (info) {
|
||||
let link = info;
|
||||
let i = 0;
|
||||
|
||||
const existingList = {};
|
||||
|
||||
while (link) {
|
||||
debug(`[${i}] ${link.fingerprint}`);
|
||||
|
||||
if (!link.valid_from || !link.valid_to) {
|
||||
break;
|
||||
}
|
||||
@@ -208,15 +215,24 @@ const parseCertificateInfo = function (info) {
|
||||
link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", ");
|
||||
link.daysRemaining = getDaysRemaining(new Date(), link.validTo);
|
||||
|
||||
existingList[link.fingerprint] = true;
|
||||
|
||||
// Move up the chain until loop is encountered
|
||||
if (link.issuerCertificate == null) {
|
||||
break;
|
||||
} else if (link.fingerprint == link.issuerCertificate.fingerprint) {
|
||||
} else if (link.issuerCertificate.fingerprint in existingList) {
|
||||
debug(`[Last] ${link.issuerCertificate.fingerprint}`);
|
||||
link.issuerCertificate = null;
|
||||
break;
|
||||
} else {
|
||||
link = link.issuerCertificate;
|
||||
}
|
||||
|
||||
// Should be no use, but just in case.
|
||||
if (i > 500) {
|
||||
throw new Error("Dead loop occurred in parseCertificateInfo");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return info;
|
||||
@@ -226,6 +242,7 @@ exports.checkCertificate = function (res) {
|
||||
const info = res.request.res.socket.getPeerCertificate(true);
|
||||
const valid = res.request.res.socket.authorized || false;
|
||||
|
||||
debug("Parsing Certificate Info");
|
||||
const parsedInfo = parseCertificateInfo(info);
|
||||
|
||||
return {
|
||||
@@ -332,3 +349,24 @@ exports.convertToUTF8 = (body) => {
|
||||
const str = iconv.decode(body, guessEncoding);
|
||||
return str.toString();
|
||||
};
|
||||
|
||||
let logFile;
|
||||
|
||||
try {
|
||||
logFile = fs.createWriteStream("./data/error.log", {
|
||||
flags: "a"
|
||||
});
|
||||
} catch (_) { }
|
||||
|
||||
exports.errorLog = (error, outputToConsole = true) => {
|
||||
try {
|
||||
if (logFile) {
|
||||
const dateTime = R.isoDateTime();
|
||||
logFile.write(`[${dateTime}] ` + nodeJsUtil.format(error) + "\n");
|
||||
|
||||
if (outputToConsole) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
} catch (_) { }
|
||||
};
|
||||
|
@@ -189,7 +189,7 @@ textarea.form-control {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.table-hover > tbody > tr:hover {
|
||||
.table-hover > tbody > tr:hover > * {
|
||||
--bs-table-accent-bg: #070a10;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
@@ -346,6 +346,10 @@ textarea.form-control {
|
||||
&.active {
|
||||
background-color: #cdf8f4;
|
||||
}
|
||||
.tags {
|
||||
// Removes margin to line up tags list with uptime percentage
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ $dark-font-color2: #020b05;
|
||||
$dark-bg: #0d1117;
|
||||
$dark-bg2: #070a10;
|
||||
$dark-border-color: #1d2634;
|
||||
$dark-header-bg: #161b22;
|
||||
|
||||
$easing-in: cubic-bezier(0.54, 0.78, 0.55, 0.97);
|
||||
$easing-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
@@ -167,7 +167,7 @@ export default {
|
||||
},
|
||||
|
||||
getBeatTitle(beat) {
|
||||
return `${this.$root.datetime(beat.time)} - ${beat.msg}`;
|
||||
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@@ -137,7 +137,7 @@ export default {
|
||||
justify-content: space-between;
|
||||
|
||||
.dark & {
|
||||
background-color: #161b22;
|
||||
background-color: $dark-header-bg;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,34 @@
|
||||
<template>
|
||||
<LineChart :chart-data="chartData" :options="chartOptions" />
|
||||
<div>
|
||||
<div class="period-options">
|
||||
<button type="button" class="btn btn-light dropdown-toggle btn-period-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ chartPeriodOptions[chartPeriodHrs] }}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li v-for="(item, key) in chartPeriodOptions" :key="key">
|
||||
<a class="dropdown-item" :class="{ active: chartPeriodHrs == key }" href="#" @click="chartPeriodHrs = key">{{ item }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chart-wrapper" :class="{ loading : loading}">
|
||||
<LineChart :chart-data="chartData" :options="chartOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { BarController, BarElement, Chart, Filler, LinearScale, LineController, LineElement, PointElement, TimeScale, Tooltip } from "chart.js";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import "chartjs-adapter-dayjs";
|
||||
import { LineChart } from "vue-chart-3";
|
||||
import { useToast } from "vue-toastification";
|
||||
import { UP, DOWN, PENDING } from "../util.ts";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
const toast = useToast();
|
||||
|
||||
Chart.register(LineController, BarController, LineElement, PointElement, TimeScale, BarElement, LinearScale, Tooltip, Filler);
|
||||
|
||||
@@ -24,8 +42,23 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
loading: false,
|
||||
|
||||
// Configurable filtering on top of the returned data
|
||||
chartPeriodHrs: 6,
|
||||
chartPeriodHrs: 0,
|
||||
|
||||
chartPeriodOptions: {
|
||||
0: this.$t("recent"),
|
||||
3: "3h",
|
||||
6: "6h",
|
||||
24: "24h",
|
||||
168: "1w",
|
||||
},
|
||||
|
||||
// A heartbeatList for 3h, 6h, 24h, 1w
|
||||
// Uses the $root.heartbeatList when value is null
|
||||
heartbeatList: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -117,7 +150,7 @@ export default {
|
||||
},
|
||||
callbacks: {
|
||||
label: (context) => {
|
||||
return ` ${new Intl.NumberFormat().format(context.parsed.y)} ms`
|
||||
return ` ${new Intl.NumberFormat().format(context.parsed.y)} ms`;
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -125,27 +158,36 @@ export default {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
chartData() {
|
||||
let pingData = []; // Ping Data for Line Chart, y-axis contains ping time
|
||||
let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down, 0 if target is up
|
||||
if (this.monitorId in this.$root.heartbeatList) {
|
||||
this.$root.heartbeatList[this.monitorId]
|
||||
.filter(
|
||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
||||
.map((beat) => {
|
||||
const x = this.$root.datetime(beat.time);
|
||||
pingData.push({
|
||||
x,
|
||||
y: beat.ping,
|
||||
});
|
||||
downData.push({
|
||||
x,
|
||||
y: beat.status === 0 ? 1 : 0,
|
||||
})
|
||||
|
||||
let heartbeatList = this.heartbeatList ||
|
||||
(this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) ||
|
||||
[];
|
||||
|
||||
heartbeatList
|
||||
.filter(
|
||||
// Filtering as data gets appended
|
||||
// not the most efficient, but works for now
|
||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(
|
||||
dayjs().subtract(Math.max(this.chartPeriodHrs, 6), "hours")
|
||||
)
|
||||
)
|
||||
.map((beat) => {
|
||||
const x = this.$root.datetime(beat.time);
|
||||
pingData.push({
|
||||
x,
|
||||
y: beat.ping,
|
||||
});
|
||||
}
|
||||
downData.push({
|
||||
x,
|
||||
y: beat.status === DOWN ? 1 : 0,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
datasets: [
|
||||
{
|
||||
@@ -172,5 +214,110 @@ export default {
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// Update chart data when the selected chart period changes
|
||||
chartPeriodHrs: function (newPeriod) {
|
||||
if (newPeriod == "0") {
|
||||
newPeriod = null;
|
||||
this.heartbeatList = null;
|
||||
} else {
|
||||
this.loading = true;
|
||||
|
||||
this.$root.getMonitorBeats(this.monitorId, newPeriod, (res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
} else {
|
||||
this.heartbeatList = res.data;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Setup Watcher on the root heartbeatList,
|
||||
// And mirror latest change to this.heartbeatList
|
||||
this.$watch(() => this.$root.heartbeatList[this.monitorId],
|
||||
(heartbeatList) => {
|
||||
if (this.chartPeriodHrs != 0) {
|
||||
const newBeat = heartbeatList.at(-1);
|
||||
if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) {
|
||||
this.heartbeatList.push(heartbeatList.at(-1));
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
.form-select {
|
||||
width: unset;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.period-options {
|
||||
padding: 0.1em 1em;
|
||||
margin-bottom: -1.2em;
|
||||
float: right;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
.dropdown-menu {
|
||||
padding: 0;
|
||||
min-width: 50px;
|
||||
font-size: 0.9em;
|
||||
|
||||
.dark & {
|
||||
background: $dark-bg;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
border-radius: 0.3rem;
|
||||
padding: 2px 16px 4px 16px;
|
||||
|
||||
.dark & {
|
||||
background: $dark-bg;
|
||||
}
|
||||
|
||||
.dark &:hover {
|
||||
background: $dark-font-color;
|
||||
}
|
||||
}
|
||||
|
||||
.dark & .dropdown-item.active {
|
||||
background: $primary;
|
||||
color: $dark-font-color2;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-period-toggle {
|
||||
padding: 2px 15px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: $link-color;
|
||||
opacity: 0.7;
|
||||
font-size: 0.9em;
|
||||
|
||||
&::after {
|
||||
vertical-align: 0.155em;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
&.loading {
|
||||
filter: blur(10px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -41,6 +41,9 @@
|
||||
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
||||
{{ monitor.element.name }}
|
||||
</div>
|
||||
<div 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">
|
||||
<HeartbeatBar size="small" :monitor-id="monitor.element.id" />
|
||||
@@ -59,12 +62,14 @@
|
||||
import Draggable from "vuedraggable";
|
||||
import HeartbeatBar from "./HeartbeatBar.vue";
|
||||
import Uptime from "./Uptime.vue";
|
||||
import Tag from "./Tag.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Draggable,
|
||||
HeartbeatBar,
|
||||
Uptime,
|
||||
Tag,
|
||||
},
|
||||
props: {
|
||||
editMode: {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span :class="className">{{ uptime }}</span>
|
||||
<span :class="className" :title="24 + $t('-hour')">{{ uptime }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
15
src/components/notifications/Bark.vue
Normal file
15
src/components/notifications/Bark.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="Bark Endpoint" class="form-label">{{ $t("Bark Endpoint") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="Bark Endpoint" v-model="$parent.notification.barkEndpoint" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
<p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
|
||||
</div>
|
||||
<i18n-t tag="div" keypath="wayToGetTeamsURL" class="form-text">
|
||||
<a
|
||||
href="https://github.com/Finb/Bark"
|
||||
target="_blank"
|
||||
>{{ $t("here") }}</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</template>
|
38
src/components/notifications/ClickSendSMS.vue
Normal file
38
src/components/notifications/ClickSendSMS.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="clicksendsms-login" class="form-label">API Username</label>
|
||||
<div class="form-text">
|
||||
{{ $t("apiCredentials") }}
|
||||
<a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
|
||||
</div>
|
||||
<input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
|
||||
<label for="clicksendsms-key" class="form-label">API Key</label>
|
||||
<HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-text">
|
||||
{{ $t("checkPrice", [$t("clicksendsms")]) }}
|
||||
<a href="https://www.clicksend.com/us/pricing" target="_blank">https://clicksend.com/us/pricing</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="clicksendsms-to-number" class="form-label">Recipient Number</label>
|
||||
<input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="clicksendsms-sender-name" class="form-label">From Name/Number -
|
||||
<a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">More Info</a>
|
||||
</label>
|
||||
<input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||
<div class="form-text">Leave blank to use a shared sender number.</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
@@ -11,7 +11,7 @@
|
||||
<div class="form-text">
|
||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
|
||||
<a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
|
||||
<a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://docs.rocket.chat/guides/administration/administration/integrations</a>
|
||||
</i18n-t>
|
||||
<p style="margin-top: 8px;">
|
||||
{{ $t("aboutChannelName", [$t("rocket.chat")]) }}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="secure" class="form-label">Secure</label>
|
||||
<label for="secure" class="form-label">{{ $t("Security") }}</label>
|
||||
<select id="secure" v-model="$parent.notification.smtpSecure" class="form-select">
|
||||
<option :value="false">{{ $t("secureOptionNone") }}</option>
|
||||
<option :value="true">{{ $t("secureOptionTLS") }}</option>
|
||||
|
28
src/components/notifications/SerwerSMS.vue
Normal file
28
src/components/notifications/SerwerSMS.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-username" class="form-label">{{ $t('serwersmsAPIUser') }}</label>
|
||||
<input id="serwersms-username" v-model="$parent.notification.serwersmsUsername" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-key" class="form-label">{{ $t('serwersmsAPIPassword') }}</label>
|
||||
<HiddenInput id="serwersms-key" v-model="$parent.notification.serwersmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-phone-number" class="form-label">{{ $t("serwersmsPhoneNumber") }}</label>
|
||||
<input id="serwersms-phone-number" v-model="$parent.notification.serwersmsPhoneNumber" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-sender-name" class="form-label">{{ $t("serwersmsSenderName") }}</label>
|
||||
<input id="serwersms-sender-name" v-model="$parent.notification.serwersmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
13
src/components/notifications/Stackfield.vue
Normal file
13
src/components/notifications/Stackfield.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="stackfield-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="stackfield-webhook-url" v-model="$parent.notification.stackfieldwebhookURL" type="text" class="form-control" required>
|
||||
|
||||
<div class="form-text">
|
||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
|
||||
<a href="https://www.stackfield.com/developer-api#AnchorAPI2" target="_blank">https://www.stackfield.com/developer-api#AnchorAPI2</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@@ -25,13 +25,7 @@
|
||||
</p>
|
||||
|
||||
<p style="margin-top: 8px;">
|
||||
<template v-if="$parent.notification.telegramBotToken">
|
||||
<a :href="telegramGetUpdatesURL" target="_blank" style="word-break: break-word;">{{ telegramGetUpdatesURL }}</a>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ telegramGetUpdatesURL }}
|
||||
</template>
|
||||
<a :href="telegramGetUpdatesURL('withToken')" target="_blank" style="word-break: break-word;">{{ telegramGetUpdatesURL("masked") }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,49 +34,51 @@
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
import axios from "axios";
|
||||
import { useToast } from "vue-toastification"
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
computed: {
|
||||
telegramGetUpdatesURL() {
|
||||
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`
|
||||
methods: {
|
||||
telegramGetUpdatesURL(mode = "masked") {
|
||||
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
||||
|
||||
if (this.$parent.notification.telegramBotToken) {
|
||||
token = this.$parent.notification.telegramBotToken;
|
||||
if (mode === "withToken") {
|
||||
token = this.$parent.notification.telegramBotToken;
|
||||
} else if (mode === "masked") {
|
||||
token = "*".repeat(this.$parent.notification.telegramBotToken.length);
|
||||
}
|
||||
}
|
||||
|
||||
return `https://api.telegram.org/bot${token}/getUpdates`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async autoGetTelegramChatID() {
|
||||
try {
|
||||
let res = await axios.get(this.telegramGetUpdatesURL)
|
||||
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
||||
|
||||
if (res.data.result.length >= 1) {
|
||||
let update = res.data.result[res.data.result.length - 1]
|
||||
let update = res.data.result[res.data.result.length - 1];
|
||||
|
||||
if (update.channel_post) {
|
||||
this.notification.telegramChatID = update.channel_post.chat.id;
|
||||
} else if (update.message) {
|
||||
this.notification.telegramChatID = update.message.chat.id;
|
||||
} else {
|
||||
throw new Error(this.$t("chatIDNotFound"))
|
||||
throw new Error(this.$t("chatIDNotFound"));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error(this.$t("chatIDNotFound"))
|
||||
throw new Error(this.$t("chatIDNotFound"));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
toast.error(error.message)
|
||||
toast.error(error.message);
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -11,6 +11,7 @@ import Pushover from "./Pushover.vue";
|
||||
import Pushy from "./Pushy.vue";
|
||||
import Octopush from "./Octopush.vue";
|
||||
import PromoSMS from "./PromoSMS.vue";
|
||||
import ClickSendSMS from "./ClickSendSMS.vue";
|
||||
import LunaSea from "./LunaSea.vue";
|
||||
import Feishu from "./Feishu.vue";
|
||||
import Apprise from "./Apprise.vue";
|
||||
@@ -20,6 +21,9 @@ import Mattermost from "./Mattermost.vue";
|
||||
import Matrix from "./Matrix.vue";
|
||||
import AliyunSMS from "./AliyunSms.vue";
|
||||
import DingDing from "./DingDing.vue";
|
||||
import Bark from "./Bark.vue";
|
||||
import SerwerSMS from "./SerwerSMS.vue";
|
||||
import Stackfield from './Stackfield.vue';
|
||||
|
||||
/**
|
||||
* Manage all notification form.
|
||||
@@ -40,6 +44,7 @@ const NotificationFormList = {
|
||||
"pushy": Pushy,
|
||||
"octopush": Octopush,
|
||||
"promosms": PromoSMS,
|
||||
"clicksendsms": ClickSendSMS,
|
||||
"lunasea": LunaSea,
|
||||
"Feishu": Feishu,
|
||||
"AliyunSMS": AliyunSMS,
|
||||
@@ -48,7 +53,10 @@ const NotificationFormList = {
|
||||
"line": Line,
|
||||
"mattermost": Mattermost,
|
||||
"matrix": Matrix,
|
||||
"DingDing": DingDing
|
||||
"DingDing": DingDing,
|
||||
"Bark": Bark,
|
||||
"serwersms": SerwerSMS,
|
||||
"stackfield": Stackfield,
|
||||
}
|
||||
|
||||
export default NotificationFormList
|
||||
|
25
src/components/settings/About.vue
Normal file
25
src/components/settings/About.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="logo d-flex flex-column justify-content-center align-items-center">
|
||||
<object class="my-4" width="200" height="200" data="/icon.svg" />
|
||||
<div class="fs-4 fw-bold">Uptime Kuma</div>
|
||||
<div>{{ $t("Version") }}: {{ $root.info.version }}</div>
|
||||
<div class="my-1 update-link"><a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.logo {
|
||||
margin: 4em 1em;
|
||||
}
|
||||
.update-link {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
143
src/components/settings/Appearance.vue
Normal file
143
src/components/settings/Appearance.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<label for="language" class="form-label">
|
||||
{{ $t("Language") }}
|
||||
</label>
|
||||
<select id="language" v-model="$root.language" class="form-select">
|
||||
<option
|
||||
v-for="(lang, i) in $i18n.availableLocales"
|
||||
:key="`Lang${i}`"
|
||||
:value="lang"
|
||||
>
|
||||
{{ $i18n.messages[lang].languageName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
|
||||
<div>
|
||||
<div
|
||||
class="btn-group"
|
||||
role="group"
|
||||
aria-label="Basic checkbox toggle button group"
|
||||
>
|
||||
<input
|
||||
id="btncheck1"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="light"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck1">
|
||||
{{ $t("Light") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck2"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="dark"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck2">
|
||||
{{ $t("Dark") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck3"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="auto"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck3">
|
||||
{{ $t("Auto") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
|
||||
<div>
|
||||
<div
|
||||
class="btn-group"
|
||||
role="group"
|
||||
aria-label="Basic checkbox toggle button group"
|
||||
>
|
||||
<input
|
||||
id="btncheck4"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="normal"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck4">
|
||||
{{ $t("Normal") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck5"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="bottom"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck5">
|
||||
{{ $t("Bottom") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck6"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="none"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck6">
|
||||
{{ $t("None") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.btn-check:active + .btn-outline-primary,
|
||||
.btn-check:checked + .btn-outline-primary,
|
||||
.btn-check:hover + .btn-outline-primary {
|
||||
color: #fff;
|
||||
|
||||
.dark & {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
213
src/components/settings/Backup.vue
Normal file
213
src/components/settings/Backup.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<h4 class="mt-4 mb-2">{{ $t("Export Backup") }}</h4>
|
||||
|
||||
<p>
|
||||
{{ $t("backupDescription") }} <br />
|
||||
({{ $t("backupDescription2") }}) <br />
|
||||
</p>
|
||||
|
||||
<div class="mb-2">
|
||||
<button class="btn btn-primary" @click="downloadBackup">
|
||||
{{ $t("Export") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<strong>{{ $t("backupDescription3") }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<h4 class="mt-4 mb-2">{{ $t("Import Backup") }}</h4>
|
||||
|
||||
<label class="form-label">{{ $t("Options") }}:</label>
|
||||
<br />
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioKeep"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="keep"
|
||||
/>
|
||||
<label class="form-check-label" for="radioKeep">
|
||||
{{ $t("Keep both") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioSkip"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="skip"
|
||||
/>
|
||||
<label class="form-check-label" for="radioSkip">
|
||||
{{ $t("Skip existing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioOverwrite"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="overwrite"
|
||||
/>
|
||||
<label class="form-check-label" for="radioOverwrite">
|
||||
{{ $t("Overwrite") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-text mb-2">
|
||||
{{ $t("importHandleDescription") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<input
|
||||
id="importBackup"
|
||||
type="file"
|
||||
class="form-control"
|
||||
accept="application/json"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-2 justify-content-end">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary"
|
||||
:disabled="processing"
|
||||
@click="confirmImport"
|
||||
>
|
||||
<div
|
||||
v-if="processing"
|
||||
class="spinner-border spinner-border-sm me-1"
|
||||
></div>
|
||||
{{ $t("Import") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="importAlert"
|
||||
class="alert alert-danger mt-3"
|
||||
style="padding: 6px 16px"
|
||||
>
|
||||
{{ importAlert }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Confirm
|
||||
ref="confirmImport"
|
||||
btn-style="btn-danger"
|
||||
:yes-text="$t('Yes')"
|
||||
:no-text="$t('No')"
|
||||
@yes="importBackup"
|
||||
>
|
||||
{{ $t("confirmImportMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
processing: false,
|
||||
importHandle: "skip",
|
||||
importAlert: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
confirmImport() {
|
||||
this.$refs.confirmImport.show();
|
||||
},
|
||||
|
||||
downloadBackup() {
|
||||
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
|
||||
let fileName = `Uptime_Kuma_Backup_${time}.json`;
|
||||
let monitorList = Object.values(this.$root.monitorList);
|
||||
let exportData = {
|
||||
version: this.$root.info.version,
|
||||
notificationList: this.$root.notificationList,
|
||||
monitorList: monitorList,
|
||||
};
|
||||
exportData = JSON.stringify(exportData, null, 4);
|
||||
let downloadItem = document.createElement("a");
|
||||
downloadItem.setAttribute(
|
||||
"href",
|
||||
"data:application/json;charset=utf-8," +
|
||||
encodeURIComponent(exportData)
|
||||
);
|
||||
downloadItem.setAttribute("download", fileName);
|
||||
downloadItem.click();
|
||||
},
|
||||
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("importBackup").files;
|
||||
|
||||
if (uploadItem.length <= 0) {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertNoFile"));
|
||||
}
|
||||
|
||||
if (uploadItem.item(0).type !== "application/json") {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertWrongFileType"));
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.readAsText(uploadItem.item(0));
|
||||
|
||||
fileReader.onload = (item) => {
|
||||
this.$root.uploadBackup(
|
||||
item.target.result,
|
||||
this.importHandle,
|
||||
(res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
#importBackup {
|
||||
&::file-selector-button {
|
||||
color: $primary;
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: $dark-font-color2;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
192
src/components/settings/General.vue
Normal file
192
src/components/settings/General.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div>
|
||||
<form class="my-4" @submit.prevent="saveGeneral">
|
||||
<!-- Timezone -->
|
||||
<div class="mb-4">
|
||||
<label for="timezone" class="form-label">
|
||||
{{ $t("Timezone") }}
|
||||
</label>
|
||||
<select id="timezone" v-model="$root.userTimezone" class="form-select">
|
||||
<option value="auto">
|
||||
{{ $t("Auto") }}: {{ guessTimezone }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(timezone, index) in timezoneList"
|
||||
:key="index"
|
||||
:value="timezone.value"
|
||||
>
|
||||
{{ timezone.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Search Engine -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">
|
||||
{{ $t("Search Engine Visibility") }}
|
||||
</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="searchEngineIndexYes"
|
||||
v-model="settings.searchEngineIndex"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="flexRadioDefault"
|
||||
:value="true"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="searchEngineIndexYes">
|
||||
{{ $t("Allow indexing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="searchEngineIndexNo"
|
||||
v-model="settings.searchEngineIndex"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="flexRadioDefault"
|
||||
:value="false"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="searchEngineIndexNo">
|
||||
{{ $t("Discourage search engines from indexing site") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entry Page -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">{{ $t("Entry Page") }}</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="entryPageYes"
|
||||
v-model="settings.entryPage"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="statusPage"
|
||||
value="dashboard"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="entryPageYes">
|
||||
{{ $t("Dashboard") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="entryPageNo"
|
||||
v-model="settings.entryPage"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="statusPage"
|
||||
value="statusPage"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="entryPageNo">
|
||||
{{ $t("Status Page") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Base URL -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="primaryBaseURL">
|
||||
{{ $t("Primary Base URL") }}
|
||||
</label>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
id="primaryBaseURL"
|
||||
v-model="settings.primaryBaseURL"
|
||||
class="form-control"
|
||||
name="primaryBaseURL"
|
||||
placeholder="https://"
|
||||
pattern="https?://.+"
|
||||
/>
|
||||
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">
|
||||
{{ $t("Auto Get") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-text"></div>
|
||||
</div>
|
||||
|
||||
<!-- Steam API Key -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="steamAPIKey">
|
||||
{{ $t("Steam API Key") }}
|
||||
</label>
|
||||
<HiddenInput
|
||||
id="steamAPIKey"
|
||||
v-model="settings.steamAPIKey"
|
||||
autocomplete="one-time-code"
|
||||
/>
|
||||
<div class="form-text">
|
||||
{{ $t("steamApiKeyDescription") }}
|
||||
<a href="https://steamcommunity.com/dev" target="_blank">
|
||||
https://steamcommunity.com/dev
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../../components/HiddenInput.vue";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import { timezoneList } from "../../util-frontend";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
timezoneList: timezoneList(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
},
|
||||
guessTimezone() {
|
||||
return dayjs.tz.guess();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
saveGeneral() {
|
||||
localStorage.timezone = this.$root.userTimezone;
|
||||
this.saveSettings();
|
||||
},
|
||||
autoGetPrimaryBaseURL() {
|
||||
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
133
src/components/settings/MonitorHistory.vue
Normal file
133
src/components/settings/MonitorHistory.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<label for="keepDataPeriodDays" class="form-label">
|
||||
{{
|
||||
$t("clearDataOlderThan", [
|
||||
settings.keepDataPeriodDays,
|
||||
])
|
||||
}}
|
||||
</label>
|
||||
<input
|
||||
id="keepDataPeriodDays"
|
||||
v-model="settings.keepDataPeriodDays"
|
||||
type="number"
|
||||
class="form-control"
|
||||
required
|
||||
min="1"
|
||||
step="1"
|
||||
/>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<button class="btn btn-primary" type="button" @click="saveSettings()">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<div class="my-3">
|
||||
<button class="btn btn-outline-info me-2" @click="shrinkDatabase">
|
||||
{{ $t("Shrink Database") }} ({{ databaseSizeDisplay }})
|
||||
</button>
|
||||
<div class="form-text mt-2 mb-4 ms-2">{{ $t("shrinkDatabaseDescription") }}</div>
|
||||
</div>
|
||||
<button
|
||||
id="clearAllStats-btn"
|
||||
class="btn btn-outline-danger me-2 mb-2"
|
||||
@click="confirmClearStatistics"
|
||||
>
|
||||
{{ $t("Clear all statistics") }}
|
||||
</button>
|
||||
</div>
|
||||
<Confirm
|
||||
ref="confirmClearStatistics"
|
||||
btn-style="btn-danger"
|
||||
:yes-text="$t('Yes')"
|
||||
:no-text="$t('No')"
|
||||
@yes="clearStatistics"
|
||||
>
|
||||
{{ $t("confirmClearStatisticsMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import { debug } from "../../util.ts";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
databaseSize: 0,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
},
|
||||
databaseSizeDisplay() {
|
||||
return (
|
||||
Math.round((this.databaseSize / 1024 / 1024) * 10) / 10 + " MB"
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadDatabaseSize();
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadDatabaseSize() {
|
||||
debug("load database size");
|
||||
this.$root.getSocket().emit("getDatabaseSize", (res) => {
|
||||
if (res.ok) {
|
||||
this.databaseSize = res.size;
|
||||
debug("database size: " + res.size);
|
||||
} else {
|
||||
debug(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
shrinkDatabase() {
|
||||
this.$root.getSocket().emit("shrinkDatabase", (res) => {
|
||||
if (res.ok) {
|
||||
this.loadDatabaseSize();
|
||||
toast.success("Done");
|
||||
} else {
|
||||
debug(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
confirmClearStatistics() {
|
||||
this.$refs.confirmClearStatistics.show();
|
||||
},
|
||||
|
||||
clearStatistics() {
|
||||
this.$root.clearStatistics((res) => {
|
||||
if (res.ok) {
|
||||
this.$router.go();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
46
src/components/settings/Notifications.vue
Normal file
46
src/components/settings/Notifications.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="notification-list my-4">
|
||||
<p v-if="$root.notificationList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $t("notificationDescription") }}
|
||||
</p>
|
||||
|
||||
<ul class="list-group mb-3" style="border-radius: 1rem;">
|
||||
<li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
|
||||
{{ notification.name }}<br>
|
||||
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
|
||||
{{ $t("Setup Notification") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<NotificationDialog ref="notificationDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NotificationDialog from "../../components/NotificationDialog.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotificationDialog
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
323
src/components/settings/Security.vue
Normal file
323
src/components/settings/Security.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="settingsLoaded" class="my-4">
|
||||
<!-- Change Password -->
|
||||
<template v-if="!settings.disableAuth">
|
||||
<p>
|
||||
{{ $t("Current User") }}: <strong>{{ username }}</strong>
|
||||
<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>
|
||||
<form class="mb-3" @submit.prevent="savePassword">
|
||||
<div class="mb-3">
|
||||
<label for="current-password" class="form-label">
|
||||
{{ $t("Current Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="current-password"
|
||||
v-model="password.currentPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new-password" class="form-label">
|
||||
{{ $t("New Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="new-password"
|
||||
v-model="password.newPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="repeat-new-password" class="form-label">
|
||||
{{ $t("Repeat New Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="repeat-new-password"
|
||||
v-model="password.repeatNewPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
:class="{ 'is-invalid': invalidPassword }"
|
||||
required
|
||||
/>
|
||||
<div class="invalid-feedback">
|
||||
{{ $t("passwordNotMatchMsg") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{{ $t("Update Password") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<div v-if="! settings.disableAuth" class="mt-5 mb-3">
|
||||
<h5 class="my-4">
|
||||
{{ $t("Two Factor Authentication") }}
|
||||
</h5>
|
||||
<div class="mb-4">
|
||||
<button
|
||||
class="btn btn-primary me-2"
|
||||
type="button"
|
||||
@click="$refs.TwoFADialog.show()"
|
||||
>
|
||||
{{ $t("2FA Settings") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<!-- Advanced -->
|
||||
<h5 class="my-4">{{ $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>
|
||||
<button v-if="! settings.disableAuth" id="disableAuth-btn" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 === '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>Si è certi di voler <strong>disabilitare l'autenticazione</strong>?</p>
|
||||
<p>È per <strong>chi ha l'autenticazione gestita da terze parti</strong> messa 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 === '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>
|
||||
|
||||
<!-- English (en) -->
|
||||
<template v-else>
|
||||
<p>Are you sure want to <strong>disable auth</strong>?</p>
|
||||
<p>It is for <strong>someone who have 3rd-party auth</strong> in front of Uptime Kuma such as Cloudflare Access.</p>
|
||||
<p>Please use it carefully.</p>
|
||||
</template>
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import TwoFADialog from "../../components/TwoFADialog.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
TwoFADialog
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
username: "",
|
||||
invalidPassword: false,
|
||||
password: {
|
||||
currentPassword: "",
|
||||
newPassword: "",
|
||||
repeatNewPassword: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
"password.repeatNewPassword"() {
|
||||
this.invalidPassword = false;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadUsername();
|
||||
},
|
||||
|
||||
methods: {
|
||||
savePassword() {
|
||||
if (this.password.newPassword !== this.password.repeatNewPassword) {
|
||||
this.invalidPassword = true;
|
||||
} else {
|
||||
this.$root
|
||||
.getSocket()
|
||||
.emit("changePassword", this.password, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
if (res.ok) {
|
||||
this.password.currentPassword = "";
|
||||
this.password.newPassword = "";
|
||||
this.password.repeatNewPassword = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadUsername() {
|
||||
const jwtPayload = this.$root.getJWTPayload();
|
||||
|
||||
if (jwtPayload) {
|
||||
this.username = jwtPayload.username;
|
||||
}
|
||||
},
|
||||
|
||||
disableAuth() {
|
||||
this.settings.disableAuth = true;
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
enableAuth() {
|
||||
this.settings.disableAuth = false;
|
||||
this.saveSettings();
|
||||
this.$root.storage().removeItem("token");
|
||||
location.reload();
|
||||
},
|
||||
|
||||
confirmDisableAuth() {
|
||||
this.$refs.confirmDisableAuth.show();
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
</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>
|
85
src/i18n.js
85
src/i18n.js
@@ -1,56 +1,45 @@
|
||||
import { createI18n } from "vue-i18n/index";
|
||||
import daDK from "./languages/da-DK";
|
||||
import deDE from "./languages/de-DE";
|
||||
import en from "./languages/en";
|
||||
import esEs from "./languages/es-ES";
|
||||
import etEE from "./languages/et-EE";
|
||||
import fa from "./languages/fa";
|
||||
import frFR from "./languages/fr-FR";
|
||||
import hu from "./languages/hu";
|
||||
import itIT from "./languages/it-IT";
|
||||
import idID from "./languages/id-ID";
|
||||
import ja from "./languages/ja";
|
||||
import koKR from "./languages/ko-KR";
|
||||
import nlNL from "./languages/nl-NL";
|
||||
import nbNO from "./languages/nb-NO";
|
||||
import pl from "./languages/pl";
|
||||
import ptBR from "./languages/pt-BR";
|
||||
import bgBG from "./languages/bg-BG";
|
||||
import ruRU from "./languages/ru-RU";
|
||||
import sr from "./languages/sr";
|
||||
import srLatn from "./languages/sr-latn";
|
||||
import svSE from "./languages/sv-SE";
|
||||
import trTR from "./languages/tr-TR";
|
||||
import zhCN from "./languages/zh-CN";
|
||||
import zhHK from "./languages/zh-HK";
|
||||
|
||||
const languageList = {
|
||||
en,
|
||||
"zh-HK": zhHK,
|
||||
"bg-BG": bgBG,
|
||||
"de-DE": deDE,
|
||||
"nl-NL": nlNL,
|
||||
"nb-NO": nbNO,
|
||||
"es-ES": esEs,
|
||||
"fa": fa,
|
||||
"pt-BR": ptBR,
|
||||
"fr-FR": frFR,
|
||||
"hu": hu,
|
||||
"it-IT": itIT,
|
||||
"id-ID" : idID,
|
||||
"ja": ja,
|
||||
"da-DK": daDK,
|
||||
"sr": sr,
|
||||
"sr-latn": srLatn,
|
||||
"sv-SE": svSE,
|
||||
"tr-TR": trTR,
|
||||
"ko-KR": koKR,
|
||||
"ru-RU": ruRU,
|
||||
"zh-CN": zhCN,
|
||||
"pl": pl,
|
||||
"et-EE": etEE,
|
||||
"zh-HK": "繁體中文 (香港)",
|
||||
"bg-BG": "Български",
|
||||
"de-DE": "Deutsch (Deutschland)",
|
||||
"nl-NL": "Nederlands",
|
||||
"nb-NO": "Norsk",
|
||||
"es-ES": "Español",
|
||||
"fa": "Farsi",
|
||||
"pt-BR": "Português (Brasileiro)",
|
||||
"fr-FR": "Français (France)",
|
||||
"hu": "Magyar",
|
||||
"hr-HR": "Hrvatski",
|
||||
"it-IT": "Italiano (Italian)",
|
||||
"id-ID": "Bahasa Indonesia (Indonesian)",
|
||||
"ja": "日本語",
|
||||
"da-DK": "Danish (Danmark)",
|
||||
"sr": "Српски",
|
||||
"sr-latn": "Srpski",
|
||||
"sv-SE": "Svenska",
|
||||
"tr-TR": "Türkçe",
|
||||
"ko-KR": "한국어",
|
||||
"ru-RU": "Русский",
|
||||
"zh-CN": "简体中文",
|
||||
"pl": "Polski",
|
||||
"et-EE": "eesti",
|
||||
"vi": "Vietnamese",
|
||||
"zh-TW": "繁體中文 (台灣)"
|
||||
};
|
||||
|
||||
let messages = {
|
||||
en,
|
||||
};
|
||||
|
||||
for (let lang in languageList) {
|
||||
messages[lang] = {
|
||||
languageName: languageList[lang]
|
||||
};
|
||||
}
|
||||
|
||||
const rtlLangs = ["fa"];
|
||||
|
||||
export const currentLocale = () => localStorage.locale
|
||||
@@ -67,5 +56,5 @@ export const i18n = createI18n({
|
||||
fallbackLocale: "en",
|
||||
silentFallbackWarn: true,
|
||||
silentTranslationWarn: true,
|
||||
messages: languageList,
|
||||
messages: messages,
|
||||
});
|
||||
|
@@ -4,11 +4,8 @@
|
||||
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/pages/Settings.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`).
|
||||
6. Import your language file in `src/i18n.js` and add it to `languageList` constant.
|
||||
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.
|
||||
|
||||
One of good examples:
|
||||
https://github.com/louislam/uptime-kuma/pull/316/files
|
||||
|
||||
If you do not have programming skills, let me know in [Issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏
|
||||
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. 😏
|
||||
|
@@ -2,11 +2,11 @@ export default {
|
||||
languageName: "Български",
|
||||
checkEverySecond: "Ще се извършва на всеки {0} секунди",
|
||||
retryCheckEverySecond: "Ще се извършва на всеки {0} секунди",
|
||||
retriesDescription: "Максимакен брой опити преди услугата да бъде маркирана като недостъпна и да бъде изпратено известие",
|
||||
retriesDescription: "Максимакен брой опити преди маркиране на услугата като недостъпна и изпращане на известие",
|
||||
ignoreTLSError: "Игнорирай TLS/SSL грешки за HTTPS уебсайтове",
|
||||
upsideDownModeDescription: "Обърни статуса от достъпен на недостъпен. Ако услугата е достъпна се вижда НЕДОСТЪПНА.",
|
||||
upsideDownModeDescription: "Обръща статуса от достъпен на недостъпен. Ако услугата е достъпна, ще се вижда като НЕДОСТЪПНА.",
|
||||
maxRedirectDescription: "Максимален брой пренасочвания, които да бъдат следвани. Въведете 0 за да изключите пренасочване.",
|
||||
acceptedStatusCodesDescription: "Изберете статус кодове, които се считат за успешен отговор.",
|
||||
acceptedStatusCodesDescription: "Изберете статус кодове, които да се считат за успешен отговор.",
|
||||
passwordNotMatchMsg: "Повторената парола не съвпада.",
|
||||
notificationDescription: "Моля, задайте известието към монитор(и), за да функционира.",
|
||||
keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра",
|
||||
@@ -48,7 +48,7 @@ export default {
|
||||
Status: "Статус",
|
||||
DateTime: "Дата и час",
|
||||
Message: "Отговор",
|
||||
"No important events": "Няма важни събития",
|
||||
"No important events": "Все още няма събития",
|
||||
Resume: "Възобнови",
|
||||
Edit: "Редактирай",
|
||||
Delete: "Изтрий",
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
"Accepted Status Codes": "Допустими статус кодове",
|
||||
Save: "Запази",
|
||||
Notifications: "Известявания",
|
||||
"Not available, please setup.": "Не е налично. Моля, настройте.",
|
||||
"Not available, please setup.": "Не са налични. Моля, настройте.",
|
||||
"Setup Notification": "Настройки за известявания",
|
||||
Light: "Светла",
|
||||
Dark: "Тъмна",
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
Timezone: "Часова зона",
|
||||
"Search Engine Visibility": "Видимост за търсачки",
|
||||
"Allow indexing": "Разреши индексиране",
|
||||
"Discourage search engines from indexing site": "Обезкуражи индексирането на сайта от търсачките",
|
||||
"Discourage search engines from indexing site": "Не позволявай на търсачките да индексират този сайт",
|
||||
"Change Password": "Промени парола",
|
||||
"Current Password": "Текуща парола",
|
||||
"New Password": "Нова парола",
|
||||
@@ -107,8 +107,8 @@ export default {
|
||||
Password: "Парола",
|
||||
"Remember me": "Запомни ме",
|
||||
Login: "Вход",
|
||||
"No Monitors, please": "Моля, без монитори",
|
||||
"add one": "добави един",
|
||||
"No Monitors, please": "Все още няма монитори. Моля, добавете поне ",
|
||||
"add one": "един.",
|
||||
"Notification Type": "Тип известяване",
|
||||
Email: "Имейл",
|
||||
Test: "Тест",
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
"Clear Data": "Изтрий данни",
|
||||
Events: "Събития",
|
||||
Heartbeats: "Проверки",
|
||||
"Auto Get": "Автоматияно получаване",
|
||||
"Auto Get": "Авт. попълване",
|
||||
backupDescription: "Можете да архивирате всички монитори и всички известия в JSON файл.",
|
||||
backupDescription2: "PS: Данни за история и събития не са включени.",
|
||||
backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.",
|
||||
@@ -141,7 +141,7 @@ export default {
|
||||
Overwrite: "Презапиши",
|
||||
Options: "Опции",
|
||||
"Keep both": "Запази двете",
|
||||
"Verify Token": "Проверка на токен код",
|
||||
"Verify Token": "Провери токен код",
|
||||
"Setup 2FA": "Настройка 2FA",
|
||||
"Enable 2FA": "Включи 2FA",
|
||||
"Disable 2FA": "Изключи 2FA",
|
||||
@@ -179,8 +179,8 @@ export default {
|
||||
"Edit Status Page": "Редактиране Статус страница",
|
||||
"Go to Dashboard": "Към Таблото",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
smtp: "Email (SMTP)",
|
||||
webhook: "Уеб кука",
|
||||
smtp: "Имейл (SMTP)",
|
||||
discord: "Discord",
|
||||
teams: "Microsoft Teams",
|
||||
signal: "Signal",
|
||||
@@ -197,4 +197,115 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Status Page": "Статус страница",
|
||||
"Primary Base URL": "Основен базов URL адрес",
|
||||
"Push URL": "Генериран Push URL адрес",
|
||||
needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди",
|
||||
pushOptionalParams: "Допълнителни, но незадължителни параметри: {0}",
|
||||
defaultNotificationName: "Моето {notification} известяване ({number})",
|
||||
here: "тук",
|
||||
Required: "Задължително поле",
|
||||
"Bot Token": "Бот токен",
|
||||
wayToGetTelegramToken: "Можете да получите токен от {0}.",
|
||||
"Chat ID": "Чат ID",
|
||||
supportTelegramChatID: "Поддържа Direct Chat / Group / Channel's Chat ID",
|
||||
wayToGetTelegramChatID: "Можете да получите вашето чат ID, като изпратите съобщение на бота, след което е нужно да посетите този URL адрес за да го видите:",
|
||||
"YOUR BOT TOKEN HERE": "ВАШИЯТ БОТ ТОКЕН ТУК",
|
||||
chatIDNotFound: "Чат ID не е намерено. Моля, първо изпратете съобщение до този бот",
|
||||
"Post URL": "Post URL адрес",
|
||||
"Content Type": "Тип съдържание",
|
||||
webhookJsonDesc: "{0} е подходящ за всички съвременни http сървъри, като например express.js",
|
||||
webhookFormDataDesc: "{multipart} е подходящ за PHP, нужно е да анализирате json чрез {decodeFunction}",
|
||||
secureOptionNone: "Няма (25) / STARTTLS (587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Игнорирай TLS грешките",
|
||||
"From Email": "От имейл адрес",
|
||||
emailCustomSubject: "Модифициране на тема",
|
||||
"To Email": "Получател имейл адрес",
|
||||
smtpCC: "Явно копие до имейл адрес:",
|
||||
smtpBCC: "Скрито копие до имейл адрес:",
|
||||
"Discord Webhook URL": "Discord URL адрес на уеб кука",
|
||||
wayToGetDiscordURL: "Може да създадете, от меню \"Настройки на сървъра\" -> \"Интеграции\" -> \"Уеб куки\" -> \"Нова уеб кука\"",
|
||||
"Bot Display Name": "Име на бота, което да се показва",
|
||||
"Prefix Custom Message": "Модифицирано обръщение",
|
||||
"Hello @everyone is...": "Здравейте, {'@'}everyone е...",
|
||||
"Webhook URL": "Уеб кука URL адрес",
|
||||
wayToGetTeamsURL: "Можете да научите как се създава URL адрес за уеб кука {0}.",
|
||||
Number: "Номер",
|
||||
Recipients: "Получатели",
|
||||
needSignalAPI: "Необходимо е да разполагате със Signal клиент с REST API.",
|
||||
wayToCheckSignalURL: "Може да посетите този URL адрес, ако се нуждаете от помощ при настройването:",
|
||||
signalImportant: "ВАЖНО: Не може да смесвате \"Групи\" и \"Номера\" в поле \"Получатели\"!",
|
||||
"Application Token": "Токен код за приложението",
|
||||
"Server URL": "URL адрес на сървъра",
|
||||
Priority: "Приоритет",
|
||||
"Icon Emoji": "Иконка Емотикон",
|
||||
"Channel Name": "Канал име",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL адрес",
|
||||
aboutWebhooks: "Повече информация относно уеб куки на: {0}",
|
||||
aboutChannelName: "Въведете името на канала в поле {0} \"Канал име\", ако желаете да заобиколите канала от уеб куката. Например: #other-channel",
|
||||
aboutKumaURL: "Ако оставите празно полето \"Uptime Kuma URL адрес\", по подразбиране ще се използва GitHub страницата на проекта.",
|
||||
emojiCheatSheet: "Подсказки за емотикони: {0}",
|
||||
"User Key": "Потребителски ключ",
|
||||
Device: "Устройство",
|
||||
"Message Title": "Заглавие на съобщението",
|
||||
"Notification Sound": "Звуков сигнал",
|
||||
"More info on:": "Повече информация на: {0}",
|
||||
pushoverDesc1: "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.",
|
||||
pushoverDesc2: "Ако желаете да изпратите известявания до различни устройства, попълнете полето Устройство.",
|
||||
"SMS Type": "SMS тип",
|
||||
octopushTypePremium: "Премиум (Бърз - препоръчителен в случай на тревога)",
|
||||
octopushTypeLowCost: "Евтин (Бавен - понякога бива блокиран от оператора)",
|
||||
checkPrice: "Тарифни планове на {0}:",
|
||||
octopushLegacyHint: "Дали използвате съвместима версия на Octopush (2011-2020) или нова версия?",
|
||||
"Check octopush prices": "Тарифни планове на octopush {0}.",
|
||||
octopushPhoneNumber: "Телефонен номер (в международен формат, например: +33612345678) ",
|
||||
octopushSMSSender: "SMS подател Име: 3-11 знака - букви, цифри и интервал (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea ID на устройство",
|
||||
"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 - Конзола за разработчици",
|
||||
lineDevConsoleTo: "Line - Конзола за разработчици - {0}",
|
||||
"Basic Settings": "Основни настройки",
|
||||
"User ID": "Потребител ID",
|
||||
"Messaging API": "API за известяване",
|
||||
wayToGetLineChannelToken: "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което може да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.",
|
||||
"Icon URL": "URL адрес за иконка",
|
||||
aboutIconURL: "Може да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.",
|
||||
aboutMattermostChannelName: "Може да замените канала по подразбиране, към който публикува уеб куката, като въведете името на канала в полето \"Канал име\". Tрябва да бъде активирано в настройките за уеб кука на Mattermost. Например: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - евтин, но бавен. Често е претоварен. Само за получатели от Полша.",
|
||||
promosmsTypeFlash: "SMS FLASH - Съобщението автоматично се показва на устройството на получателя. Само за получатели от Полша.",
|
||||
promosmsTypeFull: "SMS FULL - Високо ниво на SMS услуга. Може да използвате Вашето име като подател (Необходимо е първо да регистрирате името). Надежден метод за съобщения тип тревога.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Най-висок приоритет в системата. Много бърза и надеждна, но същвременно скъпа услуга. (Около два пъти по-висока цена в сравнение с SMS FULL).",
|
||||
promosmsPhoneNumber: "Телефонен номер (за получатели от Полша, може да пропуснете въвеждането на код за населено място)",
|
||||
promosmsSMSSender: "SMS Подател име: Предварително регистрирано име или някое от имената по подразбиране: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu URL адрес за уеб кука",
|
||||
matrixHomeserverURL: "Сървър URL адрес (започва с http(s):// и порт по желание)",
|
||||
"Internal Room Id": "ID на вътрешна стая",
|
||||
matrixDesc1: "Може да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известяванията. Токен код за достъп ще получите изпълнявайки {0}",
|
||||
Method: "Метод",
|
||||
Body: "Съобщение",
|
||||
Headers: "Хедъри",
|
||||
PushUrl: "Push URL адрес",
|
||||
HeadersInvalidFormat: "Заявените хедъри не са валидни JSON: ",
|
||||
BodyInvalidFormat: "Заявеното съобщение не е валиден JSON: ",
|
||||
"Monitor History": "История на мониторите",
|
||||
clearDataOlderThan: "Ще се съхранява {0} дни.",
|
||||
records: "записа",
|
||||
"One record": "Един запис",
|
||||
steamApiKeyDescription: "За да мониторирате Steam Gameserver се нуждаете от Steam Web-API ключ. Може да регистрирате Вашия API ключ тук: ",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
apiCredentials: "API удостоверяване",
|
||||
PasswordsDoNotMatch: "Паролите не съвпадат.",
|
||||
"Current User": "Текущ потребител",
|
||||
recent: "Скорошни",
|
||||
shrinkDatabaseDescription: "Инициира \"VACUUM\" за \"SQLite\" база данни. Ако Вашата база данни е създадена след версия 1.10.0, \"AUTO_VACUUM\" функцията е активна и това действие не нужно.",
|
||||
};
|
||||
|
@@ -197,4 +197,111 @@ export default {
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Primary Base URL": "Primär URL",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Du solltest diese URL alle {0} Sekunden aufrufen",
|
||||
pushOptionalParams: "Optionale Parameter: {0}",
|
||||
defaultNotificationName: "Meine {notification} Alarm ({number})",
|
||||
here: "hier",
|
||||
Required: "Erforderlich",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "Hier kannst du einen Token erhalten {0}.",
|
||||
"Chat ID": "Chat ID",
|
||||
supportTelegramChatID: "Unterstützt Direkt Chat / Gruppe / Kanal Chat-ID's",
|
||||
wayToGetTelegramChatID: "Du kannst die Chat-ID erhalten, indem du eine Nachricht an den Bot sendest und zu dieser URL gehst, um die chat_id: zu sehen.",
|
||||
"YOUR BOT TOKEN HERE": "HIER DEIN BOT TOKEN",
|
||||
chatIDNotFound: "Chat-ID wurde nicht gefunden: bitte sende zuerst eine Nachricht an diesen Bot",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} ist gut für alle modernen HTTP-Server sowie Express.js",
|
||||
webhookFormDataDesc: "{multipart} ist gut für PHP. Die JSON muss mit {decodeFunction} geparst werden",
|
||||
secureOptionNone: "Keine / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "TLS-Fehler ignorieren",
|
||||
"From Email": "Von Email",
|
||||
emailCustomSubject: "Benutzerdefinierter Betreff",
|
||||
"To Email": "Zu Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Du kannst diesen erhalten, indem du zu den Servereinstellungen gehst -> Integrationen -> Neuer Webhook",
|
||||
"Bot Display Name": "Bot-Anzeigename",
|
||||
"Prefix Custom Message": "Benutzerdefinierter Nachrichten Präfix",
|
||||
"Hello @everyone is...": "Hallo {'@'}everyone ist...",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Hier erfährst du, wie eine Webhook-URL erstellt werden kann {0}.",
|
||||
Number: "Nummer",
|
||||
Recipients: "Empfänger",
|
||||
needSignalAPI: "Es wird ein Signal Client mit REST-API benötigt.",
|
||||
wayToCheckSignalURL: "Du kannst diese URL aufrufen, um zu sehen, wie du eine einrichtest:",
|
||||
signalImportant: "WICHTIG: Gruppen und Nummern können in Empfängern nicht gemischt werden!",
|
||||
"Application Token": "Anwendungs Token",
|
||||
"Server URL": "Server URL",
|
||||
Priority: "Priorität",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "Kanalname",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Weitere Informationen zu Webhooks auf: {0}",
|
||||
aboutChannelName: "Gebe den Kanalnamen ein auf {0} Feld Kanalname, wenn du den Webhook-Kanal umgehen möchtest. Ex: #other-channel",
|
||||
aboutKumaURL: "Wenn das Feld für die Uptime Kuma URL leer gelassen wird, wird es standardmäßig die GitHub Projekt Seite verwenden.",
|
||||
emojiCheatSheet: "Emoji Cheat Sheet: {0}",
|
||||
"User Key": "Benutzerschlüssel",
|
||||
Device: "Gerät",
|
||||
"Message Title": "Nachrichtentitel",
|
||||
"Notification Sound": "Benachrichtigungston",
|
||||
"More info on:": "Mehr Infos auf: {0}",
|
||||
pushoverDesc1: "Notfallpriorität (2) hat Standardmäßig 30 Sekunden Auszeit, zwischen den Versuchen und läuft nach 1 Stunde ab.",
|
||||
pushoverDesc2: "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.",
|
||||
"SMS Type": "SMS Typ",
|
||||
octopushTypePremium: "Premium (Schnell - zur Benachrichtigung empfohlen)",
|
||||
octopushTypeLowCost: "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)",
|
||||
checkPrice: "Prüfe {0} Preise:",
|
||||
octopushLegacyHint: "Verwendest du die Legacy-Version von Octopush (2011-2020) oder die neue Version?",
|
||||
"Check octopush prices": "Überprüfe die Oktopush Preise {0}.",
|
||||
octopushPhoneNumber: "Telefonnummer (Internationales Format, z.B : +49612345678) ",
|
||||
octopushSMSSender: "Name des SMS-Absenders : 3-11 alphanumerische Zeichen und Leerzeichen (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea Geräte ID",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "Beispiel: {0}",
|
||||
"Read more:": "Weiterlesen: {0}",
|
||||
"Status:": "Status: {0}",
|
||||
"Read more": "Weiterlesen",
|
||||
appriseInstalled: "Apprise ist installiert.",
|
||||
appriseNotInstalled: "Apprise ist nicht installiert. {0}",
|
||||
"Access Token": "Access Token",
|
||||
"Channel access token": "Channel access token",
|
||||
"Line Developers Console": "Line Developers Console",
|
||||
lineDevConsoleTo: "Line Developers Console - {0}",
|
||||
"Basic Settings": "Basic Settings",
|
||||
"User ID": "User ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "Rufe zuerst {0} auf, erstelle dann einen Provider und Channel (Messaging API). Als nächstes kannst du den Channel access token und die User ID aus den oben genannten Menüpunkten abrufen.",
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "Du kannst einen Link zu einem Bild in 'Icon URL' übergeben um das Standardprofilbild zu überschreiben. Wird nicht verwendet, wenn ein Icon Emoji gesetzt ist.",
|
||||
aboutMattermostChannelName: "Du kannst den Standardkanal, auf dem der Webhook postet überschreiben, indem der Kanalnamen in das Feld 'Channel Name' eingeben wird. Dies muss in den Mattermost Webhook-Einstellungen aktiviert werden. Ex: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - billig, aber langsam und oft überladen. Nur auf polnische Empfänger beschränkt.",
|
||||
promosmsTypeFlash: "SMS FLASH - Die Nachricht wird automatisch auf dem Empfängergerät angezeigt. Nur auf polnische Empfänger beschränkt.",
|
||||
promosmsTypeFull: "SMS FULL - Premium Stufe von SMS, es kann der Absendernamen verwendet werden (Der Name musst zuerst registriert werden). Zuverlässig für Warnungen.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Höchste Priorität im System. Sehr schnell und zuverlässig, aber teuer (Ungefähr das doppelte von SMS FULL).",
|
||||
promosmsPhoneNumber: "Phone number (Für polnische Empfänger können die Vorwahlen übersprungen werden)",
|
||||
promosmsSMSSender: "Name des SMS-Absenders : vorregistrierter Name oder einer der Standardwerte: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu Webhook URL",
|
||||
matrixHomeserverURL: "Heimserver URL (mit http(s):// und optionalen Ports)",
|
||||
"Internal Room Id": "Interne Raum-ID",
|
||||
matrixDesc1: "Die interne Raum-ID findest du im erweiterten Bereich der Raumeinstellungen im Matrix-Client. Es sollte es aussehen wie z.B. !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Es wird dringend empfohlen, dass ein neuen Benutzer erstellt wird und nicht den Zugriffstoken deines eigenen Matrix-Benutzers verwendest. Anderfalls ermöglicht es vollen Zugriff auf dein Konto und alle Räume, denen du beigetreten bist. Erstelle stattdessen einen neuen Benutzer und lade ihn nur in den Raum ein, in dem du die Benachrichtigung erhalten möchtest. Du kannst den Zugriffstoken erhalten, indem du folgendes ausführst {0}",
|
||||
Method: "Method",
|
||||
Body: "Body",
|
||||
Headers: "Headers",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "Die Header ist kein gültiges JSON: ",
|
||||
BodyInvalidFormat: "Der Body ist kein gültiges JSON: ",
|
||||
"Monitor History": "Monitor Verlauf",
|
||||
clearDataOlderThan: "Bewahre die Monitor-Verlaufsdaten für {0} Tage auf.",
|
||||
PasswordsDoNotMatch: "Passwörter stimmen nicht überein.",
|
||||
records: "Einträge",
|
||||
"One record": "Ein Eintrag",
|
||||
steamApiKeyDescription: "Um einen Steam Game Server zu überwachen, wird ein Steam Web-API-Schlüssel benötigt. Dieser kann hier registriert werden: ",
|
||||
"Current User": "Aktueller Benutzer",
|
||||
};
|
||||
|
@@ -1,28 +1,28 @@
|
||||
export default {
|
||||
languageName: "English",
|
||||
checkEverySecond: "Check every {0} seconds.",
|
||||
retryCheckEverySecond: "Retry every {0} seconds.",
|
||||
checkEverySecond: "Check every {0} seconds",
|
||||
retryCheckEverySecond: "Retry every {0} seconds",
|
||||
retriesDescription: "Maximum retries before the service is marked as down and a notification is sent",
|
||||
ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
|
||||
upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",
|
||||
maxRedirectDescription: "Maximum number of redirects to follow. Set to 0 to disable redirects.",
|
||||
acceptedStatusCodesDescription: "Select status codes which are considered as a successful response.",
|
||||
passwordNotMatchMsg: "The repeat password does not match.",
|
||||
notificationDescription: "Please assign a notification to monitor(s) to get it to work.",
|
||||
keywordDescription: "Search keyword in plain html or JSON response and it is case-sensitive",
|
||||
notificationDescription: "Notifications must be assigned to a monitor to function.",
|
||||
keywordDescription: "Search keyword in plain HTML or JSON response. The search is case-sensitive.",
|
||||
pauseDashboardHome: "Pause",
|
||||
deleteMonitorMsg: "Are you sure want to delete this monitor?",
|
||||
deleteNotificationMsg: "Are you sure want to delete this notification for all monitors?",
|
||||
resoverserverDescription: "Cloudflare is the default server, you can change the resolver server anytime.",
|
||||
rrtypeDescription: "Select the RR-Type you want to monitor",
|
||||
resoverserverDescription: "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?",
|
||||
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. You can still disable the notification separately for each monitor.",
|
||||
enableDefaultNotificationDescription: "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.",
|
||||
clearEventsMsg: "Are you sure want to delete all events for this monitor?",
|
||||
clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?",
|
||||
confirmClearStatisticsMsg: "Are you sure you want to delete ALL statistics?",
|
||||
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.",
|
||||
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.",
|
||||
twoFAVerifyLabel: "Please type in your token to verify that 2FA is working",
|
||||
confirmImportMsg: "Are you sure you want to import the backup? Please verify you've selected the correct import option.",
|
||||
twoFAVerifyLabel: "Please enter your token to verify 2FA:",
|
||||
tokenValidSettingsMsg: "Token is valid! You can now save the 2FA settings.",
|
||||
confirmEnableTwoFAMsg: "Are you sure you want to enable 2FA?",
|
||||
confirmDisableTwoFAMsg: "Are you sure you want to disable 2FA?",
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
"Max. Redirects": "Max. Redirects",
|
||||
"Accepted Status Codes": "Accepted Status Codes",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "You should call this url every {0} seconds.",
|
||||
needPushEvery: "You should call this URL every {0} seconds.",
|
||||
pushOptionalParams: "Optional parameters: {0}",
|
||||
Save: "Save",
|
||||
Notifications: "Notifications",
|
||||
@@ -135,9 +135,9 @@ export default {
|
||||
Events: "Events",
|
||||
Heartbeats: "Heartbeats",
|
||||
"Auto Get": "Auto Get",
|
||||
backupDescription: "You can backup all monitors and all notifications into a JSON file.",
|
||||
backupDescription2: "PS: History and event data is not included.",
|
||||
backupDescription3: "Sensitive data such as notification tokens is included in the export file, please keep it carefully.",
|
||||
backupDescription: "You can backup all monitors and notifications into a JSON file.",
|
||||
backupDescription2: "Note: history and event data is not included.",
|
||||
backupDescription3: "Sensitive data such as notification tokens are included in the export file; please store export securely.",
|
||||
alertNoFile: "Please select a file to import.",
|
||||
alertWrongFileType: "Please select a JSON file.",
|
||||
"Clear all statistics": "Clear all Statistics",
|
||||
@@ -157,8 +157,8 @@ export default {
|
||||
"Show URI": "Show URI",
|
||||
Tags: "Tags",
|
||||
"Add New below or Select...": "Add New below or Select...",
|
||||
"Tag with this name already exist.": "Tag with this name already exist.",
|
||||
"Tag with this value already exist.": "Tag with this value already exist.",
|
||||
"Tag with this name already exist.": "Tag with this name already exists.",
|
||||
"Tag with this value already exist.": "Tag with this value already exists.",
|
||||
color: "color",
|
||||
"value (optional)": "value (optional)",
|
||||
Gray: "Gray",
|
||||
@@ -183,24 +183,23 @@ export default {
|
||||
"Edit Status Page": "Edit Status Page",
|
||||
"Go to Dashboard": "Go to Dashboard",
|
||||
"Status Page": "Status Page",
|
||||
// Start notification form
|
||||
defaultNotificationName: "My {notification} Alert ({number})",
|
||||
here: "here",
|
||||
"Required": "Required",
|
||||
"telegram": "Telegram",
|
||||
Required: "Required",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "You can get a token from {0}.",
|
||||
"Chat ID": "Chat ID",
|
||||
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
|
||||
wayToGetTelegramChatID: "You can get your chat id by sending message to the bot and go to this url to view the chat_id:",
|
||||
wayToGetTelegramChatID: "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
|
||||
chatIDNotFound: "Chat ID is not found, please send a message to this bot first",
|
||||
"webhook": "Webhook",
|
||||
chatIDNotFound: "Chat ID is not found; please send a message to this bot first",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} is good for any modern http servers such as express.js",
|
||||
webhookFormDataDesc: "{multipart} is good for PHP, you just need to parse the json by {decodeFunction}",
|
||||
"smtp": "Email (SMTP)",
|
||||
webhookJsonDesc: "{0} is good for any modern HTTP servers such as Express.js",
|
||||
webhookFormDataDesc: "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignore TLS Error",
|
||||
@@ -209,45 +208,46 @@ export default {
|
||||
"To Email": "To Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"discord": "Discord",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
|
||||
"Bot Display Name": "Bot Display Name",
|
||||
"Prefix Custom Message": "Prefix Custom Message",
|
||||
"Hello @everyone is...": "Hello {'@'}everyone is...",
|
||||
"teams": "Microsoft Teams",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",
|
||||
"signal": "Signal",
|
||||
"Number": "Number",
|
||||
"Recipients": "Recipients",
|
||||
wayToGetTeamsURL: "You can learn how to create a webhook URL {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Number",
|
||||
Recipients: "Recipients",
|
||||
needSignalAPI: "You need to have a signal client with REST API.",
|
||||
wayToCheckSignalURL: "You can check this url to view how to setup one:",
|
||||
wayToCheckSignalURL: "You can check this URL to view how to set one up:",
|
||||
signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
|
||||
"gotify": "Gotify",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Application Token",
|
||||
"Server URL": "Server URL",
|
||||
"Priority": "Priority",
|
||||
"slack": "Slack",
|
||||
Priority: "Priority",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "Channel Name",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "More info about webhooks on: {0}",
|
||||
aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the webhook channel. Ex: #other-channel",
|
||||
aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.",
|
||||
aboutWebhooks: "More info about Webhooks on: {0}",
|
||||
aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel",
|
||||
aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.",
|
||||
emojiCheatSheet: "Emoji cheat sheet: {0}",
|
||||
"rocket.chat": "Rocket.chat",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Support 50+ Notification services)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "User Key",
|
||||
"Device": "Device",
|
||||
Device: "Device",
|
||||
"Message Title": "Message Title",
|
||||
"Notification Sound": "Notification Sound",
|
||||
"More info on:": "More info on: {0}",
|
||||
@@ -255,8 +255,9 @@ export default {
|
||||
pushoverDesc2: "If you want to send notifications to different devices, fill out Device field.",
|
||||
"SMS Type": "SMS Type",
|
||||
octopushTypePremium: "Premium (Fast - recommended for alerting)",
|
||||
octopushTypeLowCost: "Low Cost (Slow, sometimes blocked by operator)",
|
||||
octopushTypeLowCost: "Low Cost (Slow - sometimes blocked by operator)",
|
||||
checkPrice: "Check {0} prices:",
|
||||
apiCredentials: "API credentials",
|
||||
octopushLegacyHint: "Do you use the legacy version of Octopush (2011-2020) or the new version?",
|
||||
"Check octopush prices": "Check octopush prices {0}.",
|
||||
octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
|
||||
@@ -276,33 +277,79 @@ export default {
|
||||
"Basic Settings": "Basic Settings",
|
||||
"User ID": "User ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.",
|
||||
wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user ID from the above mentioned menu items.",
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",
|
||||
aboutMattermostChannelName: "You can override the default channel that webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel",
|
||||
"matrix": "Matrix",
|
||||
aboutMattermostChannelName: "You can override the default channel that the Webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in the Mattermost Webhook settings. Ex: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.",
|
||||
promosmsTypeFlash: "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.",
|
||||
promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use Your Sender Name (You need to register name first). Reliable for alerts.",
|
||||
promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use your Sender Name (You need to register name first). Reliable for alerts.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
|
||||
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
|
||||
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookUrl",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
||||
"Internal Room Id": "Internal Room Id",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
matrixDesc1: "You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running {0}",
|
||||
// End notification form
|
||||
Method: "Method",
|
||||
Body: "Body",
|
||||
Headers: "Headers",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "The request headers are not valid JSON: ",
|
||||
BodyInvalidFormat: "The request body is not valid JSON: ",
|
||||
"Monitor History": "Monitor History:",
|
||||
"Monitor History": "Monitor History",
|
||||
clearDataOlderThan: "Keep monitor history data for {0} days.",
|
||||
PasswordsDoNotMatch: "Passwords do not match.",
|
||||
records: "records",
|
||||
"One record": "One record",
|
||||
"Showing {from} to {to} of {count} records": "Showing {from} to {to} of {count} records",
|
||||
steamApiKeyDescription: "For monitoring a Steam Gameserver you need a steam Web-API key. You can register your api key here: ",
|
||||
steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ",
|
||||
"Current User": "Current User",
|
||||
recent: "Recent",
|
||||
Done: "Done",
|
||||
Info: "Info",
|
||||
Security: "Security",
|
||||
"Steam API Key": "Steam API Key",
|
||||
"Shrink Database": "Shrink Database",
|
||||
"Pick a RR-Type...": "Pick a RR-Type...",
|
||||
"Pick Accepted Status Codes...": "Pick Accepted Status Codes...",
|
||||
Default: "Default",
|
||||
"HTTP Options": "HTTP Options",
|
||||
"Create Incident": "Create Incident",
|
||||
Title: "Title",
|
||||
Content: "Content",
|
||||
Style: "Style",
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
danger: "danger",
|
||||
primary: "primary",
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
Post: "Post",
|
||||
"Please input title and content": "Please input title and content",
|
||||
Created: "Created",
|
||||
"Last Updated": "Last Updated",
|
||||
Unpin: "Unpin",
|
||||
"Switch to Light Theme": "Switch to Light Theme",
|
||||
"Switch to Dark Theme": "Switch to Dark Theme",
|
||||
"Show Tags": "Show Tags",
|
||||
"Hide Tags": "Hide Tags",
|
||||
Description: "Description",
|
||||
"No monitors available.": "No monitors available.",
|
||||
"Add one": "Add one",
|
||||
"No Monitors": "No Monitors",
|
||||
"Add one": "Add one",
|
||||
"Untitled Group": "Untitled Group",
|
||||
Services: "Services",
|
||||
Discard: "Discard",
|
||||
Cancel: "Cancel",
|
||||
"Powered by": "Powered by",
|
||||
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API Username (incl. webapi_ prefix)",
|
||||
serwersmsAPIPassword: "API Password",
|
||||
serwersmsPhoneNumber: "Phone number",
|
||||
serwersmsSenderName: "SMS Sender Name (registered via customer portal)",
|
||||
"stackfield": "Stackfield",
|
||||
};
|
||||
|
@@ -198,9 +198,9 @@ export default {
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Monitor History": "Historial de monitor:",
|
||||
"Monitor History": "Historial de monitor",
|
||||
clearDataOlderThan: "Mantener los datos del historial del monitor durante {0} días.",
|
||||
records: "registros",
|
||||
"One record": "Un registro",
|
||||
"Showing {from} to {to} of {count} records": "Mostrando desde {from} a {to} de {count} registros",
|
||||
steamApiKeyDescription: "Para monitorear un servidor de juegos de Steam, necesita una clave Steam Web-API. Puede registrar su clave API aquí: ",
|
||||
};
|
||||
|
@@ -182,9 +182,6 @@ export default {
|
||||
"Uptime Kuma": "آپتایم کوما",
|
||||
records: "مورد",
|
||||
"One record": "یک مورد",
|
||||
"Showing {from} to {to} of {count} records": "نمایش از {from} تا {to} از {count} مورد",
|
||||
First: "اولین",
|
||||
Last: "آخرین",
|
||||
Info: "اطلاعات",
|
||||
"Powered by": "نیرو گرفته از",
|
||||
telegram: "Telegram",
|
||||
|
@@ -179,11 +179,10 @@ export default {
|
||||
"Edit Status Page": "Modifier la page de statut",
|
||||
"Go to Dashboard": "Accéder au tableau de bord",
|
||||
"Status Page": "Status Page",
|
||||
// Start notification form
|
||||
defaultNotificationName: "Ma notification {notification} numéro ({number})",
|
||||
here: "ici",
|
||||
"Required": "Requis",
|
||||
"telegram": "Telegram",
|
||||
Required: "Requis",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "Vous pouvez obtenir un token depuis {0}.",
|
||||
"Chat ID": "Chat ID",
|
||||
@@ -191,12 +190,12 @@ export default {
|
||||
wayToGetTelegramChatID: "Vous pouvez obtenir l'ID du chat en envoyant un message avec le bot puis en récupérant l'URL pour voir l'ID du salon :",
|
||||
"YOUR BOT TOKEN HERE": "VOTRE TOKEN BOT ICI",
|
||||
chatIDNotFound: "ID du salon introuvable, envoyez un message via le bot avant",
|
||||
"webhook": "Webhook",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} est bien/bon pour tous les serveurs HTTP modernes comme express.js",
|
||||
webhookFormDataDesc: "{multipart} est bien/bon pour du PHP, vous avez juste besoin de mettre le json via/depuis {decodeFunction}",
|
||||
"smtp": "Email (SMTP)",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "Aucun / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignorer les erreurs TLS",
|
||||
@@ -204,26 +203,26 @@ export default {
|
||||
"To Email": "Vers l'Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"discord": "Discord",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Vous pouvez l'obtenir en allant dans 'Paramètres du Serveur' -> 'Intégrations' -> 'Créer un Webhook'",
|
||||
"Bot Display Name": "Nom du bot (affiché)",
|
||||
"Prefix Custom Message": "Prefix Custom Message",
|
||||
"Hello @everyone is...": "Bonjour {'@'}everyone il...",
|
||||
"teams": "Microsoft Teams",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Vous pouvez apprendre comment créer un Webhook {0}.",
|
||||
"signal": "Signal",
|
||||
"Number": "Numéro",
|
||||
"Recipients": "Destinataires",
|
||||
signal: "Signal",
|
||||
Number: "Numéro",
|
||||
Recipients: "Destinataires",
|
||||
needSignalAPI: "Vous avez besoin d'un client Signal avec l'API REST.",
|
||||
wayToCheckSignalURL: "Vous pouvez regarder l'URL sur comment le mettre en place :",
|
||||
signalImportant: "IMPORTANT: Vous ne pouvez pas mixer les groupes et les numéros en destinataires !",
|
||||
"gotify": "Gotify",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Application Token",
|
||||
"Server URL": "Server URL",
|
||||
"Priority": "Priorité",
|
||||
"slack": "Slack",
|
||||
Priority: "Priorité",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "Nom du salon",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
@@ -242,7 +241,7 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Clé d'utilisateur",
|
||||
"Device": "Appareil",
|
||||
Device: "Appareil",
|
||||
"Message Title": "Titre du message",
|
||||
"Notification Sound": "Son de notification",
|
||||
"More info on:": "Plus d'informations sur: {0}",
|
||||
@@ -273,12 +272,36 @@ export default {
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "Vous pouvez mettre un lien vers l'image dans \"Icon URL\" pour remplacer l'image de profil par défaut. Ne sera pas utilisé si Icon Emoji est défini.",
|
||||
aboutMattermostChannelName: "Vous pouvez remplacer le salon par défaut que le Webhook utilise en mettant le nom du salon dans le champ \"Channel Name\". Vous aurez besoin de l'activer depuis les paramètres de Mattermost. Ex : #autre-salon",
|
||||
"matrix": "Matrix",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - Pas cher mais lent et souvent surchargé. Limité uniquement aux déstinataires Polonais.",
|
||||
promosmsTypeFlash: "SMS FLASH - Le message sera automatiquement affiché sur l'appareil du destinataire. Limité uniquement aux déstinataires Polonais.",
|
||||
promosmsTypeFull: "SMS FULL - Version Premium des SMS, Vous pouvez mettre le nom de l'expéditeur (Vous devez vous enregistrer avant). Fiable pour les alertes.",
|
||||
promosmsTypeSpeed: "SMS SPEED - La plus haute des priorités dans le système. Très rapide et fiable mais cher (environ le double du prix d'un SMS FULL).",
|
||||
promosmsPhoneNumber: "Numéro de téléphone (Poiur les déstinataires Polonais, vous pouvez enlever les codes interna.)",
|
||||
promosmsSMSSender: "SMS Expéditeur : Nom pré-enregistré ou l'un de base: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
// End notification form
|
||||
"Primary Base URL": "Primary Base URL",
|
||||
emailCustomSubject: "Sujet personalisé",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
checkPrice: "Vérification {0} tarifs:",
|
||||
apiCredentials: "Crédentials de l'API",
|
||||
octopushLegacyHint: "Vous utilisez l'ancienne version d'Octopush (2011-2020) ou la nouvelle version ?",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "L'URL du serveur (avec http(s):// et le port de manière facultatif)",
|
||||
"Internal Room Id": "ID de la salle interne",
|
||||
matrixDesc1: "Vous pouvez trouvez l'ID de salle interne en regardant dans la section avancée des paramètres dans le client Matrix. C'est sensé ressembler à: !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Il est fortement recommandé de créer un nouvel utilisateur et de ne pas utiliser le jeton d'accès de votre propre utilisateur Matrix, car il vous donnera un accès complet à votre compte et à toutes les salles que vous avez rejointes. Au lieu de cela, créez un nouvel utilisateur et invitez-le uniquement dans la salle dans laquelle vous souhaitez recevoir la notification. Vous pouvez obtenir le jeton d'accès en exécutant {0}",
|
||||
Method: "Méthode",
|
||||
Body: "Le corps",
|
||||
Headers: "En-têtes",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "L'en-têtes de la requête n'est pas dans un format JSON valide: ",
|
||||
BodyInvalidFormat: "Le corps de la requête n'est pas dans un format JSON valide: ",
|
||||
"Monitor History": "Historique de la sonde",
|
||||
clearDataOlderThan: "Garder l'historique des données de la sonde durant {0} jours.",
|
||||
PasswordsDoNotMatch: "Les mots de passe ne correspondent pas.",
|
||||
records: "Enregistrements",
|
||||
"One record": "Un enregistrement",
|
||||
steamApiKeyDescription: "Pour surveiller un serveur Steam, vous avez besoin d'une clé Steam Web-API. Vous pouvez enregistrer votre clé ici: ",
|
||||
"Current User": "Utilisateur actuel",
|
||||
recent: "Récent",
|
||||
};
|
||||
|
350
src/languages/hr-HR.js
Normal file
350
src/languages/hr-HR.js
Normal file
@@ -0,0 +1,350 @@
|
||||
export default {
|
||||
languageName: "Hrvatski",
|
||||
checkEverySecond: "Provjera svake {0} sekunde",
|
||||
retryCheckEverySecond: "Ponovni pokušaj svake {0} sekunde",
|
||||
retriesDescription: "Broj ponovnih pokušaja prije nego će se servis označiti kao nedostupan te poslati obavijest",
|
||||
ignoreTLSError: "Ignoriraj TLS/SSL pogreške za HTTPS web stranice",
|
||||
upsideDownModeDescription: "Preokreni logiku statusa. Ako se primi pozitivan odgovor, smatra se da je usluga nedostupna.",
|
||||
maxRedirectDescription: "Maksimalan broj preusmjeravanja. Postaviti na 0 kako bi se preusmjeravanja onemogućila.",
|
||||
acceptedStatusCodesDescription: "Odaberite statusne kodove koji se smatraju uspješnim odgovorom.",
|
||||
passwordNotMatchMsg: "Lozinke se ne poklapaju.",
|
||||
notificationDescription: "Obavijesti će funkcionirati samo ako su dodijeljene monitoru.",
|
||||
keywordDescription: "Ključna riječ za pretragu, u obliku običnog HTML-a ili u JSON formatu. Pretraga je osjetljiva na velika i mala slova.",
|
||||
deleteMonitorMsg: "Jeste li sigurni da želite izbrisati monitor?",
|
||||
deleteNotificationMsg: "Jeste li sigurni da želite izbrisati ovu obavijest za sve monitore?",
|
||||
resoverserverDescription: "Cloudflare je zadani DNS poslužitelj. Možete to promijeniti u bilo kojem trenutku.",
|
||||
rrtypeDescription: "Odaberite vrstu DNS zapisa o resursu kojeg želite pratiti",
|
||||
pauseMonitorMsg: "Jeste li sigurni da želite pauzirati?",
|
||||
enableDefaultNotificationDescription: "Ova će obavijesti biti omogućena za sve nove monitore. Možete ju ručno onemogućiti za pojedini monitor.",
|
||||
clearEventsMsg: "Jeste li sigurni da želite izbrisati sve zapise o događajima za ovaj monitor?",
|
||||
clearHeartbeatsMsg: "Jeste li sigurni da želite izbrisati sve zapise o provjerama za ovaj monitor?",
|
||||
confirmClearStatisticsMsg: "Jeste li sigurni da želite izbrisati SVE statistike?",
|
||||
importHandleDescription: "Odaberite opciju \"Preskoči postojeće\" ako želite preskočiti uvoz postojećih monitora i obavijesti ako dođe do poklapanja u imenu. Opcija \"Prepiši\" će izbrisati postojeće monitore i obavijesti.",
|
||||
confirmImportMsg: "Jeste li sigurni da želite pokrenuti uvoz? Provjerite jeste li odabrali ispravnu opciju uvoza.",
|
||||
twoFAVerifyLabel: "Unesite svoj 2FA token:",
|
||||
tokenValidSettingsMsg: "Token je važeći! Sada možete spremiti postavke dvofaktorske autentikacije.",
|
||||
confirmEnableTwoFAMsg: "Želite li omogućiti dvofaktorsku autentikaciju?",
|
||||
confirmDisableTwoFAMsg: "Jeste li sigurni da želite onemogućiti dvofaktorsku autentikaciju?",
|
||||
Settings: "Postavke",
|
||||
Dashboard: "Kontrolna ploča",
|
||||
"New Update": "Novo ažuriranje",
|
||||
Language: "Jezik",
|
||||
Appearance: "Izgled",
|
||||
Theme: "Tema",
|
||||
General: "Općenito",
|
||||
"Primary Base URL": "Osnovni URL",
|
||||
Version: "Inačica",
|
||||
"Check Update On GitHub": "Provjeri dostupnost nove inačice na GitHubu",
|
||||
List: "Popis",
|
||||
Add: "Dodaj",
|
||||
"Add New Monitor": "Dodaj novi Monitor",
|
||||
"Quick Stats": "Statistika",
|
||||
Up: "Dostupno",
|
||||
Down: "Nedostupno",
|
||||
Pending: "U tijeku",
|
||||
Unknown: "Nepoznato",
|
||||
pauseDashboardHome: "Pauzirano",
|
||||
Name: "Naziv",
|
||||
Status: "Status",
|
||||
DateTime: "Vremenska oznaka",
|
||||
Message: "Izvještaj",
|
||||
"No important events": "Nema važnih događaja",
|
||||
Pause: "Pauziraj",
|
||||
Resume: "Nastavi",
|
||||
Edit: "Uredi",
|
||||
Delete: "Obriši",
|
||||
Current: "Trenutno",
|
||||
Uptime: "Dostupnost",
|
||||
"Cert Exp.": "Istek cert.",
|
||||
days: "dana",
|
||||
day: "dan",
|
||||
"-day": "-dnevno",
|
||||
hour: "sat",
|
||||
"-hour": "-satno",
|
||||
Response: "Odgovor",
|
||||
Ping: "Odziv",
|
||||
"Monitor Type": "Vrsta Monitora",
|
||||
Keyword: "Ključna riječ",
|
||||
"Friendly Name": "Prilagođen naziv",
|
||||
URL: "URL",
|
||||
Hostname: "Domaćin",
|
||||
Port: "Port",
|
||||
"Heartbeat Interval": "Interval provjere",
|
||||
Retries: "Broj ponovnih pokušaja",
|
||||
"Heartbeat Retry Interval": "Interval ponovnih pokušaja",
|
||||
Advanced: "Napredne postavke",
|
||||
"Upside Down Mode": "Obrnuti način",
|
||||
"Max. Redirects": "Maksimalan broj preusmjeravanja",
|
||||
"Accepted Status Codes": "Prihvaćeni statusni kodovi",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Potrebno je slati zahtjeve na URL svakih {0} sekundi.",
|
||||
pushOptionalParams: "Neobavezni parametri: {0}",
|
||||
Save: "Spremi",
|
||||
Notifications: "Obavijesti",
|
||||
"Not available, please setup.": "Obavijesti nisu dostupne, potrebno dodati novu obavijest.",
|
||||
"Setup Notification": "Dodaj obavijest",
|
||||
Light: "Svijetli način",
|
||||
Dark: "Tamni način",
|
||||
Auto: "Automatski",
|
||||
"Theme - Heartbeat Bar": "Tema za traku dostupnosti",
|
||||
Normal: "Normalno",
|
||||
Bottom: "Ispod",
|
||||
None: "Isključeno",
|
||||
Timezone: "Vremenska zona",
|
||||
"Search Engine Visibility": "Vidljivost tražilicama",
|
||||
"Allow indexing": "Dopusti indeksiranje",
|
||||
"Discourage search engines from indexing site": "Sprječavanje indeksiranja",
|
||||
"Change Password": "Promjena lozinke",
|
||||
"Current Password": "Trenutna lozinka",
|
||||
"New Password": "Nova lozinka",
|
||||
"Repeat New Password": "Potvrdite novu lozinku",
|
||||
"Update Password": "Spremi novu lozinku",
|
||||
"Disable Auth": "Onemogući autentikaciju",
|
||||
"Enable Auth": "Omogući autentikaciju",
|
||||
Logout: "Odjava",
|
||||
Leave: "Poništi",
|
||||
"I understand, please disable": "Razumijem, svejedno onemogući",
|
||||
Confirm: "Potvrda",
|
||||
Yes: "Da",
|
||||
No: "Ne",
|
||||
Username: "Korisničko ime",
|
||||
Password: "Lozinka",
|
||||
"Remember me": "Zapamti me",
|
||||
Login: "Prijava",
|
||||
"No Monitors, please": "Nema monitora, ",
|
||||
"add one": "dodaj jedan",
|
||||
"Notification Type": "Tip obavijesti",
|
||||
Email: "E-pošta",
|
||||
Test: "Testiraj",
|
||||
"Certificate Info": "Informacije o certifikatu",
|
||||
"Resolver Server": "DNS poslužitelj",
|
||||
"Resource Record Type": "Vrsta DNS zapisa",
|
||||
"Last Result": "Posljednji rezultat",
|
||||
"Create your admin account": "Stvori administratorski račun",
|
||||
"Repeat Password": "Potvrda lozinke",
|
||||
"Import Backup": "Uvoz sigurnosne kopije",
|
||||
"Export Backup": "Izvoz sigurnosne kopije",
|
||||
Export: "Izvoz",
|
||||
Import: "Uvoz",
|
||||
respTime: "Vrijeme odgovora (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
"Default enabled": "Omogući za nove monitore",
|
||||
"Apply on all existing monitors": "Primijeni na postojeće monitore",
|
||||
Create: "Kreiraj",
|
||||
"Clear Data": "Obriši podatke",
|
||||
Events: "Događaji",
|
||||
Heartbeats: "Provjere",
|
||||
"Auto Get": "Automatski dohvat",
|
||||
backupDescription: "Moguće je napraviti sigurnosnu kopiju svih monitora i obavijesti koja će biti spremljena kao JSON datoteka.",
|
||||
backupDescription2: "Napomena: povijest i podaci o događajima nisu uključeni u sigurnosnu kopiju.",
|
||||
backupDescription3: "Osjetljivi podaci poput tokena za obavijesti uključeni su u sigurnosnu kopiju. Zato je potrebno čuvati izvoz na sigurnom mjestu.",
|
||||
alertNoFile: "Datoteka za uvoz nije odabrana.",
|
||||
alertWrongFileType: "Datoteka za uvoz nije u JSON formatu.",
|
||||
"Clear all statistics": "Obriši sve statistike",
|
||||
"Skip existing": "Preskoči postojeće",
|
||||
Overwrite: "Prepiši",
|
||||
Options: "Opcije",
|
||||
"Keep both": "Zadrži sve",
|
||||
"Verify Token": "Provjeri Token",
|
||||
"Setup 2FA": "Postavi dvofaktorsku autentikaciju",
|
||||
"Enable 2FA": "Omogući dvofaktorsku autentikaciju",
|
||||
"Disable 2FA": "Onemogući dvofaktorsku autentikaciju",
|
||||
"2FA Settings": "Postavke 2FA",
|
||||
"Two Factor Authentication": "Dvofaktorska autentikacija",
|
||||
Active: "Aktivna",
|
||||
Inactive: "Neaktivno",
|
||||
Token: "Token",
|
||||
"Show URI": "Pokaži URI",
|
||||
Tags: "Oznake",
|
||||
"Add New below or Select...": "Dodajte novu oznaku ispod ili odaberite...",
|
||||
"Tag with this name already exist.": "Oznaka s tim nazivom već postoji",
|
||||
"Tag with this value already exist.": "Oznaka s tom vrijednošću već postoji.",
|
||||
color: "Boja",
|
||||
"value (optional)": "Vrijednost (neobavezno)",
|
||||
Gray: "Siva",
|
||||
Red: "Crvena",
|
||||
Orange: "Narančasta",
|
||||
Green: "Zelena",
|
||||
Blue: "Plava",
|
||||
Indigo: "Indigo",
|
||||
Purple: "Ljubičasta",
|
||||
Pink: "Ružičasta",
|
||||
"Search...": "Pretraga...",
|
||||
"Avg. Ping": "Prosječni odziv",
|
||||
"Avg. Response": "Prosječni odgovor",
|
||||
"Entry Page": "Početna stranica",
|
||||
statusPageNothing: "Ovdje nema ničega, dodajte grupu ili monitor.",
|
||||
"No Services": "Nema usluga",
|
||||
"All Systems Operational": "Svi sustavi su operativni",
|
||||
"Partially Degraded Service": "Usluga djelomično nedostupna",
|
||||
"Degraded Service": "Usluga nedostupna",
|
||||
"Add Group": "Dodaj grupu",
|
||||
"Add a monitor": "Dodaj monitor",
|
||||
"Edit Status Page": "Uredi Statusnu stranicu",
|
||||
"Go to Dashboard": "Na Kontrolnu ploču",
|
||||
"Status Page": "Statusna stranica",
|
||||
defaultNotificationName: "Moja {number}. {notification} obavijest",
|
||||
here: "ovdje",
|
||||
Required: "Potrebno",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Token bota",
|
||||
wayToGetTelegramToken: "Token možete nabaviti preko {0}.",
|
||||
"Chat ID": "ID razgovora",
|
||||
supportTelegramChatID: "Podržani su ID-jevi izravnih razgovora, grupa i kanala",
|
||||
wayToGetTelegramChatID: "ID razgovora možete saznati tako da botu pošaljete poruku te odete na ovaj URL:",
|
||||
"YOUR BOT TOKEN HERE": "OVDJE IDE TOKEN BOTA",
|
||||
chatIDNotFound: "ID razgovora nije pronađen; prvo morate poslati poruku botu",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "URL Post zahtjeva",
|
||||
"Content Type": "Tip sadržaja (Content Type)",
|
||||
webhookJsonDesc: "{0} je dobra opcija za moderne HTTP poslužitelje poput Express.js-a",
|
||||
webhookFormDataDesc: "{multipart} je moguća alternativa za PHP, samo je potrebno parsirati JSON koristeći {decodeFunction}",
|
||||
smtp: "E-mail (SMTP)",
|
||||
secureOptionNone: "Bez sigurnosti / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignoriraj greške TLS-a",
|
||||
"From Email": "Adresa za \"From\" polje",
|
||||
emailCustomSubject: "Prilagođeno \"Subject\" polje",
|
||||
"To Email": "Odredišne adrese e-pošte",
|
||||
smtpCC: "Cc",
|
||||
smtpBCC: "Bcc",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "URL Discord webhooka",
|
||||
wayToGetDiscordURL: "Ovo možete dobiti tako da odete na Postavke servera -> Integracije -> Napravi webhook",
|
||||
"Bot Display Name": "Nadimak Bota unutar servera",
|
||||
"Prefix Custom Message": "Prefiks prilagođene poruke",
|
||||
"Hello @everyone is...": "Pozdrav {'@'}everyone...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "URL webhooka",
|
||||
wayToGetTeamsURL: "Više informacija o Teams webhookovima možete pročitati {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Broj",
|
||||
Recipients: "Primatelji",
|
||||
needSignalAPI: "Potreban je klijent s REST sučeljem.",
|
||||
wayToCheckSignalURL: "Više informacija o postavljanju Signal klijenta:",
|
||||
signalImportant: "VAŽNO: Grupe i brojevi se ne mogu istovremeno koristiti kao primatelji!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Token Aplikacije",
|
||||
"Server URL": "URL poslužitelja",
|
||||
Priority: "Prioritet",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Emotikon",
|
||||
"Channel Name": "Naziv kanala",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Dodatne informacije o webhookovima su dostupne na: {0}",
|
||||
aboutChannelName: "Unesite ime {0} kanala u polju Naziv kanala ako želite zaobići webhook kanal. Primjerice: #neki-kanal",
|
||||
aboutKumaURL: "Ako je polje \"Uptime Kuma URL\" prazno, koristi se zadana vrijednost koja vodi na GitHub stranicu projekta.",
|
||||
emojiCheatSheet: "Popis emotikona: {0}",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Podržava preko 50 usluga za obavijesti)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "LINE",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Korisnički ključ",
|
||||
Device: "Uređaji",
|
||||
"Message Title": "Naslov poruke",
|
||||
"Notification Sound": "Zvuk obavijesti",
|
||||
"More info on:": "Više informacija na: {0}",
|
||||
pushoverDesc1: "Hitni prioritet (2) ima zadani istek vremena od 30 sekundi između ponovnih pokušaja te će isteći nakon 1 sata.",
|
||||
pushoverDesc2: "Ako želite slati obavijesti na više uređaja, ispunite polje \"Uređaji\".",
|
||||
"SMS Type": "Tip SMS-a",
|
||||
octopushTypePremium: "Premium (Brzo - preporučeno za obavijesti)",
|
||||
octopushTypeLowCost: "Low Cost (Sporo - mobilni operateri ponekad blokiraju ove poruke)",
|
||||
checkPrice: "Provjerite {0} cijene:",
|
||||
apiCredentials: "Vjerodajnice za API",
|
||||
octopushLegacyHint: "Koristite li staru inačicu usluge Octopush (2011-2020) ili noviju inačicu?",
|
||||
"Check octopush prices": "Provjerite cijene usluge Octopush {0}.",
|
||||
octopushPhoneNumber: "Telefonski broj (međunarodni format, primjerice: +38512345678) ",
|
||||
octopushSMSSender: "Naziv SMS pošiljatelja : 3-11 alfanumeričkih znakova i razmak (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea ID Uređaja",
|
||||
"Apprise URL": "URL usluge Apprise",
|
||||
"Example:": "Primjerice: {0}",
|
||||
"Read more:": "Pročitajte više: {0}",
|
||||
"Status:": "Status: {0}",
|
||||
"Read more": "Pročitaj više",
|
||||
appriseInstalled: "Apprise je instaliran.",
|
||||
appriseNotInstalled: "Apprise nije instaliran. {0}",
|
||||
"Access Token": "Pristupni token",
|
||||
"Channel access token": "Token za pristup kanalu",
|
||||
"Line Developers Console": "LINE razvojnoj konzoli",
|
||||
lineDevConsoleTo: "LINE razvojna konzola - {0}",
|
||||
"Basic Settings": "Osnovne Postavke",
|
||||
"User ID": "Korisnički ID",
|
||||
"Messaging API": "API za razmjenu poruka",
|
||||
wayToGetLineChannelToken: "Prvo, pristupite {0}, kreirajte pružatelja usluga te kanal (API za razmjenu poruka), zatim možete dobiti token za pristup kanalu te korisnički ID za polja iznad.",
|
||||
"Icon URL": "URL slike",
|
||||
aboutIconURL: "Možete postaviti poveznicu na sliku u polju \"URL slike\" kako biste spriječili korištenje zadane slike. Ovo se polje neće koristiti ako je postavljeno polje \"Emotikon\".",
|
||||
aboutMattermostChannelName: "Možete promijeniti kanal u kojeg webhook šalje tako da ispunite polje \"Naziv kanala\". Ta opcija mora biti omogućena unutar Mattermost postavki za webhook. Primjerice: #neki-kanal",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - jeftina, ali spora opcija koja je često preopterećena. Ograničeno samo na primatelje unutar Poljske.",
|
||||
promosmsTypeFlash: "SMS FLASH - Poruka se automatski pojavljuje na uređaju primatelja. Ograničeno samo na primatelje unutar Poljske.",
|
||||
promosmsTypeFull: "SMS FULL - Premium razina usluge, dozvoljava postavljanje naziva SMS pošiljatelja (Naziv mora biti registriran). Usluga pouzdana za obavijesti.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Usluga najvećeg prioriteta. Brza i pouzdana, ali skupa (otprilike dvostruko skuplja od cijene usluge SMS FULL).",
|
||||
promosmsPhoneNumber: "Telefonski broj (za primatelje unutar Poljske nije potrebno navoditi pozivni broj države)",
|
||||
promosmsSMSSender: "Naziv SMS pošiljatelja: Registriran naziv ili jedan od zadanih: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu URL webhooka",
|
||||
matrixHomeserverURL: "URL Matrix homeservera (uključujući http(s):// te port, ako je potrebno)",
|
||||
"Internal Room Id": "Interni ID sobe",
|
||||
matrixDesc1: "Interni ID sobe se može pronaći u naprednim postavkama sobe unutar Matrix klijenta. ID sobe nalikuje idućem zapisu: !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Preporučuje se stvaranje novog korisnika te suzdržavanje od korištenja pristupnog tokena vlastitog Matrix korisnika. Novog korisnika potrebno je dodati u sobe u kojima želite primati obavijesti. Pristupni token možete dobiti pokretanjem naredbe {0}",
|
||||
Method: "Metoda",
|
||||
Body: "Tijelo",
|
||||
Headers: "Zaglavlja",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "Zaglavlja nisu nije valjani JSON: ",
|
||||
BodyInvalidFormat: "Tijelo zahtjeva nije valjani JSON: ",
|
||||
"Monitor History": "Povijest monitora",
|
||||
clearDataOlderThan: "Podaci o povijesti monitora čuvaju se {0} dana.",
|
||||
PasswordsDoNotMatch: "Lozinke se ne poklapaju.",
|
||||
records: "zapisa",
|
||||
"One record": "Jedan zapis",
|
||||
"Showing {from} to {to} of {count} records": "Prikaz zapisa {from}-{to} od sveukupno {count}",
|
||||
steamApiKeyDescription: "Za praćenje Steam poslužitelja za igru, potrebno je imati Steam Web-API ključ. Možete registrirati vlastiti ključ ovdje: ",
|
||||
"Current User": "Trenutni korisnik",
|
||||
recent: "Nedavno",
|
||||
Done: "Gotovo",
|
||||
Info: "Informacije",
|
||||
Security: "Sigurnost",
|
||||
"Shrink Database": "Smanji bazu podataka",
|
||||
"Pick a RR-Type...": "Odaberite vrstu DNS zapisa od navedenih...",
|
||||
"Pick Accepted Status Codes...": "Odaberite HTTP statusne kodove koji će biti prihvaćeni...",
|
||||
"Steam API Key": "Steam API ključ",
|
||||
Default: "Zadano",
|
||||
"HTTP Options": "HTTP Postavke",
|
||||
"Create Incident": "Novi izvještaj o incidentu",
|
||||
Title: "Naslov",
|
||||
Content: "Sadržaj",
|
||||
Style: "Stil",
|
||||
info: "informacija",
|
||||
warning: "upozorenje",
|
||||
danger: "opasnost",
|
||||
primary: "primarno",
|
||||
light: "svijetlo",
|
||||
dark: "tamno",
|
||||
Post: "Objavi",
|
||||
Created: "Stvoreno",
|
||||
"Last Updated": "Uređeno",
|
||||
"Please input title and content": "Naslov i sadržaj ne mogu biti prazni",
|
||||
Unpin: "Ukloni",
|
||||
"Switch to Light Theme": "Prebaci na svijetli način",
|
||||
"Switch to Dark Theme": "Prebaci na tamni način",
|
||||
"Show Tags": "Pokaži oznake",
|
||||
"Hide Tags": "Sakrij oznake",
|
||||
Description: "Opis",
|
||||
"No monitors available.": "Nema dostupnih monitora.",
|
||||
"Add one": "Add one",
|
||||
"No Monitors": "Bez monitora",
|
||||
"Add one": "Stvori jednog",
|
||||
"Untitled Group": "Bezimena grupa",
|
||||
Services: "Usluge",
|
||||
Discard: "Odbaci",
|
||||
Cancel: "Otkaži",
|
||||
"Powered by": "Pokreće",
|
||||
Saved: "Spremljeno",
|
||||
};
|
@@ -2,26 +2,26 @@ export default {
|
||||
languageName: "Magyar",
|
||||
checkEverySecond: "Ellenőrzés {0} másodpercenként",
|
||||
retryCheckEverySecond: "Újrapróbál {0} másodpercenként.",
|
||||
retriesDescription: "Maximális próbálkozás mielőtt a szolgáltatás leállt jelőlést kap és értesítés kerül kiküldésre",
|
||||
ignoreTLSError: "TLS/SSL hibák figyelnen kívül hagyása HTTPS weboldalaknál",
|
||||
retriesDescription: "Maximális próbálkozás mielőtt a szolgáltatás 'Leállt' jelölést kap és értesítés kerül kiküldésre",
|
||||
ignoreTLSError: "TLS/SSL hibák figyelmen kívül hagyása HTTPS weboldalaknál",
|
||||
upsideDownModeDescription: "Az állapot megfordítása. Ha a szolgáltatás elérhető, akkor lesz leállt állapotú.",
|
||||
maxRedirectDescription: "Az átirányítások maximális száma. állítsa 0-ra az átirányítás tiltásához.",
|
||||
acceptedStatusCodesDescription: "Válassza ki az állapot kódokat amelyek sikeres válasznak fognak számítani.",
|
||||
passwordNotMatchMsg: "A megismételt jelszó nem egyezik.",
|
||||
notificationDescription: "Kérem, rendeljen egy értesítést a figyeléshez, hogy működjön.",
|
||||
keywordDescription: "Kulcsszó keresése a html-ben vagy a JSON válaszban. (kis-nagybetű érzékeny)",
|
||||
keywordDescription: "Kulcsszó keresése a HTML-ben vagy a JSON válaszban. (kis-nagybetű érzékeny)",
|
||||
pauseDashboardHome: "Szünetel",
|
||||
deleteMonitorMsg: "Biztos, hogy törölni akarja ezt a figyelőt?",
|
||||
deleteNotificationMsg: "Biztos, hogy törölni akarja ezt az értesítést az összes figyelőnél?",
|
||||
resoverserverDescription: "A Cloudflare az alapértelmezett szerver, bármikor meg tudja változtatni a resolver server-t.",
|
||||
rrtypeDescription: "Válassza ki az RR-Típust a figyelőhöz",
|
||||
rrtypeDescription: "Válassza ki az RR-típust a figyelőhöz",
|
||||
pauseMonitorMsg: "Biztos, hogy szüneteltetni akarja?",
|
||||
enableDefaultNotificationDescription: "Minden új figyelőhöz ez az értesítés engedélyezett lesz alapértelmezetten. Kikapcsolhatja az értesítést külön minden figyelőnél.",
|
||||
clearEventsMsg: "Biztos, hogy törölni akar miden eseményt ennél a figyelnél?",
|
||||
clearHeartbeatsMsg: "Biztos, hogy törölni akar minden heartbeat-et ennél a figyelőnél?",
|
||||
confirmClearStatisticsMsg: "Biztos, hogy törölni akat MINDEN statisztikát?",
|
||||
clearHeartbeatsMsg: "Biztos, hogy törölni akar minden életjelet ennél a figyelőnél?",
|
||||
confirmClearStatisticsMsg: "Biztos, hogy törölni akar MINDEN statisztikát?",
|
||||
importHandleDescription: "Válassza a 'Meglévő kihagyását', ha ki szeretné hagyni az azonos nevő figyelőket vagy értesítésket. A 'Felülírás' törölni fog minden meglévő figyelőt és értesítést.",
|
||||
confirmImportMsg: "Biztos, hogy importálja a mentést? Győzödjön meg róla, hogy jól választotta ki az importálás opciót.",
|
||||
confirmImportMsg: "Biztos, hogy importálja a mentést? Győződjön meg róla, hogy jól választotta ki az importálás opciót.",
|
||||
twoFAVerifyLabel: "Kérem, adja meg a token-t, hogy a 2FA működését ellenőrizzük",
|
||||
tokenValidSettingsMsg: "A token érvényes! El tudja menteni a 2FA beállításait.",
|
||||
confirmEnableTwoFAMsg: "Biztosan engedélyezi a 2FA-t?",
|
||||
@@ -54,23 +54,23 @@ export default {
|
||||
Delete: "Törlés",
|
||||
Current: "Aktuális",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Tanúsítvány lejár",
|
||||
days: "napok",
|
||||
"Cert Exp.": "SSL lejárat",
|
||||
days: "nap",
|
||||
day: "nap",
|
||||
"-day": "-nap",
|
||||
"-day": " nap",
|
||||
hour: "óra",
|
||||
"-hour": "-óra",
|
||||
"-hour": " óra",
|
||||
Response: "Válasz",
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "Figyelő típusa",
|
||||
Keyword: "Kulcsszó",
|
||||
"Friendly Name": "Rövid név",
|
||||
URL: "URL",
|
||||
Hostname: "Hostnév",
|
||||
Hostname: "Hosztnév",
|
||||
Port: "Port",
|
||||
"Heartbeat Interval": "Heartbeat időköz",
|
||||
"Heartbeat Interval": "Életjel időköz",
|
||||
Retries: "Újrapróbálkozás",
|
||||
"Heartbeat Retry Interval": "Heartbeat újrapróbálkozások időköze",
|
||||
"Heartbeat Retry Interval": "Életjel újrapróbálkozások időköze",
|
||||
Advanced: "Haladó",
|
||||
"Upside Down Mode": "Fordított mód",
|
||||
"Max. Redirects": "Max. átirányítás",
|
||||
@@ -82,8 +82,8 @@ export default {
|
||||
Light: "Világos",
|
||||
Dark: "Sötét",
|
||||
Auto: "Auto",
|
||||
"Theme - Heartbeat Bar": "Téma - Heartbeat Bar",
|
||||
Normal: "Normal",
|
||||
"Theme - Heartbeat Bar": "Téma - Életjel sáv",
|
||||
Normal: "Normál",
|
||||
Bottom: "Nyomógomb",
|
||||
None: "Nincs",
|
||||
Timezone: "Időzóna",
|
||||
@@ -97,9 +97,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",
|
||||
Logout: "Kijelenetkezés",
|
||||
Logout: "Kijelentkezés",
|
||||
Leave: "Elhagy",
|
||||
"I understand, please disable": "Megértettem, kérem tilsa le",
|
||||
"I understand, please disable": "Megértettem, kérem tiltsa le",
|
||||
Confirm: "Megerősítés",
|
||||
Yes: "Igen",
|
||||
No: "Nem",
|
||||
@@ -113,7 +113,7 @@ export default {
|
||||
Email: "Email",
|
||||
Test: "Teszt",
|
||||
"Certificate Info": "Tanúsítvány információk",
|
||||
"Resolver Server": "Resolver szerver",
|
||||
"Resolver Server": "DNS szerver",
|
||||
"Resource Record Type": "Resource Record típusa",
|
||||
"Last Result": "Utolsó eredmény",
|
||||
"Create your admin account": "Hozza létre az adminisztrátor felhasználót",
|
||||
@@ -129,11 +129,11 @@ export default {
|
||||
Create: "Létrehozás",
|
||||
"Clear Data": "Adatok törlése",
|
||||
Events: "Események",
|
||||
Heartbeats: "Heartbeats",
|
||||
"Auto Get": "Auto Get",
|
||||
backupDescription: "Ki tudja menteni az összes figyelőt és értesítést egy JSON fájlba.",
|
||||
backupDescription2: "Ui.: Történeti és esemény adatokat nem tartalmaz.",
|
||||
backupDescription3: "Érzékeny adatok, pl. szolgáltatás kulcsok is vannak az export fájlban. Figyelmesen őrizze!",
|
||||
Heartbeats: "Életjelek",
|
||||
"Auto Get": "Auto lekérd.",
|
||||
backupDescription: "Mentheti az összes figyelőt és értesítést egy JSON fájlba.",
|
||||
backupDescription2: "Megj: Történeti és esemény adatokat nem tartalmaz.",
|
||||
backupDescription3: "Érzékeny adatok, pl. szolgáltatás kulcsok is vannak az export fájlban. Figyeljen erre!",
|
||||
alertNoFile: "Válaszzon ki egy fájlt az importáláshoz.",
|
||||
alertWrongFileType: "Válasszon egy JSON fájlt.",
|
||||
"Clear all statistics": "Összes statisztika törlése",
|
||||
@@ -144,17 +144,17 @@ export default {
|
||||
"Verify Token": "Token ellenőrzése",
|
||||
"Setup 2FA": "2FA beállítása",
|
||||
"Enable 2FA": "2FA engedélyezése",
|
||||
"Disable 2FA": "2FA toltása",
|
||||
"Disable 2FA": "2FA tiltása",
|
||||
"2FA Settings": "2FA beállítások",
|
||||
"Two Factor Authentication": "Two Factor Authentication",
|
||||
"Two Factor Authentication": "Kétfaktoros hitelesítés",
|
||||
Active: "Aktív",
|
||||
Inactive: "Inaktív",
|
||||
Token: "Token",
|
||||
"Show URI": "URI megmutatása",
|
||||
Tags: "Cimkék",
|
||||
Tags: "Címkék",
|
||||
"Add New below or Select...": "Adjon hozzá lentre vagy válasszon...",
|
||||
"Tag with this name already exist.": "Ilyen nevű cimke már létezik.",
|
||||
"Tag with this value already exist.": "Ilyen értékű cimke már létezik.",
|
||||
"Tag with this name already exist.": "Ilyen nevű címke már létezik.",
|
||||
"Tag with this value already exist.": "Ilyen értékű címke már létezik.",
|
||||
color: "szín",
|
||||
"value (optional)": "érték (opcionális)",
|
||||
Gray: "Szürke",
|
||||
@@ -169,15 +169,15 @@ export default {
|
||||
"Avg. Ping": "Átl. ping",
|
||||
"Avg. Response": "Átl. válasz",
|
||||
"Entry Page": "Nyitólap",
|
||||
statusPageNothing: "Semmi nincs itt, kérem, adjon hozzá egy figyelőt.",
|
||||
statusPageNothing: "Semmi nincs itt. Adjon hozzá egy vagy több figyelőt.",
|
||||
"No Services": "Nincs szolgáltatás",
|
||||
"All Systems Operational": "Minden rendszer működik",
|
||||
"Partially Degraded Service": "Részlegesen leállt szolgáltatás",
|
||||
"Degraded Service": "Leállt szolgáltatás",
|
||||
"Add Group": "Csoport hozzáadása",
|
||||
"Add a monitor": "Figyelő hozzáadása",
|
||||
"Edit Status Page": "Sátusz oldal szerkesztése",
|
||||
"Go to Dashboard": "Menj az irányítópulthoz",
|
||||
"Edit Status Page": "Státusz oldal szerkesztése",
|
||||
"Go to Dashboard": "Irányítópulthoz",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
smtp: "Email (SMTP)",
|
||||
@@ -192,9 +192,162 @@ export default {
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Support 50+ Notification services)",
|
||||
apprise: "Apprise (50+ értesítési szolgáltatás)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Status Page": "Status Page",
|
||||
"Status Page": "Státusz oldal",
|
||||
"Primary Base URL": "Elsődleges URL",
|
||||
"Push URL": "Meghívandó URL",
|
||||
needPushEvery: "Ezt az URL-t kell meghívni minden {0} másodpercben.",
|
||||
pushOptionalParams: "Opcionális paraméterek: {0}",
|
||||
defaultNotificationName: "{notification} értesítésem ({number})",
|
||||
here: "itt",
|
||||
Required: "Kötelező",
|
||||
"Bot Token": "BOT token",
|
||||
wayToGetTelegramToken: "Innen kaphat token-t: {0}.",
|
||||
"Chat ID": "Csevegés ID",
|
||||
supportTelegramChatID: "Támogatja a közvetlen csevegést, csoportnak küldést és csatona ID-t is",
|
||||
wayToGetTelegramChatID: "A csevegés ID-t kinyerheti azzal, hogy küld egy üzenetet a bot-nak és erre az URL-re ellátogat, ahol láthatja a chat_id:-t",
|
||||
"YOUR BOT TOKEN HERE": "AZ ÖN BOT TOKENJE ITT",
|
||||
chatIDNotFound: "Csevegés ID nem található, küldjön egy első üzenetet a bot-nak",
|
||||
"Post URL": "Cél URL (Post)",
|
||||
"Content Type": "Tartalom típus (Content Type)",
|
||||
webhookJsonDesc: "{0} ideális a moderh HTTP szerverekhez, mint az Express.js",
|
||||
webhookFormDataDesc: "{multipart} ideális a PHP-hez. A JSON értelmezhető ezzel: {decodeFunction}",
|
||||
secureOptionNone: "Nincs / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "TLS hiba figyelmen kívül hagyása",
|
||||
"From Email": "Feladó email",
|
||||
emailCustomSubject: "Egyedi tárgy",
|
||||
"To Email": "Cél email",
|
||||
smtpCC: "Másolat",
|
||||
smtpBCC: "Titkos másolat",
|
||||
"Discord Webhook URL": "Discord cím (webhook URL)",
|
||||
wayToGetDiscordURL: "Kaphat egy ilyet, ha ellátogat a Server Settings -> Integrations -> Create Webhook oldalra",
|
||||
"Bot Display Name": "Bot megjelenő neve",
|
||||
"Prefix Custom Message": "Egyedi előtét üzenet",
|
||||
"Hello @everyone is...": "Hello {'@'}mindenki...",
|
||||
"Webhook URL": "Cím (webhook URL)",
|
||||
wayToGetTeamsURL: "Itt megnézheti, hogy kell ilyen URL-t készíteni: {0}.",
|
||||
Number: "Szám",
|
||||
Recipients: "Címzettek",
|
||||
needSignalAPI: "Egy Signal kliensre van szüksége, amihez REST API tartozik.",
|
||||
wayToCheckSignalURL: "Itt megnézheti, hogy hozhat létre egyet:",
|
||||
signalImportant: "FONTOS! Nem keverheti a csoportokat és számokat a címzetteknél.",
|
||||
"Application Token": "Alkalmazás token",
|
||||
"Server URL": "Szerver URL",
|
||||
Priority: "Prioritás",
|
||||
"Icon Emoji": "Emoji ikonok",
|
||||
"Channel Name": "Csatorna neve",
|
||||
"Uptime Kuma URL": "Uptime Kuma cím",
|
||||
aboutWebhooks: "Webhook-okról több info: {0}",
|
||||
aboutChannelName: "Adja meg a {0} csatorna nevét ha szeretné elkerülni a webhook-ot. Pl: #masik-csatorna",
|
||||
aboutKumaURL: "Ha üresen hagyja a Uptime Kuma cím mezőt, akkor a projekt GitHub oldala lesz az alapértelmezett.",
|
||||
emojiCheatSheet: "Emoji csalás: {0}",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
"User Key": "Felhasználói kulcs",
|
||||
Device: "Eszköz",
|
||||
"Message Title": "Üzenet címe",
|
||||
"Notification Sound": "Értesítési hang",
|
||||
"More info on:": "További információ: {0}",
|
||||
pushoverDesc1: "A vészhelyzeti prioritásnak (2) 30 másodperc az újrapróbálkozási alapértéke és egy óra után lejár.",
|
||||
pushoverDesc2: "Ha különböző eszközökre szeretne értesítést küldeni, töltse ki az Eszköz mezőt.",
|
||||
"SMS Type": "SMS típusa",
|
||||
octopushTypePremium: "Premium (Fast - recommended for alerting)",
|
||||
octopushTypeLowCost: "Low Cost (Slow - sometimes blocked by operator)",
|
||||
checkPrice: "Nézze meg az {0} féle árat:",
|
||||
apiCredentials: "API kulcsok",
|
||||
octopushLegacyHint: "Az Octopush régi (2011-2020) verzióját használja vagy az újat?",
|
||||
"Check octopush prices": "Nézze meg az Octopush {0} féle árát.",
|
||||
octopushPhoneNumber: "Telefonszám (nemz. formátum, pl : +36705554433) ",
|
||||
octopushSMSSender: "SMS küldő neve : 3-11 betű/szám (a-zA-Z0-9) vagy szóköz",
|
||||
"LunaSea Device ID": "LunaSea eszköz ID",
|
||||
"Apprise URL": "Apprise cím (URL)",
|
||||
"Example:": "Például: {0}",
|
||||
"Read more:": "Itt olvashat róla: {0}",
|
||||
"Status:": "Állapot: {0}",
|
||||
"Read more": "Tovább olvasom",
|
||||
appriseInstalled: "Apprise telepítve.",
|
||||
appriseNotInstalled: "Apprise nincs telepítve. {0}",
|
||||
"Access Token": "Elérési token",
|
||||
"Channel access token": "Csatorna elérési token",
|
||||
"Line Developers Console": "Line Developers konzol",
|
||||
lineDevConsoleTo: "Line Developers konzol - {0}",
|
||||
"Basic Settings": "Alap beállítások",
|
||||
"User ID": "Felhasználó ID",
|
||||
"Messaging API": "Üzenet API",
|
||||
wayToGetLineChannelToken: "{0} első eléréséhez készítsen egy Provider-t és csatornát (Messaging API), utána kaphatja meg a csatorna elérési token-t és felhasználó ID-t az alábbi menüpontban.",
|
||||
"Icon URL": "Ikon cím (URL)",
|
||||
aboutIconURL: "Megadhat egy webcímet az Ikon cím mezőben, ezzel felülírva az alapértelmezet képet. Nem kerül felhasználásra, ha az Emoji-k be vannak állítva.",
|
||||
aboutMattermostChannelName: "Felülírhatja az alapértelmezett csatornát, ahova a webhook az adatokat küldi. Ehhez töltse ki a \"Csatorna neve\" mezőt (pl: #egyeb-csatorna). A Mattermost webhook beállításaiban további engedélyek szükségesek",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - olcsó, de lassú, gyakran túlterhelt. Csak lengyel címzettekhez.",
|
||||
promosmsTypeFlash: "SMS FLASH - Az üzenet automatikusan megjelenik a fogadó eszközön. Csak lengyel címzettekhez.",
|
||||
promosmsTypeFull: "SMS FULL - Prémium szintje az SMS-nek. Megadható a feladó neve, de előtte jóváhagyás szükséges. Ideális értesítésekhez.",
|
||||
promosmsTypeSpeed: "SMS SPEED - A legmagasabb prioritás a rendszerben. Nagyon gyors és pontos, de költséges (kb. duplája a hagyományos SMS-nek).",
|
||||
promosmsPhoneNumber: "Telefonszám (lengyel címzett esetén az országkód elhagyható)",
|
||||
promosmsSMSSender: "SMS feladónév: Előre beállított név vagy az alábbiak egyike: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu webhook cím (URL)",
|
||||
matrixHomeserverURL: "Homeserver cím (URL http(s):// előtaggal és opcionálisan port-tal)",
|
||||
"Internal Room Id": "Belső Szoba ID",
|
||||
matrixDesc1: "A belső szoba ID-t a szpbák speciális beállítások között találja meg a Matrix kliens programban. Így kell kinéznie: !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Erősen ajánlott készíteni egy új felhasználót és nem a teljes joggal rendelkező felhasználót használni. Az új felhasználó létrehozása után csak azokba a szobákba kell megjhívni a felhasználót, ahol értesítéseket szeretne kapni. Ezzel a művelettel lehet elérési token-t kérni: {0}",
|
||||
Method: "Metódus",
|
||||
Body: "Törzs",
|
||||
Headers: "Fejlécek",
|
||||
PushUrl: "Push cím (URL)",
|
||||
HeadersInvalidFormat: "A kérés fejléc nem egy valós JSON: ",
|
||||
BodyInvalidFormat: "A kérés törzse nem egy valós JSON: ",
|
||||
"Monitor History": "Vizsgálatok előzményei",
|
||||
clearDataOlderThan: "Előzmények megtartása {0} napig.",
|
||||
PasswordsDoNotMatch: "Jelszó nem egyezik.",
|
||||
records: "sorok",
|
||||
"One record": "Egy sor",
|
||||
steamApiKeyDescription: "Steam Game Server ellenőrzéséhez szükséges egy Steam Web-API kulcs. Itt létrehozhat egy API kulcsot: ",
|
||||
"Current User": "Felhasználó",
|
||||
recent: "Legújabb",
|
||||
Done: "Kész",
|
||||
Info: "Infó",
|
||||
Security: "Biztonság",
|
||||
"Steam API Key": "Steam API kulcs",
|
||||
"Shrink Database": "Adatbázis tömörítése",
|
||||
"Pick a RR-Type...": "Válasszon egy RR-típust...",
|
||||
"Pick Accepted Status Codes...": "Válasszon olyan kódot, ami elfogadottnak számít...",
|
||||
Default: "Alapért.",
|
||||
"HTTP Options": "HTTP beállítások",
|
||||
"Create Incident": "Incidens létrehozása",
|
||||
Title: "Cím",
|
||||
Content: "Tartalom",
|
||||
Style: "Stílus",
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
danger: "danger",
|
||||
primary: "primary",
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
Post: "Bejegyzés",
|
||||
"Please input title and content": "Adjon meg címet és tartalmat",
|
||||
Created: "Létrehozva",
|
||||
"Last Updated": "Utolsó mód.",
|
||||
Unpin: "Leválaszt",
|
||||
"Switch to Light Theme": "Világos témára váltás",
|
||||
"Switch to Dark Theme": "Sötét témára váltás",
|
||||
"Show Tags": "Címkék mutatása",
|
||||
"Hide Tags": "Címkék elrejtése",
|
||||
Description: "Leírás",
|
||||
"No monitors available.": "Nincs még figyelő beállítva.",
|
||||
"Add one": "Adjon hozzá egyet",
|
||||
"No Monitors": "Nincs figyelő",
|
||||
"Untitled Group": "Névtelen csoport",
|
||||
Services: "Szolgáltatások",
|
||||
Discard: "Elvet",
|
||||
Cancel: "Mégsem",
|
||||
"Powered by": "A megoldást szállítja az",
|
||||
shrinkDatabaseDescription: "VACUUM futtatása az SQLite-on. Ha az adatbázisod 1.10.0-nál újabb, akkor az AUTO_VACUUM engedélyezve van, nincs szükség a műveletre.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API felhasználónév (webapi_ előtaggal együtt)",
|
||||
serwersmsAPIPassword: "API jelszó",
|
||||
serwersmsPhoneNumber: "Telefonszám",
|
||||
serwersmsSenderName: "SMS feladó neve (regisztrált név az oldalon)",
|
||||
};
|
||||
|
@@ -179,11 +179,10 @@ export default {
|
||||
"Edit Status Page": "Edit Halaman Status",
|
||||
"Go to Dashboard": "Pergi ke Dasbor",
|
||||
"Status Page": "Halaman Status",
|
||||
// Start notification form
|
||||
defaultNotificationName: "{notification} saya Peringatan ({number})",
|
||||
here: "di sini",
|
||||
"Required": "Dibutuhkan",
|
||||
"telegram": "Telegram",
|
||||
Required: "Dibutuhkan",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
"You can get a token from": "Anda bisa mendapatkan token dari",
|
||||
"Chat ID": "Chat ID",
|
||||
@@ -191,12 +190,12 @@ export default {
|
||||
wayToGetTelegramChatID: "Anda bisa mendapatkan chat id Anda dengan mengirim pesan ke bot dan pergi ke url ini untuk melihat chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "BOT TOKEN ANDA DI SINI",
|
||||
chatIDNotFound: "Chat ID tidak ditemukan, tolong kirim pesan ke bot ini dulu",
|
||||
"webhook": "Webhook",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Tipe konten",
|
||||
webhookJsonDesc: "{0} bagus untuk peladen http modern seperti express.js",
|
||||
webhookFormDataDesc: "{multipart} bagus untuk PHP, Anda hanya perlu mengurai json dengan {decodeFunction}",
|
||||
"smtp": "Surel (SMTP)",
|
||||
smtp: "Surel (SMTP)",
|
||||
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Abaikan Kesalahan TLS",
|
||||
@@ -204,26 +203,26 @@ export default {
|
||||
"To Email": "Ke Surel",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"discord": "Discord",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Anda bisa mendapatkan ini dengan pergi ke Server Settings -> Integrations -> Create Webhook",
|
||||
"Bot Display Name": "Nama Bot",
|
||||
"Prefix Custom Message": "Awalan Pesan",
|
||||
"Hello @everyone is...": "Halo {'@'}everyone is...",
|
||||
"teams": "Microsoft Teams",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Anda dapat mempelajari cara membuat url webhook {0}.",
|
||||
"signal": "Sinyal",
|
||||
"Number": "Nomer",
|
||||
"Recipients": "Penerima",
|
||||
signal: "Sinyal",
|
||||
Number: "Nomer",
|
||||
Recipients: "Penerima",
|
||||
needSignalAPI: "Anda harus memiliki klien sinyal dengan REST API.",
|
||||
wayToCheckSignalURL: "Anda dapat memeriksa url ini untuk melihat cara menyiapkannya:",
|
||||
signalImportant: "PENTING: Anda tidak dapat mencampur grup dan nomor di penerima!",
|
||||
"gotify": "Gotify",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Token Aplikasi",
|
||||
"Server URL": "URL Peladen",
|
||||
"Priority": "Prioritas",
|
||||
"slack": "Slack",
|
||||
Priority: "Prioritas",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Ikon Emoji",
|
||||
"Channel Name": "Nama Saluran",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
@@ -242,7 +241,7 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Kunci pengguna",
|
||||
"Device": "Perangkat",
|
||||
Device: "Perangkat",
|
||||
"Message Title": "Judul Pesan",
|
||||
"Notification Sound": "Suara Nofifikasi",
|
||||
"More info on:": "Info lebih lanjut tentang: {0}",
|
||||
@@ -273,7 +272,7 @@ export default {
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "Anda dapat memberikan tautan ke gambar di \"Icon URL\" untuk mengganti gambar profil bawaan. Tidak akan digunakan jika Ikon Emoji diset.",
|
||||
aboutMattermostChannelName: "Anda dapat mengganti saluran bawaan tujuan posting webhook dengan memasukkan nama saluran ke dalam Kolom \"Channel Name\". Ini perlu diaktifkan di pengaturan webhook Mattermost. contoh: #other-channel",
|
||||
"matrix": "Matrix",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - murah tapi lambat dan sering kelebihan beban. Terbatas hanya untuk penerima Polandia.",
|
||||
promosmsTypeFlash: "SMS FLASH - Pesan akan otomatis muncul di perangkat penerima. Terbatas hanya untuk penerima Polandia.",
|
||||
promosmsTypeFull: "SMS FULL - SMS tingkat premium, Anda dapat menggunakan Nama Pengirim Anda (Anda harus mendaftarkan nama terlebih dahulu). Dapat diAndalkan untuk peringatan.",
|
||||
@@ -281,5 +280,4 @@ export default {
|
||||
promosmsPhoneNumber: "Nomor telepon (untuk penerima Polandia Anda dapat melewati kode area)",
|
||||
promosmsSMSSender: "Nama Pengirim SMS : Nama pra-registrasi atau salah satu bawaan: InfoSMS, Info SMS, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookUrl",
|
||||
// End notification form
|
||||
};
|
||||
|
@@ -33,6 +33,7 @@ export default {
|
||||
Appearance: "Aspetto",
|
||||
Theme: "Tema",
|
||||
General: "Generali",
|
||||
"Primary Base URL": "URL base primario",
|
||||
Version: "Versione",
|
||||
"Check Update On GitHub": "Controlla aggiornamenti su GitHub",
|
||||
List: "Lista",
|
||||
@@ -64,7 +65,7 @@ export default {
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "Tipo di Monitoraggio",
|
||||
Keyword: "Parola chiave",
|
||||
"Friendly Name": "Nome Amichevole",
|
||||
"Friendly Name": "Nomignolo",
|
||||
URL: "URL",
|
||||
Hostname: "Nome Host",
|
||||
Port: "Porta",
|
||||
@@ -75,6 +76,9 @@ export default {
|
||||
"Upside Down Mode": "Modalità capovolta",
|
||||
"Max. Redirects": "Reindirizzamenti massimi",
|
||||
"Accepted Status Codes": "Codici di stato accettati",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Notificare questo URL ogni {0} secondi.",
|
||||
pushOptionalParams: "Parametri aggiuntivi: {0}",
|
||||
Save: "Salva",
|
||||
Notifications: "Notifiche",
|
||||
"Not available, please setup.": "Non disponibili, da impostare.",
|
||||
@@ -130,9 +134,9 @@ export default {
|
||||
"Clear Data": "Cancella dati",
|
||||
Events: "Eventi",
|
||||
Heartbeats: "Controlli",
|
||||
"Auto Get": "Auto Get",
|
||||
"Auto Get": "Rileva",
|
||||
backupDescription: "È possibile fare il backup di tutti i monitoraggi e di tutte le notifiche in un file JSON.",
|
||||
backupDescription2: "P.S.: lo storico e i dati relativi agli eventi non saranno inclusi.",
|
||||
backupDescription2: "P.S.: lo storico e i dati relativi agli eventi non saranno inclusi",
|
||||
backupDescription3: "Dati sensibili come i token di autenticazione saranno inclusi nel backup, tenere quindi in un luogo sicuro.",
|
||||
alertNoFile: "Selezionare il file da importare.",
|
||||
alertWrongFileType: "Selezionare un file JSON.",
|
||||
@@ -152,49 +156,200 @@ export default {
|
||||
Token: "Token",
|
||||
"Show URI": "Mostra URI",
|
||||
Tags: "Etichette",
|
||||
"Add New below or Select...": "Aggiungine una oppure scegli...",
|
||||
"Add New below or Select...": "Aggiungi oppure scegli...",
|
||||
"Tag with this name already exist.": "Un'etichetta con questo nome già esiste.",
|
||||
"Tag with this value already exist.": "Un'etichetta con questo valore già esiste.",
|
||||
color: "colori",
|
||||
"value (optional)": "valore (opzionale)",
|
||||
color: "colore",
|
||||
"value (optional)": "descrizione (opzionale)",
|
||||
Gray: "Grigio",
|
||||
Red: "Rosso",
|
||||
Orange: "Arancione",
|
||||
Green: "Verde",
|
||||
Blue: "Blu",
|
||||
Indigo: "Indigo",
|
||||
Indigo: "Indaco",
|
||||
Purple: "Viola",
|
||||
Pink: "Rosa",
|
||||
"Search...": "Cerca...",
|
||||
"Avg. Ping": "Ping medio",
|
||||
"Avg. Response": "Risposta media",
|
||||
"Entry Page": "Entry Page",
|
||||
"Avg. Ping": "Tempo medio di risposta al ping",
|
||||
"Avg. Response": "Tempo medio di risposta",
|
||||
"Entry Page": "Pagina Principale",
|
||||
statusPageNothing: "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.",
|
||||
"No Services": "Nessun Servizio",
|
||||
"All Systems Operational": "Tutti i sistemi sono operativi",
|
||||
"All Systems Operational": "Tutti i sistemi sono funzionali",
|
||||
"Partially Degraded Service": "Servizio parzialmente degradato",
|
||||
"Degraded Service": "Servizio degradato",
|
||||
"Add Group": "Aggiungi Gruppo",
|
||||
"Add a monitor": "Aggiungi un monitoraggio",
|
||||
"Edit Status Page": "Modifica pagina di stato",
|
||||
"Go to Dashboard": "Vai al Cruscotto",
|
||||
"Status Page": "Status Page",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
smtp: "Email (SMTP)",
|
||||
discord: "Discord",
|
||||
teams: "Microsoft Teams",
|
||||
signal: "Signal",
|
||||
gotify: "Gotify",
|
||||
slack: "Slack",
|
||||
"Status Page": "Pagina di stato",
|
||||
defaultNotificationName: "Allarme {notification} ({number})",
|
||||
here: "qui",
|
||||
"Required": "Richiesto",
|
||||
"telegram": "Telegram",
|
||||
"Bot Token": "Token del Bot",
|
||||
"You can get a token from": "Puoi ricevere un token da",
|
||||
"Chat ID": "ID Chat",
|
||||
supportTelegramChatID: "Supporta Chat dirette / di Gruppo / ID Canale",
|
||||
wayToGetTelegramChatID: "Puoi ricereve l'ID chat mandando un messaggio al bot andando in questo url per visualizzare il chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "QUI IL TOKEN DEL BOT",
|
||||
chatIDNotFound: "Non trovo l'ID chat. Prima bisogna mandare un messaggio al bot",
|
||||
"webhook": "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} va bene per qualsiasi server http moderno ad esempio express.js",
|
||||
webhookFormDataDesc: "{multipart} va bene per PHP, c'è solo bisogno di analizzare il json con {decodeFunction}",
|
||||
"smtp": "E-mail (SMTP)",
|
||||
secureOptionNone: "Nessuno / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignora gli errori TLS",
|
||||
"From Email": "Mittente",
|
||||
emailCustomSubject: "Oggetto personalizzato",
|
||||
"To Email": "Destinatario",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "CCn",
|
||||
"discord": "Discord",
|
||||
"Discord Webhook URL": "URL Webhook di Discord",
|
||||
wayToGetDiscordURL: "È possibile recuperarlo da Impostazioni server -> Integrazioni -> Creare Webhook",
|
||||
"Bot Display Name": "Nome del Bot",
|
||||
"Prefix Custom Message": "Prefisso per il messaggio personalizzato",
|
||||
"Hello @everyone is...": "Ciao a {'@'}everyone ...",
|
||||
"teams": "Microsoft Teams",
|
||||
"Webhook URL": "URL Webhook",
|
||||
wayToGetTeamsURL: "È possibile imparare a creare un URL Webhook {0}.",
|
||||
"signal": "Signal",
|
||||
"Number": "Numero",
|
||||
"Recipients": "Destinatari",
|
||||
needSignalAPI: "È necessario avere un client Signal con le API REST.",
|
||||
wayToCheckSignalURL: "Controllare questo url per capire come impostarne uno:",
|
||||
signalImportant: "IMPORTANTE: Non è possibile mischiare gruppi e numeri all'interno dei destinatari!",
|
||||
"gotify": "Gotify",
|
||||
"Application Token": "Token Applicazione",
|
||||
"Server URL": "URL Server",
|
||||
"Priority": "Priorità",
|
||||
"slack": "Slack",
|
||||
"Icon Emoji": "Icona Emoji",
|
||||
"Channel Name": "Nome Canale",
|
||||
"Uptime Kuma URL": "Indirizzo Uptime Kuma",
|
||||
aboutWebhooks: "Maggiori informazioni riguardo ai webhooks su: {0}",
|
||||
aboutChannelName: "Inserire il nome del canale nel campo \"Nome Canale\" {0} se si vuole bypassare il canale webhook. Ad esempio: #altro-canale",
|
||||
aboutKumaURL: "Se si lascia bianco il campo Indirizzo Uptime Kuma, la pagina GitHub sarà il valore predefinito.",
|
||||
emojiCheatSheet: "Lista Emoji: {0}",
|
||||
"rocket.chat": "Rocket.chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Support 50+ Notification services)",
|
||||
apprise: "Apprise (Supporta più di 50 servizi di notifica)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Chiave Utente",
|
||||
"Device": "Dispositivo",
|
||||
"Message Title": "Titolo Messaggio",
|
||||
"Notification Sound": "Suono di Notifica",
|
||||
"More info on:": "Maggiori informazioni su: {0}",
|
||||
pushoverDesc1: "Priorità di Emergenza (2) ha 30 secondi di timeout tra un tentativo e l'altro e scadrà dopo un'ora.",
|
||||
pushoverDesc2: "Se si vuole inviare la notifica a dispositivi differenti, riempire il campo Dispositivi.",
|
||||
"SMS Type": "Tipo di SMS",
|
||||
octopushTypePremium: "Premium (Veloce - raccomandato per allertare)",
|
||||
octopushTypeLowCost: "A Basso Costo (Lento - talvolta bloccato dall'operatore)",
|
||||
checkPrice: "Controlla {0} prezzi:",
|
||||
apiCredentials: "Credenziali API",
|
||||
octopushLegacyHint: "Si vuole utilizzare la vecchia versione (2011-2020) oppure la nuova versione di Octopush?",
|
||||
"Check octopush prices": "Controlla i prezzi di Octopush {0}.",
|
||||
octopushPhoneNumber: "Numero di telefono (formato internazionale (p.e.): +33612345678) ",
|
||||
octopushSMSSender: "Nome del mittente: 3-11 caratteri alfanumerici e spazi (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "ID dispositivo LunaSea",
|
||||
"Apprise URL": "URL Apprise",
|
||||
"Example:": "Esempio: {0}",
|
||||
"Read more:": "Maggiori informazioni: {0}",
|
||||
"Status:": "Stato: {0}",
|
||||
"Read more": "Maggiori informazioni",
|
||||
appriseInstalled: "Apprise è installato.",
|
||||
appriseNotInstalled: "Apprise non è installato. {0}",
|
||||
"Access Token": "Token di accesso",
|
||||
"Channel access token": "Token di accesso al canale",
|
||||
"Line Developers Console": "Console sviluppatori Line",
|
||||
lineDevConsoleTo: "Console sviluppatori Line - {0}",
|
||||
"Basic Settings": "Impostazioni Base",
|
||||
"User ID": "ID Utente",
|
||||
"Messaging API": "API di Messaggistica",
|
||||
wayToGetLineChannelToken: "Prima accedi a {0}, crea un provider e un canale (API di Messaggistica), dopodiché puoi avere il token di accesso e l'id utente dal menù sopra.",
|
||||
"Icon URL": "URL Icona",
|
||||
aboutIconURL: "È possibile impostare un collegameno a una immagine in \"URL Icona\" per modificare l'immagine di profilo. Non verrà utilizzata se è impostata l'Icona Emoji.",
|
||||
aboutMattermostChannelName: "È possibile modificare il canale predefinito che dove il webhook manda messaggi immettendo il nome del canale nel campo \"Nome Canale\". Questo va abilitato nelle impostazioni webhook di Mattermost webhook. P.E.: #altro-canale",
|
||||
"matrix": "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - economico, ma lento e spesso sovraccarico. Limitato solamente a destinatari Polacchi.",
|
||||
promosmsTypeFlash: "SMS FLASH - Il messaggio sarà automaticamente mostrato sul dispositivo dei destinatari. Limitato solo a destinatari Polacchi.",
|
||||
promosmsTypeFull: "SMS FULL - Premium, È possibile utilizzare il proprio come come mittente (è necessario prima registrare il nome). Affidabile per gli allarmi.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Maggior priorità. Rapido, affidabile, ma costoso (costa il doppio di SMS FULL).",
|
||||
promosmsPhoneNumber: "Numero di Telefono (per destinatari Polacchi si può omettere il codice area)",
|
||||
promosmsSMSSender: "Mittente SMS : Nome preregistrato oppure uno dei seguenti: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "URL WebHook di Feishu",
|
||||
matrixHomeserverURL: "URL Server (con http(s):// e opzionalmente la porta)",
|
||||
"Internal Room Id": "ID Stanza Interna",
|
||||
matrixDesc1: "È possibile recuperare l'ID della stanza all'interno delle impostazioni avanzate della stanza nel client Matrix. Dovrebbe essere simile a !QMdRCpUIfLwsfjxye6:server.di.casa.",
|
||||
matrixDesc2: "È altamente raccomandata la creazione di un nuovo utente e di non utilizare il proprio token di accesso Matrix poiché darà pieno controllo al proprio account e a tutte le stanze in cui si ha accesso. Piuttosto, si crei un nuovo utente per invitarlo nella stanza dove si vuole ricevere le notifiche. Si può accedere al token eseguendo {0}",
|
||||
Method: "Metodo",
|
||||
Body: "Corpo",
|
||||
Headers: "Intestazioni",
|
||||
PushUrl: "URL di Push",
|
||||
HeadersInvalidFormat: "L'intestazione di richiesta non è un JSON valido: ",
|
||||
BodyInvalidFormat: "Il corpo di richiesta non è un JSON valido: ",
|
||||
"Monitor History": "Storico monitoraggio",
|
||||
clearDataOlderThan: "Mantieni lo storico per {0} giorni.",
|
||||
PasswordsDoNotMatch: "Le password non corrispondono.",
|
||||
records: "records",
|
||||
"One record": "One record",
|
||||
steamApiKeyDescription: "Per monitorare un server di gioco Steam si necessita della chiave Web-API di Steam. È possibile registrare la propria chiave API qui: ",
|
||||
"Current User": "Utente corrente",
|
||||
recent: "Recenti",
|
||||
Done: "Fatto",
|
||||
Info: "Info",
|
||||
Security: "Sicurezza",
|
||||
"Steam API Key": "Chiave API di Steam",
|
||||
"Shrink Database": "Comprimi Database",
|
||||
"Pick a RR-Type...": "Scegli un tipo di RR...",
|
||||
"Pick Accepted Status Codes...": "Scegli i codici di Stato Accettati...",
|
||||
Default: "Predefinito",
|
||||
"HTTP Options": "Opzioni HTTP",
|
||||
"Create Incident": "Crea Incident",
|
||||
Title: "Titolo",
|
||||
Content: "Contenuto",
|
||||
Style: "Stile",
|
||||
info: "informativo",
|
||||
warning: "attenzione",
|
||||
danger: "critico",
|
||||
primary: "primario",
|
||||
light: "chiaro",
|
||||
dark: "scuro",
|
||||
Post: "Posta",
|
||||
"Please input title and content": "Inserire il titolo e il contenuto",
|
||||
Created: "Creato",
|
||||
"Last Updated": "Ultima modifica",
|
||||
Unpin: "Stacca",
|
||||
"Switch to Light Theme": "Utilizza tema chiaro",
|
||||
"Switch to Dark Theme": "Utilizza tema scuro",
|
||||
"Show Tags": "Mostra etichette",
|
||||
"Hide Tags": "Nascondi etichette",
|
||||
Description: "Descrizione",
|
||||
"No monitors available.": "Nessun monitoraggio disponibile.",
|
||||
"Add one": "Aggiungi",
|
||||
"No Monitors": "Nessun monitoraggio",
|
||||
"Add one": "Aggiungi",
|
||||
"Untitled Group": "Gruppo senza titolo",
|
||||
Services: "Servizi",
|
||||
Discard: "Scarta",
|
||||
Cancel: "Annulla",
|
||||
"Powered by": "Servito da",
|
||||
shrinkDatabaseDescription: "Lancia il comando VACUUM sul database SQLite. Se il database è stato creato dopo la versione 1.10.0, AUTO_VACUUM è già abilitato e questa azione non è necessaria.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "Nome utente API (incl. prefisso webapi_)",
|
||||
serwersmsAPIPassword: "Password API",
|
||||
serwersmsPhoneNumber: "Numero di Telefono",
|
||||
serwersmsSenderName: "Nome del mittente SMS (registrato via portale cliente)",
|
||||
"stackfield": "Stackfield",
|
||||
};
|
||||
|
@@ -17,7 +17,7 @@ export default {
|
||||
pauseMonitorMsg: "一時停止しますか?",
|
||||
Settings: "設定",
|
||||
Dashboard: "ダッシュボード",
|
||||
"New Update": "New Update",
|
||||
"New Update": "新しいアップデート",
|
||||
Language: "言語",
|
||||
Appearance: "外観",
|
||||
Theme: "テーマ",
|
||||
@@ -53,7 +53,7 @@ export default {
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "監視タイプ",
|
||||
Keyword: "キーワード",
|
||||
"Friendly Name": "Friendly Name",
|
||||
"Friendly Name": "分かりやすい名前",
|
||||
URL: "URL",
|
||||
Hostname: "ホスト名",
|
||||
Port: "ポート",
|
||||
@@ -104,60 +104,60 @@ export default {
|
||||
"Resolver Server": "問い合わせ先DNSサーバ",
|
||||
"Resource Record Type": "DNSレコード設定",
|
||||
"Last Result": "最終結果",
|
||||
"Create your admin account": "Create your admin account",
|
||||
"Repeat Password": "Repeat Password",
|
||||
respTime: "Resp. Time (ms)",
|
||||
"Create your admin account": "Adminアカウントの作成",
|
||||
"Repeat Password": "パスワード確認",
|
||||
respTime: "応答時間 (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
Create: "Create",
|
||||
clearEventsMsg: "Are you sure want to delete all events for this monitor?",
|
||||
clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?",
|
||||
confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?",
|
||||
"Clear Data": "Clear Data",
|
||||
Events: "Events",
|
||||
Heartbeats: "Heartbeats",
|
||||
"Auto Get": "Auto Get",
|
||||
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. You can still disable the notification separately for each monitor.",
|
||||
"Default enabled": "Default enabled",
|
||||
"Also apply to existing monitors": "Also apply to existing monitors",
|
||||
Export: "Export",
|
||||
Import: "Import",
|
||||
backupDescription: "You can backup all monitors and all notifications into a JSON file.",
|
||||
backupDescription2: "PS: History and event data is not included.",
|
||||
backupDescription3: "Sensitive data such as notification tokens is included in the export file, please keep it carefully.",
|
||||
alertNoFile: "Please select a file to import.",
|
||||
alertWrongFileType: "Please select a JSON file.",
|
||||
twoFAVerifyLabel: "Please type in your token to verify that 2FA is working",
|
||||
tokenValidSettingsMsg: "Token is valid! You can now save the 2FA settings.",
|
||||
confirmEnableTwoFAMsg: "Are you sure you want to enable 2FA?",
|
||||
confirmDisableTwoFAMsg: "Are you sure you want to disable 2FA?",
|
||||
"Apply on all existing monitors": "Apply on all existing monitors",
|
||||
"Verify Token": "Verify Token",
|
||||
"Setup 2FA": "Setup 2FA",
|
||||
"Enable 2FA": "Enable 2FA",
|
||||
"Disable 2FA": "Disable 2FA",
|
||||
"2FA Settings": "2FA Settings",
|
||||
"Two Factor Authentication": "Two Factor Authentication",
|
||||
Create: "作成",
|
||||
clearEventsMsg: "この監視のすべての記録を削除してもよろしいですか?",
|
||||
clearHeartbeatsMsg: "この監視のすべての異常記録を削除してもよろしいですか?",
|
||||
confirmClearStatisticsMsg: "すべての統計を削除してもよろしいですか?",
|
||||
"Clear Data": "データを削除",
|
||||
Events: "統計",
|
||||
Heartbeats: "異常記録",
|
||||
"Auto Get": "自動取得",
|
||||
enableDefaultNotificationDescription: "監視を作成するごとに、この通知方法はデフォルトで有効になります。監視ごとに通知を無効にすることもできます。",
|
||||
"Default enabled": "デフォルトで有効にする",
|
||||
"Also apply to existing monitors": "既存のモニターにも適用する",
|
||||
Export: "エクスポート",
|
||||
Import: "インポート",
|
||||
backupDescription: "すべての監視と通知方法をJSONファイルにできます。",
|
||||
backupDescription2: "※ 履歴と統計のデータはバックアップされません。",
|
||||
backupDescription3: "通知に使用するトークンなどの機密データも含まれています。注意して扱ってください。",
|
||||
alertNoFile: "インポートするファイルを選択してください。",
|
||||
alertWrongFileType: "JSONファイルを選択してください。",
|
||||
twoFAVerifyLabel: "トークンを入力して、2段階認証を有効にします。",
|
||||
tokenValidSettingsMsg: "トークンの確認が完了しました! 「保存」をしてください。",
|
||||
confirmEnableTwoFAMsg: "2段階認証を「有効」にします。よろしいですか?",
|
||||
confirmDisableTwoFAMsg: "2段階認証を「無効」にします。よろしいですか?",
|
||||
"Apply on all existing monitors": "既存のすべてのモニターに適用する",
|
||||
"Verify Token": "認証する",
|
||||
"Setup 2FA": "2段階認証の設定",
|
||||
"Enable 2FA": "2段階認証を有効にする",
|
||||
"Disable 2FA": "2段階認証を無効にする",
|
||||
"2FA Settings": "2段階認証の設定",
|
||||
"Two Factor Authentication": "2段階認証",
|
||||
Active: "Active",
|
||||
Inactive: "Inactive",
|
||||
Token: "Token",
|
||||
"Show URI": "Show URI",
|
||||
"Clear all statistics": "Clear all Statistics",
|
||||
"Clear all statistics": "すべての記録を削除",
|
||||
retryCheckEverySecond: "Retry every {0} seconds.",
|
||||
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.",
|
||||
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.",
|
||||
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
|
||||
"Import Backup": "Import Backup",
|
||||
"Export Backup": "Export Backup",
|
||||
"Skip existing": "Skip existing",
|
||||
Overwrite: "Overwrite",
|
||||
Options: "Options",
|
||||
"Keep both": "Keep both",
|
||||
Tags: "Tags",
|
||||
"Add New below or Select...": "Add New below or Select...",
|
||||
"Tag with this name already exist.": "Tag with this name already exist.",
|
||||
"Tag with this value already exist.": "Tag with this value already exist.",
|
||||
color: "color",
|
||||
"value (optional)": "value (optional)",
|
||||
importHandleDescription: "同じ名前のすべての監視または通知方法を上書きしない場合は、「既存のをスキップ」を選択します。 「上書きする」は、既存のすべてのモニターと通知を削除します。",
|
||||
confirmImportMsg: "バックアップをインポートしてもよろしいですか?希望するオプションを選択してください。",
|
||||
"Heartbeat Retry Interval": "異常検知後の再試行間隔",
|
||||
"Import Backup": "バックアップのインポート",
|
||||
"Export Backup": "バックアップのエクスポート",
|
||||
"Skip existing": "既存のをスキップする",
|
||||
Overwrite: "上書きする",
|
||||
Options: "オプション",
|
||||
"Keep both": "どちらも保持する",
|
||||
Tags: "タグ",
|
||||
"Add New below or Select...": "新規追加または選択...",
|
||||
"Tag with this name already exist.": "この名前のタグはすでに存在しています。",
|
||||
"Tag with this value already exist.": "この値のタグはすでに存在しています。",
|
||||
color: "色",
|
||||
"value (optional)": "値 (optional)",
|
||||
Gray: "Gray",
|
||||
Red: "Red",
|
||||
Orange: "Orange",
|
||||
@@ -166,20 +166,20 @@ export default {
|
||||
Indigo: "Indigo",
|
||||
Purple: "Purple",
|
||||
Pink: "Pink",
|
||||
"Search...": "Search...",
|
||||
"Avg. Ping": "Avg. Ping",
|
||||
"Avg. Response": "Avg. Response",
|
||||
"Entry Page": "Entry Page",
|
||||
statusPageNothing: "Nothing here, please add a group or a monitor.",
|
||||
"Search...": "検索...",
|
||||
"Avg. Ping": "平均Ping時間",
|
||||
"Avg. Response": "平均応答時間",
|
||||
"Entry Page": "エントリーページ",
|
||||
statusPageNothing: "ここには何もありません。グループまたは監視を追加してください。",
|
||||
"No Services": "No Services",
|
||||
"All Systems Operational": "All Systems Operational",
|
||||
"Partially Degraded Service": "Partially Degraded Service",
|
||||
"Degraded Service": "Degraded Service",
|
||||
"Add Group": "Add Group",
|
||||
"Add a monitor": "Add a monitor",
|
||||
"Edit Status Page": "Edit Status Page",
|
||||
"Go to Dashboard": "Go to Dashboard",
|
||||
"Status Page": "Status Page",
|
||||
"All Systems Operational": "すべてのサービスが稼働中",
|
||||
"Partially Degraded Service": "部分的にサービスが停止中",
|
||||
"Degraded Service": "サービスが停止中",
|
||||
"Add Group": "グループの追加",
|
||||
"Add a monitor": "監視の追加",
|
||||
"Edit Status Page": "ステータスページ編集",
|
||||
"Go to Dashboard": "ダッシュボード",
|
||||
"Status Page": "ステータスページ",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
smtp: "Email (SMTP)",
|
||||
|
@@ -179,11 +179,10 @@ export default {
|
||||
"Edit Status Page": "Rediger statusside",
|
||||
"Go to Dashboard": "Gå til Dashboard",
|
||||
"Status Page": "Statusside",
|
||||
// Start notification form
|
||||
defaultNotificationName: "Min {notification} varsling ({number})",
|
||||
here: "here",
|
||||
"Required": "Obligatorisk",
|
||||
"telegram": "Telegram",
|
||||
Required: "Obligatorisk",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "Du kan få et token fra {0}.",
|
||||
"Chat ID": "Chat ID",
|
||||
@@ -191,12 +190,12 @@ export default {
|
||||
wayToGetTelegramChatID: "Du kan få chat-ID-en din ved å sende meldingen til boten og gå til denne nettadressen for å se chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "DITT BOT TOKEN HER",
|
||||
chatIDNotFound: "Chat-ID ble ikke funnet. Send en melding til denne boten først",
|
||||
"webhook": "Webhook",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} er bra for alle moderne HTTP-servere som express.js",
|
||||
webhookFormDataDesc: "{multipart} er bra for PHP, du trenger bare å analysere JSON etter {decodeFunction}",
|
||||
"smtp": "E-post (SMTP)",
|
||||
smtp: "E-post (SMTP)",
|
||||
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignorer TLS feilmelding",
|
||||
@@ -204,26 +203,26 @@ export default {
|
||||
"To Email": "Til E-post",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"discord": "Discord",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Du kan få dette ved å gå til Serverinnstillinger -> Integrasjoner -> Webhooks -> Ny webhook",
|
||||
"Bot Display Name": "Bot Visningsnavn",
|
||||
"Prefix Custom Message": "Prefiks tilpasset melding",
|
||||
"Hello @everyone is...": "Hei {'@'}everyone det er...",
|
||||
"teams": "Microsoft Teams",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Du kan lære hvordan du oppretter en webhook-URL {0}.",
|
||||
"signal": "Signal",
|
||||
"Number": "Nummer",
|
||||
"Recipients": "Mottakere",
|
||||
signal: "Signal",
|
||||
Number: "Nummer",
|
||||
Recipients: "Mottakere",
|
||||
needSignalAPI: "Du må ha en Signal-klient med REST API.",
|
||||
wayToCheckSignalURL: "Du kan sjekke denne nettadressen for å se hvordan du konfigurerer en:",
|
||||
signalImportant: "VIKTIG: Du kan ikke blande grupper og nummere i mottakere!",
|
||||
"gotify": "Gotify",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Application Token",
|
||||
"Server URL": "Server URL",
|
||||
"Priority": "Prioritet",
|
||||
"slack": "Slack",
|
||||
Priority: "Prioritet",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "Kanal navn",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
@@ -242,7 +241,7 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "User Key",
|
||||
"Device": "Device",
|
||||
Device: "Device",
|
||||
"Message Title": "Message Title",
|
||||
"Notification Sound": "Notification Sound",
|
||||
"More info on:": "More info on: {0}",
|
||||
@@ -273,12 +272,11 @@ export default {
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",
|
||||
aboutMattermostChannelName: "You can override the default channel that webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel",
|
||||
"matrix": "Matrix",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.",
|
||||
promosmsTypeFlash: "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.",
|
||||
promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use Your Sender Name (You need to register name first). Reliable for alerts.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
|
||||
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
|
||||
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
// End notification form
|
||||
};
|
||||
|
@@ -116,7 +116,7 @@ export default {
|
||||
"Repeat Password": "Herhaal wachtwoord",
|
||||
Export: "Exporteren",
|
||||
Import: "Importeren",
|
||||
respTime: "resp. tijd (ms)",
|
||||
respTime: "reactietijd (ms)",
|
||||
notAvailableShort: "N.v.t.",
|
||||
"Default enabled": "Default enabled",
|
||||
"Apply on all existing monitors": "Pas toe op alle bestaande monitors",
|
||||
@@ -138,48 +138,48 @@ export default {
|
||||
"Two Factor Authentication": "Two Factor Authenticatie",
|
||||
Active: "Actief",
|
||||
Inactive: "Inactief",
|
||||
"Also apply to existing monitors": "Also apply to existing monitors",
|
||||
"Also apply to existing monitors": "Voeg ook toe aan bestaande monitors",
|
||||
Token: "Token",
|
||||
"Show URI": "Toon URI",
|
||||
"Clear all statistics": "Wis alle statistieken",
|
||||
retryCheckEverySecond: "Retry every {0} seconds.",
|
||||
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.",
|
||||
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.",
|
||||
retryCheckEverySecond: "Probeer elke {0} seconden.",
|
||||
importHandleDescription: "Kies 'Sla bestaande over' als je elke monitor of melding met dezelfde naam wilt overslaan. Kies 'Overschrijf' als je elke monitor of notificatie wilt verwijderen.",
|
||||
confirmImportMsg: "Weet je zeker dat je dit bestand wilt importeren?",
|
||||
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
|
||||
"Import Backup": "Import Backup",
|
||||
"Export Backup": "Export Backup",
|
||||
"Skip existing": "Skip existing",
|
||||
Overwrite: "Overwrite",
|
||||
Options: "Options",
|
||||
"Keep both": "Keep both",
|
||||
Tags: "Tags",
|
||||
"Add New below or Select...": "Add New below or Select...",
|
||||
"Tag with this name already exist.": "Tag with this name already exist.",
|
||||
"Tag with this value already exist.": "Tag with this value already exist.",
|
||||
color: "color",
|
||||
"value (optional)": "value (optional)",
|
||||
Gray: "Gray",
|
||||
Red: "Red",
|
||||
Orange: "Orange",
|
||||
Green: "Green",
|
||||
Blue: "Blue",
|
||||
"Import Backup": "Importeer Backup",
|
||||
"Export Backup": "Exporteer Backup",
|
||||
"Skip existing": "Sla bestaande over",
|
||||
Overwrite: "Overschrijf",
|
||||
Options: "Opties",
|
||||
"Keep both": "Bewaar beide",
|
||||
Tags: "Labels",
|
||||
"Add New below or Select...": "Voeg nieuwe toe of selecteer...",
|
||||
"Tag with this name already exist.": "Label met deze naam bestaat al",
|
||||
"Tag with this value already exist.": "Label met deze waarde bestaat al",
|
||||
color: "Kleur",
|
||||
"value (optional)": "waarde (optioneel)",
|
||||
Gray: "Grijs",
|
||||
Red: "Rood",
|
||||
Orange: "Oranje",
|
||||
Green: "Groen",
|
||||
Blue: "Blauw",
|
||||
Indigo: "Indigo",
|
||||
Purple: "Purple",
|
||||
Pink: "Pink",
|
||||
"Search...": "Search...",
|
||||
"Avg. Ping": "Avg. Ping",
|
||||
"Avg. Response": "Avg. Response",
|
||||
Purple: "Paars",
|
||||
Pink: "Roze",
|
||||
"Search...": "Zoeken...",
|
||||
"Avg. Ping": "Gemiddelde Ping",
|
||||
"Avg. Response": "Gemiddelde Response",
|
||||
"Entry Page": "Entry Page",
|
||||
statusPageNothing: "Nothing here, please add a group or a monitor.",
|
||||
statusPageNothing: "Niets hier, voeg een groep of monitor toe.",
|
||||
"No Services": "No Services",
|
||||
"All Systems Operational": "All Systems Operational",
|
||||
"Partially Degraded Service": "Partially Degraded Service",
|
||||
"Degraded Service": "Degraded Service",
|
||||
"Add Group": "Add Group",
|
||||
"Add a monitor": "Add a monitor",
|
||||
"Edit Status Page": "Edit Status Page",
|
||||
"Go to Dashboard": "Go to Dashboard",
|
||||
"Status Page": "Status Page",
|
||||
"All Systems Operational": "Alle systemen operationeel",
|
||||
"Partially Degraded Service": "Gedeeltelijk verminderde prestaties",
|
||||
"Degraded Service": "Verminderde prestaties",
|
||||
"Add Group": "Voeg groep toe",
|
||||
"Add a monitor": "Voeg monitor toe",
|
||||
"Edit Status Page": "Wijzig status pagina",
|
||||
"Go to Dashboard": "Ga naar Dashboard",
|
||||
"Status Page": "Status Pagina",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
smtp: "Email (SMTP)",
|
||||
@@ -203,5 +203,5 @@ export default {
|
||||
Headers: "Headers",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "The request headers is geen geldige JSON: ",
|
||||
BodyInvalidFormat: "De request body is geen geldige JSON: "
|
||||
BodyInvalidFormat: "De request body is geen geldige JSON: ",
|
||||
};
|
||||
|
@@ -28,7 +28,7 @@ export default {
|
||||
confirmDisableTwoFAMsg: "Jesteś pewien, że chcesz wyłączyć 2FA?",
|
||||
Settings: "Ustawienia",
|
||||
Dashboard: "Panel",
|
||||
"New Update": "Nowa Aktualizacja",
|
||||
"New Update": "Nowa aktualizacja",
|
||||
Language: "Język",
|
||||
Appearance: "Wygląd",
|
||||
Theme: "Motyw",
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
"Check Update On GitHub": "Sprawdź aktualizację na GitHub",
|
||||
List: "Lista",
|
||||
Add: "Dodaj",
|
||||
"Add New Monitor": "Dodaj Monitor",
|
||||
"Add New Monitor": "Dodaj monitor",
|
||||
"Quick Stats": "Szybki podgląd statystyk",
|
||||
Up: "Online",
|
||||
Down: "Offline",
|
||||
@@ -53,8 +53,8 @@ export default {
|
||||
Edit: "Edytuj",
|
||||
Delete: "Usuń",
|
||||
Current: "Aktualny",
|
||||
Uptime: "Czas Pracy",
|
||||
"Cert Exp.": "Certyfikat Wygasa",
|
||||
Uptime: "Czas pracy",
|
||||
"Cert Exp.": "Certyfikat wygasa",
|
||||
days: "dni",
|
||||
day: "dzień",
|
||||
"-day": " dni",
|
||||
@@ -62,9 +62,9 @@ export default {
|
||||
"-hour": " godzin",
|
||||
Response: "Odpowiedź",
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "Rodzaj Monitora",
|
||||
"Monitor Type": "Rodzaj monitora",
|
||||
Keyword: "Słowo kluczowe",
|
||||
"Friendly Name": "Przyjazna Nazwa",
|
||||
"Friendly Name": "Przyjazna nazwa",
|
||||
URL: "URL",
|
||||
Hostname: "Hostname",
|
||||
Port: "Port",
|
||||
@@ -73,12 +73,12 @@ export default {
|
||||
"Heartbeat Retry Interval": "Częstotliwość ponawiania bicia serca",
|
||||
Advanced: "Zaawansowane",
|
||||
"Upside Down Mode": "Tryb odwrócony",
|
||||
"Max. Redirects": "Maks. Przekierowań",
|
||||
"Max. Redirects": "Maks. przekierowań",
|
||||
"Accepted Status Codes": "Akceptowane kody statusu",
|
||||
Save: "Zapisz",
|
||||
Notifications: "Powiadomienia",
|
||||
"Not available, please setup.": "Niedostępne, proszę skonfigurować.",
|
||||
"Setup Notification": "Skonfiguruj Powiadomienie",
|
||||
"Setup Notification": "Skonfiguruj powiadomienie",
|
||||
Light: "Jasny",
|
||||
Dark: "Ciemny",
|
||||
Auto: "Automatyczny",
|
||||
@@ -118,16 +118,16 @@ export default {
|
||||
"Last Result": "Ostatni wynik",
|
||||
"Create your admin account": "Utwórz swoje konto administratora",
|
||||
"Repeat Password": "Powtórz hasło",
|
||||
"Import Backup": "Importuj Kopię",
|
||||
"Export Backup": "Eksportuj Kopię",
|
||||
"Import Backup": "Importuj kopię zapasową",
|
||||
"Export Backup": "Eksportuj kopię zapasową",
|
||||
Export: "Eksportuj",
|
||||
Import: "Importuj",
|
||||
respTime: "Czas Odp. (ms)",
|
||||
respTime: "Czas odp. (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
"Default enabled": "Włącz domyślnie",
|
||||
"Apply on all existing monitors": "Zastosuj do istniejących monitorów",
|
||||
Create: "Stwórz",
|
||||
"Clear Data": "Usuń Dane",
|
||||
"Clear Data": "Usuń dane",
|
||||
Events: "Wydarzenia",
|
||||
Heartbeats: "Bicia serca",
|
||||
"Auto Get": "Wykryj",
|
||||
@@ -179,106 +179,137 @@ export default {
|
||||
"Edit Status Page": "Edytuj stronę statusu",
|
||||
"Go to Dashboard": "Idź do panelu",
|
||||
"Status Page": "Strona statusu",
|
||||
// Start notification form
|
||||
defaultNotificationName: "Moje powiadomienie {notification} ({number})",
|
||||
here: "tutaj",
|
||||
"Required": "Wymagane",
|
||||
"telegram": "Telegram",
|
||||
"Bot Token": "Token Bota",
|
||||
Required: "Wymagane",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Token bota",
|
||||
wayToGetTelegramToken: "Token można uzyskać z {0}.",
|
||||
"Chat ID": "Identyfikator Czatu",
|
||||
supportTelegramChatID: "Czat wsprarcia technicznego / Bezpośrednia Rozmowa / Czat Grupowy",
|
||||
"Chat ID": "Identyfikator czatu",
|
||||
supportTelegramChatID: "Czat wsparcia technicznego / Bezpośrednia rozmowa / Czat grupowy",
|
||||
wayToGetTelegramChatID: "Możesz uzyskać swój identyfikator czatu, wysyłając wiadomość do bota i przechodząc pod ten adres URL, aby wyświetlić identyfikator czatu:",
|
||||
"YOUR BOT TOKEN HERE": "TWOJ TOKEN BOTA",
|
||||
chatIDNotFound: "Identyfikator czatu nie znaleziony, najpierw napisz do bota",
|
||||
"webhook": "Webhook",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Adres URL",
|
||||
"Content Type": "Rodzaj danych",
|
||||
webhookJsonDesc: "{0} jest dobry w przypadku serwerów HTTP, takich jak express.js",
|
||||
webhookFormDataDesc: "{multipart} jest dobry dla PHP, musisz jedynie przetowrzyć dane przez {decodeFunction}",
|
||||
"smtp": "Email (SMTP)",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "Brak / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Zignrouj Błędy TLS",
|
||||
"Ignore TLS Error": "Zignoruj błędy TLS",
|
||||
"From Email": "Nadawca (OD)",
|
||||
"To Email": "Odbiorca (DO)",
|
||||
smtpCC: "DW",
|
||||
smtpBCC: "UDW",
|
||||
"discord": "Discord",
|
||||
"Discord Webhook URL": "URL Webhook Discorda",
|
||||
wayToGetDiscordURL: "Możesz go uzyskać przechodząc do Ustawienia Serwera -> Integracje -> Tworzenie Webhooka",
|
||||
"Bot Display Name": "Wyświetlana Nazwa Bota",
|
||||
"Prefix Custom Message": "Własny Początek Wiadomości",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "URL webhook Discorda",
|
||||
wayToGetDiscordURL: "Możesz go uzyskać przechodząc do Ustawienia serwera -> Integracje -> Tworzenie webhooka",
|
||||
"Bot Display Name": "Wyświetlana nazwa bota",
|
||||
"Prefix Custom Message": "Własny początek wiadomości",
|
||||
"Hello @everyone is...": "Hej {'@'}everyone ...",
|
||||
"teams": "Microsoft Teams",
|
||||
"Webhook URL": "URL Webhooka",
|
||||
wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",
|
||||
"signal": "Signal",
|
||||
"Number": "Numer",
|
||||
"Recipients": "Odbiorcy",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "URL webhooka",
|
||||
wayToGetTeamsURL: "Możesz dowiedzieć się, jak utworzyć adres url webhooka {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Numer",
|
||||
Recipients: "Odbiorcy",
|
||||
needSignalAPI: "Musisz posiadać klienta Signal z REST API.",
|
||||
wayToCheckSignalURL: "W celu dowiedzenia się, jak go skonfigurować, odwiedź poniższy link:",
|
||||
signalImportant: "UWAGA: Nie można mieszać nazw grup i numerów odbiorców!",
|
||||
"gotify": "Gotify",
|
||||
"Application Token": "Token Aplikacji",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Token aplikacji",
|
||||
"Server URL": "Server URL",
|
||||
"Priority": "Priorytet",
|
||||
"slack": "Slack",
|
||||
"Icon Emoji": "Ikona Emoji",
|
||||
"Channel Name": "Nazwa Kanału",
|
||||
Priority: "Priorytet",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Ikona emoji",
|
||||
"Channel Name": "Nazwa kanału",
|
||||
"Uptime Kuma URL": "Adres Uptime Kuma",
|
||||
aboutWebhooks: "Więcej informacji na temat webhooków: {0}",
|
||||
aboutChannelName: "Podaj nazwę kanału {0} w polu Nazwa Kanału, jeśli chcesz pominąć kanał webhooka. Np.: #inny-kanal",
|
||||
aboutKumaURL: "Jeśli pozostawisz pole Adres Uptime Kuma puste, domyślnie będzie to strona projektu na Github.",
|
||||
emojiCheatSheet: "Ściąga Emoji: {0}",
|
||||
aboutChannelName: "Podaj nazwę kanału {0} w polu Nazwa kanału, jeśli chcesz pominąć kanał webhooka. Np.: #inny-kanal",
|
||||
aboutKumaURL: "Jeśli pozostawisz pole Adres Uptime Kuma puste, domyślnie będzie to strona projektu na GitHub.",
|
||||
emojiCheatSheet: "Ściąga emoji: {0}",
|
||||
"rocket.chat": "Rocket.chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Obsługuje 50+ usług powiadomień)",
|
||||
apprise: "Apprise (obsługuje 50+ usług powiadomień)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "Klucz Użytkownika",
|
||||
"Device": "Urządzenie",
|
||||
"Message Title": "Tytuł Wiadomości",
|
||||
"Notification Sound": "Dźwięk Powiadomienia",
|
||||
"User Key": "Klucz użytkownika",
|
||||
Device: "Urządzenie",
|
||||
"Message Title": "Tytuł wiadomości",
|
||||
"Notification Sound": "Dźwięk powiadomienia",
|
||||
"More info on:": "Więcej informacji na: {0}",
|
||||
pushoverDesc1: "Priorytet awaryjny (2) ma domyślny 30-sekundowy limit czasu między kolejnymi próbami i wygaśnie po 1 godzinie.",
|
||||
pushoverDesc2: "Jeśli chcesz wysyłać powiadomienia na różne urządzenia, wypełnij pole Urządzenie.",
|
||||
"SMS Type": "Rodzaj SMS",
|
||||
octopushTypePremium: "Premium (Szybki - rekomendowany dla powiadomień)",
|
||||
octopushTypeLowCost: "Low Cost (Wolny, czasami blokowany przez operatorów)",
|
||||
octopushTypePremium: "Premium (szybki - rekomendowany dla powiadomień)",
|
||||
octopushTypeLowCost: "Low Cost (wolny, czasami blokowany przez operatorów)",
|
||||
"Check octopush prices": "Sprawdź ceny Octopush {0}.",
|
||||
octopushPhoneNumber: "Numer Telefonu (Format międzynarodowy np.: +33612345678)",
|
||||
octopushSMSSender: "Nadawca SMS : 3-11 znaków alfanumerycznych i spacji (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "Idetyfikator Urządzenia LunaSea",
|
||||
octopushPhoneNumber: "Numer telefonu (format międzynarodowy np.: +33612345678)",
|
||||
octopushSMSSender: "Nadawca SMS: 3-11 znaków alfanumerycznych i spacji (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "Identyfikator urządzenia LunaSea",
|
||||
"Apprise URL": "URL Apprise",
|
||||
"Example:": "Przykład: {0}",
|
||||
"Read more:": "Czytaj Dalej: {0}",
|
||||
"Read more:": "Czytaj dalej: {0}",
|
||||
"Status:": "Status: {0}",
|
||||
"Read more": "Czytaj dalej",
|
||||
appriseInstalled: "Apprise jest zostało zainstalowane.",
|
||||
appriseNotInstalled: "Apprise nie zostało zainstalowane. {0}",
|
||||
"Access Token": "Token Dostępu",
|
||||
"Channel access token": "Token Dostępu Kanału",
|
||||
"Line Developers Console": "Konsola Dewelopersja Line",
|
||||
lineDevConsoleTo: "Konsola Dewelopersja Line - {0}",
|
||||
"Basic Settings": "Ustawienia Ogólne",
|
||||
"User ID": "Idetyfikator Użytkownika",
|
||||
"Messaging API": "API Wiadomości",
|
||||
"Access Token": "Token dostępu",
|
||||
"Channel access token": "Token dostępu kanału",
|
||||
"Line Developers Console": "Konsola deweloperska Line",
|
||||
lineDevConsoleTo: "Konsola deweloperska Line - {0}",
|
||||
"Basic Settings": "Ustawienia ogólne",
|
||||
"User ID": "Identyfikator użytkownika",
|
||||
"Messaging API": "API wiadomości",
|
||||
wayToGetLineChannelToken: "Najpierw uzyskaj dostęp do {0}, utwórz dostawcę i kanał (Messaging API), a następnie możesz uzyskać token dostępu do kanału i identyfikator użytkownika z wyżej wymienionych pozycji menu.",
|
||||
"Icon URL": "Adres Ikony",
|
||||
aboutIconURL: "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.",
|
||||
aboutMattermostChannelName: "You can override the default channel that webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel",
|
||||
"matrix": "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - Tanie, lecz wolne. Dostępne tylko w Polsce",
|
||||
promosmsTypeFlash: "SMS FLASH - Wiadomość automatycznie wyświetli się na urządzeniu. Dostępne tylko w Polsce.",
|
||||
promosmsTypeFull: "SMS FULL - Szybkie i dostępne międzynarodowo. Wersja premium usługi, która pozwala min. ustawić własną nazwę nadawcy.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Wysyłka priorytetowa, posiada wszystkie zalety SMS FULL",
|
||||
promosmsPhoneNumber: "Numer Odbiorcy",
|
||||
promosmsSMSSender: "Nadawca SMS (Wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
||||
// End notification form
|
||||
aboutIconURL: "Możesz podać link do zdjęcia w \"Adres URL ikony\", aby zastąpić domyślne zdjęcie profilowe. Nie będzie używany, jeśli ustawiona jest ikona emoji.",
|
||||
aboutMattermostChannelName: "Możesz zastąpić domyślny kanał, na którym publikowane są posty webhooka, wpisując nazwę kanału w polu \"Nazwa kanału\". Należy to włączyć w ustawieniach webhooka Mattermost. Np.: #inny-kanał",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - tanie, lecz wolne. Dostępne tylko w Polsce",
|
||||
promosmsTypeFlash: "SMS FLASH - wiadomość automatycznie wyświetli się na urządzeniu. Dostępne tylko w Polsce.",
|
||||
promosmsTypeFull: "SMS FULL - szybkie i dostępne międzynarodowo. Wersja premium usługi, która pozwala min. ustawić własną nazwę nadawcy.",
|
||||
promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, posiada wszystkie zalety SMS FULL",
|
||||
promosmsPhoneNumber: "Numer odbiorcy",
|
||||
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
||||
"Primary Base URL": "Główny URL",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
||||
pushOptionalParams: "Parametry opcjonalne: {0}",
|
||||
emailCustomSubject: "Niestandardowy temat",
|
||||
checkPrice: "Sprawdź ceny {0}:",
|
||||
octopushLegacyHint: "Czy używasz starszej wersji Octopush (2011-2020) czy nowej wersji?",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "Adres URL serwera domowego (z http(s):// i opcjonalnie port)",
|
||||
"Internal Room Id": "Wewnętrzne ID pokoju",
|
||||
matrixDesc1: "Możesz znaleźć wewnętrzne ID pokoju patrząc w zaawansowanej sekcji ustawień pokoju w twoim kliencie Matrix. Powinien on wyglądać jak !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Jest wysoce zalecane, abyś stworzył nowego użytkownika i nie używał tokena dostępu swojego użytkownika Matrix, ponieważ pozwoli on na pełny dostęp do twojego konta i wszystkich pokoi, do których dołączyłeś. Zamiast tego, utwórz nowego użytkownika i zaproś go tylko do pokoju, w którym chcesz otrzymywać powiadomienia. Możesz uzyskać token dostępu przez uruchomienie {0}",
|
||||
Method: "Metoda",
|
||||
Body: "Treść",
|
||||
Headers: "Nagłówki",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "Nagłówki żądania nie są w poprawnym formacie JSON: ",
|
||||
BodyInvalidFormat: "Treść żądania nie jest w poprawnym formacie JSON: ",
|
||||
"Monitor History": "Historia monitorów",
|
||||
clearDataOlderThan: "Przechowuj dane dotyczące historii monitorowania {0} dni.",
|
||||
PasswordsDoNotMatch: "Hasła nie pasują.",
|
||||
records: "rekordy",
|
||||
"One record": "Jeden rekord",
|
||||
steamApiKeyDescription: "Do monitorowania serwera gier Steam potrzebny jest klucz Steam Web-API. Możesz zarejestrować swój klucz API tutaj: ",
|
||||
"Current User": "Aktualny użytkownik",
|
||||
recent: "Ostatnie",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
apiCredentials: "Poświadczenia API",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "Nazwa użytkownika API (z prefiksem webapi_)",
|
||||
serwersmsAPIPassword: "Hasło API",
|
||||
serwersmsPhoneNumber: "Numer telefonu",
|
||||
serwersmsSenderName: "Nazwa nadawcy (zatwierdzona w panelu klienta)",
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
languageName: "Русский",
|
||||
checkEverySecond: "проверять каждые {0} секунд",
|
||||
checkEverySecond: "Проверка каждые {0} секунд",
|
||||
retriesDescription: "Максимальное количество попыток перед пометкой сервиса как недоступного и отправкой уведомления",
|
||||
ignoreTLSError: "Игнорировать ошибку TLS/SSL для HTTPS сайтов",
|
||||
upsideDownModeDescription: "Реверс статуса сервиса. Если сервис доступен, то он помечается как НЕДОСТУПНЫЙ.",
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
"Add New Monitor": "Новый монитор",
|
||||
"Quick Stats": "Статистика",
|
||||
Up: "Доступен",
|
||||
Down: "Н/Д",
|
||||
Down: "Недоступен",
|
||||
Pending: "Ожидание",
|
||||
Unknown: "Неизвестно",
|
||||
Pause: "Пауза",
|
||||
@@ -65,8 +65,8 @@ export default {
|
||||
"Accepted Status Codes": "Допустимые коды статуса",
|
||||
Save: "Сохранить",
|
||||
Notifications: "Уведомления",
|
||||
"Not available, please setup.": "Доступных уведомлений нет, необходима настройка.",
|
||||
"Setup Notification": "Настроить уведомления",
|
||||
"Not available, please setup.": "Доступных уведомлений нет, необходимо создать.",
|
||||
"Setup Notification": "Создать уведомление",
|
||||
Light: "Светлая",
|
||||
Dark: "Тёмная",
|
||||
Auto: "Авто",
|
||||
@@ -142,7 +142,7 @@ export default {
|
||||
Token: "Токен",
|
||||
"Show URI": "Показать URI",
|
||||
"Clear all statistics": "Удалить всю статистику",
|
||||
retryCheckEverySecond: "повторять каждые {0} секунд",
|
||||
retryCheckEverySecond: "Повтор каждые {0} секунд",
|
||||
importHandleDescription: "Выберите \"Пропустить существующие\", если вы хотите пропустить каждый монитор или уведомление с таким же именем. \"Перезаписать\" удалит каждый существующий монитор или уведомление и добавит заново. Вариант \"Не проверять\" принудительно восстанавливает все мониторы и уведомления, даже если они уже существуют.",
|
||||
confirmImportMsg: "Вы действительно хотите восстановить резервную копию? Убедитесь, что вы выбрали подходящий вариант импорта.",
|
||||
"Heartbeat Retry Interval": "Интервал повтора опроса",
|
||||
@@ -185,7 +185,7 @@ export default {
|
||||
"Switch to Dark Theme": "Тёмная тема",
|
||||
"Switch to Light Theme": "Светлая тема",
|
||||
telegram: "Telegram",
|
||||
webhook: "Webhook",
|
||||
webhook: "Вебхук",
|
||||
smtp: "Email (SMTP)",
|
||||
discord: "Discord",
|
||||
teams: "Microsoft Teams",
|
||||
@@ -198,8 +198,114 @@ export default {
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Support 50+ Notification services)",
|
||||
apprise: "Apprise (Поддержка 50+ сервисов уведомлений)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Primary Base URL": "Основной URL",
|
||||
"Push URL": "URL пуша",
|
||||
needPushEvery: "К этому URL необходимо обращаться каждые {0} секунд",
|
||||
pushOptionalParams: "Опциональные параметры: {0}",
|
||||
defaultNotificationName: "Моё уведомление {notification} ({number})",
|
||||
here: "здесь",
|
||||
Required: "Требуется",
|
||||
"Bot Token": "Токен бота",
|
||||
wayToGetTelegramToken: "Вы можете взять токен здесь - {0}.",
|
||||
"Chat ID": "ID чата",
|
||||
supportTelegramChatID: "Поддерживаются ID чатов, групп и каналов",
|
||||
wayToGetTelegramChatID: "Вы можете взять ID вашего чата, отправив сообщение боту и перейдя по этому URL для просмотра chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "ВАШ ТОКЕН БОТА ЗДЕСЬ",
|
||||
chatIDNotFound: "ID чата не найден; пожалуйста отправьте сначала сообщение боту",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Тип контента",
|
||||
webhookJsonDesc: "{0} подходит для любых современных HTTP-серверов, например Express.js",
|
||||
webhookFormDataDesc: "{multipart} подходит для PHP. JSON-вывод необходимо будет обработать с помощью {decodeFunction}",
|
||||
secureOptionNone: "Нет / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Игнорировать ошибки TLS",
|
||||
"From Email": "От кого",
|
||||
emailCustomSubject: "Своя тема",
|
||||
"To Email": "Кому",
|
||||
smtpCC: "Копия",
|
||||
smtpBCC: "Скрытая копия",
|
||||
"Discord Webhook URL": "Discord вебхук URL",
|
||||
wayToGetDiscordURL: "Вы можете создать его в Параметрах сервера -> Интеграции -> Создать вебхук",
|
||||
"Bot Display Name": "Отображаемое имя бота",
|
||||
"Prefix Custom Message": "Свой префикс сообщения",
|
||||
"Hello @everyone is...": "Привет {'@'}everyone это...",
|
||||
"Webhook URL": "URL вебхука",
|
||||
wayToGetTeamsURL: "Как создать URL вебхука вы можете узнать здесь - {0}.",
|
||||
Number: "Номер",
|
||||
Recipients: "Получатели",
|
||||
needSignalAPI: "Вам необходим клиент Signal с поддержкой REST API.",
|
||||
wayToCheckSignalURL: "Пройдите по этому URL, чтобы узнать как настроить такой клиент:",
|
||||
signalImportant: "ВАЖНО: Нельзя смешивать в Получателях группы и номера!",
|
||||
"Application Token": "Токен приложения",
|
||||
"Server URL": "URL сервера",
|
||||
Priority: "Приоритет",
|
||||
"Icon Emoji": "Иконка Emoji",
|
||||
"Channel Name": "Имя канала",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Больше информации о вебхуках: {0}",
|
||||
aboutChannelName: "Введите имя канала в поле {0} Имя канала, если вы хотите обойти канал вебхука. Например: #other-channel",
|
||||
aboutKumaURL: "Если поле Uptime Kuma URL в настройках останется пустым, по умолчанию будет использоваться ссылка на проект на GitHub.",
|
||||
emojiCheatSheet: "Шпаргалка по Emoji: {0}",
|
||||
"User Key": "Ключ пользователя",
|
||||
Device: "Устройство",
|
||||
"Message Title": "Заголовок сообщения",
|
||||
"Notification Sound": "Звук уведомления",
|
||||
"More info on:": "Больше информации: {0}",
|
||||
pushoverDesc1: "Экстренный приоритет (2) имеет таймаут повтора по умолчанию 30 секунд и истекает через 1 час.",
|
||||
pushoverDesc2: "Если вы хотите отправлять уведомления различным устройствам, необходимо заполнить поле Устройство.",
|
||||
"SMS Type": "Тип SMS",
|
||||
octopushTypePremium: "Премиум (Быстрый - рекомендуется для алертов)",
|
||||
octopushTypeLowCost: "Дешёвый (Медленный - иногда блокируется операторами)",
|
||||
checkPrice: "Тарифы {0}:",
|
||||
octopushLegacyHint: "Вы используете старую версию Octopush (2011-2020) или новую?",
|
||||
"Check octopush prices": "Тарифы Octopush {0}.",
|
||||
octopushPhoneNumber: "Номер телефона (межд. формат, например: +79831234567) ",
|
||||
octopushSMSSender: "Имя отправителя SMS: 3-11 символов алвафита, цифр и пробелов (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "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",
|
||||
lineDevConsoleTo: "Консоль разработчиков Line - {0}",
|
||||
"Basic Settings": "Базовые настройки",
|
||||
"User ID": "ID пользователя",
|
||||
"Messaging API": "API сообщений",
|
||||
wayToGetLineChannelToken: "Сначала зайдите в {0}, создайте провайдера и канал (API сообщений), затем вы сможете получить токен доступа канала и ID пользователя из вышеупомянутых пунктов меню.",
|
||||
"Icon URL": "URL иконки",
|
||||
aboutIconURL: "Вы можете предоставить ссылку на иконку в поле \"URL иконки\" чтобы переопределить картинку профиля по умолчанию. Не используется, если задана иконка Emoji.",
|
||||
aboutMattermostChannelName: "Вы можете переопределить канал по умолчанию, в который вебхук пишет, введя имя канала в поле \"Имя канала\". Это необходимо включить в настройках вебхука Mattermost. Например: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - дёшево и медленно, часто перегружен. Только для получателей из Польши.",
|
||||
promosmsTypeFlash: "SMS FLASH - сообщения автоматически появятся на устройстве получателя. Только для получателей из Польши.",
|
||||
promosmsTypeFull: "SMS FULL - премиум-уровень SMS, можно использовать своё имя отправителя (предварительно зарегистрировав его). Надёжно для алертов.",
|
||||
promosmsTypeSpeed: "SMS SPEED - наивысший приоритет в системе. Очень быстро и надёжно, но очень дорого (в два раза дороже, чем SMS FULL).",
|
||||
promosmsPhoneNumber: "Номер телефона (для получателей из Польши можно пропустить код региона)",
|
||||
promosmsSMSSender: "Имя отправителя SMS: Зарегистрированное или одно из имён по умолчанию: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "URL сервера (вместе с http(s):// и опционально порт)",
|
||||
"Internal Room Id": "Внутренний ID комнаты",
|
||||
matrixDesc1: "Внутренний ID комнаты можно найти в Подробностях в параметрах канала вашего Matrix клиента. Он должен выглядеть примерно как !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Рекомендуется создать нового пользователя и не использовать токен доступа личного пользователя Matrix, т.к. это влечёт за собой полный доступ к аккаунту и к комнатам, в которых вы состоите. Вместо этого создайте нового пользователя и пригласите его только в ту комнату, в которой вы хотите получать уведомления. Токен доступа можно получить, выполнив команду {0}",
|
||||
Method: "Метод",
|
||||
Body: "Тело",
|
||||
Headers: "Заголовки",
|
||||
PushUrl: "URL пуша",
|
||||
HeadersInvalidFormat: "Заголовки запроса некорректны JSON: ",
|
||||
BodyInvalidFormat: "Тело запроса некорректно JSON: ",
|
||||
"Monitor History": "История мониторов",
|
||||
clearDataOlderThan: "Сохранять историю мониторов в течение {0} дней.",
|
||||
PasswordsDoNotMatch: "Пароли не совпадают.",
|
||||
records: "записей",
|
||||
"One record": "Одна запись",
|
||||
steamApiKeyDescription: "Для мониторинга игрового сервера Steam вам необходим Web-API ключ Steam. Зарегистрировать его можно здесь: ",
|
||||
};
|
||||
|
310
src/languages/vi.js
Normal file
310
src/languages/vi.js
Normal file
@@ -0,0 +1,310 @@
|
||||
export default {
|
||||
languageName: "Vietnamese",
|
||||
checkEverySecond: "Kiểm tra mỗi {0} giây.",
|
||||
retryCheckEverySecond: "Thử lại mỗi {0} giây.",
|
||||
retriesDescription: "Số lần thử lại tối đa trước khi dịch vụ được đánh dấu là down và gửi thông báo.",
|
||||
ignoreTLSError: "Bỏ qua lỗi TLS/SSL với các web HTTPS.",
|
||||
upsideDownModeDescription: "Trạng thái đảo ngược, nếu dịch vụ có thể truy cập được nghĩa là DOWN.",
|
||||
maxRedirectDescription: "Số lần chuyển hướng (redirect) tối đa. Đặt thành 0 để tắt chuyển hướng",
|
||||
acceptedStatusCodesDescription: "Chọn mã code trạng thái được coi là phản hồi thành công.",
|
||||
passwordNotMatchMsg: "Mật khẩu nhập lại không khớp.",
|
||||
notificationDescription: "Vui lòng chỉ định một kênh thông báo.",
|
||||
keywordDescription: "Từ khoá tìm kiếm phản hồi ở dạng html hoặc JSON, có phân biệt chữ HOA - thường",
|
||||
pauseDashboardHome: "Tạm dừng",
|
||||
deleteMonitorMsg: "Bạn chắc chắn muốn xóa monitor này chứ?",
|
||||
deleteNotificationMsg: "Bạn có chắc chắn muốn xóa kênh thông báo này cho tất cả monitor?",
|
||||
resoverserverDescription: "Cloudflare là máy chủ mặc định, bạn có thể thay đổi bất cứ lúc nào.",
|
||||
rrtypeDescription: "Hãy chọn RR-Type mà bạn muốn giám sát",
|
||||
pauseMonitorMsg: "Bạn chắc chắn muốn tạm dừng chứ?",
|
||||
enableDefaultNotificationDescription: "Bật làm mặc định cho mọi monitor mới về sau. Bạn vẫn có thể tắt thông báo riêng cho từng monitor.",
|
||||
clearEventsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ sự kiện cho monitor này chứ?",
|
||||
clearHeartbeatsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ heartbeats cho monitor này chứ?",
|
||||
confirmClearStatisticsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ số liệu thống kê?",
|
||||
importHandleDescription: "Chọn 'Skip existing' nếu bạn muốn bỏ qua mọi monitor và kênh thông báo trùng tên. 'Overwrite' sẽ ghi đè lên tất cả các monitor và kênh thông báo.",
|
||||
confirmImportMsg: "Bạn có chắc chắn muốn khôi phục bản bản sao lưu này không?.",
|
||||
twoFAVerifyLabel: "Vui lòng nhập mã token của bạn để xác minh rằng 2FA đang hoạt động",
|
||||
tokenValidSettingsMsg: "Mã token hợp lệ! Bạn có thể lưu cài đặt 2FA bây giờ.",
|
||||
confirmEnableTwoFAMsg: "Bạn chắc chắn muốn bật 2FA chứ?",
|
||||
confirmDisableTwoFAMsg: "Bạn chắc chắn muốn tắt 2FA chứ?",
|
||||
Settings: "Cài đặt",
|
||||
Dashboard: "Dashboard",
|
||||
"New Update": "Bản cập nhật mới",
|
||||
Language: "Ngôn ngữ",
|
||||
Appearance: "Giao diện",
|
||||
Theme: "Theme",
|
||||
General: "Chung",
|
||||
"Primary Base URL": "URL chính",
|
||||
Version: "Phiên bản",
|
||||
"Check Update On GitHub": "Kiểm tra bản cập nhật mới trên GitHub",
|
||||
List: "List",
|
||||
Add: "Thêm",
|
||||
"Add New Monitor": "Thêm mới Monitor",
|
||||
"Quick Stats": "Thống kê nhanh",
|
||||
Up: "Up",
|
||||
Down: "Down",
|
||||
Pending: "Chờ xử lý",
|
||||
Unknown: "Không xác định",
|
||||
Pause: "Tạm dừng",
|
||||
Name: "Tên",
|
||||
Status: "Trạng thái",
|
||||
DateTime: "Ngày tháng",
|
||||
Message: "Trạng thái request",
|
||||
"No important events": "Không có sự kiện quan trọng nào",
|
||||
Resume: "Khôi phục",
|
||||
Edit: "Sửa",
|
||||
Delete: "Xoá",
|
||||
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ờ",
|
||||
"-hour": "-giờ",
|
||||
Response: "Phản hồi",
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "Kiểu monitor",
|
||||
Keyword: "Từ khoá",
|
||||
"Friendly Name": "Tên monitor",
|
||||
URL: "URL",
|
||||
Hostname: "Hostname",
|
||||
Port: "Port",
|
||||
"Heartbeat Interval": "Tần suất kiểm tra",
|
||||
Retries: "Thử lại",
|
||||
"Heartbeat Retry Interval": "Tần suất kiểm tra lại",
|
||||
Advanced: "Nâng cao",
|
||||
"Upside Down Mode": "Chế độ đảo ngược",
|
||||
"Max. Redirects": "Số chuyển hướng tối đa",
|
||||
"Accepted Status Codes": "Codes trạng thái chấp nhận",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Bạn nên gọi URL mỗi {0} giây.",
|
||||
pushOptionalParams: "Tuỳ chỉnh parameters: {0}",
|
||||
Save: "Lưu",
|
||||
Notifications: "Thông báo",
|
||||
"Not available, please setup.": "Chưa sẵn sàng, hãy cài đặt.",
|
||||
"Setup Notification": "Cài đặt thông báo",
|
||||
Light: "Sáng",
|
||||
Dark: "Tối",
|
||||
Auto: "Tự động",
|
||||
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
|
||||
Normal: "Bình thường",
|
||||
Bottom: "Dưới",
|
||||
None: "Không có",
|
||||
Timezone: "Múi giờ",
|
||||
"Search Engine Visibility": "Hiển thị với các công cụ tìm kiếm",
|
||||
"Allow indexing": "Cho phép indexing",
|
||||
"Discourage search engines from indexing site": "Ngăn chặn các công cụ tìm kiếm indexing trang",
|
||||
"Change Password": "Thay đổi mật khẩu",
|
||||
"Current Password": "Mật khẩu hiện tại",
|
||||
"New Password": "Mật khẩu mới",
|
||||
"Repeat New Password": "Lặp lại mật khẩu mới",
|
||||
"Update Password": "Cập nhật mật khẩu",
|
||||
"Disable Auth": "Tắt xác minh",
|
||||
"Enable Auth": "Bật xác minh",
|
||||
Logout: "Đăng xuất",
|
||||
Leave: "Rời",
|
||||
"I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!",
|
||||
Confirm: "Xác nhận",
|
||||
Yes: "Có",
|
||||
No: "Không",
|
||||
Username: "Tài khoản",
|
||||
Password: "Mật khẩu",
|
||||
"Remember me": "Lưu phiên đăng nhập",
|
||||
Login: "Đăng nhập",
|
||||
"No Monitors, please": "Không có monitor nào",
|
||||
"add one": "Thêm mới",
|
||||
"Notification Type": "Kiểu thông báo",
|
||||
Email: "Email",
|
||||
Test: "Thử",
|
||||
"Certificate Info": "Thông tin Certificate",
|
||||
"Resolver Server": "Máy chủ Resolver",
|
||||
"Resource Record Type": "Loại bản ghi",
|
||||
"Last Result": "Kết quả cuối cùng",
|
||||
"Create your admin account": "Tạo tài khoản quản trị",
|
||||
"Repeat Password": "Lặp lại mật khẩu",
|
||||
"Import Backup": "Khôi phục bản sao lưu",
|
||||
"Export Backup": "Xuất bản sao lưu",
|
||||
Export: "Xuất",
|
||||
Import: "Khôi phục",
|
||||
respTime: "Thời gian phản hồi (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
"Default enabled": "Mặc định bật",
|
||||
"Apply on all existing monitors": "Áp dụng cho tất cả monitor đang có",
|
||||
Create: "Tạo",
|
||||
"Clear Data": "Xoá dữ liệu",
|
||||
Events: "Sự kiện",
|
||||
Heartbeats: "Heartbeats",
|
||||
"Auto Get": "Tự động lấy",
|
||||
backupDescription: "Bạn có thể sao lưu tất cả các monitor và tất cả các thông báo vào một file JSON.",
|
||||
backupDescription2: "PS: Không bao gồm dữ liệu lịch sử các sự kiện.",
|
||||
backupDescription3: "Hãy lưu giữ file này cẩn thận vì trong file đó chứa cả các mã token thông báo.",
|
||||
alertNoFile: "Hãy chọn file để khôi phục.",
|
||||
alertWrongFileType: "Hãy chọn file JSON.",
|
||||
"Clear all statistics": "Xoá tất cả thống kê",
|
||||
"Skip existing": "Skip existing",
|
||||
Overwrite: "Ghi đè",
|
||||
Options: "Tuỳ chọn",
|
||||
"Keep both": "Giữ lại cả hai",
|
||||
"Verify Token": "Xác minh Token",
|
||||
"Setup 2FA": "Cài đặt 2FA",
|
||||
"Enable 2FA": "Bật 2FA",
|
||||
"Disable 2FA": "Tắt 2FA",
|
||||
"2FA Settings": "Cài đặt 2FA",
|
||||
"Two Factor Authentication": "Xác thực hai yếu tố",
|
||||
Active: "Hoạt động",
|
||||
Inactive: "Ngừng hoạt động",
|
||||
Token: "Token",
|
||||
"Show URI": "Hiển thị URI",
|
||||
Tags: "Tags",
|
||||
"Add New below or Select...": "Thêm mới ở dưới hoặc Chọn...",
|
||||
"Tag with this name already exist.": "Tag với tên đã tồn tại.",
|
||||
"Tag with this value already exist.": "Tag với value đã tồn tại.",
|
||||
color: "Màu sắc",
|
||||
"value (optional)": "Value (tuỳ chọn)",
|
||||
Gray: "Xám",
|
||||
Red: "Đỏ",
|
||||
Orange: "Cam",
|
||||
Green: "Xanh lá",
|
||||
Blue: "Xanh da trời",
|
||||
Indigo: "Chàm",
|
||||
Purple: "Tím",
|
||||
Pink: "Hồng",
|
||||
"Search...": "Tìm kiếm...",
|
||||
"Avg. Ping": "Ping Trung bình",
|
||||
"Avg. Response": "Phản hồi trung bình",
|
||||
"Entry Page": "Entry Page",
|
||||
statusPageNothing: "Không có gì, hãy thêm nhóm monitor hoặc monitor.",
|
||||
"No Services": "Không có dịch vụ",
|
||||
"All Systems Operational": "Tất cả các hệ thống hoạt động bình thường",
|
||||
"Partially Degraded Service": "Dịch vụ xuống cấp một phần",
|
||||
"Degraded Service": "Degraded Service",
|
||||
"Add Group": "Thêm nhóm",
|
||||
"Add a monitor": "Thêm monitor",
|
||||
"Edit Status Page": "Sửa trang trạng thái",
|
||||
"Go to Dashboard": "Đi tới Dashboard",
|
||||
"Status Page": "Trang trạng thái",
|
||||
defaultNotificationName: "My {notification} Alerts ({number})",
|
||||
here: "tại đây",
|
||||
Required: "Bắt buộc",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Bot Token",
|
||||
wayToGetTelegramToken: "Bạn có thể lấy mã token từ",
|
||||
"Chat ID": "Chat ID",
|
||||
supportTelegramChatID: "Hỗ trợ chat trực tiếp / Nhóm / Kênh Chat ID",
|
||||
wayToGetTelegramChatID: "Bạn có thể lấy chat id của mình bằng cách gửi tin nhắn tới bot và truy cập url này để xem chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "MÃ BOT TOKEN CỦA BẠN",
|
||||
chatIDNotFound: "Không tìm thấy Chat ID, vui lòng gửi tin nhắn cho bot này trước",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "URL đăng",
|
||||
"Content Type": "Loại nội dung",
|
||||
webhookJsonDesc: "{0} phù hợp với bất kỳ máy chủ http hiện đại nào như express.js",
|
||||
webhookFormDataDesc: "{multipart} phù hợp với PHP, bạn chỉ cần phân tích cú pháp json bằng {decodeFunction}",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "None / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Bỏ qua lỗi TLS",
|
||||
"From Email": "Từ Email",
|
||||
emailCustomSubject: "Tuỳ chỉnh tiêu đề",
|
||||
"To Email": "Tới Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "Để lấy Discord, hãy vào: Server Settings -> Integrations -> Create Webhook",
|
||||
"Bot Display Name": "Tên hiển thị của Bot",
|
||||
"Prefix Custom Message": "Tiền tố tin nhắn tuỳ chọn",
|
||||
"Hello @everyone is...": "Xin chào {'@'} mọi người đang...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Bạn có thể học cách tạo webhook url {0}.",
|
||||
signal: "Tín hiệu",
|
||||
Number: "Số",
|
||||
Recipients: "Người nhận",
|
||||
needSignalAPI: "Bạn cần một tín hiệu client với REST API.",
|
||||
wayToCheckSignalURL: "Bạn có thể kiểm tra url này để xem cách thiết lập:",
|
||||
signalImportant: "QUAN TRỌNG: Bạn không thể kết hợp các nhóm và số trong người nhận!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Mã Token ứng dụng",
|
||||
"Server URL": "URL máy chủ",
|
||||
Priority: "Mức ưu tiên",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Icon Emoji",
|
||||
"Channel Name": "Tên Channel",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Thông tin thêm về webhook trên: {0}",
|
||||
aboutChannelName: "Nhập tên kênh trên {0} trường Channel Name nếu bạn muốn bỏ qua kênh webhook. vd: #other-channel",
|
||||
aboutKumaURL: "Nếu bạn để trống trường Uptime Kuma URL, mặc định sẽ là trang Project Github.",
|
||||
emojiCheatSheet: "Bảng tra cứu Emoji: {0}",
|
||||
"rocket.chat": "Rocket.chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Hỗ trợ 50+ dịch vụ thông báo)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "User Key",
|
||||
Device: "Thiết bị",
|
||||
"Message Title": "Tiêu đề tin nhắn",
|
||||
"Notification Sound": "Âm thanh thông báo",
|
||||
"More info on:": "Thông tin chi tiết tại: {0}",
|
||||
pushoverDesc1: "Mức ưu tiên khẩn cấp (2) có thời gian chờ mặc định là 30 giây giữa các lần thử lại và sẽ hết hạn sau 1 giờ.",
|
||||
pushoverDesc2: "Nếu bạn muốn gửi thông báo đến các thiết bị khác nhau, hãy điền vào trường Thiết bị.",
|
||||
"SMS Type": "SMS Type",
|
||||
octopushTypePremium: "Premium (Nhanh - Khuyến nghị nên dùng cho cảnh báo)",
|
||||
octopushTypeLowCost: "Giá rẻ (Chậm, thỉnh thoảng bị chặn)",
|
||||
checkPrice: "Kiểm tra giá {0}:",
|
||||
apiCredentials: "API credentials",
|
||||
octopushLegacyHint: "Bạn muốn sử dụng phiên bản cũ của Octopush (2011-2020) hay phiên bản mới?",
|
||||
"Check octopush prices": "Kiểm tra giá octopush {0}.",
|
||||
octopushPhoneNumber: "Số điện thoại (Định dạng intl, vd : +84123456789) ",
|
||||
octopushSMSSender: "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea ID thiết bị",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "Ví dụ: {0}",
|
||||
"Read more:": "Đọc thêm: {0}",
|
||||
"Status:": "Trạng thái: {0}",
|
||||
"Read more": "Đọc thêm",
|
||||
appriseInstalled: "Đã cài đặt Apprise.",
|
||||
appriseNotInstalled: "Chưa cài đặt Apprise. {0}",
|
||||
"Access Token": "Token truy cập",
|
||||
"Channel access token": "Token kênh truy cập",
|
||||
"Line Developers Console": "Line Developers Console",
|
||||
lineDevConsoleTo: "Line Developers Console - {0}",
|
||||
"Basic Settings": "Cài đặt cơ bản",
|
||||
"User ID": "User ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "Trước tiên, hãy truy cập {0},tạo nhà cung cấp và kênh (Messaging API), sau đó bạn có thể nhận mã token truy cập kênh và id người dùng từ các mục menu được đề cập ở trên.",
|
||||
"Icon URL": "Icon URL",
|
||||
aboutIconURL: "Bạn có thể cung cấp liên kết đến ảnh trong \"Icon URL\" để ghi đè ảnh hồ sơ mặc định. Sẽ không được sử dụng nếu Biểu tượng cảm xúc được thiết lập.",
|
||||
aboutMattermostChannelName: "Bạn có thể ghi đè kênh mặc định mà webhook đăng lên bằng cách nhập tên kênh vào trường \"Channel Name\". Điều này cần được bật trong cài đặt Mattermost webhook. Ví dụ: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - rẻ nhưng chậm và thường xuyên quá tải. Chỉ dành cho người Ba Lan.",
|
||||
promosmsTypeFlash: "SMS FLASH - Tin nhắn sẽ tự động hiển thị trên thiết bị của người nhận. Chỉ dành cho người Ba Lan.",
|
||||
promosmsTypeFull: "SMS FULL - SMS cao cấp, Bạn có thể sử dụng Tên Người gửi (Bạn cần đăng ký tên trước). Đáng tin cậy cho các cảnh báo.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Ưu tiên cao nhất trong hệ thống. Rất nhanh chóng và đáng tin cậy nhưng tốn kém, (giá gấp đôi SMS FULL).",
|
||||
promosmsPhoneNumber: "Số điện thoại (Bỏ qua mã vùng với người Ba Lan)",
|
||||
promosmsSMSSender: "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookUrl",
|
||||
matrixHomeserverURL: "Homeserver URL (với http(s):// và port tuỳ chỉnh)",
|
||||
"Internal Room Id": "Room ID Nội bộ",
|
||||
matrixDesc1: "Bạn có thể tìm thấy room ID nội bộ bằng cách tìm trong mục advanced của phần room settings trong Matrix client của bạn. Nó có dạng giống như !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Bạn nên tạo người dùng mới và đừng sử dụng mã token truy cập của Matrix user vì nó sẽ cho phép truy cập toàn quyền vào tài khoản của bạn và tất cả các phòng bạn đã tham gia. Thay vào đó, hãy tạo một người dùng mới và chỉ mời người đó vào phòng mà bạn muốn nhận thông báo. Bạn có thể lấy được mã token truy cập bằng cách chạy {0}",
|
||||
Method: "Method",
|
||||
Body: "Body",
|
||||
Headers: "Headers",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "Header request không hợp lệ JSON: ",
|
||||
BodyInvalidFormat: "Tequest body không hợp lệ JSON: ",
|
||||
"Monitor History": "Lịch sử Monitor",
|
||||
clearDataOlderThan: "Giữ dữ liệu lịch sử monitor {0} ngày.",
|
||||
PasswordsDoNotMatch: "Passwords không khớp.",
|
||||
records: "records",
|
||||
"One record": "One record",
|
||||
steamApiKeyDescription: "Để monitor các Steam Game Server bạn cần một Steam Web-API key. Bạn có thể đăng ký API key tại đây: ",
|
||||
"Current User": "User hiện tại",
|
||||
recent: "Gần đây",
|
||||
};
|
@@ -65,7 +65,7 @@ export default {
|
||||
"Max. Redirects": "重定向次数",
|
||||
"Accepted Status Codes": "有效状态码",
|
||||
"Push URL": "推送链接",
|
||||
needPushEvery: "你需要每 {0} 秒调用一次。",
|
||||
needPushEvery: "你需要每 {0} 秒调用一次",
|
||||
pushOptionalParams: "可选参数:{0}",
|
||||
Save: "保存",
|
||||
Notifications: "消息通知",
|
||||
|
310
src/languages/zh-TW.js
Normal file
310
src/languages/zh-TW.js
Normal file
@@ -0,0 +1,310 @@
|
||||
export default {
|
||||
languageName: "繁體中文 (台灣)",
|
||||
checkEverySecond: "每 {0} 秒檢查一次",
|
||||
retryCheckEverySecond: "每 {0} 秒重試一次",
|
||||
retriesDescription: "在服務被標記為離線並傳送通知前的最大重試次數",
|
||||
ignoreTLSError: "忽略 HTTPS 網站的 TLS/SSL 錯誤",
|
||||
upsideDownModeDescription: "反轉顯示狀態。若服務可以連線,將顯示離線。",
|
||||
maxRedirectDescription: "最大重新導向跟隨次數。設為 0 將停用重新導向。",
|
||||
acceptedStatusCodesDescription: "選擇視為成功回應的狀態碼。",
|
||||
passwordNotMatchMsg: "密碼不相符。",
|
||||
notificationDescription: "必須將通知指派給監測器才能運作。",
|
||||
keywordDescription: "HTML 或 JSON 回應的搜尋關鍵字。區分大小寫。",
|
||||
pauseDashboardHome: "暫停",
|
||||
deleteMonitorMsg: "您確定要刪除此監測器嗎?",
|
||||
deleteNotificationMsg: "您確定要為所有監測器刪除此通知嗎?",
|
||||
resoverserverDescription: "Cloudflare 為預設伺服器。您可以隨時更換解析伺服器。",
|
||||
rrtypeDescription: "選擇您想要監測的資源記錄",
|
||||
pauseMonitorMsg: "您確定要暫停嗎?",
|
||||
enableDefaultNotificationDescription: "預設情況下,新監測器將啟用此通知。您仍可分別停用各監測器的通知。",
|
||||
clearEventsMsg: "您確定要刪除此監測器的所有事件嗎?",
|
||||
clearHeartbeatsMsg: "您確定要刪除此監測器的所有心跳嗎?",
|
||||
confirmClearStatisticsMsg: "您確定要刪除所有統計資料嗎?",
|
||||
importHandleDescription: "若您想跳過所有相同名稱的監測器或通知,請選擇 '略過現有'。選擇 '覆寫' 將刪除所有現有的監測器及通知。",
|
||||
confirmImportMsg: "您確定要匯入備份嗎?請確認是否選擇正確的匯入設定。",
|
||||
twoFAVerifyLabel: "請輸入權杖以驗證雙步驟驗證:",
|
||||
tokenValidSettingsMsg: "權杖有效!您可以儲存雙步驟驗證設定了。",
|
||||
confirmEnableTwoFAMsg: "您確定要啟用雙步驟驗證嗎?",
|
||||
confirmDisableTwoFAMsg: "您確定要停用雙步驟驗證嗎?",
|
||||
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: "Ping",
|
||||
"Monitor Type": "監測器類型",
|
||||
Keyword: "關鍵字",
|
||||
"Friendly Name": "易記名稱",
|
||||
URL: "網址",
|
||||
Hostname: "主機名稱",
|
||||
Port: "連接埠",
|
||||
"Heartbeat Interval": "心跳間隔",
|
||||
Retries: "重試次數",
|
||||
"Heartbeat Retry Interval": "心跳重試間隔",
|
||||
Advanced: "進階",
|
||||
"Upside Down Mode": "顛倒模式",
|
||||
"Max. Redirects": "最大重新導向次數",
|
||||
"Accepted Status Codes": "可接受的狀態碼",
|
||||
"Push URL": "推送網址",
|
||||
needPushEvery: "您應每 {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": "啟用驗證",
|
||||
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": "資源記錄類型",
|
||||
"Last Result": "最後結果",
|
||||
"Create your admin account": "建立您的管理員帳號",
|
||||
"Repeat Password": "確認密碼",
|
||||
"Import Backup": "匯入備份",
|
||||
"Export Backup": "匯出備份",
|
||||
Export: "匯出",
|
||||
Import: "匯入",
|
||||
respTime: "回應時間 (毫秒)",
|
||||
notAvailableShort: "N/A",
|
||||
"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": "設置雙步驟驗證",
|
||||
"Enable 2FA": "啟用雙步驟驗證",
|
||||
"Disable 2FA": "停用雙步驟驗證",
|
||||
"2FA Settings": "雙步驟驗證設定",
|
||||
"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": "平均回應",
|
||||
"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": "狀態頁",
|
||||
defaultNotificationName: "我的 {notification} 通知 ({number})",
|
||||
here: "此處",
|
||||
Required: "必填",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "機器人權杖",
|
||||
wayToGetTelegramToken: "您可以從 {0} 取得權杖。",
|
||||
"Chat ID": "聊天 ID",
|
||||
supportTelegramChatID: "支援 對話/群組/頻道的聊天 ID",
|
||||
wayToGetTelegramChatID: "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:",
|
||||
"YOUR BOT TOKEN HERE": "在此填入您的機器人權杖",
|
||||
chatIDNotFound: "找不到 Chat ID;請先傳送訊息給機器人",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post 網址",
|
||||
"Content Type": "內容類型",
|
||||
webhookJsonDesc: "{0} 適合任何現代的 HTTP 伺服器,如 Express.js",
|
||||
webhookFormDataDesc: "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析。",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "無 / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "忽略 TLS 錯誤",
|
||||
"From Email": "寄件人",
|
||||
emailCustomSubject: "自訂主旨",
|
||||
"To Email": "收件人",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook 網址",
|
||||
wayToGetDiscordURL: "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||
"Bot Display Name": "機器人顯示名稱",
|
||||
"Prefix Custom Message": "前綴自訂訊息",
|
||||
"Hello @everyone is...": "Hello {'@'}everyone is...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook 網址",
|
||||
wayToGetTeamsURL: "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。",
|
||||
signal: "Signal",
|
||||
Number: "號碼",
|
||||
Recipients: "收件人",
|
||||
needSignalAPI: "您需要有 REST API 的 Signal 客戶端。",
|
||||
wayToCheckSignalURL: "您可以前往下列網址以了解如何設定:",
|
||||
signalImportant: "注意: 不得混合收件人的群組和號碼!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "應用程式權杖",
|
||||
"Server URL": "伺服器網址",
|
||||
Priority: "優先度",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Emoji 圖示",
|
||||
"Channel Name": "頻道名稱",
|
||||
"Uptime Kuma URL": "Uptime Kuma 網址",
|
||||
aboutWebhooks: "更多關於 Webhook 的資訊: {0}",
|
||||
aboutChannelName: "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道",
|
||||
aboutKumaURL: "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。",
|
||||
emojiCheatSheet: "Emoji 一覽表: {0}",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (支援 50 種以上的通知服務)",
|
||||
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": "簡訊類型",
|
||||
octopushTypePremium: "Premium (快速 - 建議用於警報)",
|
||||
octopushTypeLowCost: "Low Cost (緩慢 - 有時會被營運商阻擋)",
|
||||
checkPrice: "查看 {0} 價格:",
|
||||
apiCredentials: "API 認證",
|
||||
octopushLegacyHint: "您使用的是舊版的 Octopush (2011-2020) 還是新版?",
|
||||
"Check octopush prices": "查看 octopush 價格 {0}。",
|
||||
octopushPhoneNumber: "電話號碼 (intl 格式,例如:+33612345678) ",
|
||||
octopushSMSSender: "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea 裝置 ID",
|
||||
"Apprise URL": "Apprise 網址",
|
||||
"Example:": "範例:{0}",
|
||||
"Read more:": "深入瞭解:{0}",
|
||||
"Status:": "狀態:{0}",
|
||||
"Read more": "深入瞭解",
|
||||
appriseInstalled: "已安裝 Apprise。",
|
||||
appriseNotInstalled: "尚未安裝 Apprise。{0}",
|
||||
"Access Token": "存取權杖",
|
||||
"Channel access token": "頻道存取權杖",
|
||||
"Line Developers Console": "Line 開發者控制台",
|
||||
lineDevConsoleTo: "Line 開發者控制台 - {0}",
|
||||
"Basic Settings": "基本設定",
|
||||
"User ID": "使用者 ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。",
|
||||
"Icon URL": "圖示網址",
|
||||
aboutIconURL: "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
|
||||
aboutMattermostChannelName: "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFlash: "SMS FLASH - 訊息會自動在收件人的裝置上顯示。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFull: "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。",
|
||||
promosmsTypeSpeed: "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。",
|
||||
promosmsPhoneNumber: "電話號碼 (若收件人位於波蘭則無需輸入區域代碼)",
|
||||
promosmsSMSSender: "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS",
|
||||
"Feishu WebHookUrl": "飛書 WebHook 網址",
|
||||
matrixHomeserverURL: "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
matrixDesc1: "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。",
|
||||
matrixDesc2: "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖",
|
||||
Method: "方法",
|
||||
Body: "主體",
|
||||
Headers: "標頭",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "要求標頭不是有效的 JSON:",
|
||||
BodyInvalidFormat: "請求主體不是有效的 JSON:",
|
||||
"Monitor History": "監測器歷史紀錄",
|
||||
clearDataOlderThan: "保留 {0} 天內的監測器歷史紀錄。",
|
||||
PasswordsDoNotMatch: "密碼不相符。",
|
||||
records: "記錄",
|
||||
"One record": "一項記錄",
|
||||
"Showing {from} to {to} of {count} records": "正在顯示 {count} 項記錄中的 {from} 至 {to} 項",
|
||||
steamApiKeyDescription: "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰:",
|
||||
"Current User": "目前使用者",
|
||||
};
|
@@ -29,7 +29,7 @@
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="$root.loggedIn" class="nav-item">
|
||||
<router-link to="/settings" class="nav-link">
|
||||
<router-link to="/settings" class="nav-link" :class="{ active: $route.path.includes('settings') }">
|
||||
<font-awesome-icon icon="cog" /> {{ $t("Settings") }}
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -188,8 +188,8 @@ main {
|
||||
|
||||
.dark {
|
||||
header {
|
||||
background-color: #161b22;
|
||||
border-bottom-color: #161b22 !important;
|
||||
background-color: $dark-header-bg;
|
||||
border-bottom-color: $dark-header-bg !important;
|
||||
|
||||
span {
|
||||
color: #f0f6fc;
|
||||
|
@@ -12,6 +12,7 @@ import mobile from "./mixins/mobile";
|
||||
import publicMixin from "./mixins/public";
|
||||
import socket from "./mixins/socket";
|
||||
import theme from "./mixins/theme";
|
||||
import lang from "./mixins/lang";
|
||||
import { router } from "./router";
|
||||
import { appName } from "./util.ts";
|
||||
|
||||
@@ -22,6 +23,7 @@ const app = createApp({
|
||||
mobile,
|
||||
datetime,
|
||||
publicMixin,
|
||||
lang,
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
|
33
src/mixins/lang.js
Normal file
33
src/mixins/lang.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { currentLocale } from "../i18n";
|
||||
import { setPageLocale } from "../util-frontend";
|
||||
const langModules = import.meta.glob("../languages/*.js");
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
language: currentLocale(),
|
||||
};
|
||||
},
|
||||
|
||||
async created() {
|
||||
if (this.language !== "en") {
|
||||
await this.changeLang(this.language);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
async language(lang) {
|
||||
await this.changeLang(lang);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async changeLang(lang) {
|
||||
let message = (await langModules["../languages/" + lang + ".js"]()).default;
|
||||
this.$i18n.setLocaleMessage(lang, message);
|
||||
this.$i18n.locale = lang;
|
||||
localStorage.locale = lang;
|
||||
setPageLocale();
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,5 +1,6 @@
|
||||
import { io } from "socket.io-client";
|
||||
import { useToast } from "vue-toastification";
|
||||
import jwt_decode from "jwt-decode";
|
||||
const toast = useToast();
|
||||
|
||||
let socket;
|
||||
@@ -217,6 +218,15 @@ export default {
|
||||
return (this.remember) ? localStorage : sessionStorage;
|
||||
},
|
||||
|
||||
getJWTPayload() {
|
||||
const jwtToken = this.$root.storage().token;
|
||||
|
||||
if (jwtToken && jwtToken !== "autoLogin") {
|
||||
return jwt_decode(jwtToken);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getSocket() {
|
||||
return socket;
|
||||
},
|
||||
@@ -265,10 +275,10 @@ export default {
|
||||
},
|
||||
|
||||
logout() {
|
||||
socket.emit("logout", () => { });
|
||||
this.storage().removeItem("token");
|
||||
this.socket.token = null;
|
||||
this.loggedIn = false;
|
||||
|
||||
this.clearData();
|
||||
},
|
||||
|
||||
@@ -328,6 +338,10 @@ export default {
|
||||
clearStatistics(callback) {
|
||||
socket.emit("clearStatistics", callback);
|
||||
},
|
||||
|
||||
getMonitorBeats(monitorID, period, callback) {
|
||||
socket.emit("getMonitorBeats", monitorID, period, callback);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@@ -83,17 +83,10 @@ export default {
|
||||
perPage: 25,
|
||||
heartBeatList: [],
|
||||
paginationConfig: {
|
||||
texts:{
|
||||
count:`${this.$t("Showing {from} to {to} of {count} records")}|{count} ${this.$t("records")}|${this.$t("One record")}`,
|
||||
first:this.$t("First"),
|
||||
last:this.$t("Last"),
|
||||
nextPage:'>',
|
||||
nextChunk:'>>',
|
||||
prevPage:'<',
|
||||
prevChunk:'<<'
|
||||
}
|
||||
}
|
||||
}
|
||||
hideCount: true,
|
||||
chunksNavigation: "scroll",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
stats() {
|
||||
@@ -106,7 +99,7 @@ export default {
|
||||
|
||||
for (let monitorID in this.$root.monitorList) {
|
||||
let beat = this.$root.lastHeartbeatList[monitorID];
|
||||
let monitor = this.$root.monitorList[monitorID]
|
||||
let monitor = this.$root.monitorList[monitorID];
|
||||
|
||||
if (monitor && ! monitor.active) {
|
||||
result.pause++;
|
||||
@@ -132,7 +125,7 @@ export default {
|
||||
let result = [];
|
||||
|
||||
for (let monitorID in this.$root.importantHeartbeatList) {
|
||||
let list = this.$root.importantHeartbeatList[monitorID]
|
||||
let list = this.$root.importantHeartbeatList[monitorID];
|
||||
result = result.concat(list);
|
||||
}
|
||||
|
||||
@@ -140,7 +133,7 @@ export default {
|
||||
let monitor = this.$root.monitorList[beat.monitorID];
|
||||
|
||||
if (monitor) {
|
||||
beat.name = monitor.name
|
||||
beat.name = monitor.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +160,7 @@ export default {
|
||||
return this.heartBeatList.slice(startIndex, endIndex);
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -209,16 +209,9 @@ export default {
|
||||
toggleCertInfoBox: false,
|
||||
showPingChartBox: true,
|
||||
paginationConfig: {
|
||||
texts: {
|
||||
count: `${this.$t("Showing {from} to {to} of {count} records")}|{count} ${this.$t("records")}|${this.$t("One record")}`,
|
||||
first: this.$t("First"),
|
||||
last: this.$t("Last"),
|
||||
nextPage: ">",
|
||||
nextChunk: ">>",
|
||||
prevPage: "<",
|
||||
prevChunk: "<<"
|
||||
}
|
||||
}
|
||||
hideCount: true,
|
||||
chunksNavigation: "scroll",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@@ -103,7 +103,7 @@
|
||||
:close-on-select="true"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="false"
|
||||
placeholder="Pick a RR-Type..."
|
||||
:placeholder="$t('Pick a RR-Type...')"
|
||||
:preselect-first="false"
|
||||
:max-height="500"
|
||||
:taggable="false"
|
||||
@@ -177,7 +177,7 @@
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
placeholder="Pick Accepted Status Codes..."
|
||||
:placeholder="$t('Pick Accepted Status Codes...')"
|
||||
:preselect-first="false"
|
||||
:max-height="600"
|
||||
:taggable="true"
|
||||
@@ -194,7 +194,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-5 mb-1">
|
||||
<button class="btn btn-primary" type="submit" :disabled="processing">{{ $t("Save") }}</button>
|
||||
<button id="monitor-submit-btn" class="btn btn-primary" type="submit" :disabled="processing">{{ $t("Save") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
|
||||
</label>
|
||||
|
||||
<span v-if="notification.isDefault == true" class="badge bg-primary ms-2">Default</span>
|
||||
<span v-if="notification.isDefault == true" class="badge bg-primary ms-2">{{ $t("Default") }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
|
||||
@@ -265,6 +265,19 @@
|
||||
<label for="headers" class="form-label">{{ $t("Headers") }}</label>
|
||||
<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>
|
||||
|
||||
<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" class="form-control" :placeholder="$t('Password')">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -340,11 +353,17 @@ export default {
|
||||
},
|
||||
|
||||
bodyPlaceholder() {
|
||||
return "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}";
|
||||
return this.$t("Example:", [`
|
||||
{
|
||||
"key": "value"
|
||||
}`]);
|
||||
},
|
||||
|
||||
headersPlaceholder() {
|
||||
return "{\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n}";
|
||||
return this.$t("Example:", [`
|
||||
{
|
||||
"HeaderName": "HeaderValue"
|
||||
}`]);
|
||||
}
|
||||
|
||||
},
|
||||
@@ -472,7 +491,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// Beautiful the JSON format
|
||||
// Beautify the JSON format
|
||||
if (this.monitor.body) {
|
||||
this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4);
|
||||
}
|
||||
|
@@ -1,460 +1,85 @@
|
||||
<template>
|
||||
<transition name="slide-fade" appear>
|
||||
<div>
|
||||
<h1 v-show="show" class="mb-3">
|
||||
{{ $t("Settings") }}
|
||||
</h1>
|
||||
<div>
|
||||
<h1 v-show="show" class="mb-3">
|
||||
{{ $t("Settings") }}
|
||||
</h1>
|
||||
|
||||
<div class="shadow-box">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2 class="mb-2">{{ $t("Appearance") }}</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="language" class="form-label">{{ $t("Language") }}</label>
|
||||
<select id="language" v-model="$i18n.locale" class="form-select">
|
||||
<option v-for="(lang, i) in $i18n.availableLocales" :key="`Lang${i}`" :value="lang">
|
||||
{{ $i18n.messages[lang].languageName }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="shadow-box">
|
||||
<div class="row">
|
||||
<div class="settings-menu">
|
||||
<router-link
|
||||
v-for="(item, key) in subMenus"
|
||||
:key="key"
|
||||
:to="`/settings/${key}`"
|
||||
>
|
||||
<div class="menu-item">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
|
||||
|
||||
<div>
|
||||
<div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
|
||||
<input id="btncheck1" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="light">
|
||||
<label class="btn btn-outline-primary" for="btncheck1">{{ $t("Light") }}</label>
|
||||
|
||||
<input id="btncheck2" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="dark">
|
||||
<label class="btn btn-outline-primary" for="btncheck2">{{ $t("Dark") }}</label>
|
||||
|
||||
<input id="btncheck3" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="auto">
|
||||
<label class="btn btn-outline-primary" for="btncheck3">{{ $t("Auto") }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
|
||||
<div>
|
||||
<div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
|
||||
<input id="btncheck4" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="normal">
|
||||
<label class="btn btn-outline-primary" for="btncheck4">{{ $t("Normal") }}</label>
|
||||
|
||||
<input id="btncheck5" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="bottom">
|
||||
<label class="btn btn-outline-primary" for="btncheck5">{{ $t("Bottom") }}</label>
|
||||
|
||||
<input id="btncheck6" v-model="$root.userHeartbeatBar" type="radio" class="btn-check" name="heartbeatBarTheme" autocomplete="off" value="none">
|
||||
<label class="btn btn-outline-primary" for="btncheck6">{{ $t("None") }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- General Settings -->
|
||||
<h2 class="mt-5 mb-2">{{ $t("General") }}</h2>
|
||||
|
||||
<form class="mb-3" @submit.prevent="saveGeneral">
|
||||
<!-- Timezone -->
|
||||
<div class="mb-4">
|
||||
<label for="timezone" class="form-label">{{ $t("Timezone") }}</label>
|
||||
<select id="timezone" v-model="$root.userTimezone" class="form-select">
|
||||
<option value="auto">
|
||||
{{ $t("Auto") }}: {{ guessTimezone }}
|
||||
</option>
|
||||
<option v-for="(timezone, index) in timezoneList" :key="index" :value="timezone.value">
|
||||
{{ timezone.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Search Engine -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">{{ $t("Search Engine Visibility") }}</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input id="searchEngineIndexYes" v-model="settings.searchEngineIndex" class="form-check-input" type="radio" name="flexRadioDefault" :value="true" required>
|
||||
<label class="form-check-label" for="searchEngineIndexYes">
|
||||
{{ $t("Allow indexing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input id="searchEngineIndexNo" v-model="settings.searchEngineIndex" class="form-check-input" type="radio" name="flexRadioDefault" :value="false" required>
|
||||
<label class="form-check-label" for="searchEngineIndexNo">
|
||||
{{ $t("Discourage search engines from indexing site") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entry Page -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">{{ $t("Entry Page") }}</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input id="entryPageYes" v-model="settings.entryPage" class="form-check-input" type="radio" name="statusPage" value="dashboard" required>
|
||||
<label class="form-check-label" for="entryPageYes">
|
||||
{{ $t("Dashboard") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input id="entryPageNo" v-model="settings.entryPage" class="form-check-input" type="radio" name="statusPage" value="statusPage" required>
|
||||
<label class="form-check-label" for="entryPageNo">
|
||||
{{ $t("Status Page") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Base URL -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="primaryBaseURL">{{ $t("Primary Base URL") }}</label>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://" pattern="https?://.+">
|
||||
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">Auto Get</button>
|
||||
</div>
|
||||
|
||||
<div class="form-text">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Steam API Key -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="steamAPIKey">{{ $t("Steam API Key") }}</label>
|
||||
<input id="steamAPIKey" v-model="settings.steamAPIKey" class="form-control" name="steamAPIKey">
|
||||
<div class="form-text">
|
||||
{{ $t("steamApiKeyDescription") }}<a href="https://steamcommunity.com/dev" target="_blank">https://steamcommunity.com/dev</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Monitor History -->
|
||||
<div class="mb-4">
|
||||
<h4 class="mt-4">{{ $t("Monitor History") }}</h4>
|
||||
<div class="mt-2">
|
||||
<label for="keepDataPeriodDays" class="form-label">{{ $t("clearDataOlderThan", [ settings.keepDataPeriodDays ]) }}</label>
|
||||
<input id="keepDataPeriodDays" v-model="settings.keepDataPeriodDays" type="number" class="form-control" required min="1" step="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<template v-if="loaded">
|
||||
<!-- Change Password -->
|
||||
<template v-if="! settings.disableAuth">
|
||||
<h2 class="mt-5 mb-2">{{ $t("Change Password") }}</h2>
|
||||
<form class="mb-3" @submit.prevent="savePassword">
|
||||
<div class="mb-3">
|
||||
<label for="current-password" class="form-label">{{ $t("Current Password") }}</label>
|
||||
<input id="current-password" v-model="password.currentPassword" type="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new-password" class="form-label">{{ $t("New Password") }}</label>
|
||||
<input id="new-password" v-model="password.newPassword" type="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="repeat-new-password" class="form-label">{{ $t("Repeat New Password") }}</label>
|
||||
<input id="repeat-new-password" v-model="password.repeatNewPassword" type="password" class="form-control" :class="{ 'is-invalid' : invalidPassword }" required>
|
||||
<div class="invalid-feedback">
|
||||
{{ $t("passwordNotMatchMsg") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{{ $t("Update Password") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<div v-if="! settings.disableAuth" class="mt-5 mb-3">
|
||||
<h2 class="mb-2">
|
||||
{{ $t("Two Factor Authentication") }}
|
||||
</h2>
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.TwoFADialog.show()">{{ $t("2FA Settings") }}</button>
|
||||
</div>
|
||||
|
||||
<h2 class="mt-5 mb-2">{{ $t("Export Backup") }}</h2>
|
||||
|
||||
<p>
|
||||
{{ $t("backupDescription") }} <br />
|
||||
({{ $t("backupDescription2") }}) <br />
|
||||
</p>
|
||||
|
||||
<div class="mb-2">
|
||||
<button class="btn btn-primary" @click="downloadBackup">{{ $t("Export") }}</button>
|
||||
</div>
|
||||
|
||||
<p><strong>{{ $t("backupDescription3") }}</strong></p>
|
||||
|
||||
<h2 class="mt-5 mb-2">{{ $t("Import Backup") }}</h2>
|
||||
|
||||
<label class="form-label">{{ $t("Options") }}:</label>
|
||||
<br>
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="radioKeep" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="keep">
|
||||
<label class="form-check-label" for="radioKeep">{{ $t("Keep both") }}</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="radioSkip" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="skip">
|
||||
<label class="form-check-label" for="radioSkip">{{ $t("Skip existing") }}</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="radioOverwrite" v-model="importHandle" class="form-check-input" type="radio" name="radioImportHandle" value="overwrite">
|
||||
<label class="form-check-label" for="radioOverwrite">{{ $t("Overwrite") }}</label>
|
||||
</div>
|
||||
<div class="form-text mb-2">
|
||||
{{ $t("importHandleDescription") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<input id="importBackup" type="file" class="form-control" accept="application/json">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-2 justify-content-end">
|
||||
<button type="button" class="btn btn-outline-primary" :disabled="processing" @click="confirmImport">
|
||||
<div v-if="processing" class="spinner-border spinner-border-sm me-1"></div>
|
||||
{{ $t("Import") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="importAlert" class="alert alert-danger mt-3" style="padding: 6px 16px;">
|
||||
{{ importAlert }}
|
||||
</div>
|
||||
|
||||
<h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button>
|
||||
<button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
|
||||
<button v-if="! settings.disableAuth" class="btn btn-danger me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button>
|
||||
<button class="btn btn-outline-danger me-1 mb-1" @click="confirmClearStatistics">{{ $t("Clear all statistics") }}</button>
|
||||
</div>
|
||||
</template>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<div class="settings-content-header">
|
||||
{{ subMenus[currentPage].title }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div v-if="$root.isMobile" class="mt-3" />
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="notification-list ">
|
||||
<h2>{{ $t("Notifications") }}</h2>
|
||||
<p v-if="$root.notificationList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $t("notificationDescription") }}
|
||||
</p>
|
||||
|
||||
<ul class="list-group mb-3" style="border-radius: 1rem;">
|
||||
<li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
|
||||
{{ notification.name }}<br>
|
||||
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
|
||||
{{ $t("Setup Notification") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<h2 class="mt-5">{{ $t("Info") }}</h2>
|
||||
|
||||
{{ $t("Version") }}: {{ $root.info.version }} <br />
|
||||
<a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a>
|
||||
<div class="mx-3">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="slide-fade" appear>
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NotificationDialog ref="notificationDialog" />
|
||||
<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 === '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 === '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-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>Si è certi di voler <strong>disabilitare l'autenticazione</strong>?</p>
|
||||
<p>È per <strong>chi ha l'autenticazione gestita da terze parti</strong> messa 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 === '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>
|
||||
|
||||
<!-- English (en) -->
|
||||
<template v-else>
|
||||
<p>Are you sure want to <strong>disable auth</strong>?</p>
|
||||
<p>It is for <strong>someone who have 3rd-party auth</strong> in front of Uptime Kuma such as Cloudflare Access.</p>
|
||||
<p>Please use it carefully.</p>
|
||||
</template>
|
||||
</Confirm>
|
||||
|
||||
<Confirm ref="confirmClearStatistics" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="clearStatistics">
|
||||
{{ $t("confirmClearStatisticsMsg") }}
|
||||
</Confirm>
|
||||
<Confirm ref="confirmImport" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="importBackup">
|
||||
{{ $t("confirmImportMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../components/Confirm.vue";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import NotificationDialog from "../components/NotificationDialog.vue";
|
||||
import TwoFADialog from "../components/TwoFADialog.vue";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
import { timezoneList, setPageLocale } from "../util-frontend";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotificationDialog,
|
||||
TwoFADialog,
|
||||
Confirm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
timezoneList: timezoneList(),
|
||||
guessTimezone: dayjs.tz.guess(),
|
||||
show: true,
|
||||
invalidPassword: false,
|
||||
password: {
|
||||
currentPassword: "",
|
||||
newPassword: "",
|
||||
repeatNewPassword: "",
|
||||
},
|
||||
settings: {
|
||||
|
||||
},
|
||||
loaded: false,
|
||||
importAlert: null,
|
||||
importHandle: "skip",
|
||||
processing: false,
|
||||
settings: {},
|
||||
settingsLoaded: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
"password.repeatNewPassword"() {
|
||||
this.invalidPassword = false;
|
||||
|
||||
computed: {
|
||||
currentPage() {
|
||||
let pathEnd = useRoute().path.split("/").at(-1);
|
||||
if (pathEnd == "settings" || pathEnd == null) {
|
||||
return "general";
|
||||
}
|
||||
return pathEnd;
|
||||
},
|
||||
|
||||
"$i18n.locale"() {
|
||||
localStorage.locale = this.$i18n.locale;
|
||||
setPageLocale();
|
||||
subMenus() {
|
||||
return {
|
||||
general: {
|
||||
title: this.$t("General"),
|
||||
},
|
||||
appearance: {
|
||||
title: this.$t("Appearance"),
|
||||
},
|
||||
notifications: {
|
||||
title: this.$t("Notifications"),
|
||||
},
|
||||
"monitor-history": {
|
||||
title: this.$t("Monitor History"),
|
||||
},
|
||||
security: {
|
||||
title: this.$t("Security"),
|
||||
},
|
||||
backup: {
|
||||
title: this.$t("Backup"),
|
||||
},
|
||||
about: {
|
||||
title: this.$t("About"),
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -463,27 +88,6 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
saveGeneral() {
|
||||
localStorage.timezone = this.$root.userTimezone;
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
savePassword() {
|
||||
if (this.password.newPassword !== this.password.repeatNewPassword) {
|
||||
this.invalidPassword = true;
|
||||
} else {
|
||||
this.$root.getSocket().emit("changePassword", this.password, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
if (res.ok) {
|
||||
this.password.currentPassword = "";
|
||||
this.password.newPassword = "";
|
||||
this.password.repeatNewPassword = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadSettings() {
|
||||
this.$root.getSocket().emit("getSettings", (res) => {
|
||||
this.settings = res.data;
|
||||
@@ -500,7 +104,7 @@ export default {
|
||||
this.settings.keepDataPeriodDays = 180;
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
this.settingsLoaded = true;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -510,89 +114,6 @@ export default {
|
||||
this.loadSettings();
|
||||
});
|
||||
},
|
||||
|
||||
confirmDisableAuth() {
|
||||
this.$refs.confirmDisableAuth.show();
|
||||
},
|
||||
|
||||
confirmClearStatistics() {
|
||||
this.$refs.confirmClearStatistics.show();
|
||||
},
|
||||
|
||||
confirmImport() {
|
||||
this.$refs.confirmImport.show();
|
||||
},
|
||||
|
||||
disableAuth() {
|
||||
this.settings.disableAuth = true;
|
||||
this.saveSettings();
|
||||
},
|
||||
|
||||
enableAuth() {
|
||||
this.settings.disableAuth = false;
|
||||
this.saveSettings();
|
||||
this.$root.storage().removeItem("token");
|
||||
},
|
||||
|
||||
downloadBackup() {
|
||||
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
|
||||
let fileName = `Uptime_Kuma_Backup_${time}.json`;
|
||||
let monitorList = Object.values(this.$root.monitorList);
|
||||
let exportData = {
|
||||
version: this.$root.info.version,
|
||||
notificationList: this.$root.notificationList,
|
||||
monitorList: monitorList,
|
||||
};
|
||||
exportData = JSON.stringify(exportData, null, 4);
|
||||
let downloadItem = document.createElement("a");
|
||||
downloadItem.setAttribute("href", "data:application/json;charset=utf-8," + encodeURIComponent(exportData));
|
||||
downloadItem.setAttribute("download", fileName);
|
||||
downloadItem.click();
|
||||
},
|
||||
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("importBackup").files;
|
||||
|
||||
if (uploadItem.length <= 0) {
|
||||
this.processing = false;
|
||||
return this.importAlert = this.$t("alertNoFile");
|
||||
}
|
||||
|
||||
if (uploadItem.item(0).type !== "application/json") {
|
||||
this.processing = false;
|
||||
return this.importAlert = this.$t("alertWrongFileType");
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.readAsText(uploadItem.item(0));
|
||||
|
||||
fileReader.onload = item => {
|
||||
this.$root.uploadBackup(item.target.result, this.importHandle, (res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
clearStatistics() {
|
||||
this.$root.clearStatistics((res) => {
|
||||
if (res.ok) {
|
||||
this.$router.go();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
autoGetPrimaryBaseURL() {
|
||||
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -602,37 +123,7 @@ export default {
|
||||
|
||||
.shadow-box {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.btn-check:active + .btn-outline-primary,
|
||||
.btn-check:checked + .btn-outline-primary,
|
||||
.btn-check:hover + .btn-outline-primary {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
|
||||
.btn-check:active + .btn-outline-primary,
|
||||
.btn-check:checked + .btn-outline-primary,
|
||||
.btn-check:hover + .btn-outline-primary {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#importBackup {
|
||||
&::file-selector-button {
|
||||
color: $primary;
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: $dark-font-color2;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
min-height: calc(100vh - 155px);
|
||||
}
|
||||
|
||||
footer {
|
||||
@@ -642,4 +133,59 @@ footer {
|
||||
padding-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.settings-menu {
|
||||
flex: 0 0 auto;
|
||||
width: 300px;
|
||||
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
border-radius: 10px;
|
||||
margin: 0.5em;
|
||||
padding: 0.7em 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: $highlight-white;
|
||||
|
||||
.dark & {
|
||||
background: $dark-header-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.active .menu-item {
|
||||
background: $highlight-white;
|
||||
border-left: 4px solid $primary;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
.dark & {
|
||||
background: $dark-header-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
flex: 0 0 auto;
|
||||
width: calc(100% - 300px);
|
||||
|
||||
.settings-content-header {
|
||||
width: calc(100% + 20px);
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
border-radius: 0 10px 0 0;
|
||||
margin-top: -20px;
|
||||
margin-right: -20px;
|
||||
padding: 12.5px 1em;
|
||||
font-size: 26px;
|
||||
|
||||
.dark & {
|
||||
background: $dark-header-bg;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -75,7 +75,7 @@ export default {
|
||||
this.processing = true;
|
||||
|
||||
if (this.password !== this.repeatPassword) {
|
||||
toast.error("Repeat password do not match.");
|
||||
toast.error(this.$t("PasswordsDoNotMatch"));
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user