Compare commits

..

177 Commits
1.8.0 ... 1.9.1

Author SHA1 Message Date
Louis Lam
c6fc385289 update to 1.9.1 2021-10-19 00:20:27 +08:00
Louis Lam
c645658161 Merge remote-tracking branch 'origin/master' 2021-10-19 00:19:41 +08:00
Louis Lam
182597944d fix #721 2021-10-19 00:19:26 +08:00
Louis Lam
8eaa8116c3 update email 2021-10-18 23:55:00 +08:00
Louis Lam
3512faad14 move kubernetes folder to the k8s-unofficial branch 2021-10-18 23:38:29 +08:00
Louis Lam
f11417e854 [upload artifacts] no idea why suddenly not working via env var, hardcode the VERSION instead 2021-10-18 20:43:17 +08:00
Louis Lam
5f36d2acda fix upload artifacts 2021-10-18 20:02:50 +08:00
Louis Lam
cc36ff5210 update to 1.9.0 2021-10-18 19:46:15 +08:00
Louis Lam
c363d3374e fix "build-docker-nightly-alpine" wrong path 2021-10-18 19:45:50 +08:00
Louis Lam
65a8cb5307 Merge pull request #738 from NixNotCastey/promosms
PromoSMS - Fixed values for sms type
2021-10-18 18:28:58 +08:00
Lukas
f74b2662c5 Fixed values for sms type 2021-10-18 12:02:54 +02:00
Louis Lam
6e18f39eb4 [steam] code cleanup 2021-10-18 17:15:28 +08:00
Louis Lam
68d44dd9b3 [steam] do not request if there is no steam api key 2021-10-18 17:11:41 +08:00
Louis Lam
20d59e5a13 fix and move the steam api key to settings page 2021-10-18 17:02:05 +08:00
Louis Lam
ae31eb6ba9 Merge branch 'master' into Revyn112_master
# Conflicts:
#	server/model/monitor.js
#	src/languages/en.js
#	src/pages/EditMonitor.vue
2021-10-18 15:50:35 +08:00
Louis Lam
df5efcc71c Merge pull request #730 from sargonas/sargonas-patch-1
Update README.md
2021-10-17 23:25:19 +08:00
J. Eckert
4cd66b20b1 Update README.md
additional spelling correction
2021-10-17 08:08:44 -07:00
J. Eckert
1276102c18 Update README.md
minor grammatical edits for slight improvements. (Honestly though, your grammar is not that bad at all for not being a native speaker!)
2021-10-16 23:07:25 -07:00
Louis Lam
6944b35ea7 Merge pull request #667 from zsxeee/i18n
Missing i18n and zh-CN translation
2021-10-16 22:47:58 +08:00
Louis Lam
88757ebbbe Merge pull request #722 from dpatrongomez/master
Add spanish translation for monitor history and records
2021-10-16 22:46:29 +08:00
Daniel Patrón Gómez
0a73b84ae6 Add records translations and fix pause translation 2021-10-16 12:45:41 +02:00
Daniel Patrón Gómez
15f36f96c3 Add spanish translation for monitor history 2021-10-16 12:15:39 +02:00
Louis Lam
edcaf93446 Merge pull request #721 from dpatrongomez/master
Add Status Translation
2021-10-16 17:41:46 +08:00
Louis Lam
dec175d55f Merge pull request #719 from bertyhell/bugfix/edit-monitor
fix(edit-monitor): no need to escape placeholder {} if not translated
2021-10-16 17:38:00 +08:00
Louis Lam
9a60f69f66 Merge pull request #720 from bertyhell/bugfix/fix-error-on-first-hearthbeat
fix(monitor): safely get status of previous beat if first beat
2021-10-16 17:34:44 +08:00
Daniel Patrón Gómez
53a008ae2b Add Status Translation 2021-10-16 11:32:15 +02:00
Bert Verhelst
1d63dd9ddd fix(monitor): safely get status of previous beat if first beat 2021-10-16 11:28:03 +02:00
Bert Verhelst
61627545a5 fix(edit-monitor): no need to escape placeholder {} if not translated 2021-10-16 11:26:32 +02:00
Louis Lam
176fa6b60d merge package-lock.json 2021-10-16 16:32:28 +08:00
Louis Lam
cb43ecb46e Merge branch 'master' into background-jobs
# Conflicts:
#	package-lock.json
#	package.json
#	src/languages/en.js
2021-10-16 15:06:59 +08:00
Louis Lam
2e24312f67 [test] jest please 2021-10-16 14:54:45 +08:00
Louis Lam
6ff3cb275e Merge pull request #642 from andreasbrett/patch-2
Harden 2FA/TOTP implementation according to rfc6238 (part 3)
2021-10-16 14:30:25 +08:00
Louis Lam
9d364b28b1 Merge pull request #715 from januridp/master
Fix(Language): Bahasa Indonesia (Indonesian)
2021-10-16 13:02:57 +08:00
Louis Lam
bc3e3f9118 [test] update 2021-10-16 13:02:04 +08:00
Louis Lam
0f3ab7b1d8 [test] increase the timeout for reset-password 2021-10-16 12:56:33 +08:00
januridp
d94fbede32 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:39:32 +07:00
januridp
76e619c066 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:36:07 +07:00
januridp
4e4f94ab98 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:29:51 +07:00
januridp
ed3a558397 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:15:01 +07:00
Louis Lam
a419aa527f Merge remote-tracking branch 'origin/master' 2021-10-16 01:33:56 +08:00
Louis Lam
4d26825cbe [test] reset-password 2021-10-16 01:33:44 +08:00
Louis Lam
7276f34d90 fix reset-password 2021-10-16 00:57:26 +08:00
Louis Lam
e1eeb44e7f Update SECURITY.md 2021-10-15 19:44:29 +08:00
Louis Lam
f4b8da0a5c Merge branch 'feature/add-support-for-method-body-and-headers' 2021-10-15 19:02:49 +08:00
Louis Lam
4178983df3 Merge remote-tracking branch 'origin/master' 2021-10-15 19:01:04 +08:00
Louis Lam
7ac0ab2e34 [http options] beautify the json format when clicked the save button 2021-10-15 18:57:27 +08:00
Louis Lam
cd211a6be7 [http options] fine tune 2021-10-15 18:36:40 +08:00
Louis Lam
4e71ab7406 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-15 16:07:05 +08:00
Louis Lam
76c68071f1 Merge pull request #709 from iooner/patch-1
Update fr-FR.js
2021-10-15 15:16:22 +08:00
iooner
8242a1586d Update fr-FR.js 2021-10-14 22:23:01 +02:00
Louis Lam
c593a962c2 Merge pull request #627 from NixNotCastey/smtp-subject
Add support for custom subject in emails
2021-10-15 00:54:31 +08:00
Louis Lam
c9b4d2ae2a Merge pull request #698 from erktime/master
Add monitor name context to Slack fallback text.
2021-10-14 23:31:48 +08:00
Louis Lam
37105d720b Merge pull request #706 from firattemel/master
Update tr-TR.js
2021-10-14 22:40:45 +08:00
Louis Lam
3b74b727f2 [Push Type] fix missing important flag and missing up notification 2021-10-14 22:32:15 +08:00
firattemel
2f0119bc3f Update tr-TR.js 2021-10-14 17:29:13 +03:00
Louis Lam
a7d2a34dae fix ping bug 2021-10-14 18:48:40 +08:00
Louis Lam
60acb91fc8 Merge pull request #687 from xjoker/master
Add new notification `Aliyun Sms` and `DingDing`
2021-10-14 17:02:23 +08:00
Louis Lam
f51156f18e run eslint for #687 2021-10-14 16:24:03 +08:00
Louis Lam
8338881927 [SMTP] change {{HOSTNAME}} to {{HOSTNAME_OR_URL}}, support for http montior type, some UI improvements 2021-10-14 16:07:25 +08:00
Louis Lam
674b387c95 Merge branch 'master' into smtp-subject 2021-10-14 14:59:54 +08:00
Louis Lam
5ff9a64e5e [Push Type] Fix missing duration calculation (#685) 2021-10-14 14:42:34 +08:00
Louis Lam
4bee57ea7f Merge remote-tracking branch 'giacomo892/patch-1'
# Conflicts:
#	server/ping-lite.js
2021-10-14 14:10:51 +08:00
Louis Lam
f75c9e4f0c add UPTIME_KUMA_HOST, UPTIME_KUMA_PORT and special handling for FreeBSD 2021-10-14 14:09:16 +08:00
xJoker
8ab4788f80 Update src/components/notifications/index.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 09:33:36 +08:00
xJoker
4e4ab0577e Update src/components/notifications/index.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 09:33:31 +08:00
xJoker
6e04ec436e Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:45 +08:00
xJoker
2d471a5e84 Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:33 +08:00
xJoker
cae194f58f Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:24 +08:00
Aaron Erkenswick
655ccc86b9 Add monitor name context to Slack fallback text.
The text block of a slack notification payload is used for mobile
devices and plain text previews. This change allows slack users to see
the name of the failing service without having to open up Slack to read
the entire message.
2021-10-13 11:47:23 -07:00
Louis Lam
e2dbacb383 Fix encoding problem of ping result for non-English Windows 2021-10-14 00:22:49 +08:00
Lukas
89b34b5748 Use double curly brackets and sanity check for customSubject 2021-10-13 18:05:18 +02:00
Louis Lam
9b05e86c25 set newLine to LF for ts compiler 2021-10-13 22:31:36 +08:00
Louis Lam
2ff7c4de5d [test] genSecret 2021-10-13 22:16:46 +08:00
Louis Lam
178e5cd2c0 Merge pull request #689 from hrtkpf/master
fix translations (de-DE and en)
2021-10-13 17:47:20 +08:00
hrtkpf
84507268ad fix translations (de-DE and en) 2021-10-13 11:13:51 +02:00
wuwenjing
843992c410 Add DingDing notification 2021-10-13 16:13:46 +08:00
zsxeee
33f773fcd0 Move param out of the translation file 2021-10-13 15:36:07 +08:00
zsxeee
26841a64f0 Update zh-CN.js 2021-10-13 15:10:59 +08:00
wuwenjing
57a76e6129 remove alicloud/pop-core keep simple 2021-10-13 14:41:59 +08:00
giacomo892
3fe3450533 Prioritize port passed from args
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-13 08:29:55 +02:00
Lukas
330cd6e058 Minor rehabilitanty impedyment
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-13 07:32:09 +02:00
wuwenjing
a2f2253221 Add aliyun sms notification 2021-10-13 11:55:01 +08:00
Louis Lam
1e5ce92917 Merge pull request #678 from wellart/master
add indonesian language
2021-10-13 11:23:21 +08:00
Louis Lam
0d7c2960b0 Merge pull request #683 from Saibamen/fix_markdown
Fix some of markdownlint warnings
2021-10-13 11:01:37 +08:00
Louis Lam
82343de972 Merge pull request #682 from Saibamen/patch-1
Fix build and tests
2021-10-13 10:54:30 +08:00
Adam Stachowicz
521d57c483 Fix some of markdownlint warnings 2021-10-13 02:01:34 +02:00
Adam Stachowicz
281671b938 Fix build
Add `cross-env` deleted in 11c3c636e0
2021-10-13 01:10:17 +02:00
Lukas
30d8aadf12 Slightly refactor 2021-10-12 23:24:34 +02:00
KangAlleW
2939bd4138 fix locale 2021-10-13 03:12:26 +07:00
KangAlleW
dcf15c3eb7 edit i18n.js 2021-10-13 02:58:09 +07:00
KangAlleW
7c9ed98408 rename id.js to id-ID.js 2021-10-13 02:57:36 +07:00
KangAlleW
4b9f0a3fe6 add indonesian language and edit settings.vue 2021-10-13 02:53:46 +07:00
KangAlleW
5b67fec084 add indonesian language 2021-10-13 02:46:11 +07:00
Louis Lam
407581ee07 move jest config files to config dir 2021-10-13 02:53:59 +08:00
Louis Lam
11c3c636e0 move dockerfile, docker-compose.yml to docker folder 2021-10-13 02:32:02 +08:00
Louis Lam
911d4ea37b move vite.config.js to config folder 2021-10-13 02:27:25 +08:00
Louis Lam
4039c6549e Merge remote-tracking branch 'origin/master' 2021-10-13 02:16:03 +08:00
giacomo892
d733ec018e Prioritize host arg
Otherwise launching the program with the --host argument does nothing
2021-10-12 19:37:58 +02:00
Nelson Chan
03b07730d3 Fix: Increase default kept period 2021-10-12 23:28:21 +08:00
Louis Lam
dbc87d8ab3 Merge pull request #670 from dhfhfk/master
Update ko-KR.js
2021-10-12 22:27:27 +08:00
dhfhfk
05b691d4c9 Fix typo 2021-10-12 20:39:15 +09:00
Louis Lam
029d6412da Merge pull request #669 from pemassi/patch-2
Update ko-KR.js
2021-10-12 18:27:11 +08:00
Louis Lam
9f12f95cce Merge pull request #666 from Rohlik/patch-1
Fix length
2021-10-12 17:32:20 +08:00
Kyungyoon Kim
c1112a32df Update ko-KR.js 2021-10-12 18:19:44 +09:00
Kyungyoon Kim
efc78acfeb Update ko-KR.js 2021-10-12 18:17:13 +09:00
zsxeee
9e01959d15 Update zh-CN.js 2021-10-12 16:30:39 +08:00
zsxeee
3fe91c52cb Fix i18n
Missing webhook json description
Ajust Telegram context-based sentence, (also changed translated language files)
Missing primary base url label
Wrong PromoSMS i18n
Missing Octopush legacy hint
Missing Matrix i18n
Missing push url i18n
2021-10-12 16:29:18 +08:00
Tomas Rohrer
5269dcec60 Fix length
In fact, it is 10 min demo :)
2021-10-12 10:21:03 +02:00
zsxeee
a2cc7d1db9 Avoid directory not found error 2021-10-12 15:10:32 +08:00
Louis Lam
18c5a16783 Update README.md 2021-10-12 11:15:02 +08:00
Andreas Brett
2538bd04ce notp verification defaults 2021-10-11 20:18:40 +02:00
Louis Lam
b7528b9a4e Merge pull request #657 from thomasleveil/patch-2
[i18n] minor fixes to french translations
2021-10-12 02:17:53 +08:00
Thomas LÉVEIL
9c058054b9 [i18n] minor update to french translations 2021-10-11 19:55:02 +02:00
Louis Lam
c6683e2a9b Merge pull request #648 from atlochowski/patch-1
Update pl.js
2021-10-12 00:04:33 +08:00
Louis Lam
b558708be2 Merge pull request #650 from xjoker/master
Add Feishu as notification provider
2021-10-12 00:02:29 +08:00
Louis Lam
fd0dd2d284 Merge pull request #652 from GhostSlayer/patch-1
Create PM2 Config file
2021-10-11 23:50:37 +08:00
xJoker
1bc77a06e5 Update server/notification-providers/feishu.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-11 20:38:32 +08:00
xJoker
69c623ac2b Update server/notification-providers/feishu.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-11 20:38:19 +08:00
Atlochowski
69ffee55dd Update pl.js 2021-10-11 14:33:00 +02:00
Slayer
d769c4426c Create PM2 Config file
I don't know if this is a good idea, but users that prefer to use PM2 instead, they can run `pm2 start` instead of `pm2 start server/server.js --name uptime-kuma` with this configuration
2021-10-11 15:22:52 +03:00
Atlochowski
ebf0671fef Update pl.js 2021-10-11 13:45:25 +02:00
Louis Lam
97af09fd50 Update README.md 2021-10-11 19:41:56 +08:00
Louis Lam
5b19e3f025 Update README.md 2021-10-11 19:40:51 +08:00
wuwenjing
ce2df137e6 change text to using variable msg 2021-10-11 17:53:13 +08:00
wuwenjing
6d9b71c054 Add Feishu notification 2021-10-11 17:20:09 +08:00
Atlochowski
a433de74e6 Update pl.js
small fix
2021-10-11 09:24:45 +02:00
LouisLam
8e3f43d60b Merge remote-tracking branch 'origin/master' 2021-10-11 13:28:42 +08:00
Louis Lam
2a1fd93444 Merge pull request #643 from andreasbrett/patch-3
translation fixes (german)
2021-10-11 12:55:25 +08:00
Andreas Brett
e223e826a3 linting 2021-10-11 01:02:54 +02:00
Andreas Brett
503d1f0a91 translation fixes 2021-10-10 23:20:56 +02:00
Andreas Brett
b5b391c73b avoid default values for token verification
override default values: window=1, window size=30 (see https://github.com/louislam/uptime-kuma/issues/640)
2021-10-10 22:13:18 +02:00
Louis Lam
ad0cde6554 Merge pull request #633 from dhfhfk/master
Update ko-KR
2021-10-11 01:04:32 +08:00
Louis Lam
25d18f0da3 Merge pull request #636 from DanielRTRD/add-norwegian-lang
Add Norwegian Language
2021-10-11 01:03:55 +08:00
RisedSky
e9445bb2e3 Merge pull request #1 from RisedSky/RisedSky-patch-FR-Lang
Update fr-FR.js (again)
2021-10-10 19:03:22 +02:00
RisedSky
ecc25ba596 Update fr-FR.js
Fixed wrong string
Also, "Primary Base URL" is missing from english file
2021-10-10 19:02:35 +02:00
LouisLam
e5286b0973 eslint for ko-KR.js 2021-10-11 00:52:46 +08:00
LouisLam
4e94cb9aad fix upload dist path 2021-10-11 00:51:18 +08:00
Daniel S. Billing
037fdd73a3 Norsk 2021-10-10 18:50:18 +02:00
Daniel S. Billing
bb9a936658 Translated some of the notifications services 2021-10-10 18:49:03 +02:00
Bert Verhelst
5445c2a2ff fix(monitor): revert unintentional change to comment 2021-10-10 18:41:29 +02:00
Bert Verhelst
dc08510e72 Merge remote-tracking branch 'origin/master' into feature/add-support-for-method-body-and-headers 2021-10-10 18:40:53 +02:00
Daniel S. Billing
62805014df Update Settings.vue 2021-10-10 18:38:19 +02:00
Daniel S. Billing
c79e80442a Update i18n.js 2021-10-10 18:38:15 +02:00
Daniel S. Billing
f0ff96afd9 Create nb-NO.js 2021-10-10 18:31:17 +02:00
Louis Lam
8083368a81 Merge pull request #634 from RisedSky/patch-1
Update fr-FR.js
2021-10-10 23:58:09 +08:00
RisedSky
afb75e07d5 Update fr-FR.js
Fixed string (Device => Appareil in French)
2021-10-10 17:37:05 +02:00
RisedSky
efd3822930 Update fr-FR.js
All the lines corresponds to the English version (sorted correctly)
Plus, updated all the translated strings
2021-10-10 17:33:02 +02:00
dhfhfk
2adac64c83 Update ko-KR.js 2021-10-11 00:02:37 +09:00
Louis
cee225bcb2 no idea why npm prune --production suddenly not working, switch to npm ci --production 2021-10-10 17:55:31 +08:00
Louis
8958c21736 Merge remote-tracking branch 'origin/master' 2021-10-10 17:41:54 +08:00
Louis Lam
4ba2025451 minor 2021-10-10 14:40:19 +08:00
Bert Verhelst
5137c80c07 fix(monitor): handle empty headers 2021-10-09 21:51:24 +02:00
Lukas
792f3c7c5c Add support for values of Name, Hostname and Status 2021-10-09 21:48:28 +02:00
Bert Verhelst
8a739af5ad Merge remote-tracking branch 'origin/master' into feature/add-support-for-method-body-and-headers 2021-10-09 21:44:22 +02:00
Lukas
edb75808d8 Merge branch 'louislam:master' into smtp-subject 2021-10-09 20:37:12 +02:00
Lukas
5e3ea3293c Very basic email subject customization 2021-10-09 20:32:45 +02:00
Nelson Chan
ac80631bcd Fix: Run clear data at specific time 2021-10-10 00:16:29 +08:00
Nelson Chan
8caf47988c Fix: Allow setting settings type 2021-10-10 00:16:13 +08:00
Nelson Chan
6cf2eb036d Fix: Improve settings layout and wording 2021-10-09 23:51:05 +08:00
Nelson Chan
dca5a59dbc Feat: Implement data clearing logic & frontend 2021-10-09 23:33:47 +08:00
Nelson Chan
656a4d6270 WIP: Enable background jobs
WIP: Remove better-sqlite3
2021-10-09 21:46:59 +08:00
Bert Verhelst
d71d27220b fix(edit-monitor): store headers as JSON 2021-10-09 12:42:32 +02:00
Bert Verhelst
fba4f86552 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-09 12:35:08 +02:00
Bert Verhelst
b8093e909b fix(edit-monitor): fix minification of translations containing { } 2021-10-09 11:38:12 +02:00
Bert Verhelst
c3c273f9df fix(edit-monitor): fix regex to allow a single header 2021-10-09 11:20:33 +02:00
Bert Verhelst
daab2a05f5 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-09 11:13:16 +02:00
Bert Verhelst
ec4b7e4064 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-07 18:22:59 +02:00
Bert Verhelst
8be4bf0e16 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-07 08:56:29 +02:00
Bert Verhelst
162ef04c41 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-06 21:56:28 +02:00
Bert Verhelst
a0ffa42b42 fix(translations): add translations for method body and headers to dutch 2021-10-05 18:21:31 +02:00
Bert Verhelst
550825927c Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-05 18:19:07 +02:00
Bert Verhelst
afeb424dc0 fix(edit-monitor): add translations to en.js 2021-10-05 09:20:24 +02:00
Bert Verhelst
6b44116245 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-05 08:54:40 +02:00
Bert Verhelst
7ee89fab5c fix(edit-monitor): Make json capitalised
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-04 11:29:43 +02:00
Bert Verhelst
3f0b85e5a8 feat(http-requests): add support for methods, body and headers for http 2021-10-02 16:48:27 +02:00
Denis Freund
efbadd0737 only allow ip address for hostname when monitor type is steam 2021-09-28 13:38:46 +02:00
Denis Freund
b67b4d5afd add steam gameserver for monitoring 2021-09-27 11:17:57 +02:00
74 changed files with 2914 additions and 700 deletions

View File

@@ -9,9 +9,8 @@ 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:

View File

@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
louis@uptimekuma.louislam.net.
uptime@kuma.pet.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View File

@@ -6,7 +6,7 @@ The project was created with vite.js (vue3). Then I created a sub-directory call
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.
# Key Technical Skills
## Key Technical Skills
- Node.js (You should know what are promise, async/await and arrow function etc.)
- Socket.io
@@ -15,7 +15,7 @@ The frontend code build into "dist" directory. The server (express.js) exposes t
- Bootstrap
- SQLite
# Directories
## Directories
- data (App data)
- dist (Frontend build)
@@ -25,50 +25,50 @@ The frontend code build into "dist" directory. The server (express.js) exposes t
- src (Frontend source code)
- test (unit test)
# Can I create a pull request for Uptime Kuma?
## 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.
If you are not sure, feel free to create an empty pull request draft first.
## Pull Request Examples
### Pull Request Examples
### ✅ High - Medium Priority
#### ✅ High - Medium Priority
- Add a new notification
- Add a chart
- Fix a bug
- Translations
### *️⃣ Requires one more reviewer
#### *️⃣ Requires one more reviewer
I do not have such knowledge to test it.
- Add k8s supports
### *️⃣ Low Priority
#### *️⃣ Low Priority
It changed my current workflow and require further studies.
- Change my release approach
### ❌ Won't Merge
#### ❌ Won't Merge
- Duplicated pull request
- Buggy
- Existing logic is completely modified or deleted
- A function that is completely out of scope
# Project Styles
## Project Styles
I personally do not like something need to learn so much and need to config so much before you can finally start the app.
- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort to get it run
- Single container for Docker users, no very complex docker-composer file. Just map the volume and expose the port, then good to go
- Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go
- Settings should be configurable in the frontend. Env var is not encouraged.
- Easy to use
# Coding Styles
## Coding Styles
- 4 spaces indentation
- Follow `.editorconfig`
@@ -80,20 +80,20 @@ I personally do not like something need to learn so much and need to config so m
- SQLite: underscore_type
- CSS/SCSS: dash-type
# Tools
## Tools
- Node.js >= 14
- Git
- IDE that supports ESLint and EditorConfig (I am using Intellji Idea)
- A SQLite tool (SQLite Expert Personal is suggested)
# Install dependencies
## Install dependencies
```bash
npm ci
```
# How to start the Backend Dev Server
## How to start the Backend Dev Server
(2021-09-23 Update)
@@ -103,7 +103,7 @@ npm run start-server-dev
It binds to `0.0.0.0:3001` by default.
## Backend Details
### Backend Details
It is mainly a socket.io app + express.js.
@@ -116,24 +116,26 @@ express.js is just used for serving the frontend built files (index.html, .js an
- scoket-handler (Socket.io Handlers)
- server.js (Server main logic)
# How to start the Frontend Dev Server
## How to start the Frontend Dev Server
1. Set the env var `NODE_ENV` to "development".
2. Start the frontend dev server by the following command.
```bash
npm run dev
```
It binds to `0.0.0.0:3000` by default.
You can use Vue.js devtools Chrome extension for debugging.
## Build the frontend
### Build the frontend
```bash
npm run build
```
## Frontend Details
### Frontend Details
Uptime Kuma Frontend is a single page application (SPA). Most paths are handled by Vue Router.
@@ -143,24 +145,23 @@ As you can see, most data in frontend is stored in root level, even though you c
The data and socket logic are in `src/mixins/socket.js`.
# Database Migration
## Database Migration
1. Create `patch-{name}.sql` in `./db/`
2. Add your patch filename in the `patchList` list in `./server/database.js`
# Unit Test
## Unit Test
It is an end-to-end testing. It is using Jest and Puppeteer.
```
```bash
npm run build
npm test
```
By default, the Chromium window will be shown up during the test. Specifying `HEADLESS_TEST=1` for terminal environments.
# Update Dependencies
## Update Dependencies
Install `ncu`
https://github.com/raineorshine/npm-check-updates
@@ -170,10 +171,10 @@ 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
Patch release = the third digit ([Semantic Versioning](https://semver.org/))
# Translations
## Translations
Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages

View File

@@ -2,7 +2,6 @@
<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>
<div align="center" width="100%">
<img src="./public/icon.svg" width="128" alt="" />
</div>
@@ -17,17 +16,20 @@ Try it!
https://demo.uptime.kuma.pet
It is a 5 minutes live demo, all data will be deleted after that. The server is located at Tokyo, if you live far away from here, it may affact your experience. I suggest that you should install to try it.
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.
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.
* Monitoring uptime for HTTP(s) / TCP / Ping / DNS Record / Push.
* 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/issues/284).
* 20 seconds interval.
* 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.
* [Multi Languages](https://github.com/louislam/uptime-kuma/tree/master/src/languages)
* Simple Status Page
* Ping Chart
* Certificate Info
## 🔧 How to Install
@@ -38,7 +40,7 @@ docker volume create uptime-kuma
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
```
Browse to http://localhost:3001 after started.
Browse to http://localhost:3001 after starting.
### 💪🏻 Without Docker
@@ -56,11 +58,11 @@ npm run setup
node server/server.js
# (Recommended) Option 2. Run in background using PM2
# Install PM2 if you don't have: npm install pm2 -g
# Install PM2 if you don't have it: npm install pm2 -g
pm2 start server/server.js --name uptime-kuma
```
Browse to http://localhost:3001 after started.
Browse to http://localhost:3001 after starting.
### Advanced Installation
@@ -116,11 +118,13 @@ If you love this project, please consider giving me a ⭐.
## 🗣️ Discussion
### Issues Page
You can discuss or ask for help in [Issues](https://github.com/louislam/uptime-kuma/issues).
### Subreddit
My Reddit account: louislamlam
You can mention me if you ask question on Reddit.
You can mention me if you ask a question on Reddit.
https://www.reddit.com/r/UptimeKuma/
## Contribute

View File

@@ -5,13 +5,15 @@
Use this section to tell people about which versions of your project are
currently being supported with security updates.
#### Uptime Kuma Versions:
### Uptime Kuma Versions
| Version | Supported |
| ------- | ------------------ |
| 1.7.X | :white_check_mark: |
| < 1.7 | |
| 1.8.X | :white_check_mark: |
| <= 1.7.X | ❌ |
### Upgradable Docker Tags
#### Upgradable Docker Tags:
| Tag | Supported |
| ------- | ------------------ |
| 1 | :white_check_mark: |
@@ -23,6 +25,7 @@ currently being supported with security updates.
| 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.

View File

@@ -4,4 +4,8 @@ if (process.env.TEST_FRONTEND) {
config.presets = ["@babel/preset-env"];
}
if (process.env.TEST_BACKEND) {
config.plugins = ["babel-plugin-rewire"];
}
module.exports = config;

View File

@@ -0,0 +1,5 @@
module.exports = {
"rootDir": "..",
"testRegex": "./test/backend.spec.js",
};

View File

@@ -1,5 +1,5 @@
module.exports = {
"rootDir": ".",
"rootDir": "..",
"testRegex": "./test/frontend.spec.js",
};

View File

@@ -5,7 +5,7 @@ module.exports = {
"__DEV__": true
},
"testRegex": "./test/e2e.spec.js",
"rootDir": ".",
"rootDir": "..",
"testTimeout": 30000,
};

View File

@@ -1,9 +1,9 @@
import legacy from "@vitejs/plugin-legacy"
import vue from "@vitejs/plugin-vue"
import { defineConfig } from "vite"
import legacy from "@vitejs/plugin-legacy";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
const postCssScss = require("postcss-scss")
const postcssRTLCSS = require('postcss-rtlcss');
const postCssScss = require("postcss-scss");
const postcssRTLCSS = require("postcss-rtlcss");
// https://vitejs.dev/config/
export default defineConfig({
@@ -20,5 +20,5 @@ export default defineConfig({
"map": false,
"plugins": [postcssRTLCSS]
}
},
})
},
});

View File

@@ -0,0 +1,13 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD method TEXT default 'GET' not null;
ALTER TABLE monitor
ADD body TEXT default null;
ALTER TABLE monitor
ADD headers TEXT default null;
COMMIT;

View File

@@ -6,7 +6,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . .
RUN npm ci && \
npm run build && \
npm prune --production && \
npm ci --production && \
chmod +x /app/extra/entrypoint.sh
@@ -34,7 +34,7 @@ RUN apt update && \
ARG GITHUB_TOKEN
ARG TARGETARCH
ARG PLATFORM=debian
ARG VERSION
ARG VERSION=1.9.0
ARG FILE=$PLATFORM-$TARGETARCH-$VERSION.tar.gz
ARG DIST=dist.tar.gz
@@ -47,5 +47,5 @@ RUN chmod +x /app/extra/upload-github-release-asset.sh
# Dist only
RUN cd /app && tar -zcvf $DIST dist
RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=$DIST
RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=/app/$DIST

View File

@@ -6,7 +6,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . .
RUN npm ci && \
npm run build && \
npm prune --production && \
npm ci --production && \
chmod +x /app/extra/entrypoint.sh

6
ecosystem.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
apps: [{
name: "uptime-kuma",
script: "./server/server.js",
}]
}

View File

@@ -12,50 +12,59 @@ const rl = readline.createInterface({
output: process.stdout
});
(async () => {
const main = async () => {
Database.init(args);
await Database.connect();
try {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
rl.close();
break;
} else {
console.log("Passwords do not match, please try again.");
// 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("Password reset successfully.");
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
break;
} else {
console.log("Passwords do not match, please try again.");
}
}
console.log("Password reset successfully.");
}
} catch (e) {
console.error("Error: " + e.message);
}
await Database.close();
rl.close();
console.log("Finished. You should restart the Uptime Kuma server.")
})();
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,
};

View File

@@ -26,10 +26,12 @@ const copyRecursiveSync = function (src, dest) {
}
};
console.log("Arguments:", process.argv)
console.log("Arguments:", process.argv);
const baseLangCode = process.argv[2] || "en";
console.log("Base Lang: " + baseLangCode);
fs.rmdirSync("./languages", { recursive: true });
if (fs.existsSync("./languages")) {
fs.rmdirSync("./languages", { recursive: true });
}
copyRecursiveSync("../../src/languages", "./languages");
const en = (await import("./languages/en.js")).default;
@@ -39,7 +41,7 @@ console.log("Files:", files);
for (const file of files) {
if (!file.endsWith(".js")) {
console.log("Skipping " + file)
console.log("Skipping " + file);
continue;
}

View File

@@ -1,32 +0,0 @@
# Uptime-Kuma K8s Deployment
⚠ Warning: K8s deployment is provided by contributors. I have no experience with K8s and I can't fix error in the future. I only test Docker and Node.js. Use at your own risk.
## How does it work?
Kustomize is a tool which builds a complete deployment file for all config elements.
You can edit the files in the ```uptime-kuma``` folder except the ```kustomization.yml``` until you know what you're doing.
If you want to choose another namespace you can edit the ```kustomization.yml``` in the ```kubernetes```-Folder and change the ```namespace: uptime-kuma``` to something you like.
It creates a certificate with the specified Issuer and creates the Ingress for the Uptime-Kuma ClusterIP-Service.
## What do I have to edit?
You have to edit the ```ingressroute.yml``` to your needs.
This ingressroute.yml is for the [nginx-ingress-controller](https://kubernetes.github.io/ingress-nginx/) in combination with the [cert-manager](https://cert-manager.io/).
- Host
- Secrets and secret names
- (Cluster)Issuer (optional)
- The Version in the Deployment-File
- Update:
- Change to newer version and run the above commands, it will update the pods one after another
## How To use
- Install [kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/)
- Edit files mentioned above to your needs
- Run ```kustomize build > apply.yml```
- Run ```kubectl apply -f apply.yml```
Now you should see some k8s magic and Uptime-Kuma should be available at the specified address.

View File

@@ -1,10 +0,0 @@
namespace: uptime-kuma
namePrefix: uptime-kuma-
commonLabels:
app: uptime-kuma
bases:
- uptime-kuma

View File

@@ -1,45 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
component: uptime-kuma
name: deployment
spec:
selector:
matchLabels:
component: uptime-kuma
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
component: uptime-kuma
spec:
containers:
- name: app
image: louislam/uptime-kuma:1
ports:
- containerPort: 3001
volumeMounts:
- mountPath: /app/data
name: storage
livenessProbe:
exec:
command:
- node
- extra/healthcheck.js
initialDelaySeconds: 180
periodSeconds: 60
timeoutSeconds: 30
readinessProbe:
httpGet:
path: /
port: 3001
scheme: HTTP
volumes:
- name: storage
persistentVolumeClaim:
claimName: pvc

View File

@@ -1,39 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/server-snippets: |
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_cache_bypass $http_upgrade;
}
name: ingress
spec:
tls:
- hosts:
- example.com
secretName: example-com-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service
port:
number: 3001

View File

@@ -1,5 +0,0 @@
resources:
- deployment.yml
- service.yml
- ingressroute.yml
- pvc.yml

View File

@@ -1,10 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi

View File

@@ -1,13 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: service
spec:
selector:
component: uptime-kuma
type: ClusterIP
ports:
- name: http
port: 3001
targetPort: 3001
protocol: TCP

626
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "1.8.0",
"version": "1.9.1",
"license": "MIT",
"repository": {
"type": "git",
@@ -15,27 +15,28 @@
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore",
"lint": "npm run lint:js && npm run lint:style",
"dev": "vite --host",
"dev": "vite --host --config ./config/vite.config.js",
"start": "npm run start-server",
"start-server": "node server/server.js",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"build": "vite build",
"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 && jest ",
"jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./jest-frontend.config.js",
"jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend && jest --config=./config/jest.config.js",
"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",
"vite-preview-dist": "vite preview --host",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
"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 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.8.0-alpine --target release . --push",
"build-docker-debian": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.8.0 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.8.0-debian --target release . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
"build-docker-nightly-alpine": "docker buildx build -f 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 --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"upload-artifacts": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.8.0 && npm ci --production && npm run download-dist",
"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.1-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.1 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.9.1-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.1 && 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",
@@ -61,6 +62,8 @@
"axios": "~0.21.4",
"bcryptjs": "~2.4.3",
"bootstrap": "~5.1.1",
"chardet": "^1.3.0",
"bree": "~6.3.1",
"chart.js": "~3.5.1",
"chartjs-adapter-dayjs": "~1.0.0",
"command-exists": "~1.2.9",
@@ -70,6 +73,7 @@
"express-basic-auth": "~1.2.0",
"form-data": "~4.0.0",
"http-graceful-shutdown": "~3.1.4",
"iconv-lite": "^0.6.3",
"jsonwebtoken": "~8.5.1",
"nodemailer": "~6.6.5",
"notp": "~2.0.3",
@@ -106,6 +110,7 @@
"@vitejs/plugin-legacy": "~1.6.1",
"@vitejs/plugin-vue": "~1.9.2",
"@vue/compiler-sfc": "~3.2.19",
"babel-plugin-rewire": "~1.2.0",
"core-js": "~3.18.1",
"cross-env": "~7.0.3",
"dns2": "~2.0.1",

7
server/config.js Normal file
View File

@@ -0,0 +1,7 @@
const args = require("args-parser")(process.argv);
const demoMode = args["demo"] || false;
module.exports = {
args,
demoMode
};

View File

@@ -49,10 +49,11 @@ class Database {
"patch-incident-table.sql": true,
"patch-group-table.sql": true,
"patch-monitor-push_token.sql": true,
"patch-http-monitor-method-body-and-headers.sql": true,
}
/**
* The finally version should be 10 after merged tag feature
* The final version should be 10 after merged tag feature
* @deprecated Use patchList for any new feature
*/
static latestVersion = 10;

31
server/jobs.js Normal file
View File

@@ -0,0 +1,31 @@
const path = require("path");
const Bree = require("bree");
const { SHARE_ENV } = require("worker_threads");
const jobs = [
{
name: "clear-old-data",
interval: "at 03:14",
}
];
const initBackgroundJobs = function (args) {
const bree = new Bree({
root: path.resolve("server", "jobs"),
jobs,
worker: {
env: SHARE_ENV,
workerData: args,
},
workerMessageHandler: (message) => {
console.log("[Background Job]:", message);
}
});
bree.start();
return bree;
};
module.exports = {
initBackgroundJobs
};

View File

@@ -0,0 +1,40 @@
const { log, exit, connectDb } = require("./util-worker");
const { R } = require("redbean-node");
const { setSetting, setting } = require("../util-server");
const DEFAULT_KEEP_PERIOD = 180;
(async () => {
await connectDb();
let period = await setting("keepDataPeriodDays");
// Set Default Period
if (period == null) {
await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
period = DEFAULT_KEEP_PERIOD;
}
// Try parse setting
let parsedPeriod;
try {
parsedPeriod = parseInt(period);
} catch (_) {
log("Failed to parse setting, resetting to default..");
await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
parsedPeriod = DEFAULT_KEEP_PERIOD;
}
log(`Clearing Data older than ${parsedPeriod} days...`);
try {
await R.exec(
"DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ",
[parsedPeriod]
);
} catch (e) {
log(`Failed to clear old data: ${e.message}`);
}
exit();
})();

View File

@@ -0,0 +1,39 @@
const { parentPort, workerData } = require("worker_threads");
const Database = require("../database");
const path = require("path");
const log = function (any) {
if (parentPort) {
parentPort.postMessage(any);
}
};
const exit = function (error) {
if (error && error != 0) {
process.exit(error);
} else {
if (parentPort) {
parentPort.postMessage("done");
} else {
process.exit(0);
}
}
};
const connectDb = async function () {
const dbPath = path.join(
process.env.DATA_DIR || workerData["data-dir"] || "./data/"
);
Database.init({
"data-dir": dbPath,
});
await Database.connect();
};
module.exports = {
log,
exit,
connectDb,
};

View File

@@ -7,11 +7,11 @@ 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 } = require("../util-server");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting } = require("../util-server");
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification");
const { demoMode } = require("../server");
const { demoMode } = require("../config");
const version = require("../../package.json").version;
const apicache = require("../modules/apicache");
@@ -55,6 +55,9 @@ class Monitor extends BeanModel {
id: this.id,
name: this.name,
url: this.url,
method: this.method,
body: this.body,
headers: this.headers,
hostname: this.hostname,
port: this.port,
maxretries: this.maxretries,
@@ -138,11 +141,15 @@ class Monitor extends BeanModel {
// Do not do any queries/high loading things before the "bean.ping"
let startTime = dayjs().valueOf();
let res = await axios.get(this.url, {
const options = {
url: this.url,
method: (this.method || "get").toLowerCase(),
...(this.body ? { data: JSON.parse(this.body) } : {}),
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version,
...(this.headers ? JSON.parse(this.headers) : {}),
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
@@ -152,7 +159,8 @@ class Monitor extends BeanModel {
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
});
};
let res = await axios.request(options);
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
@@ -172,6 +180,10 @@ class Monitor extends BeanModel {
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) {
console.log(res.data);
}
if (this.type === "http") {
bean.status = UP;
} else {
@@ -260,6 +272,46 @@ class Monitor extends BeanModel {
return;
}
} else if (this.type === "steam") {
const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/";
const steamAPIKey = await setting("steamAPIKey");
const filter = `addr\\${this.hostname}:${this.port}`;
if (!steamAPIKey) {
throw new Error("Steam API Key not found");
}
let res = await axios.get(steamApiUrl, {
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version,
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: ! this.getIgnoreTls(),
}),
maxRedirects: this.maxredirects,
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
params: {
filter: filter,
key: steamAPIKey,
}
});
if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) {
bean.status = UP;
bean.msg = res.data.response.servers[0].name;
try {
bean.ping = await ping(this.hostname);
} catch (_) { }
} else {
throw new Error("Server not found on Steam");
}
} else {
bean.msg = "Unknown Monitor Type";
bean.status = PENDING;
@@ -292,54 +344,13 @@ class Monitor extends BeanModel {
let beatInterval = this.interval;
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
let isImportant = isFirstBeat ||
(previousBeat.status === UP && bean.status === DOWN) ||
(previousBeat.status === DOWN && bean.status === UP) ||
(previousBeat.status === PENDING && bean.status === DOWN);
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;
// Send only if the first beat is DOWN
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 ", [
this.id,
]);
let text;
if (bean.status === UP) {
text = "✅ Up";
} else {
text = "🔴 Down";
}
let msg = `[${this.name}] [${text}] ${bean.msg}`;
for (let notification of notificationList) {
try {
await Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON());
} catch (e) {
console.error("Cannot send notification to " + notification.name);
console.log(e);
}
}
// Clear Status Page Cache
apicache.clear();
}
await Monitor.sendNotification(isFirstBeat, this, bean);
} else {
bean.important = false;
}
@@ -546,6 +557,53 @@ class Monitor extends BeanModel {
io.to(userID).emit("uptime", monitorID, duration, uptime);
}
static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
let isImportant = isFirstBeat ||
(previousBeatStatus === UP && currentBeatStatus === DOWN) ||
(previousBeatStatus === DOWN && currentBeatStatus === UP) ||
(previousBeatStatus === PENDING && currentBeatStatus === DOWN);
return isImportant;
}
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,
]);
let text;
if (bean.status === UP) {
text = "✅ Up";
} else {
text = "🔴 Down";
}
let msg = `[${monitor.name}] [${text}] ${bean.msg}`;
for (let notification of notificationList) {
try {
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON());
} catch (e) {
console.error("Cannot send notification to " + notification.name);
console.log(e);
}
}
// Clear Status Page Cache
apicache.clear();
}
}
}
module.exports = Monitor;

View File

@@ -0,0 +1,108 @@
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
const { default: axios } = require("axios");
const Crypto = require("crypto");
const qs = require("qs");
class AliyunSMS extends NotificationProvider {
name = "AliyunSMS";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON != null) {
let msgBody = JSON.stringify({
name: monitorJSON["name"],
time: heartbeatJSON["time"],
status: this.statusToString(heartbeatJSON["status"]),
msg: heartbeatJSON["msg"],
});
if (this.sendSms(notification, msgBody)) {
return okMsg;
}
} else {
let msgBody = JSON.stringify({
name: "",
time: "",
status: "",
msg: msg,
});
if (this.sendSms(notification, msgBody)) {
return okMsg;
}
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
async sendSms(notification, msgbody) {
let params = {
PhoneNumbers: notification.phonenumber,
TemplateCode: notification.templateCode,
SignName: notification.signName,
TemplateParam: msgbody,
AccessKeyId: notification.accessKeyId,
Format: "JSON",
SignatureMethod: "HMAC-SHA1",
SignatureVersion: "1.0",
SignatureNonce: Math.random().toString(),
Timestamp: new Date().toISOString(),
Action: "SendSms",
Version: "2017-05-25",
};
params.Signature = this.sign(params, notification.secretAccessKey);
let config = {
method: "POST",
url: "http://dysmsapi.aliyuncs.com/",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
data: qs.stringify(params),
};
let result = await axios(config);
if (result.data.Message == "OK") {
return true;
}
return false;
}
/** Aliyun request sign */
sign(param, AccessKeySecret) {
let param2 = {};
let data = [];
let oa = Object.keys(param).sort();
for (let i = 0; i < oa.length; i++) {
let key = oa[i];
param2[key] = param[key];
}
for (let key in param2) {
data.push(`${encodeURIComponent(key)}=${encodeURIComponent(param2[key])}`);
}
let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`;
return Crypto
.createHmac("sha1", `${AccessKeySecret}&`)
.update(Buffer.from(StringToSign))
.digest("base64");
}
statusToString(status) {
switch (status) {
case DOWN:
return "DOWN";
case UP:
return "UP";
default:
return status;
}
}
}
module.exports = AliyunSMS;

View File

@@ -0,0 +1,79 @@
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
const { default: axios } = require("axios");
const Crypto = require("crypto");
class DingDing extends NotificationProvider {
name = "DingDing";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON != null) {
let params = {
msgtype: "markdown",
markdown: {
title: monitorJSON["name"],
text: `## [${this.statusToString(heartbeatJSON["status"])}] \n > ${heartbeatJSON["msg"]} \n > Time(UTC):${heartbeatJSON["time"]}`,
}
};
if (this.sendToDingDing(notification, params)) {
return okMsg;
}
} else {
let params = {
msgtype: "text",
text: {
content: msg
}
};
if (this.sendToDingDing(notification, params)) {
return okMsg;
}
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
async sendToDingDing(notification, params) {
let timestamp = Date.now();
let config = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
url: `${notification.webHookUrl}&timestamp=${timestamp}&sign=${encodeURIComponent(this.sign(timestamp, notification.secretKey))}`,
data: JSON.stringify(params),
};
let result = await axios(config);
if (result.data.errmsg == "ok") {
return true;
}
return false;
}
/** DingDing sign */
sign(timestamp, secretKey) {
return Crypto
.createHmac("sha256", Buffer.from(secretKey, "utf8"))
.update(Buffer.from(`${timestamp}\n${secretKey}`, "utf8"))
.digest("base64");
}
statusToString(status) {
switch (status) {
case DOWN:
return "DOWN";
case UP:
return "UP";
default:
return status;
}
}
}
module.exports = DingDing;

View File

@@ -0,0 +1,83 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Feishu extends NotificationProvider {
name = "Feishu";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let feishuWebHookUrl = notification.feishuWebHookUrl;
try {
if (heartbeatJSON == null) {
let testdata = {
msg_type: "text",
content: {
text: msg,
},
};
await axios.post(feishuWebHookUrl, testdata);
return okMsg;
}
if (heartbeatJSON["status"] == DOWN) {
let downdata = {
msg_type: "post",
content: {
post: {
zh_cn: {
title: "UptimeKuma Alert: " + monitorJSON["name"],
content: [
[
{
tag: "text",
text:
"[Down] " +
heartbeatJSON["msg"] +
"\nTime (UTC): " +
heartbeatJSON["time"],
},
],
],
},
},
},
};
await axios.post(feishuWebHookUrl, downdata);
return okMsg;
}
if (heartbeatJSON["status"] == UP) {
let updata = {
msg_type: "post",
content: {
post: {
zh_cn: {
title: "UptimeKuma Alert: " + monitorJSON["name"],
content: [
[
{
tag: "text",
text:
"[Up] " +
heartbeatJSON["msg"] +
"\nTime (UTC): " +
heartbeatJSON["time"],
},
],
],
},
},
},
};
await axios.post(feishuWebHookUrl, updata);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Feishu;

View File

@@ -39,8 +39,9 @@ class Slack extends NotificationProvider {
}
const time = heartbeatJSON["time"];
const textMsg = "Uptime Kuma Alert";
let data = {
"text": "Uptime Kuma Alert",
"text": monitorJSON ? textMsg + `: ${monitorJSON.name}` : textMsg,
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,

View File

@@ -1,5 +1,6 @@
const nodemailer = require("nodemailer");
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
class SMTP extends NotificationProvider {
@@ -20,6 +21,56 @@ class SMTP extends NotificationProvider {
pass: notification.smtpPassword,
};
}
// Lets start with default subject and empty string for custom one
let subject = msg;
// Change the subject if:
// - The msg ends with "Testing" or
// - Actual Up/Down Notification
if ((monitorJSON && heartbeatJSON) || msg.endsWith("Testing")) {
let customSubject = "";
// Our subject cannot end with whitespace it's often raise spam score
// Once I got "Cannot read property 'trim' of undefined", better be safe than sorry
if (notification.customSubject) {
customSubject = notification.customSubject.trim();
}
// If custom subject is not empty, change subject for notification
if (customSubject !== "") {
// Replace "MACROS" with corresponding variable
let replaceName = new RegExp("{{NAME}}", "g");
let replaceHostnameOrURL = new RegExp("{{HOSTNAME_OR_URL}}", "g");
let replaceStatus = new RegExp("{{STATUS}}", "g");
// Lets start with dummy values to simplify code
let monitorName = "Test";
let monitorHostnameOrURL = "testing.hostname";
let serviceStatus = "⚠️ Test";
if (monitorJSON !== null) {
monitorName = monitorJSON["name"];
if (monitorJSON["type"] === "http" || monitorJSON["type"] === "keyword") {
monitorHostnameOrURL = monitorJSON["url"];
} else {
monitorHostnameOrURL = monitorJSON["hostname"];
}
}
if (heartbeatJSON !== null) {
serviceStatus = (heartbeatJSON["status"] === DOWN) ? "🔴 Down" : "✅ Up";
}
// Break replace to one by line for better readability
customSubject = customSubject.replace(replaceStatus, serviceStatus);
customSubject = customSubject.replace(replaceName, monitorName);
customSubject = customSubject.replace(replaceHostnameOrURL, monitorHostnameOrURL);
subject = customSubject;
}
}
let transporter = nodemailer.createTransport(config);
@@ -34,7 +85,7 @@ class SMTP extends NotificationProvider {
cc: notification.smtpCC,
bcc: notification.smtpBCC,
to: notification.smtpTo,
subject: msg,
subject: subject,
text: bodyTextContent,
tls: {
rejectUnauthorized: notification.smtpIgnoreTLSError || false,

View File

@@ -18,6 +18,9 @@ const SMTP = require("./notification-providers/smtp");
const Teams = require("./notification-providers/teams");
const Telegram = require("./notification-providers/telegram");
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");
class Notification {
@@ -30,11 +33,14 @@ class Notification {
const list = [
new Apprise(),
new AliyunSms(),
new DingDing(),
new Discord(),
new Teams(),
new Gotify(),
new Line(),
new LunaSea(),
new Feishu(),
new Mattermost(),
new Matrix(),
new Octopush(),

View File

@@ -4,10 +4,7 @@ const net = require("net");
const spawn = require("child_process").spawn;
const events = require("events");
const fs = require("fs");
const WIN = /^win/.test(process.platform);
const LIN = /^linux/.test(process.platform);
const MAC = /^darwin/.test(process.platform);
const FBSD = /^freebsd/.test(process.platform);
const util = require("./util-server");
module.exports = Ping;
@@ -23,12 +20,12 @@ function Ping(host, options) {
const timeout = 10;
if (WIN) {
if (util.WIN) {
this._bin = "c:/windows/system32/ping.exe";
this._args = (options.args) ? options.args : [ "-n", "1", "-w", timeout * 1000, host ];
this._regmatch = /[><=]([0-9.]+?)ms/;
} else if (LIN) {
} else if (util.LIN) {
this._bin = "/bin/ping";
const defaultArgs = [ "-n", "-w", timeout, "-c", "1", host ];
@@ -40,7 +37,7 @@ function Ping(host, options) {
this._args = (options.args) ? options.args : defaultArgs;
this._regmatch = /=([0-9.]+?) ms/;
} else if (MAC) {
} else if (util.MAC) {
if (net.isIPv6(host) || options.ipv6) {
this._bin = "/sbin/ping6";
@@ -51,7 +48,7 @@ function Ping(host, options) {
this._args = (options.args) ? options.args : [ "-n", "-t", timeout, "-c", "1", host ];
this._regmatch = /=([0-9.]+?) ms/;
} else if (FBSD) {
} else if (util.FBSD) {
this._bin = "/sbin/ping";
const defaultArgs = [ "-n", "-t", timeout, "-c", "1", host ];
@@ -101,6 +98,9 @@ Ping.prototype.send = function (callback) {
});
this._ping.stdout.on("data", function (data) { // log stdout
if (util.WIN) {
data = convertOutput(data);
}
this._stdout = (this._stdout || "") + data;
});
@@ -112,6 +112,9 @@ Ping.prototype.send = function (callback) {
});
this._ping.stderr.on("data", function (data) { // log stderr
if (util.WIN) {
data = convertOutput(data);
}
this._stderr = (this._stderr || "") + data;
});
@@ -157,3 +160,19 @@ Ping.prototype.start = function (callback) {
Ping.prototype.stop = function () {
clearInterval(this._i);
};
/**
* Try to convert to UTF-8 for Windows, as the ping's output on Windows is not UTF-8 and could be in other languages
* Thank @pemassi
* https://github.com/louislam/uptime-kuma/issues/570#issuecomment-941984094
* @param data
* @returns {string}
*/
function convertOutput(data) {
if (util.WIN) {
if (data) {
return util.convertToUTF8(data);
}
}
return data;
}

View File

@@ -5,7 +5,7 @@ const server = require("../server");
const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor");
const dayjs = require("dayjs");
const { UP } = require("../../src/util");
const { UP, flipStatus, debug } = require("../../src/util");
let router = express.Router();
let cache = apicache.middleware;
@@ -18,9 +18,10 @@ router.get("/api/entry-page", async (_, response) => {
router.get("/api/push/:pushToken", async (request, response) => {
try {
let pushToken = request.params.pushToken;
let msg = request.query.msg || "OK";
let ping = request.query.ping;
let ping = request.query.ping || null;
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
pushToken
@@ -30,12 +31,40 @@ 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
]);
let status = UP;
if (monitor.isUpsideDown()) {
status = flipStatus(status);
}
let isFirstBeat = true;
let previousStatus = status;
let duration = 0;
let bean = R.dispense("heartbeat");
bean.monitor_id = monitor.id;
bean.time = R.isoDateTime(dayjs.utc());
bean.status = UP;
if (previousHeartbeat) {
isFirstBeat = false;
previousStatus = previousHeartbeat.status;
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
}
debug("PreviousStatus: " + previousStatus);
debug("Current Status: " + status);
bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status);
bean.monitor_id = monitor.id;
bean.status = status;
bean.msg = msg;
bean.ping = ping;
bean.duration = duration;
await R.store(bean);
@@ -45,6 +74,11 @@ router.get("/api/push/:pushToken", async (request, response) => {
response.json({
ok: true,
});
if (bean.important) {
await Monitor.sendNotification(isFirstBeat, monitor, bean);
}
} catch (e) {
response.json({
ok: false,

View File

@@ -1,6 +1,7 @@
console.log("Welcome to Uptime Kuma");
const args = require("args-parser")(process.argv);
const { sleep, debug, getRandomInt, genSecret } = require("../src/util");
const config = require("./config");
debug(args);
@@ -8,10 +9,6 @@ if (! process.env.NODE_ENV) {
process.env.NODE_ENV = "production";
}
// Demo Mode?
const demoMode = args["demo"] || false;
exports.demoMode = demoMode;
console.log("Node Env: " + process.env.NODE_ENV);
console.log("Importing Node libraries");
@@ -43,7 +40,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 } = require("./util-server");
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD } = require("./util-server");
debug("Importing Notification");
const { Notification } = require("./notification");
@@ -52,6 +49,9 @@ Notification.init();
debug("Importing Database");
const Database = require("./database");
debug("Importing Background Jobs");
const { initBackgroundJobs } = require("./jobs");
const { basicAuth } = require("./auth");
const { login } = require("./auth");
const passwordHash = require("./password-hash");
@@ -61,12 +61,28 @@ console.info("Version: " + checkVersion.version);
// 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 (::)
const hostname = process.env.HOST || args.host;
const port = parseInt(process.env.PORT || args.port || 3001);
let hostname = process.env.UPTIME_KUMA_HOST || args.host;
// Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
if (!hostname && !FBSD) {
hostname = process.env.HOST;
}
if (hostname) {
console.log("Custom hostname: " + hostname);
}
const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001);
// SSL
const sslKey = process.env.SSL_KEY || args["ssl-key"] || undefined;
const sslCert = process.env.SSL_CERT || args["ssl-cert"] || undefined;
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;
// 2FA / notp verification defaults
const twofa_verification_opts = {
"window": 1,
"time": 30
};
/**
* Run unit test after the server is ready
@@ -74,7 +90,7 @@ const sslCert = process.env.SSL_CERT || args["ssl-cert"] || undefined;
*/
const testMode = !!args["test"] || false;
if (demoMode) {
if (config.demoMode) {
console.log("==== Demo Mode ====");
}
@@ -265,7 +281,7 @@ exports.entryPage = "dashboard";
}
if (data.token) {
let verify = notp.totp.verify(data.token, user.twofa_secret);
let verify = notp.totp.verify(data.token, user.twofa_secret, twofa_verification_opts);
if (verify && verify.delta == 0) {
callback({
@@ -383,7 +399,7 @@ exports.entryPage = "dashboard";
socket.userID,
]);
let verify = notp.totp.verify(token, user.twofa_secret);
let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts);
if (verify && verify.delta == 0) {
callback({
@@ -509,6 +525,9 @@ exports.entryPage = "dashboard";
bean.name = monitor.name;
bean.type = monitor.type;
bean.url = monitor.url;
bean.method = monitor.method;
bean.body = monitor.body;
bean.headers = monitor.headers;
bean.interval = monitor.interval;
bean.retryInterval = monitor.retryInterval;
bean.hostname = monitor.hostname;
@@ -1034,6 +1053,9 @@ exports.entryPage = "dashboard";
name: monitorListData[i].name,
type: monitorListData[i].type,
url: monitorListData[i].url,
method: monitorListData[i].method || "GET",
body: monitorListData[i].body,
headers: monitorListData[i].headers,
interval: monitorListData[i].interval,
retryInterval: retryInterval,
hostname: monitorListData[i].hostname,
@@ -1239,6 +1261,8 @@ exports.entryPage = "dashboard";
}
});
initBackgroundJobs(args);
})();
async function updateMonitorNotification(monitorID, notificationIDList) {

View File

@@ -6,6 +6,14 @@ const passwordHash = require("./password-hash");
const dayjs = require("dayjs");
const { Resolver } = require("dns");
const child_process = require("child_process");
const iconv = require("iconv-lite");
const chardet = require("chardet");
// From ping-lite
exports.WIN = /^win/.test(process.platform);
exports.LIN = /^linux/.test(process.platform);
exports.MAC = /^darwin/.test(process.platform);
exports.FBSD = /^freebsd/.test(process.platform);
/**
* Init or reset JWT secret
@@ -116,7 +124,7 @@ exports.setting = async function (key) {
}
};
exports.setSetting = async function (key, value) {
exports.setSetting = async function (key, value, type = null) {
let bean = await R.findOne("setting", " `key` = ? ", [
key,
]);
@@ -124,6 +132,7 @@ exports.setSetting = async function (key, value) {
bean = R.dispense("setting");
bean.key = key;
}
bean.type = type;
bean.value = JSON.stringify(value);
await R.store(bean);
};
@@ -312,3 +321,14 @@ exports.startUnitTest = async () => {
process.exit(code);
});
};
/**
* @param body : Buffer
* @returns {string}
*/
exports.convertToUTF8 = (body) => {
const guessEncoding = chardet.detect(body);
//debug("Guess Encoding: " + guessEncoding);
const str = iconv.decode(body, guessEncoding);
return str.toString();
};

View File

@@ -14,6 +14,10 @@ h2 {
font-size: 26px;
}
textarea.form-control {
border-radius: 19px;
}
::-webkit-scrollbar {
width: 10px;
}

View File

@@ -21,7 +21,7 @@
}
.multiselect__tag {
border-radius: 50rem;
border-radius: $border-radius;
margin-bottom: 0;
padding: 6px 26px 6px 10px;
background: $primary !important;

View File

@@ -186,7 +186,7 @@ export default {
.beat {
display: inline-block;
background-color: $primary;
border-radius: 50rem;
border-radius: $border-radius;
&.empty {
background-color: aliceblue;

View File

@@ -0,0 +1,25 @@
<template>
<div class="mb-3">
<label for="accessKeyId" class="form-label">{{ $t("AccessKeyId") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="accessKeyId" v-model="$parent.notification.accessKeyId" type="text" class="form-control" required>
<label for="secretAccessKey" class="form-label">{{ $t("SecretAccessKey") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="secretAccessKey" v-model="$parent.notification.secretAccessKey" type="text" class="form-control" required>
<label for="phonenumber" class="form-label">{{ $t("Phonenumber") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="phonenumber" v-model="$parent.notification.phonenumber" type="text" class="form-control" required>
<label for="templateCode" class="form-label">{{ $t("TemplateCode") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="templateCode" v-model="$parent.notification.templateCode" type="text" class="form-control" required>
<label for="signName" class="form-label">{{ $t("SignName") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="signName" v-model="$parent.notification.signName" type="text" class="form-control" required>
<div class="form-text">
<p>Sms template must contain parameters: <br> <code>${name} ${time} ${status} ${msg}</code></p>
<i18n-t tag="p" keypath="Read more:">
<a href="https://help.aliyun.com/document_detail/101414.html" target="_blank">https://help.aliyun.com/document_detail/101414.html</a>
</i18n-t>
</div>
</div>
</template>

View File

@@ -0,0 +1,16 @@
<template>
<div class="mb-3">
<label for="WebHookUrl" class="form-label">{{ $t("WebHookUrl") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="WebHookUrl" v-model="$parent.notification.webHookUrl" type="text" class="form-control" required>
<label for="secretKey" class="form-label">{{ $t("SecretKey") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="secretKey" v-model="$parent.notification.secretKey" type="text" class="form-control" required>
<div class="form-text">
<p>For safety, must use secret key</p>
<i18n-t tag="p" keypath="Read more:">
<a href="https://developers.dingtalk.com/document/robots/custom-robot-access" target="_blank">https://developers.dingtalk.com/document/robots/custom-robot-access</a>
</i18n-t>
</div>
</div>
</template>

View File

@@ -0,0 +1,15 @@
<template>
<div class="mb-3">
<label for="Feishu-WebHookUrl" class="form-label">{{ $t("Feishu WebHookUrl") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="Feishu-WebHookUrl" v-model="$parent.notification.feishuWebHookUrl" 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://www.feishu.cn/hc/zh-CN/articles/360024984973"
target="_blank"
>{{ $t("here") }}</a>
</i18n-t>
</div>
</template>

View File

@@ -1,25 +1,25 @@
<template>
<div class="mb-3">
<label for="homeserver-url" class="form-label">Homeserver URL (with http(s):// and optionally port)</label><span style="color: red;"><sup>*</sup></span>
<label for="homeserver-url" class="form-label">{{ $t("matrixHomeserverURL") }}</label><span style="color: red;"><sup>*</sup></span>
<input id="homeserver-url" v-model="$parent.notification.homeserverUrl" type="text" class="form-control" :required="true">
</div>
<div class="mb-3">
<label for="internal-room-id" class="form-label">Internal Room Id</label><span style="color: red;"><sup>*</sup></span>
<label for="internal-room-id" class="form-label">{{ $t("Internal Room Id") }}</label><span style="color: red;"><sup>*</sup></span>
<input id="internal-room-id" v-model="$parent.notification.internalRoomId" type="text" class="form-control" required="true">
</div>
<div class="mb-3">
<label for="access-token" class="form-label">Access Token</label><span style="color: red;"><sup>*</sup></span>
<label for="access-token" class="form-label">{{ $t("Access Token") }}</label><span style="color: red;"><sup>*</sup></span>
<HiddenInput id="access-token" v-model="$parent.notification.accessToken" :required="true" autocomplete="one-time-code" :maxlength="500"></HiddenInput>
</div>
<div class="form-text">
<span style="color: red;"><sup>*</sup></span>Required
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<p style="margin-top: 8px;">
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.
</p>
<p style="margin-top: 8px;">
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 <code>curl -XPOST -d '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/r0/login"</code>.
{{ $t("matrixDesc1") }}
</p>
<i18n-t tag="p" keypath="matrixDesc2" style="margin-top: 8px;">
<code>curl -XPOST -d '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/r0/login"</code>.
</i18n-t>
</div>
</template>
@@ -30,5 +30,5 @@ export default {
components: {
HiddenInput,
},
}
};
</script>

View File

@@ -6,7 +6,7 @@
<option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option>
</select>
<div class="form-text">
Do you use the legacy version of Octopush (2011-2020) or the new version?
{{ $t("octopushLegacyHint") }}
</div>
</div>
<div class="mb-3">

View File

@@ -10,12 +10,13 @@
<select id="promosms-type-sms" v-model="$parent.notification.promosmsSMSType" class="form-select">
<option value="0">{{ $t("promosmsTypeFlash") }}</option>
<option value="1">{{ $t("promosmsTypeEco") }}</option>
<option value="2">{{ $t("promosmsTypeFull") }}</option>
<option value="3">{{ $t("promosmsTypeSpeed") }}</option>
<option value="3">{{ $t("promosmsTypeFull") }}</option>
<option value="4">{{ $t("promosmsTypeSpeed") }}</option>
</select>
<i18n-t tag="div" keypath="Check PromoSMS prices" class="form-text">
<div class="form-text">
{{ $t("checkPrice", [$t("promosms")]) }}
<a href="https://promosms.com/cennik/" target="_blank">https://promosms.com/cennik/</a>
</i18n-t>
</div>
</div>
<div class="mb-3">
<label for="promosms-phone-number" class="form-label">{{ $t("promosmsPhoneNumber") }}</label>
@@ -25,7 +26,6 @@
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
</div>
</template>
<script>

View File

@@ -57,6 +57,18 @@
<label for="to-bcc" class="form-label">{{ $t("smtpBCC") }}</label>
<input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient">
</div>
<div class="mb-3">
<label for="subject-email" class="form-label">{{ $t("emailCustomSubject") }}</label>
<input id="subject-email" v-model="$parent.notification.customSubject" type="text" class="form-control" autocomplete="false" placeholder="">
<div v-pre class="form-text">
(leave blank for default one)<br />
{{NAME}}: Service Name<br />
{{HOSTNAME_OR_URL}}: Hostname or URL<br />
{{URL}}: URL<br />
{{STATUS}}: Status<br />
</div>
</div>
</template>
<script>

View File

@@ -2,9 +2,9 @@
<div class="mb-3">
<label for="telegram-bot-token" class="form-label">{{ $t("Bot Token") }}</label>
<HiddenInput id="telegram-bot-token" v-model="$parent.notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
<div class="form-text">
{{ $t("You can get a token from") }} <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
</div>
<i18n-t tag="div" keypath="wayToGetTelegramToken" class="form-text">
<a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>
</i18n-t>
</div>
<div class="mb-3">

View File

@@ -16,7 +16,7 @@
</select>
<div class="form-text">
<p>"application/json" is good for any modern http servers such as express.js</p>
<p>{{ $t("webhookJsonDesc", ["\"application/json\""]) }}</p>
<i18n-t tag="p" keypath="webhookFormDataDesc">
<template #multipart>"multipart/form-data"</template>
<template #decodeFunction>

View File

@@ -12,11 +12,14 @@ import Pushy from "./Pushy.vue";
import Octopush from "./Octopush.vue";
import PromoSMS from "./PromoSMS.vue";
import LunaSea from "./LunaSea.vue";
import Feishu from "./Feishu.vue";
import Apprise from "./Apprise.vue";
import Pushbullet from "./Pushbullet.vue";
import Line from "./Line.vue";
import Mattermost from "./Mattermost.vue";
import Matrix from "./Matrix.vue";
import AliyunSMS from "./AliyunSms.vue";
import DingDing from "./DingDing.vue";
/**
* Manage all notification form.
@@ -38,11 +41,14 @@ const NotificationFormList = {
"octopush": Octopush,
"promosms": PromoSMS,
"lunasea": LunaSea,
"Feishu": Feishu,
"AliyunSMS": AliyunSMS,
"apprise": Apprise,
"pushbullet": Pushbullet,
"line": Line,
"mattermost": Mattermost,
"matrix": Matrix,
"DingDing": DingDing
}
export default NotificationFormList

View File

@@ -8,9 +8,11 @@ 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";
@@ -28,12 +30,14 @@ const languageList = {
"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,

View File

@@ -8,7 +8,7 @@ export default {
Theme: "Thema",
General: "Allgemein",
Version: "Version",
"Check Update On GitHub": "Überprüfen von Updates auf Github",
"Check Update On GitHub": "Auf GitHub nach Updates suchen",
List: "Liste",
Add: "Hinzufügen",
"Add New Monitor": "Neuer Monitor",
@@ -38,21 +38,21 @@ export default {
checkEverySecond: "Überprüfe alle {0} Sekunden",
Response: "Antwortzeit",
Ping: "Ping",
"Monitor Type": "Monitor Typ",
Keyword: "Schlüsselwort",
"Monitor Type": "Monitor-Typ",
Keyword: "Suchwort",
"Friendly Name": "Anzeigename",
URL: "URL",
Hostname: "Hostname",
Port: "Port",
"Heartbeat Interval": "Taktintervall",
"Heartbeat Interval": "Prüfintervall",
Retries: "Wiederholungen",
retriesDescription: "Maximale Anzahl von Wiederholungen, bevor der Dienst als inaktiv markiert und eine Benachrichtigung gesendet wird.",
Advanced: "Erweitert",
ignoreTLSError: "Ignoriere TLS/SSL Fehler von Webseiten",
"Upside Down Mode": "Umgedrehter Modus",
upsideDownModeDescription: "Drehe den Modus um, ist der Dienst erreichbar, wird er als inaktiv angezeigt.",
ignoreTLSError: "Ignoriere TLS-/SSL-Fehler von Webseiten",
"Upside Down Mode": "Invertierter Modus",
upsideDownModeDescription: "Im invertierten Modus wird der Dienst als inaktiv angezeigt, wenn er erreichbar ist.",
"Max. Redirects": "Max. Weiterleitungen",
maxRedirectDescription: "Maximale Anzahl von Weiterleitungen, denen gefolgt werden soll. Setzte auf 0, um Weiterleitungen zu deaktivieren.",
maxRedirectDescription: "Maximale Anzahl von Weiterleitungen, denen gefolgt werden soll. Auf 0 setzen, um Weiterleitungen zu deaktivieren.",
"Accepted Status Codes": "Erlaubte HTTP-Statuscodes",
acceptedStatusCodesDescription: "Wähle die Statuscodes aus, welche trotzdem als erfolgreich gewertet werden sollen.",
Save: "Speichern",
@@ -62,27 +62,27 @@ export default {
Light: "Hell",
Dark: "Dunkel",
Auto: "Auto",
"Theme - Heartbeat Bar": "Thema - Taktleiste",
"Theme - Heartbeat Bar": "Thema - Zeitleiste",
Normal: "Normal",
Bottom: "Unten",
None: "Keine",
Timezone: "Zeitzone",
"Search Engine Visibility": "Suchmaschinensichtbarkeit",
"Search Engine Visibility": "Sichtbarkeit für Suchmaschinen",
"Allow indexing": "Indizierung zulassen",
"Discourage search engines from indexing site": "Halte Suchmaschinen von der Indexierung der Seite ab",
"Change Password": "Passwort ändern",
"Current Password": "Derzeitiges Passwort",
"New Password": "Neues Passwort",
"Repeat New Password": "Wiederhole neues Passwort",
"Repeat New Password": "Neues Passwort wiederholen",
passwordNotMatchMsg: "Passwörter stimmen nicht überein. ",
"Update Password": "Ändere Passwort",
"Update Password": "Passwort aktualisieren",
"Disable Auth": "Authentifizierung deaktivieren",
"Enable Auth": "Authentifizierung aktivieren",
Logout: "Ausloggen",
notificationDescription: "Weise den Monitor(en) eine Benachrichtigung zu, damit diese Funktion greift.",
Leave: "Verlassen",
"I understand, please disable": "Ich verstehe, bitte deaktivieren",
Confirm: "Bestätige",
Confirm: "Bestätigen",
Yes: "Ja",
No: "Nein",
Username: "Benutzername",
@@ -95,7 +95,7 @@ export default {
Email: "E-Mail",
Test: "Test",
"Certificate Info": "Zertifikatsinfo",
keywordDescription: "Suche nach einem Schlüsselwort in der HTML oder JSON Ausgabe. Bitte beachte, es wird in der Groß-/Kleinschreibung unterschieden.",
keywordDescription: "Ein Suchwort in der HTML- oder JSON-Ausgabe finden. Bitte beachte: es wird zwischen Groß-/Kleinschreibung unterschieden.",
deleteMonitorMsg: "Bist du sicher, dass du den Monitor löschen möchtest?",
deleteNotificationMsg: "Möchtest du diese Benachrichtigung wirklich für alle Monitore löschen?",
resoverserverDescription: "Cloudflare ist als der Standardserver festgelegt, dieser kann jederzeit geändern werden.",
@@ -108,13 +108,13 @@ export default {
"Clear Data": "Lösche Daten",
Events: "Ereignisse",
Heartbeats: "Statistiken",
confirmClearStatisticsMsg: "Bist du dir wirklich sicher, dass du ALLE Statistiken löschen möchtest?",
"Create your admin account": "Erstelle dein Admin Konto",
confirmClearStatisticsMsg: "Bist du dir sicher, dass du ALLE Statistiken löschen möchtest?",
"Create your admin account": "Erstelle dein Admin-Konto",
"Repeat Password": "Wiederhole das Passwort",
"Resource Record Type": "Resource Record Type",
Export: "Export",
Import: "Import",
respTime: "Antw. Zeit (ms)",
respTime: "Antw.-Zeit (ms)",
notAvailableShort: "N/A",
"Default enabled": "Standardmäßig aktiviert",
"Apply on all existing monitors": "Auf alle existierenden Monitore anwenden",
@@ -125,34 +125,34 @@ export default {
backupDescription2: "PS: Verlaufs- und Ereignisdaten sind nicht enthalten.",
backupDescription3: "Sensible Daten wie Benachrichtigungstoken sind in der Exportdatei enthalten, bitte bewahre sie sorgfältig auf.",
alertNoFile: "Bitte wähle eine Datei zum Importieren aus.",
alertWrongFileType: "Bitte wähle eine JSON Datei aus.",
alertWrongFileType: "Bitte wähle eine JSON-Datei aus.",
"Clear all statistics": "Lösche alle Statistiken",
importHandleDescription: "Wähle 'Vorhandene überspringen' aus, wenn jeder Monitor oder Benachrichtigung mit demselben Namen übersprungen werden soll. 'Überschreiben' löscht jeden vorhandenen Monitor sowie Benachrichtigungen.",
importHandleDescription: "Wähle 'Vorhandene überspringen' aus, wenn jeder Monitor oder jede Benachrichtigung mit demselben Namen übersprungen werden soll. 'Überschreiben' löscht jeden vorhandenen Monitor sowie Benachrichtigungen.",
"Skip existing": "Vorhandene überspringen",
Overwrite: "Überschreiben",
Options: "Optionen",
confirmImportMsg: "Möchtest du das Backup wirklich importieren? Bitte stelle sicher, dass die richtige Import Option ausgewählt ist.",
confirmImportMsg: "Möchtest du das Backup wirklich importieren? Bitte stelle sicher, dass die richtige Import-Option ausgewählt ist.",
"Keep both": "Beide behalten",
twoFAVerifyLabel: "Bitte trage deinen Token ein, um zu verifizieren das 2FA funktioniert",
twoFAVerifyLabel: "Bitte trage deinen Token ein, um zu verifizieren, dass 2FA funktioniert",
"Verify Token": "Token verifizieren",
"Setup 2FA": "2FA Einrichten",
"Enable 2FA": "2FA Aktivieren",
"Setup 2FA": "2FA einrichten",
"Enable 2FA": "2FA aktivieren",
"Disable 2FA": "2FA deaktivieren",
"2FA Settings": "2FA Einstellungen",
"2FA Settings": "2FA-Einstellungen",
confirmEnableTwoFAMsg: "Bist du sicher, dass du 2FA aktivieren möchtest?",
confirmDisableTwoFAMsg: "Bist du sicher, dass du 2FA deaktivieren möchtest?",
tokenValidSettingsMsg: "Token gültig! Du kannst jetzt die 2FA Einstellungen speichern.",
"Two Factor Authentication": "Zwei Faktor Authentifizierung",
tokenValidSettingsMsg: "Token gültig! Du kannst jetzt die 2FA-Einstellungen speichern.",
"Two Factor Authentication": "Zwei-Faktor-Authentifizierung",
Active: "Aktiv",
Inactive: "Inaktiv",
Token: "Token",
"Show URI": "URI Anzeigen",
"Show URI": "URI anzeigen",
Tags: "Tags",
"Add New below or Select...": "Füge neuen hinzu oder wähle aus...",
"Tag with this name already exist.": "Ein Tag mit dem Namen existiert bereits.",
"Tag with this value already exist.": "Ein Tag mit dem Wert existiert bereits.",
"Add New below or Select...": "Bestehenden Tag auswählen oder neuen hinzufügen...",
"Tag with this name already exist.": "Ein Tag mit diesem Namen existiert bereits.",
"Tag with this value already exist.": "Ein Tag mit diesem Wert existiert bereits.",
color: "Farbe",
"value (optional)": "Wert (Optional)",
"value (optional)": "Wert (optional)",
Gray: "Grau",
Red: "Rot",
Orange: "Orange",
@@ -164,21 +164,21 @@ export default {
"Search...": "Suchen...",
"Heartbeat Retry Interval": "Heartbeat-Wiederholungsintervall",
retryCheckEverySecond: "Versuche alle {0} Sekunden",
"Import Backup": "Import Backup",
"Export Backup": "Export Backup",
"Avg. Ping": "Durchsch. Ping",
"Avg. Response": "Durchsch. Antwort",
"Import Backup": "Backup importieren",
"Export Backup": "Backup exportieren",
"Avg. Ping": "Durchschn. Ping",
"Avg. Response": "Durchschn. Antwort",
"Entry Page": "Einstiegsseite",
statusPageNothing: "Nichts ist hier, bitte füge eine Gruppe oder Monitor hinzu.",
statusPageNothing: "Noch ist hier nichts. Bitte füge eine Gruppe oder einen Monitor hinzu.",
"No Services": "Keine Dienste",
"All Systems Operational": "Alle Systeme Betriebsbereit",
"All Systems Operational": "Alle Systeme betriebsbereit",
"Partially Degraded Service": "Teilweise beeinträchtigter Dienst",
"Degraded Service": "Eingeschränkter Dienst",
"Add Group": "Gruppe hinzufügen",
"Add a monitor": "Monitor hinzufügen",
"Edit Status Page": "Bearbeite Statusseite",
"Edit Status Page": "Bearbeite Status-Seite",
"Go to Dashboard": "Gehe zum Dashboard",
"Status Page": "Status Seite",
"Status Page": "Status-Seite",
telegram: "Telegram",
webhook: "Webhook",
smtp: "E-Mail (SMTP)",

View File

@@ -19,7 +19,7 @@ export default {
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. 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 want to delete ALL statistics?",
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",
@@ -33,6 +33,7 @@ export default {
Appearance: "Appearance",
Theme: "Theme",
General: "General",
"Primary Base URL": "Primary Base URL",
Version: "Version",
"Check Update On GitHub": "Check Update On GitHub",
List: "List",
@@ -75,6 +76,9 @@ export default {
"Upside Down Mode": "Upside Down Mode",
"Max. Redirects": "Max. Redirects",
"Accepted Status Codes": "Accepted Status Codes",
"Push URL": "Push URL",
needPushEvery: "You should call this url every {0} seconds.",
pushOptionalParams: "Optional parameters: {0}",
Save: "Save",
Notifications: "Notifications",
"Not available, please setup.": "Not available, please setup.",
@@ -185,7 +189,7 @@ export default {
"Required": "Required",
"telegram": "Telegram",
"Bot Token": "Bot Token",
"You can get a token from": "You can get a token from",
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:",
@@ -201,6 +205,7 @@ export default {
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignore TLS Error",
"From Email": "From Email",
emailCustomSubject: "Custom Subject",
"To Email": "To Email",
smtpCC: "CC",
smtpBCC: "BCC",
@@ -251,6 +256,8 @@ export default {
"SMS Type": "SMS Type",
octopushTypePremium: "Premium (Fast - recommended for alerting)",
octopushTypeLowCost: "Low Cost (Slow, sometimes blocked by operator)",
checkPrice: "Check {0} prices:",
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) ",
octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
@@ -280,5 +287,22 @@ export default {
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",
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
"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:",
clearDataOlderThan: "Keep monitor history data for {0} days.",
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: ",
};

View File

@@ -9,7 +9,7 @@ export default {
passwordNotMatchMsg: "La contraseña repetida no coincide.",
notificationDescription: "Por favor asigne una notificación a el/los monitor(es) para hacerlos funcional(es).",
keywordDescription: "Palabra clave en HTML plano o respuesta JSON y es sensible a mayúsculas",
pauseDashboardHome: "Pausar",
pauseDashboardHome: "Pausado",
deleteMonitorMsg: "¿Seguro que quieres eliminar este monitor?",
deleteNotificationMsg: "¿Seguro que quieres eliminar esta notificación para todos los monitores?",
resoverserverDescription: "Cloudflare es el servidor por defecto, puedes cambiar el servidor de resolución en cualquier momento.",
@@ -32,7 +32,7 @@ export default {
Down: "Caído",
Pending: "Pendiente",
Unknown: "Desconocido",
Pause: "Pausa",
Pause: "Pausar",
Name: "Nombre",
Status: "Estado",
DateTime: "Fecha y Hora",
@@ -198,4 +198,9 @@ export default {
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"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",
};

View File

@@ -1,5 +1,31 @@
export default {
languageName: "Français (France)",
checkEverySecond: "Vérifier toutes les {0} secondes",
retryCheckEverySecond: "Réessayer toutes les {0} secondes.",
retriesDescription: "Nombre d'essais avant que le service soit déclaré hors-ligne.",
ignoreTLSError: "Ignorer les erreurs liées au certificat SSL/TLS",
upsideDownModeDescription: "Si le service est en ligne, il sera alors noté hors-ligne et vice-versa.",
maxRedirectDescription: "Nombre maximal de redirections avant que le service soit noté hors-ligne.",
acceptedStatusCodesDescription: "Codes HTTP considérés comme en ligne",
passwordNotMatchMsg: "Les mots de passe ne correspondent pas",
notificationDescription: "Une fois ajoutée, vous devez l'activer manuellement dans les paramètres de vos hôtes.",
keywordDescription: "Le mot clé sera recherché dans la réponse HTML/JSON reçue du site internet.",
pauseDashboardHome: "En pause",
deleteMonitorMsg: "Êtes-vous sûr de vouloir supprimer cette sonde ?",
deleteNotificationMsg: "Êtes-vous sûr de vouloir supprimer ce type de notifications ? Une fois désactivée, les services qui l'utilisent ne pourront plus envoyer de notifications.",
resoverserverDescription: "Le DNS de cloudflare est utilisé par défaut, mais vous pouvez le changer si vous le souhaitez.",
rrtypeDescription: "Veuillez séléctionner un type d'enregistrement DNS",
pauseMonitorMsg: "Êtes-vous sûr de vouloir mettre en pause cette sonde ?",
enableDefaultNotificationDescription: "Pour chaque nouvelle sonde, cette notification sera activée par défaut. Vous pouvez toujours désactiver la notification séparément pour chaque sonde.",
clearEventsMsg: "Êtes-vous sûr de vouloir supprimer tous les événements pour cette sonde ?",
clearHeartbeatsMsg: "Êtes-vous sûr de vouloir supprimer tous les vérifications pour cette sonde ?",
confirmClearStatisticsMsg: "Êtes-vous sûr de vouloir supprimer tous les statistiques ?",
importHandleDescription: "Choisissez 'Ignorer l'existant' si vous voulez ignorer chaque sonde ou notification portant le même nom. L'option 'Écraser' supprime toutes les sondes et notifications existantes.",
confirmImportMsg: "Êtes-vous sûr de vouloir importer la sauvegarde ? Veuillez vous assurer que vous avez sélectionné la bonne option d'importation.",
twoFAVerifyLabel: "Veuillez saisir votre jeton pour vérifier que le système 2FA fonctionne.",
tokenValidSettingsMsg: "Le jeton est valide ; Vous pouvez maintenant sauvegarder les paramètres 2FA.",
confirmEnableTwoFAMsg: "Êtes-vous sûr de vouloir activer le 2FA ?",
confirmDisableTwoFAMsg: "Êtes-vous sûr de vouloir désactiver le 2FA ?",
Settings: "Paramètres",
Dashboard: "Tableau de bord",
"New Update": "Mise à jour disponible",
@@ -18,7 +44,6 @@ export default {
Pending: "En attente",
Unknown: "Inconnu",
Pause: "En Pause",
pauseDashboardHome: "Éléments mis en pause",
Name: "Nom",
Status: "État",
DateTime: "Heure",
@@ -29,32 +54,27 @@ export default {
Delete: "Supprimer",
Current: "Actuellement",
Uptime: "Uptime",
"Cert Exp.": "Certificat expiré",
days: "Jours",
day: "Jour",
"-day": "Journée",
hour: "Heure",
"-hour": "Heures",
checkEverySecond: "Vérifier toutes les {0} secondes",
"Cert Exp.": "Expiration SSL",
days: "jours",
day: "jour",
"-day": "-jours",
hour: "-heure",
"-hour": "-heures",
Response: "Temps de réponse",
Ping: "Ping",
"Monitor Type": "Type de Sonde",
Keyword: "Mot-clé",
"Friendly Name": "Nom d'affichage",
URL: "URL",
Hostname: "Nom d'hôte",
Hostname: "Nom d'hôte / adresse IP",
Port: "Port",
"Heartbeat Interval": "Intervale de vérification",
Retries: "Essais",
retriesDescription: "Nombre d'essais avant que le service soit déclaré hors-ligne.",
"Heartbeat Retry Interval": "Réessayer l'intervale de vérification",
Advanced: "Avancé",
ignoreTLSError: "Ignorer les erreurs liées au certificat SSL/TLS",
"Upside Down Mode": "Mode inversé",
upsideDownModeDescription: "Si le service est en ligne, il sera alors noté hors-ligne et vice-versa.",
"Max. Redirects": "Nombre maximum de redirections",
maxRedirectDescription: "Nombre maximal de redirections avant que le service soit noté hors-ligne.",
"Accepted Status Codes": "Codes HTTP",
acceptedStatusCodesDescription: "Codes HTTP considérés comme en ligne",
"Accepted Status Codes": "Codes HTTP acceptés",
Save: "Sauvegarder",
Notifications: "Notifications",
"Not available, please setup.": "Pas de système de notification disponible, merci de le configurer",
@@ -63,9 +83,9 @@ export default {
Dark: "Sombre",
Auto: "Automatique",
"Theme - Heartbeat Bar": "Voir les services surveillés",
Normal: "Général",
Normal: "Normal",
Bottom: "En dessous",
None: "Rien",
None: "Aucun",
Timezone: "Fuseau Horaire",
"Search Engine Visibility": "Visibilité par les moteurs de recherche",
"Allow indexing": "Autoriser l'indexation par des moteurs de recherche",
@@ -74,14 +94,12 @@ export default {
"Current Password": "Mot de passe actuel",
"New Password": "Nouveau mot de passe",
"Repeat New Password": "Répéter votre nouveau mot de passe",
passwordNotMatchMsg: "Les mots de passe ne correspondent pas",
"Update Password": "Mettre à jour le mot de passe",
"Disable Auth": "Désactiver l'authentification",
"Enable Auth": "Activer l'authentification",
Logout: "Se déconnecter",
notificationDescription: "Une fois ajoutée, vous devez l'activer manuellement dans les paramètres de vos hôtes.",
Leave: "Quitter",
"I understand, please disable": "J'ai compris, désactivez-le",
"I understand, please disable": "Je comprends, désactivez-le",
Confirm: "Confirmer",
Yes: "Oui",
No: "Non",
@@ -94,43 +112,35 @@ export default {
"Notification Type": "Type de notification",
Email: "Email",
Test: "Tester",
keywordDescription: "Le mot clé sera recherché dans la réponse HTML/JSON reçue du site internet.",
"Certificate Info": "Informations sur le certificat SSL",
deleteMonitorMsg: "Êtes-vous sûr de vouloir supprimer cette sonde ?",
deleteNotificationMsg: "Êtes-vous sûr de vouloir supprimer ce type de notifications ? Une fois désactivée, les services qui l'utilisent ne pourront plus envoyer de notifications.",
"Resolver Server": "Serveur DNS utilisé",
"Resource Record Type": "Type d'enregistrement DNS recherché",
resoverserverDescription: "Le DNS de cloudflare est utilisé par défaut, mais vous pouvez le changer si vous le souhaitez.",
rrtypeDescription: "Veuillez séléctionner un type d'enregistrement DNS",
pauseMonitorMsg: "Etes vous sur de vouloir mettre en pause cette sonde ?",
"Last Result": "Dernier résultat",
"Create your admin account": "Créez votre compte administrateur",
"Repeat Password": "Répéter le mot de passe",
"Import Backup": "Importation de la sauvegarde",
"Export Backup": "Exportation de la sauvegarde",
Export: "Exporter",
Import: "Importer",
respTime: "Temps de réponse (ms)",
notAvailableShort: "N/A",
"Default enabled": "Activé par défaut",
"Apply on all existing monitors": "Appliquer sur toutes les sondes existantes",
Create: "Créer",
clearEventsMsg: "Êtes-vous sûr de vouloir supprimer tous les événements pour cette sonde ?",
clearHeartbeatsMsg: "Êtes-vous sûr de vouloir supprimer tous les vérifications pour cette sonde ? Are you sure want to delete all heartbeats for this monitor?",
confirmClearStatisticsMsg: "tes-vous sûr de vouloir supprimer tous les statistiques ?",
"Clear Data": "Effacer les données",
Events: "Evénements",
Heartbeats: "Vérfications",
"Auto Get": "Auto Get",
enableDefaultNotificationDescription: "Pour chaque nouvelle sonde, cette notification sera activée par défaut. Vous pouvez toujours désactiver la notification séparément pour chaque sonde.",
"Default enabled": "Activé par défaut",
"Also apply to existing monitors": "S'applique également aux sondes existantes",
Export: "Exporter",
Import: "Importer",
"Auto Get": "Récuperer automatiquement",
backupDescription: "Vous pouvez sauvegarder toutes les sondes et toutes les notifications dans un fichier JSON.",
backupDescription2: "PS: Les données relatives à l'historique et aux événements ne sont pas incluses.",
backupDescription3: "Les données sensibles telles que les jetons de notification sont incluses dans le fichier d'exportation, veuillez les conserver soigneusement.",
alertNoFile: "Veuillez sélectionner un fichier à importer.",
alertWrongFileType: "Veuillez sélectionner un fichier JSON à importer.",
twoFAVerifyLabel: "Veuillez saisir votre jeton pour vérifier que le système 2FA fonctionne.",
tokenValidSettingsMsg: "Le jeton est valide ! Vous pouvez maintenant sauvegarder les paramètres 2FA.",
confirmEnableTwoFAMsg: "Êtes-vous sûr de vouloir activer le 2FA ?",
confirmDisableTwoFAMsg: "Êtes-vous sûr de vouloir désactiver le 2FA ?",
"Apply on all existing monitors": "Appliquer sur toutes les sondes existantes",
"Clear all statistics": "Effacer toutes les statistiques",
"Skip existing": "Sauter l'existant",
Overwrite: "Ecraser",
Options: "Options",
"Keep both": "Garder les deux",
"Verify Token": "Vérifier le jeton",
"Setup 2FA": "Configurer 2FA",
"Enable 2FA": "Activer 2FA",
@@ -141,23 +151,12 @@ export default {
Inactive: "Inactif",
Token: "Jeton",
"Show URI": "Afficher l'URI",
"Clear all statistics": "Effacer touutes les statistiques",
retryCheckEverySecond: "Réessayer toutes les {0} secondes.",
importHandleDescription: "Choisissez 'Ignorer l'existant' si vous voulez ignorer chaque sonde ou notification portant le même nom. L'option 'Écraser' supprime tous les sondes et notifications existantes.",
confirmImportMsg: "Êtes-vous sûr d'importer la sauvegarde ? Veuillez vous assurer que vous avez sélectionné la bonne option d'importation.",
"Heartbeat Retry Interval": "Réessayer l'intervale de vérification",
"Import Backup": "Importation de la sauvegarde",
"Export Backup": "Exportation de la sauvegarde",
"Skip existing": "Sauter l'existant",
Overwrite: "Ecraser",
Options: "Options",
"Keep both": "Garder les deux",
Tags: "Étiquettes",
"Add New below or Select...": "Ajouter nouveau ci-dessous ou sélectionner...",
"Add New below or Select...": "Ajoutez-en un en dessous ou sélectionnez-le ici...",
"Tag with this name already exist.": "Une étiquette portant ce nom existe déjà.",
"Tag with this value already exist.": "Une étiquette avec cette valeur existe déjà.",
color: "couleur",
"value (optional)": "valeur (facultatif)",
color: "Couleur",
"value (optional)": "Valeur (facultatif)",
Gray: "Gris",
Red: "Rouge",
Orange: "Orange",
@@ -180,14 +179,58 @@ export default {
"Edit Status Page": "Modifier la page de statut",
"Go to Dashboard": "Accéder au tableau de bord",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
// Start notification form
defaultNotificationName: "Ma notification {notification} numéro ({number})",
here: "ici",
"Required": "Requis",
"telegram": "Telegram",
"Bot Token": "Bot Token",
wayToGetTelegramToken: "Vous pouvez obtenir un token depuis {0}.",
"Chat ID": "Chat ID",
supportTelegramChatID: "Supporte les messages privés / en groupe / l'ID du salon",
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",
"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)",
secureOptionNone: "Aucun / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignorer les erreurs TLS",
"From Email": "Depuis l'Email",
"To Email": "Vers l'Email",
smtpCC: "CC",
smtpBCC: "BCC",
"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",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "Vous pouvez apprendre comment créer un Webhook {0}.",
"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",
"Application Token": "Application Token",
"Server URL": "Server URL",
"Priority": "Priorité",
"slack": "Slack",
"Icon Emoji": "Icon Emoji",
"Channel Name": "Nom du salon",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "Plus d'informations sur les Webhooks ici: {0}",
aboutChannelName: "Mettez le nom du salon dans {0} dans 'Channel Name' si vous voulez bypass le salon Webhook. Ex : #autre-salon",
aboutKumaURL: "Si vous laissez l'URL d'Uptime Kuma vierge, elle redirigera vers la page du projet GitHub.",
emojiCheatSheet: "Emoji cheat sheet : {0}",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
@@ -198,4 +241,44 @@ export default {
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"User Key": "Clé d'utilisateur",
"Device": "Appareil",
"Message Title": "Titre du message",
"Notification Sound": "Son de notification",
"More info on:": "Plus d'informations sur: {0}",
pushoverDesc1: "Priorité d'urgence (2) a par défaut 30 secondes de délai dépassé entre les tentatives et expierera après 1 heure.",
pushoverDesc2: "Si vous voulez envoyer des notifications sur différents Appareils, remplissez le champ 'Device'.",
"SMS Type": "SMS Type",
octopushTypePremium: "Premium (Rapide - recommandé pour les alertes)",
octopushTypeLowCost: "A bas prix (Lent, bloqué de temps en temps par l'opérateur)",
"Check octopush prices": "Vérifier les prix d'octopush {0}.",
octopushPhoneNumber: "Numéro de téléphone (format intérn., ex : +33612345678) ",
octopushSMSSender: "Nom de l'envoyer : 3-11 caractères alphanumériques avec espace (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea Device ID",
"Apprise URL": "Apprise URL",
"Example:": "Exemple : {0}",
"Read more:": "En savoir plus : {0}",
"Status:": "Status : {0}",
"Read more": "En savoir plus",
appriseInstalled: "Apprise est intallé.",
appriseNotInstalled: "Apprise n'est pas intallé. {0}",
"Access Token": "Access Token",
"Channel access token": "Channel access token",
"Line Developers Console": "Line Developers Console",
lineDevConsoleTo: "Line Developers Console - {0}",
"Basic Settings": "Paramètres de base",
"User ID": "Identifiant utilisateur",
"Messaging API": "Messaging API",
wayToGetLineChannelToken: "Premièrement accéder à {0}, créez un Provider et un Salon (Messaging API), puis vous pourrez avoir le Token d'accès du salon ainsi que l'Identifiant utilisateur depuis le même menu.",
"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",
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
};

285
src/languages/id-ID.js Normal file
View File

@@ -0,0 +1,285 @@
export default {
languageName: "Bahasa Indonesia (Indonesian)",
checkEverySecond: "Cek Setiap {0} detik.",
retryCheckEverySecond: "Coba lagi setiap {0} detik.",
retriesDescription: "Percobaan ulang maksimum sebelum layanan dinyatakan tidak aktif dan notifikasi dikirim",
ignoreTLSError: "Abaikan kesalahan TLS/SSL untuk situs web HTTPS",
upsideDownModeDescription: "Balikkan statusnya. Jika layanan dapat dijangkau, TIDAK AKTIF.",
maxRedirectDescription: "Jumlah maksimum pengalihan untuk diikuti. Setel ke 0 untuk menonaktifkan pengalihan.",
acceptedStatusCodesDescription: "Pilih kode status yang dianggap sebagai tanggapan yang berhasil.",
passwordNotMatchMsg: "Sandi kedua tidak cocok.",
notificationDescription: "Harap atur notifikasi ke monitor agar berfungsi.",
keywordDescription: "Cari kata kunci dalam code html atau JSON huruf besar-kecil berpengaruh",
pauseDashboardHome: "Jeda",
deleteMonitorMsg: "Apakah Anda mau menghapus monitor ini?",
deleteNotificationMsg: "Apakah Anda mau menghapus notifikasi ini untuk semua monitor?",
resoverserverDescription: "Cloudflare adalah server bawaan, Anda dapat mengubah server resolver kapan saja.",
rrtypeDescription: "Pilih RR-Type yang mau Anda monitor",
pauseMonitorMsg: "Apakah Anda yakin mau menjeda?",
enableDefaultNotificationDescription: "Untuk setiap monitor baru, notifikasi ini akan diaktifkan secara bawaan. Anda masih dapat menonaktifkan notifikasi secara terpisah untuk setiap monitor.",
clearEventsMsg: "Apakah Anda yakin mau menghapus semua event di monitor ini?",
clearHeartbeatsMsg: "Apakah Anda yakin mau menghapus semua heartbeats di monitor ini?",
confirmClearStatisticsMsg: "Apakah Anda yakin mau menghapus semua statistik?",
importHandleDescription: "Pilih 'Lewati yang ada' jika Anda ingin melewati setiap monitor atau notifikasi dengan nama yang sama. 'Timpa' akan menghapus setiap monitor dan notifikasi yang ada.",
confirmImportMsg: "Apakah Anda yakin untuk mengimpor cadangan? Pastikan Anda telah memilih opsi impor yang tepat.",
twoFAVerifyLabel: "Silakan ketik token Anda untuk memverifikasi bahwa 2FA berfungsi",
tokenValidSettingsMsg: "Tokennya benar! Anda sekarang dapat menyimpan pengaturan 2FA.",
confirmEnableTwoFAMsg: "Apakah Anda yakin ingin mengaktifkan 2FA?",
confirmDisableTwoFAMsg: "Apakah Anda yakin ingin menonaktifkan 2FA?",
Settings: "Pengaturan",
Dashboard: "Dasbor",
"New Update": "Pembaruan Baru",
Language: "Bahasa",
Appearance: "Tampilan",
Theme: "Tema",
General: "Umum",
Version: "Versi",
"Check Update On GitHub": "Cek Pembaruan di GitHub",
List: "Daftar",
Add: "Tambah",
"Add New Monitor": "Tambah Monitor Baru",
"Quick Stats": "Statistik",
Up: "Aktif",
Down: "Tidak Aktif",
Pending: "Tertunda",
Unknown: "Tidak diketahui",
Pause: "Jeda",
Name: "Nama",
Status: "Status",
DateTime: "Tanggal Waktu",
Message: "Pesan",
"No important events": "Tidak ada peristiwa penting",
Resume: "Lanjut",
Edit: "Ubah",
Delete: "Hapus",
Current: "Saat ini",
Uptime: "Waktu aktif",
"Cert Exp.": "Cert Exp.",
days: "hari-hari",
day: "hari",
"-day": "-hari",
hour: "Jam",
"-hour": "-Jam",
Response: "Tanggapan",
Ping: "Ping",
"Monitor Type": "Tipe Monitor",
Keyword: "Keyword",
"Friendly Name": "Nama yang Ramah",
URL: "URL",
Hostname: "Hostname",
Port: "Port",
"Heartbeat Interval": "Jarak Waktu Heartbeat ",
Retries: "Coba lagi",
"Heartbeat Retry Interval": "Jarak Waktu Heartbeat Mencoba kembali ",
Advanced: "Tingkat Lanjut",
"Upside Down Mode": "Mode Terbalik",
"Max. Redirects": "Maksimal Pengalihan",
"Accepted Status Codes": "Kode Status yang Diterima",
Save: "Simpan",
Notifications: "Notifikasi",
"Not available, please setup.": "Tidak tersedia, silakan atur.",
"Setup Notification": "Setel Notifikasi",
Light: "Terang",
Dark: "Gelap",
Auto: "Otomatis",
"Theme - Heartbeat Bar": "Tema - Heartbeat Bar",
Normal: "Normal",
Bottom: "Bawah",
None: "Tidak ada",
Timezone: "Zona Waktu",
"Search Engine Visibility": "Visibilitas Mesin Pencari",
"Allow indexing": "Mengizinkan untuk diindex",
"Discourage search engines from indexing site": "Mencegah mesin pencari untuk mengindex situs",
"Change Password": "Ganti Sandi",
"Current Password": "Sandi Lama",
"New Password": "Sandi Baru",
"Repeat New Password": "Ulangi Sandi Baru",
"Update Password": "Perbarui Kata Sandi",
"Disable Auth": "Nonaktifkan Autentikasi",
"Enable Auth": "Aktifkan Autentikasi",
Logout: "Keluar",
Leave: "Pergi",
"I understand, please disable": "Saya mengerti, silakan dinonaktifkan",
Confirm: "Konfirmasi",
Yes: "Ya",
No: "Tidak",
Username: "Nama Pengguna",
Password: "Sandi",
"Remember me": "Ingat saya",
Login: "Masuk",
"No Monitors, please": "Tidak ada monitor, silakan",
"add one": "tambahkan satu",
"Notification Type": "Tipe Notifikasi",
Email: "Surel",
Test: "Tes",
"Certificate Info": "Info Sertifikasi",
"Resolver Server": "Resolver Server",
"Resource Record Type": "Resource Record Type",
"Last Result": "Hasil Terakhir",
"Create your admin account": "Buat admin akun Anda",
"Repeat Password": "Ulangi Sandi",
"Import Backup": "Impor Cadangan",
"Export Backup": "Expor Cadangan",
Export: "Expor",
Import: "Impor",
respTime: "Tanggapan. Waktu (milidetik)",
notAvailableShort: "N/A",
"Default enabled": "Bawaan diaktifkan",
"Apply on all existing monitors": "Terapkan pada semua monitor yang ada",
Create: "Buat",
"Clear Data": "Bersihkan Data",
Events: "Peristiwa",
Heartbeats: "Heartbeats",
"Auto Get": "Ambil Otomatis",
backupDescription: "Anda dapat mencadangkan semua monitor dan semua notifikasi ke dalam berkas JSON.",
backupDescription2: "Catatan: Data sejarah dan peristiwa tidak disertakan.",
backupDescription3: "Data sensitif seperti notifikasi token disertakan dalam berkas ekspor, harap simpan dengan hati-hati.",
alertNoFile: "Silakan pilih berkas untuk diimpor.",
alertWrongFileType: "Silakan pilih berkas JSON.",
"Clear all statistics": "Hapus semua statistik",
"Skip existing": "Lewati yang ada",
Overwrite: "Timpa",
Options: "Opsi",
"Keep both": "Simpan keduanya",
"Verify Token": "Verifikasi Token",
"Setup 2FA": "Pengaturan 2FA",
"Enable 2FA": "Aktifkan 2FA",
"Disable 2FA": "Nonaktifkan 2FA",
"2FA Settings": "Pengaturan 2FA",
"Two Factor Authentication": "Autentikasi Dua Faktor",
Active: "Aktif",
Inactive: "Tidak Aktif",
Token: "Token",
"Show URI": "Lihat URI",
Tags: "Tanda",
"Add New below or Select...": "Tambahkan Baru di bawah atau Pilih...",
"Tag with this name already exist.": "Tanda dengan nama ini sudah ada.",
"Tag with this value already exist.": "Tanda dengan nilai ini sudah ada.",
color: "warna",
"value (optional)": "nilai (harus diisi)",
Gray: "Abu-abu",
Red: "Merah",
Orange: "Jingga",
Green: "Hijau",
Blue: "Biru",
Indigo: "Biru Tua",
Purple: "Ungu",
Pink: "Merah Muda",
"Search...": "Cari...",
"Avg. Ping": "Rata-rata Ping",
"Avg. Response": "Rata-rata Tanggapan",
"Entry Page": "Halaman Masuk",
statusPageNothing: "Tidak ada di sini, silakan tambahkan grup atau monitor.",
"No Services": "Tidak ada Layanan",
"All Systems Operational": "Semua Sistem Berfungsi",
"Partially Degraded Service": "Layanan Terdegradasi Sebagian",
"Degraded Service": "Layanan Terdegradasi",
"Add Group": "Tambah Grup",
"Add a monitor": "Tambah monitor",
"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",
"Bot Token": "Bot Token",
"You can get a token from": "Anda bisa mendapatkan token dari",
"Chat ID": "Chat ID",
supportTelegramChatID: "Mendukung Obrolan Langsung / Grup / Channel Chat ID",
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",
"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)",
secureOptionNone: "None / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Abaikan Kesalahan TLS",
"From Email": "Dari Surel",
"To Email": "Ke Surel",
smtpCC: "CC",
smtpBCC: "BCC",
"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",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "Anda dapat mempelajari cara membuat url webhook {0}.",
"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",
"Application Token": "Token Aplikasi",
"Server URL": "URL Peladen",
"Priority": "Prioritas",
"slack": "Slack",
"Icon Emoji": "Ikon Emoji",
"Channel Name": "Nama Saluran",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "Info lain tentang webhook: {0}",
aboutChannelName: "Masukan nama saluran di {0} Kolom Nama Saluran jika Anda ingin melewati saluran webhook. Contoh: #saluran-lain",
aboutKumaURL: "Jika Anda membiarkan bidang URL Uptime Kuma kosong, itu akan menjadi bawaan ke halaman Proyek Github.",
emojiCheatSheet: "Lembar contekan emoji: {0}",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea",
apprise: "Apprise (Mendukung 50+ layanan notifikasi)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"User Key": "Kunci pengguna",
"Device": "Perangkat",
"Message Title": "Judul Pesan",
"Notification Sound": "Suara Nofifikasi",
"More info on:": "Info lebih lanjut tentang: {0}",
pushoverDesc1: "Prioritas darurat (2) memiliki batas waktu bawaan 30 detik antara percobaan ulang dan akan kadaluwarsa setelah 1 jam.",
pushoverDesc2: "Jika Anda ingin mengirim pemberitahuan ke perangkat yang berbeda, isi kolom Perangkat.",
"SMS Type": "Tipe SMS",
octopushTypePremium: "Premium (Cepat - direkomendasikan untuk mengingatkan)",
octopushTypeLowCost: "Low Cost (Lambat, terkadang diblokir oleh operator)",
"Check octopush prices": "Cek harga octopush {0}.",
octopushPhoneNumber: "Nomer Telpon/HP (format internasional, contoh : +33612345678) ",
octopushSMSSender: "Nama Pengirim SMS : 3-11 karakter alfanumerik dan spasi (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea Device ID",
"Apprise URL": "Apprise URL",
"Example:": "Contoh: {0}",
"Read more:": "Baca lebih lajut: {0}",
"Status:": "Status: {0}",
"Read more": "Baca lebih lajut",
appriseInstalled: "Apprise diinstall.",
appriseNotInstalled: "Apprise tidak diinstall. {0}",
"Access Token": "Token Akses",
"Channel access token": "Token akses saluran",
"Line Developers Console": "Konsol Pengembang Line",
lineDevConsoleTo: "Konsol Pengembang Line - {0}",
"Basic Settings": "Pengaturan Dasar",
"User ID": "ID User",
"Messaging API": "Messaging API",
wayToGetLineChannelToken: "Pertama akses {0}, buat penyedia dan saluran (Messaging API), lalu Anda bisa mendapatkan token akses saluran dan id pengguna dari item menu yang disebutkan di atas.",
"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",
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.",
promosmsTypeSpeed: "SMS SPEED - Prioritas tertinggi dalam sistem. Sangat cepat dan dapat diAndalkan tetapi mahal (sekitar dua kali lipat dari harga SMS FULL).",
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
};

View File

@@ -1,20 +1,31 @@
export default {
languageName: "한국어",
checkEverySecond: "{0} 초마다 체크해요.",
checkEverySecond: "{0}초마다 확인해요.",
retryCheckEverySecond: "{0}초마다 다시 확인해요.",
retriesDescription: "서비스가 중단된 후 알림을 보내기 전 최대 재시도 횟수",
ignoreTLSError: "HTTPS 웹사이트에서 TLS/SSL 에러 무시하기",
upsideDownModeDescription: "서버 상태를 반대로 표시해요. 서버가 작동하면 오프라인으로 표시할 거요.",
maxRedirectDescription: "최대 리다이렉트 횟수요. 0을 입력하면 리다이렉트를 꺼요.",
upsideDownModeDescription: "서버 상태를 반대로 표시해요. 서버가 작동하면 오프라인으로 표시할 거요.",
maxRedirectDescription: "최대 리다이렉트 횟수요. 0을 입력하면 리다이렉트를 꺼요.",
acceptedStatusCodesDescription: "응답 성공으로 간주할 상태 코드를 정해요.",
passwordNotMatchMsg: "비밀번호 재입력이 일치하지 않아요.",
notificationDescription: "모니터링에 알림을 설정할 수 있어요.",
keywordDescription: "Html 이나 JSON에서 대소문자를 구분해 키워드를 검색해요.",
keywordDescription: "HTML 이나 JSON에서 대소문자를 구분해 키워드를 검색해요.",
pauseDashboardHome: "일시 정지",
deleteMonitorMsg: "정말 이 모니터링을 삭제할까요?",
deleteNotificationMsg: "정말 이 알림을 모든 모니터링에서 삭제할까요?",
resoverserverDescription: "Cloudflare가 기본 서버요, 원한다면 언제나 다른 resolver 서버로 변경할 수 있어요.",
resoverserverDescription: "Cloudflare가 기본 서버요, 원한다면 언제나 다른 Resolver 서버로 변경할 수 있어요.",
rrtypeDescription: "모니터링할 RR-Type을 선택해요.",
pauseMonitorMsg: "정말 이 모니터링을 일시 정지 할까요?",
pauseMonitorMsg: "정말 이 모니터링을 일시 정지할까요?",
enableDefaultNotificationDescription: "새로 추가하는 모든 모니터링에 이 알림을 기본적으로 활성화해요. 각 모니터에 대해 별도로 알림을 비활성화할 수 있어요.",
clearEventsMsg: "정말 이 모니터링에 대한 모든 이벤트를 삭제할까요?",
clearHeartbeatsMsg: "정말 이 모니터링에 대한 모든 하트비트를 삭제할까요?",
confirmClearStatisticsMsg: "정말 모든 통계를 삭제할까요?",
importHandleDescription: "이름이 같은 모든 모니터링이나 알림을 건너뛰려면 '기존값 건너뛰기'를 선택해주세요. '덮어쓰기'는 기존의 모든 모니터링과 알림을 삭제해요.",
confirmImportMsg: "정말 백업을 가져올까요? 가져오기 옵션을 제대로 설정했는지 다시 확인해주세요.",
twoFAVerifyLabel: "토큰을 입력해 2단계 인증이 작동하는지 확인해주세요.",
tokenValidSettingsMsg: "토큰이 유효해요! 이제 2단계 인증 설정을 저장할 수 있어요.",
confirmEnableTwoFAMsg: "정말 2단계 인증을 활성화할까요?",
confirmDisableTwoFAMsg: "정말 2단계 인증을 비활성화할까요?",
Settings: "설정",
Dashboard: "대시보드",
"New Update": "새로운 업데이트",
@@ -59,13 +70,14 @@ export default {
Port: "포트",
"Heartbeat Interval": "하트비트 주기",
Retries: "재시도",
"Heartbeat Retry Interval": "하트비드 재시도 주기",
Advanced: "고급",
"Upside Down Mode": "상태 반전 모드",
"Max. Redirects": "최대 리다이렉트",
"Accepted Status Codes": "응답 성공 상태 코드",
Save: "저장",
Notifications: "알림",
"Not available, please setup.": "존재하지 않아요, 새로운거 하나 만드는건 어때요?",
"Not available, please setup.": "존재하지 않아요, 새로운 거 하나 만드는 건 어때요?",
"Setup Notification": "알림 설정",
Light: "라이트",
Dark: "다크",
@@ -73,7 +85,7 @@ export default {
"Theme - Heartbeat Bar": "테마 - 하트비트 바",
Normal: "기본값",
Bottom: "가운데",
None: "제거",
None: "없애기",
Timezone: "시간대",
"Search Engine Visibility": "검색 엔진 활성화",
"Allow indexing": "인덱싱 허용",
@@ -83,8 +95,8 @@ export default {
"New Password": "새로운 비밀번호",
"Repeat New Password": "새로운 비밀번호 재입력",
"Update Password": "비밀번호 변경",
"Disable Auth": "인증 끄기",
"Enable Auth": "인증 켜기",
"Disable Auth": "인증 비활성화",
"Enable Auth": "인증 활성화",
Logout: "로그아웃",
Leave: "나가기",
"I understand, please disable": "기능에 대해 이해했으니 꺼주세요.",
@@ -106,31 +118,29 @@ export default {
"Last Result": "최근 결과",
"Create your admin account": "관리자 계정 만들기",
"Repeat Password": "비밀번호 재입력",
"Import Backup": "백업 가져오기",
"Export Backup": "백업 내보내기",
Export: "내보내기",
Import: "가져오기",
respTime: "응답 시간 (ms)",
notAvailableShort: "N/A",
"Default enabled": "기본 알림으로 설정",
"Apply on all existing monitors": "기존 모니터링에 모두 적용하기",
Create: "생성하기",
clearEventsMsg: "정말로 이 모니터링부터 모든 이벤트를 제거할까요?",
clearHeartbeatsMsg: "정말로 이 모니터링부터 모든 하트비트를 제거할까요?",
confirmClearStatisticsMsg: "정말로 모든 통계치를 제거할까요?",
"Clear Data": "데이터 클리어",
"Clear Data": "데이터 삭제",
Events: "이벤트",
Heartbeats: "하트비트",
"Auto Get": "자동 Get",
enableDefaultNotificationDescription: "모든 모니터링에 이 알림이 기본값으로 설정될거에요. 각각 모니터링에서 이 알림을 비활성화 할 수 있어요.",
"Default enabled": "기본값 ",
"Also apply to existing monitors": "기존 모니터링에도 적용되요.",
Export: "내보내기",
Import: "가져오기",
backupDescription: "모든 모니터링과 알림을 JSON 파일 형식에 저장할 수 있어요.",
backupDescription2: "(히스토리와 이벤트 데이터는 포함되어 있지 않아요.)",
backupDescription2: "히스토리와 이벤트 데이터는 포함되어 있지 않아요.",
backupDescription3: "알림 토큰과 같은 보안 데이터가 내보내기 파일에 포함되어 있으므로 관리에 주의해주세요.",
alertNoFile: "가져오기를 하기 위해 파일을 선택해주세요.",
alertWrongFileType: "JSON 파일을 선택해주세요.",
twoFAVerifyLabel: "2단계 인증이 정상적으로 등록됬는지 확인하기 위해 토큰을 입력해주세요.",
tokenValidSettingsMsg: "토큰이 정상 값 이에요! 2단계 인증 설정을 저장할 수 있어요.",
confirmEnableTwoFAMsg: "정말로 2단계 인증을 활성화 할까요?",
confirmDisableTwoFAMsg: "정말로 2단계 인증을 비활성화 할까요?",
"Apply on all existing monitors": "기존 모니터링에 모두 적용하기",
"Clear all statistics": "모든 통계치 삭제",
"Skip existing": "기존값 건너뛰기",
Overwrite: "덮어쓰기",
Options: "옵션",
"Keep both": "두개 모두 보존",
"Verify Token": "토큰 검증",
"Setup 2FA": "2단계 인증 설정하기",
"Enable 2FA": "2단계 인증 활성화",
@@ -141,17 +151,6 @@ export default {
Inactive: "비활성화",
Token: "토큰",
"Show URI": "URI 보기",
"Clear all statistics": "모든 통계치 ",
retryCheckEverySecond: "{0} 초마다 재시도",
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.": "같은 태그 이름이 이미 존재해요.",
@@ -159,10 +158,10 @@ export default {
color: "색상",
"value (optional)": "값 (선택)",
Gray: "회색",
Red: "빨색",
Red: "빨색",
Orange: "주황색",
Green: "초록색",
Blue: "파색",
Blue: "파색",
Indigo: "남색",
Purple: "보라색",
Pink: "핑크색",
@@ -176,26 +175,108 @@ export default {
"Partially Degraded Service": "일부 시스템 비정상",
"Degraded Service": "모든 시스템 비정상",
"Add Group": "그룹 추가",
"Add a monitor": "모니터링 추가r",
"Add a monitor": "모니터링 추가",
"Edit Status Page": "상태 페이지 수정",
"Go to Dashboard": "대보드로 가기",
"Go to Dashboard": "대보드로 가기",
"Status Page": "상태 페이지",
defaultNotificationName: "내 {notification} 알림 ({number})",
here: "여기",
Required: "필수",
telegram: "Telegram",
"Bot Token": "봇 토큰",
wayToGetTelegramToken: "토큰은 여기서 얻을 수 있어요: {0}.",
"Chat ID": "채팅 ID",
supportTelegramChatID: "Direct Chat / Group / Channel's Chat ID를 지원해요.",
wayToGetTelegramChatID: "봇에 메시지를 보내 채팅 ID를 얻고 밑에 URL로 이동해 chat_id를 볼 수 있어요.",
"YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
chatIDNotFound: "채팅 ID를 찾을 수 없어요. 먼저 봇에게 메시지를 보내주세요.",
webhook: "Webhook",
"Post URL": "Post URL",
"Content Type": "Content Type",
webhookJsonDesc: "{0}은 express.js와 같은 최신 HTTP 서버에 적합해요.",
webhookFormDataDesc: "{multipart}은 PHP에 적합해요. {decodeFunction}를 기준으로 json을 디코딩하면 돼요.",
smtp: "Email (SMTP)",
secureOptionNone: "없음 / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "TLS 에러 무시하기",
"From Email": "보내는 이메일",
"To Email": "받는 이메일",
smtpCC: "참조",
smtpBCC: "숨은 참조",
discord: "Discord",
"Discord Webhook URL": "Discord Webhook URL",
wayToGetDiscordURL: "서버 설정 -> 연동 -> 웹후크 보기 -> 새 웹후크에서 얻을 수 있어요.",
"Bot Display Name": "표시 이름",
"Prefix Custom Message": "접두사 메시지",
"Hello @everyone is...": "{'@'}everyone 서버 상태 알림이에요...",
teams: "Microsoft Teams",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "{0}에서 Webhook을 어떻게 만드는지 알아봐요.",
signal: "Signal",
Number: "숫자",
Recipients: "받는 사람",
needSignalAPI: "REST API를 사용하는 Signal 클라이언트가 있어야 해요.",
wayToCheckSignalURL: "밑에 URL을 확인해 URL 설정 방법을 볼 수 있어요.",
signalImportant: "중요: 받는 사람의 그룹과 숫자는 섞을 수 없어요!",
gotify: "Gotify",
"Application Token": "애플리케이션 토큰",
"Server URL": "서버 URL",
Priority: "Priority",
slack: "Slack",
"Icon Emoji": "아이콘 이모지",
"Channel Name": "채널 이름",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "Webhook에 대한 설명: {0}",
aboutChannelName: "Webhook 채널을 우회하려면 {0} 채널 이름칸에 채널 이름을 입력해주세요. 예: #기타-채널",
aboutKumaURL: "Uptime Kuma URL칸을 공백으로 두면 기본적으로 Project Github 페이지로 설정해요.",
emojiCheatSheet: "이모지 목록 시트: {0}",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea",
apprise: "Apprise (50개 이상 알림 서비스 )",
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": "SMS 종류",
octopushTypePremium: "프리미엄 (빠름) - 알림 기능에 적합해요)",
octopushTypeLowCost: "저렴한 요금 (느림) - 가끔 차단될 수 있어요)",
"Check octopush prices": "{0}에서 Octopush 가격을 확인할 수 있어요.",
octopushPhoneNumber: "휴대전화 번호 (intl format, eg : +33612345678) ",
octopushSMSSender: "보내는 사람 이름 : 3-11개의 영숫자 및 여백공간 (a-z, A-Z, 0-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": "Messaging API 메뉴",
wayToGetLineChannelToken: "먼저 {0}에 액세스하고, 공급자 및 채널 (Messaging API)을 만든 다음, 각 메뉴 밑에 언급된 메뉴에서 채널 액세스 토큰과 사용자 ID를 얻을 수 있어요.",
"Icon URL": "아이콘 URL",
aboutIconURL: "\"Icon URL\"에 사진 링크를 입력해 프로필 사진을 설정할 수 있어요. 아이콘 이모지가 설정되어 있으면 적용되지 않을 거예요.",
aboutMattermostChannelName: "채널 이름을 입력하면 Webhook이 게시할 기본 채널을 재설정할 수 있어요. 이 설정은 Mattermost 웹훅 설정에서 활성화해야 해요. 예: #기타-채널",
matrix: "매트릭스",
promosmsTypeEco: "SMS ECO - 저렴하지만 느리고 가끔 과부하에 걸려요. 폴란드 수신자만 사용할 수 있어요. ",
promosmsTypeFlash: "SMS FLASH - 메시지가 받는 사람 장치에 자동으로 표시돼요. 폴란드 수신자만 사용할 수 있어요.",
promosmsTypeFull: "SMS FULL - SMS 프리미엄 티어, 보내는 사람 이름을 먼저 등록해야 해요. 알림 기능에 적합해요.",
promosmsTypeSpeed: "SMS SPEED - 시스템에서 가장 높은 우선순위예요. 매우 빠르고 신뢰할 수 있지만 비용이 많이 들어요 (SMS 전체 가격의 약 두 배).",
promosmsPhoneNumber: "전화 번호 (폴란드 수신자라면 지역번호를 적지 않아도 돼요.)",
promosmsSMSSender: "SMS 보내는 사람 이름 : 미리 등록된 이름 혹은 기본값 중 하나예요: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
};

284
src/languages/nb-NO.js Normal file
View File

@@ -0,0 +1,284 @@
export default {
languageName: "Norsk",
checkEverySecond: "Sjekk hvert {0} sekund.",
retryCheckEverySecond: "Prøv igjen hvert {0} sekund.",
retriesDescription: "Maksimalt antall forsøk før tjenesten er merket som nede og et varsel sendes",
ignoreTLSError: "Ignorer TLS/SSL-feil for HTTPS-nettsteder",
upsideDownModeDescription: "Snu statusen opp ned. Hvis tjenesten er tilgjengelig, er den NED.",
maxRedirectDescription: "Maksimalt antall viderekoblinger å følge. Sett til 0 for å deaktivere viderekoblinger.",
acceptedStatusCodesDescription: "Velg statuskoder som anses som et vellykket svar.",
passwordNotMatchMsg: "Passordene stemmer ikke overens.",
notificationDescription: "Tilordne et varsel for å overvåkningen for å få det til å fungere.",
keywordDescription: "Søk etter nøkkelord i vanlig HTML eller JSON, og det er versalfølsom",
pauseDashboardHome: "Pause",
deleteMonitorMsg: "Er du sikker på at du vil slette denne overvåkningen?",
deleteNotificationMsg: "Er du sikker på at du vil slette dette varselet for alle overvåkningene?",
resoverserverDescription: "Cloudflare er standardserveren, kan du når som helst endre DNS-serveren.",
rrtypeDescription: "Velg RR-typen du vil overvåke",
pauseMonitorMsg: "Er du sikker på at du vil sette en pause?",
enableDefaultNotificationDescription: "For hver ny overvåkning vil denne varslingen være aktivert som standard. Du kan fortsatt deaktivere varselet separat for hver overvåkning.",
clearEventsMsg: "Er du sikker på at du vil slette alle hendelser for denne overvåkningen?",
clearHeartbeatsMsg: "Er du sikker på at du vil slette alle hjerteslag for denne overvåkningen?",
confirmClearStatisticsMsg: "Er du sikker på at du vil slette ALL statistikk?",
importHandleDescription: "Velg 'Hopp over eksisterende' hvis du vil hoppe over hver overvåkning eller varsel med samme navn. 'Overskriv' sletter alle eksisterende overvåkninger og varsler.",
confirmImportMsg: "Er du sikker på å importere sikkerhetskopien? Sørg for at du har valgt riktig importalternativ.",
twoFAVerifyLabel: "Skriv inn tokenet ditt for å bekrefte at 2FA fungerer",
tokenValidSettingsMsg: "Token er gyldig! Du kan nå lagre 2FA-innstillingene.",
confirmEnableTwoFAMsg: "Er du sikker på at du vil aktivere 2FA?",
confirmDisableTwoFAMsg: "Er du sikker på at du vil deaktivere 2FA?",
Settings: "Innstillinger",
Dashboard: "Dashboard",
"New Update": "Ny Oppdatering",
Language: "Språk",
Appearance: "Utseende",
Theme: "Tema",
General: "Generelt",
Version: "Versjon",
"Check Update On GitHub": "Sjekk oppdatering på GitHub",
List: "Liste",
Add: "Legg til",
"Add New Monitor": "Legg til ny overvåkning",
"Quick Stats": "Statistikk",
Up: "Oppe",
Down: "Nede",
Pending: "Avventer",
Unknown: "Ukjent",
Pause: "Pause",
Name: "Navn",
Status: "Status",
DateTime: "Dato tid",
Message: "Melding",
"No important events": "Ingen viktige hendelser",
Resume: "Fortsett",
Edit: "Endre",
Delete: "Slett",
Current: "Nåværende",
Uptime: "Oppetid",
"Cert Exp.": "Sertifikat utløper",
days: "dager",
day: "dag",
"-day": "-dag",
hour: "time",
"-hour": "-time",
Response: "Respons",
Ping: "Ping",
"Monitor Type": "Overvåkningstype",
Keyword: "Stikkord",
"Friendly Name": "Vennlig navn",
URL: "URL",
Hostname: "Vertsnavn",
Port: "Port",
"Heartbeat Interval": "Hjerteslagsintervall",
Retries: "Forsøk",
"Heartbeat Retry Interval": "Hjerteslagsforsøkintervall",
Advanced: "Avansert",
"Upside Down Mode": "Opp-ned-modus",
"Max. Redirects": "Maks. viderekoblinger",
"Accepted Status Codes": "Godkjente statuskoder",
Save: "Lagre",
Notifications: "Varsler",
"Not available, please setup.": "Ikke tilgjengelig, sett opp.",
"Setup Notification": "Sett opp varsel",
Light: "Lys",
Dark: "Mørk",
Auto: "Auto",
"Theme - Heartbeat Bar": "Theme - Heartbeat Bar",
Normal: "Normal",
Bottom: "Bunn",
None: "Ingen",
Timezone: "Tidssone",
"Search Engine Visibility": "Søkemotor synlighet",
"Allow indexing": "Tillat indeksering",
"Discourage search engines from indexing site": "Avskrekk søkemotorer fra å indeksere nettstedet",
"Change Password": "Endre passord",
"Current Password": "Nåværende passord",
"New Password": "Nytt passord",
"Repeat New Password": "Gjenta nytt passord",
"Update Password": "Oppdater passord",
"Disable Auth": "Deaktiver autentisering",
"Enable Auth": "Aktiver autentisering",
Logout: "Logg ut",
Leave: "Forlat",
"I understand, please disable": "Jeg forstår, deaktiver",
Confirm: "Bekreft",
Yes: "Ja",
No: "Nei",
Username: "Brukernavn",
Password: "Passord",
"Remember me": "Husk meg",
Login: "Logg inn",
"No Monitors, please": "Ingen overvåkning, vær så snill",
"add one": "legg til en",
"Notification Type": "Meldingstype",
Email: "E-post",
Test: "Test",
"Certificate Info": "Sertifikatinformasjon",
"Resolver Server": "DNS-server",
"Resource Record Type": "DNS-posttype",
"Last Result": "Siste resultat",
"Create your admin account": "Opprett en administratorkonto",
"Repeat Password": "Gjenta passord",
"Import Backup": "Importer sikkerhetskopi",
"Export Backup": "Eksporter sikkerhetskopi",
Export: "Eksporter",
Import: "Importer",
respTime: "Svartid (ms)",
notAvailableShort: "N/A",
"Default enabled": "Standard aktivert",
"Apply on all existing monitors": "Påfør på alle eksisterende overvåkninger",
Create: "Opprett",
"Clear Data": "Slett data",
Events: "Hendelser",
Heartbeats: "Hjerteslag",
"Auto Get": "Auto Get",
backupDescription: "Du kan sikkerhetskopiere alle overvåkninger og alle varsler til en JSON-fil.",
backupDescription2: "PS: Historikk og hendelsesdata er ikke inkludert.",
backupDescription3: "Følsomme data som varslingstokener er inkludert i eksportfilen. Vennligst oppbevar dem nøye.",
alertNoFile: "Velg en fil som skal importeres.",
alertWrongFileType: "Velg en JSON-fil.",
"Clear all statistics": "Fjern all statistikk",
"Skip existing": "Hopp over eksisterende",
Overwrite: "Overskriv",
Options: "Alternativer",
"Keep both": "Behold begge",
"Verify Token": "Bekreft token",
"Setup 2FA": "Konfigurer 2FA",
"Enable 2FA": "Aktiver 2FA",
"Disable 2FA": "Deaktiver 2FA",
"2FA Settings": "2FA Innstillinger",
"Two Factor Authentication": "To-faktor autentisering",
Active: "Aktiv",
Inactive: "Inaktiv",
Token: "Token",
"Show URI": "Vis URI",
Tags: "Etiketter",
"Add New below or Select...": "Legg til nytt nedenfor eller Velg ...",
"Tag with this name already exist.": "Etikett med dette navnet eksisterer allerede.",
"Tag with this value already exist.": "Etikett med denne verdien finnes allerede.",
color: "farge",
"value (optional)": "verdi (valgfritt)",
Gray: "Grå",
Red: "Rød",
Orange: "Oransje",
Green: "Grønn",
Blue: "Blå",
Indigo: "Indigo",
Purple: "Lilla",
Pink: "Rosa",
"Search...": "Søk...",
"Avg. Ping": "Gj.sn. Ping",
"Avg. Response": "Gj.sn. Respons",
"Entry Page": "Oppføringsside",
statusPageNothing: "Ingenting her, vennligst legg til en gruppe eller en overvåkning.",
"No Services": "Ingen tjenester",
"All Systems Operational": "Alle systemer i drift",
"Partially Degraded Service": "Delvis degradert drift",
"Degraded Service": "Degradert drift",
"Add Group": "Legg til gruppe",
"Add a monitor": "Legg til en overvåkning",
"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",
"Bot Token": "Bot Token",
wayToGetTelegramToken: "Du kan få et token fra {0}.",
"Chat ID": "Chat ID",
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
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",
"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)",
secureOptionNone: "None / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignorer TLS feilmelding",
"From Email": "Fra E-post",
"To Email": "Til E-post",
smtpCC: "CC",
smtpBCC: "BCC",
"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",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "Du kan lære hvordan du oppretter en webhook-URL {0}.",
"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",
"Application Token": "Application Token",
"Server URL": "Server URL",
"Priority": "Prioritet",
"slack": "Slack",
"Icon Emoji": "Icon Emoji",
"Channel Name": "Kanal navn",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "Mer informasjon om webhooks på: {0}",
aboutChannelName: "Skriv inn kanalnavnet på {0} Kanalnavn-feltet hvis du vil omgå webhook-kanalen. Eks: #other-channel",
aboutKumaURL: "Hvis du lar Uptime Kuma URL feltet være blank, den blir som standard til Github-siden for dette prosjektet.",
emojiCheatSheet: "Emoji cheat sheet: {0}",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"User Key": "User Key",
"Device": "Device",
"Message Title": "Message Title",
"Notification Sound": "Notification Sound",
"More info on:": "More info on: {0}",
pushoverDesc1: "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
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)",
"Check octopush prices": "Check octopush prices {0}.",
octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea Device ID",
"Apprise URL": "Apprise URL",
"Example:": "Example: {0}",
"Read more:": "Read more: {0}",
"Status:": "Status: {0}",
"Read more": "Read more",
appriseInstalled: "Apprise is installed.",
appriseNotInstalled: "Apprise is not installed. {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: "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",
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
};

View File

@@ -198,4 +198,10 @@ export default {
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
Method: "Methode",
Body: "Body",
Headers: "Headers",
PushUrl: "Push URL",
HeadersInvalidFormat: "The request headers is geen geldige JSON: ",
BodyInvalidFormat: "De request body is geen geldige JSON: "
};

View File

@@ -10,7 +10,7 @@ export default {
passwordNotMatchMsg: "Powtórzone hasło nie pasuje.",
notificationDescription: "Proszę przypisać powiadomienie do monitora(ów), aby działało.",
keywordDescription: "Wyszukiwanie słów kluczowych w zwykłym html lub odpowiedzi JSON. Wielkość liter ma znaczenie.",
pauseDashboardHome: "Wstrzymaj",
pauseDashboardHome: "Wstrzymane",
deleteMonitorMsg: "Czy na pewno chcesz usunąć ten monitor?",
deleteNotificationMsg: "Czy na pewno chcesz usunąć to powiadomienie dla wszystkich monitorów?",
resoverserverDescription: "Cloudflare jest domyślnym serwerem, możesz zmienić serwer resolver w każdej chwili.",
@@ -43,7 +43,7 @@ export default {
Down: "Offline",
Pending: "Oczekuje",
Unknown: "Nieznane",
Pause: "Wstrzymane",
Pause: "Wstrzymaj",
Name: "Nazwa",
Status: "Status",
DateTime: "Data i godzina",
@@ -100,7 +100,7 @@ export default {
Logout: "Wyloguj",
Leave: "Zostaw",
"I understand, please disable": "Rozumiem, proszę wyłączyć",
Confirm: "Potiwerdź",
Confirm: "Potwierdź",
Yes: "Tak",
No: "Nie",
Username: "Nazwa użytkownika",
@@ -185,7 +185,7 @@ export default {
"Required": "Wymagane",
"telegram": "Telegram",
"Bot Token": "Token Bota",
"You can get a token from": "Token można uzyskać z",
wayToGetTelegramToken: "Token można uzyskać z {0}.",
"Chat ID": "Identyfikator Czatu",
supportTelegramChatID: "Czat wsprarcia 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:",

View File

@@ -116,39 +116,39 @@ export default {
Events: "Olaylar",
Heartbeats: "Sağlık Durumları",
"Auto Get": "Otomatik Al",
retryCheckEverySecond: "Retry every {0} seconds.",
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. You can still disable the notification separately for each monitor.",
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",
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?",
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
"Import Backup": "Import Backup",
"Export Backup": "Export Backup",
Export: "Export",
Import: "Import",
"Default enabled": "Default enabled",
"Apply on all existing monitors": "Apply on all existing monitors",
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.",
"Clear all statistics": "Clear all Statistics",
"Skip existing": "Skip existing",
Overwrite: "Overwrite",
Options: "Options",
"Keep both": "Keep both",
"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",
Active: "Active",
Inactive: "Inactive",
retryCheckEverySecond: "{0} Saniyede bir dene.",
enableDefaultNotificationDescription: "Bu bildirim her yeni serviste aktif olacaktır. Bildirimi servisler için ayrı ayrı deaktive edebilirsiniz. ",
importHandleDescription: "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir. ",
confirmImportMsg: "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz. ",
twoFAVerifyLabel: "Lütfen tokeni yazarak 2FA doğrulamanın çalıştığından emin olunuz.",
tokenValidSettingsMsg: "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz. ",
confirmEnableTwoFAMsg: "2FA'ı etkinleştirmek istediğinizden emin misiniz?",
confirmDisableTwoFAMsg: "2FA'ı devre dışı bırakmak istediğinize emin misiniz?",
"Heartbeat Retry Interval": "Sağlık Dırımları Tekrar Deneme Sıklığı",
"Import Backup": "Yedeği içe aktar",
"Export Backup": "Yedeği dışa aktar",
Export: "Dışa aktar",
Import: "İçe aktar",
"Default enabled": "Varsayılan etkinleştirilmiş",
"Apply on all existing monitors": "Var olan bütün servislere uygula",
backupDescription: "Bütün servisleri ve bildirimleri JSON dosyasına yedekleyebilirsiniz.",
backupDescription2: "Not: Geçmiş ve etkinlik verileri içinde değildir.",
backupDescription3: "Dışa aktarma dosyasında bildirim tokeni gibi hassas veriler bulunur, dikkatli bir şekilde saklayınız.",
alertNoFile: "İçeri aktarmak için bir dosya seçiniz.",
alertWrongFileType: "Lütfen bir JSON dosyası seçiniz.",
"Clear all statistics": "Bütün istatistikleri temizle",
"Skip existing": "Var olanı atla",
Overwrite: "Üzerine yaz",
Options: "Seçenekler",
"Keep both": "İkisini sakla",
"Verify Token": "Tokeni doğrula",
"Setup 2FA": "2FA Kur",
"Enable 2FA": "2FA Etkinleştir",
"Disable 2FA": "2FA Devre dışı bırak",
"2FA Settings": "2FA Ayarları",
"Two Factor Authentication": "İki Faktörlü Kimlik Doğrulama (2FA)",
Active: "Aktif",
Inactive: "İnaktif",
Token: "Token",
"Show URI": "Show URI",
Tags: "Tags",

View File

@@ -22,7 +22,8 @@ export default {
Appearance: "外观设置",
Theme: "主题",
General: "基本设置",
Version: "Version",
"Primary Base URL": "站点地址URL",
Version: "版本",
"Check Update On GitHub": "检查更新",
List: "列表",
Add: "添加",
@@ -43,7 +44,7 @@ export default {
Delete: "删除",
Current: "当前",
Uptime: "可用率",
"Cert Exp.": "证书期",
"Cert Exp.": "证书有效期",
days: "天",
day: "天",
"-day": " 天",
@@ -63,6 +64,9 @@ export default {
"Upside Down Mode": "反向监控",
"Max. Redirects": "重定向次数",
"Accepted Status Codes": "有效状态码",
"Push URL": "推送链接",
needPushEvery: "你需要每 {0} 秒调用一次。",
pushOptionalParams: "可选参数:{0}",
Save: "保存",
Notifications: "消息通知",
"Not available, please setup.": "无可用通道,请先设置",
@@ -103,10 +107,10 @@ export default {
"Certificate Info": "证书信息",
"Resolver Server": "解析服务器",
"Resource Record Type": "资源记录类型",
"Last Result": "Last Result",
"Last Result": "最后结果",
"Create your admin account": "创建管理员账号",
"Repeat Password": "重复密码",
respTime: "Resp. Time (ms)",
respTime: "响应时间(毫秒)",
notAvailableShort: "N/A",
Create: "创建",
clearEventsMsg: "确定要删除此监控项的所有事件吗?",
@@ -126,21 +130,21 @@ export default {
backupDescription3: "导出的文件中可能包含敏感信息,如消息通知的 Token 信息,请小心存放!",
alertNoFile: "请选择一个文件导入",
alertWrongFileType: "请选择一个 JSON 格式的文件",
twoFAVerifyLabel: "请输入Token以验证2FA(二次验证)是否正常工作",
tokenValidSettingsMsg: "Token有效您现在可以保存2FA(二次验证)设置",
confirmEnableTwoFAMsg: "确定要启用2FA(二次验证)吗?",
confirmDisableTwoFAMsg: "确定要禁用2FA(二次验证)吗?",
twoFAVerifyLabel: "请输入Token以验证 2FA二次验证是否正常工作",
tokenValidSettingsMsg: "Token有效您现在可以保存 2FA二次验证设置",
confirmEnableTwoFAMsg: "确定要启用 2FA二次验证吗?",
confirmDisableTwoFAMsg: "确定要禁用 2FA二次验证吗?",
"Apply on all existing monitors": "应用到所有监控项",
"Verify Token": "验证Token",
"Setup 2FA": "设置2FA",
"Enable 2FA": "启用2FA",
"Disable 2FA": "禁用2FA",
"2FA Settings": "2FA设置",
"Verify Token": "验证 Token",
"Setup 2FA": "设置 2FA",
"Enable 2FA": "启用 2FA",
"Disable 2FA": "禁用 2FA",
"2FA Settings": "2FA 设置",
"Two Factor Authentication": "双因素认证",
Active: "效",
Inactive: "效",
Active: "效",
Inactive: "未生效",
Token: "Token",
"Show URI": "显示URI",
"Show URI": "显示链接",
"Clear all statistics": "清除所有统计数据",
retryCheckEverySecond: "重试间隔 {0} 秒",
importHandleDescription: "如果想跳过同名的监控项或通知,请选择“跳过”;“覆盖”将删除所有现有的监控项和通知。",
@@ -167,7 +171,7 @@ export default {
Purple: "紫色",
Pink: "粉色",
"Search...": "搜索...",
"Avg. Ping": "平均Ping",
"Avg. Ping": "平均 Ping",
"Avg. Response": "平均响应",
"Entry Page": "入口页面",
statusPageNothing: "这里什么也没有,请添加一个分组或一个监控项。",
@@ -182,7 +186,7 @@ export default {
"Status Page": "状态页",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
smtp: "电子邮件(SMTP",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
@@ -194,8 +198,97 @@ export default {
octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
apprise: "Apprise (支持50+种通知服务)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"Feishu WebHookUrl": "飞书 WebHook 地址",
defaultNotificationName: "{notification} 通知({number}",
here: "这里",
Required: "必填",
"Bot Token": "Bot Token",
wayToGetTelegramToken: "你可以从 {0} 获取 Token。",
"Chat ID": "Chat ID",
supportTelegramChatID: "支持对话/群组/频道的 ID",
wayToGetTelegramChatID: "你可以发送一条消息给你的机器人然后到下面的链接来查看你的 chat_id",
"YOUR BOT TOKEN HERE": "这里替换成你的 BOT TOKEN",
chatIDNotFound: "没有找到 Chat ID请先给你的机器人发送一条消息。",
"Post URL": "目标链接",
"Content Type": "Content Type",
webhookJsonDesc: "{0} 适合现代的服务,比如 express.js",
webhookFormDataDesc: "{multipart} 适合PHP解码使用 {decodeFunction}",
secureOptionNone: "无 / STARTTLS25587",
secureOptionTLS: "TLS465",
"Ignore TLS Error": "忽略 TLS 错误",
"From Email": "发信人",
"To Email": "收信人",
smtpCC: "抄送",
smtpBCC: "密送",
"Discord Webhook URL": "Discord Webhook 链接",
wayToGetDiscordURL: "获取方式:服务器设置 -> 整合 -> 创建 Webhook",
"Bot Display Name": "机器人显示名称",
"Prefix Custom Message": "自定义消息前缀",
"Hello @everyone is...": "{'@'}所有人,……",
"Webhook URL": "Webhook 链接",
wayToGetTeamsURL: "你可以在 {0} 获取 Webhook 链接。",
Number: "号码",
Recipients: "收件人",
needSignalAPI: "你需要有一个带 REST API 的 Signal 客户端。",
wayToCheckSignalURL: "你可以通过下面的链接来了解如何设置:",
signalImportant: "重要:你不能混合设定收件人的分组和号码!",
"Application Token": "Application Token",
"Server URL": "服务器链接",
Priority: "优先级",
"Icon Emoji": "Emoji 图标",
"Channel Name": "频道名称",
"Uptime Kuma URL": "Uptime Kuma 链接",
aboutWebhooks: "关于 Webhook 的更多信息:{0}",
aboutChannelName: "如果你想绕过 Webhook 设定的频道,请在设定 {0} 的频道名称字段为你想要的频道。例:#other-channel",
aboutKumaURL: "如果保留 Uptime Kuma 链接为空,将会默认指向项目的 Github 页面。",
emojiCheatSheet: "Emoji 参考表:{0}",
"User Key": "User Key",
Device: "设备",
"Message Title": "消息标题",
"Notification Sound": "通知铃声",
"More info on:": "更多信息:{0}",
pushoverDesc1: "紧急优先级2会在一小时内每30秒重试一次。",
pushoverDesc2: "如果你想发送通知给不同的设备,请填写“设备”字段。",
"SMS Type": "短信类型",
octopushTypePremium: "Premium快 - 推荐用于警报)",
octopushTypeLowCost: "Low Cost慢 - 有时会被运营商屏蔽)",
"Check octopush prices": "查看 Octopush 的价格 {0}。",
octopushPhoneNumber: "电话号码(国际格式,例:+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": "Access Token",
"Channel access token": "频道 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: "首先访问 {0}创建一个提供者和频道Messaging API然后你就可以从上面提到的地方获取频道的 access token 和用户 ID。",
"Icon URL": "图标链接",
aboutIconURL: "你可以在“Icon URL”中提供一个图片地址来覆盖默认的资料图片。如果设置了 Emoji 图标此字段会被忽略。",
aboutMattermostChannelName: "如果你想覆盖 Webhook 设定的频道,请在“频道名称”字段为你想要的频道。这需要在 Mattermost 的 Webhook 设定中启用。例:#other-channel",
matrix: "Matrix",
promosmsTypeEco: "SMS ECO - 便宜但是慢,并且容易超负荷。仅限波兰地区的收件人。",
promosmsTypeFlash: "SMS FLASH - 消息会自动显示在收件人设备上。仅限波兰地区的收件人。",
promosmsTypeFull: "SMS FULL - 高等级,你可以使用你自己的发件人名称(你需要先注册一个). 对于警报来说更可靠。",
promosmsTypeSpeed: "SMS SPEED - 最高优先级。非常快速可靠,但更贵(越两倍 SMS FULL 等级的价格)。",
promosmsPhoneNumber: "电话号码(波兰地区收件人可以不填区号)",
promosmsSMSSender: "短信发件人名称已注册的名称或以下默认值之一InfoSMSSMS InfoMaxSMSINFOSMS",
checkPrice: "查看 {0} 的价格:",
octopushLegacyHint: "你是否在使用旧版本的 Octopush2011-2020",
matrixHomeserverURL: "服务器链接(开头带 http(s):// 和可能的需要的端口号)",
"Internal Room Id": "Internal Room Id",
matrixDesc1: "你可以在 Matrix 客户端房间设置的高级选项找到 Internal Room Id。格式类似于 !QMdRCpUIfLwsfjxye6:home.server。",
matrixDesc2: "请不要使用你自己的 Access Token这将开放你所有的账户权限和你加入的房间权限。你可以创建一个新的用户并邀请它至你允许的的房间中。你可以运行以下命令来获取 Access Token{0}",
};

View File

@@ -347,7 +347,7 @@ export default {
let result = {};
let unknown = {
text: "Unknown",
text: this.$t("Unknown"),
color: "secondary",
};
@@ -358,17 +358,17 @@ export default {
result[monitorID] = unknown;
} else if (lastHeartBeat.status === 1) {
result[monitorID] = {
text: "Up",
text: this.$t("Up"),
color: "primary",
};
} else if (lastHeartBeat.status === 0) {
result[monitorID] = {
text: "Down",
text: this.$t("Down"),
color: "danger",
};
} else if (lastHeartBeat.status === 2) {
result[monitorID] = {
text: "Pending",
text: this.$t("Pending"),
color: "warning",
};
} else {

View File

@@ -29,6 +29,9 @@
<option value="push">
Push
</option>
<option value="steam">
Steam Game Server
</option>
</select>
</div>
@@ -46,11 +49,11 @@
<!-- Push URL -->
<div v-if="monitor.type === 'push' " class="my-3">
<label for="push-url" class="form-label">{{ $t("Push URL") }}</label>
<label for="push-url" class="form-label">{{ $t("PushUrl") }}</label>
<CopyableInput id="push-url" v-model="pushURL" type="url" disabled="disabled" />
<div class="form-text">
You should call this url every {{ monitor.interval }} seconds.<br />
Optional parameters: msg, ping
{{ $t("needPushEvery", [monitor.interval]) }}<br />
{{ $t("pushOptionalParams", ["msg, ping"]) }}
</div>
</div>
@@ -63,18 +66,21 @@
</div>
</div>
<!-- TCP Port / Ping / DNS only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' " class="my-3">
<!-- Hostname -->
<!-- TCP Port / Ping / DNS / Steam only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam'" class="my-3">
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
</div>
<!-- For TCP Port Type -->
<div v-if="monitor.type === 'port' " class="my-3">
<!-- Port -->
<!-- For TCP Port / Steam Type -->
<div v-if="monitor.type === 'port' || monitor.type === 'steam'" class="my-3">
<label for="port" class="form-label">{{ $t("Port") }}</label>
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
</div>
<!-- DNS Resolver Server -->
<!-- For DNS Type -->
<template v-if="monitor.type === 'dns'">
<div class="my-3">
@@ -140,7 +146,7 @@
</label>
</div>
<div v-if="monitor.type !== 'push'" class="my-3 form-check">
<div class="my-3 form-check">
<input id="upside-down" v-model="monitor.upsideDown" class="form-check-input" type="checkbox">
<label class="form-check-label" for="upside-down">
{{ $t("Upside Down Mode") }}
@@ -195,6 +201,7 @@
<div class="col-md-6">
<div v-if="$root.isMobile" class="mt-3" />
<!-- Notifications -->
<h2 class="mb-2">{{ $t("Notifications") }}</h2>
<p v-if="$root.notificationList.length === 0">
{{ $t("Not available, please setup.") }}
@@ -214,6 +221,51 @@
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
{{ $t("Setup Notification") }}
</button>
<!-- HTTP Options -->
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
<!-- Method -->
<div class="my-3">
<label for="method" class="form-label">{{ $t("Method") }}</label>
<select id="method" v-model="monitor.method" class="form-select">
<option value="GET">
GET
</option>
<option value="POST">
POST
</option>
<option value="PUT">
PUT
</option>
<option value="PATCH">
PATCH
</option>
<option value="DELETE">
DELETE
</option>
<option value="HEAD">
HEAD
</option>
<option value="OPTIONS">
OPTIONS
</option>
</select>
</div>
<!-- Body -->
<div class="my-3">
<label for="body" class="form-label">{{ $t("Body") }}</label>
<textarea id="body" v-model="monitor.body" class="form-control" :placeholder="bodyPlaceholder"></textarea>
</div>
<!-- Headers -->
<div class="my-3">
<label for="headers" class="form-label">{{ $t("Headers") }}</label>
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
</div>
</template>
</div>
</div>
</div>
@@ -285,6 +337,14 @@ export default {
pushURL() {
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping=";
},
bodyPlaceholder() {
return "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}";
},
headersPlaceholder() {
return "{\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n}";
}
},
@@ -295,7 +355,7 @@ export default {
},
"monitor.interval"(value, oldValue) {
// Link interval and retryInerval if they are the same value.
// Link interval and retryInterval if they are the same value.
if (this.monitor.retryInterval === oldValue) {
this.monitor.retryInterval = value;
}
@@ -349,6 +409,7 @@ export default {
type: "http",
name: "",
url: "https://",
method: "GET",
interval: 60,
retryInterval: this.interval,
maxretries: 0,
@@ -383,9 +444,43 @@ export default {
},
isInputValid() {
if (this.monitor.body) {
try {
JSON.parse(this.monitor.body);
} catch (err) {
toast.error(this.$t("BodyInvalidFormat") + err.message);
return false;
}
}
if (this.monitor.headers) {
try {
JSON.parse(this.monitor.headers);
} catch (err) {
toast.error(this.$t("HeadersInvalidFormat") + err.message);
return false;
}
}
return true;
},
async submit() {
this.processing = true;
if (!this.isInputValid()) {
this.processing = false;
return;
}
// Beautiful the JSON format
if (this.monitor.body) {
this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4);
}
if (this.monitor.headers) {
this.monitor.headers = JSON.stringify(JSON.parse(this.monitor.headers), null, 4);
}
if (this.isAdd) {
this.$root.add(this.monitor, async (res) => {
@@ -422,8 +517,12 @@ export default {
};
</script>
<style scoped>
<style lang="scss" scoped>
.shadow-box {
padding: 20px;
}
textarea {
min-height: 200px;
}
</style>

View File

@@ -108,7 +108,7 @@
<!-- Primary Base URL -->
<div class="mb-4">
<label class="form-label" for="primaryBaseURL">Primary Base URL</label>
<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?://.+">
@@ -119,6 +119,25 @@
</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") }}
@@ -333,6 +352,12 @@
<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>
@@ -357,6 +382,12 @@
<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>
@@ -465,6 +496,10 @@ export default {
this.settings.entryPage = "dashboard";
}
if (this.settings.keepDataPeriodDays === undefined) {
this.settings.keepDataPeriodDays = 180;
}
this.loaded = true;
});
},

View File

@@ -1,118 +1,118 @@
"use strict";
// Common Util for frontend and backend
//
// DOT NOT MODIFY util.js!
// Need to run "tsc" to compile if there are any changes.
//
// Backend uses the compiled file util.js
// Frontend uses util.ts
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
const _dayjs = require("dayjs");
const dayjs = _dayjs;
exports.isDev = process.env.NODE_ENV === "development";
exports.appName = "Uptime Kuma";
exports.DOWN = 0;
exports.UP = 1;
exports.PENDING = 2;
exports.STATUS_PAGE_ALL_DOWN = 0;
exports.STATUS_PAGE_ALL_UP = 1;
exports.STATUS_PAGE_PARTIAL_DOWN = 2;
function flipStatus(s) {
if (s === exports.UP) {
return exports.DOWN;
}
if (s === exports.DOWN) {
return exports.UP;
}
return s;
}
exports.flipStatus = flipStatus;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
exports.sleep = sleep;
/**
* PHP's ucfirst
* @param str
*/
function ucfirst(str) {
if (!str) {
return str;
}
const firstLetter = str.substr(0, 1);
return firstLetter.toUpperCase() + str.substr(1);
}
exports.ucfirst = ucfirst;
function debug(msg) {
if (exports.isDev) {
console.log(msg);
}
}
exports.debug = debug;
function polyfill() {
/**
* String.prototype.replaceAll() polyfill
* https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
* @author Chris Ferdinandi
* @license MIT
*/
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function (str, newStr) {
// If a regex pattern
if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") {
return this.replace(str, newStr);
}
// If a string
return this.replace(new RegExp(str, "g"), newStr);
};
}
}
exports.polyfill = polyfill;
class TimeLogger {
constructor() {
this.startTime = dayjs().valueOf();
}
print(name) {
if (exports.isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
}
}
}
exports.TimeLogger = TimeLogger;
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
exports.getRandomArbitrary = getRandomArbitrary;
/**
* From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
*
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
exports.getRandomInt = getRandomInt;
function genSecret(length = 64) {
let secret = "";
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let charsLength = chars.length;
for (let i = 0; i < length; i++) {
secret += chars.charAt(Math.floor(Math.random() * charsLength));
}
return secret;
}
exports.genSecret = genSecret;
function getMonitorRelativeURL(id) {
return "/dashboard/" + id;
}
exports.getMonitorRelativeURL = getMonitorRelativeURL;
"use strict";
// Common Util for frontend and backend
//
// DOT NOT MODIFY util.js!
// Need to run "tsc" to compile if there are any changes.
//
// Backend uses the compiled file util.js
// Frontend uses util.ts
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
const _dayjs = require("dayjs");
const dayjs = _dayjs;
exports.isDev = process.env.NODE_ENV === "development";
exports.appName = "Uptime Kuma";
exports.DOWN = 0;
exports.UP = 1;
exports.PENDING = 2;
exports.STATUS_PAGE_ALL_DOWN = 0;
exports.STATUS_PAGE_ALL_UP = 1;
exports.STATUS_PAGE_PARTIAL_DOWN = 2;
function flipStatus(s) {
if (s === exports.UP) {
return exports.DOWN;
}
if (s === exports.DOWN) {
return exports.UP;
}
return s;
}
exports.flipStatus = flipStatus;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
exports.sleep = sleep;
/**
* PHP's ucfirst
* @param str
*/
function ucfirst(str) {
if (!str) {
return str;
}
const firstLetter = str.substr(0, 1);
return firstLetter.toUpperCase() + str.substr(1);
}
exports.ucfirst = ucfirst;
function debug(msg) {
if (exports.isDev) {
console.log(msg);
}
}
exports.debug = debug;
function polyfill() {
/**
* String.prototype.replaceAll() polyfill
* https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
* @author Chris Ferdinandi
* @license MIT
*/
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function (str, newStr) {
// If a regex pattern
if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") {
return this.replace(str, newStr);
}
// If a string
return this.replace(new RegExp(str, "g"), newStr);
};
}
}
exports.polyfill = polyfill;
class TimeLogger {
constructor() {
this.startTime = dayjs().valueOf();
}
print(name) {
if (exports.isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
}
}
}
exports.TimeLogger = TimeLogger;
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
exports.getRandomArbitrary = getRandomArbitrary;
/**
* From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
*
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
exports.getRandomInt = getRandomInt;
function genSecret(length = 64) {
let secret = "";
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let charsLength = chars.length;
for (let i = 0; i < length; i++) {
secret += chars.charAt(Math.floor(Math.random() * charsLength));
}
return secret;
}
exports.genSecret = genSecret;
function getMonitorRelativeURL(id) {
return "/dashboard/" + id;
}
exports.getMonitorRelativeURL = getMonitorRelativeURL;

44
test/backend.spec.js Normal file
View File

@@ -0,0 +1,44 @@
const { genSecret, sleep } = require("../src/util");
describe("Test genSecret", () => {
beforeAll(() => {
});
it("should be correct length", () => {
let secret = genSecret(-1);
expect(secret).toEqual("");
secret = genSecret(0);
expect(secret).toEqual("");
secret = genSecret(1);
expect(secret.length).toEqual(1);
secret = genSecret(2);
expect(secret.length).toEqual(2);
secret = genSecret(64);
expect(secret.length).toEqual(64);
secret = genSecret(9000);
expect(secret.length).toEqual(9000);
secret = genSecret(90000);
expect(secret.length).toEqual(90000);
});
it("should contain first and last possible chars", () => {
let secret = genSecret(90000);
expect(secret).toContain("A");
expect(secret).toContain("9");
});
});
describe("Test reset-password", () => {
it("should able to run", async () => {
await require("../extra/reset-password").main();
}, 120000);
});

View File

@@ -1,6 +1,7 @@
{
"compileOnSave": true,
"compilerOptions": {
"newLine": "LF",
"target": "es2018",
"module": "commonjs",
"lib": [