Compare commits

..

525 Commits

Author SHA1 Message Date
Frank Elsinga
c247ecdb10 tested reverting a change to the testing harness 2024-10-04 03:57:25 +02:00
Frank Elsinga
28c5191195 fixed the skip condition correctly targetting the platforms I want 2024-10-04 03:57:25 +02:00
Frank Elsinga
a6a662e751 made sure that skipping of the testcase uses the provided mechanisms 2024-10-04 03:57:25 +02:00
Frank Elsinga
e97fd40b54 excluded arm from testcontainer runs 2024-10-04 03:57:25 +02:00
UptimeKumaBot
e80a6ef728 Translations Update from Weblate (#5072)
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
Co-authored-by: Marco <marco@nanoweb.ch>
Co-authored-by: Ilkka Myller <ilkka.myller@nodefield.com>
Co-authored-by: Michal <black23@gmail.com>
Co-authored-by: Harry Suryapambagya <harsxv@gmail.com>
Co-authored-by: Lee Min Waan <rakis9076@gmail.com>
Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Co-authored-by: stanol <stanol777@gmail.com>
Co-authored-by: Hyeonho Kang <hyeonhokang10@o365.ice.go.kr>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: MrEddX <mreddx@chatrix.one>
Co-authored-by: Sajjad Jazini <s.jazini@vatanmail.ir>
Co-authored-by: Ossie Stinga <ossie.stinga@gmail.com>
Co-authored-by: Elia Ronchetti <e.ronchetti@campus.unimib.it>
Co-authored-by: B3CKDOOR <B3CKDOOR@users.noreply.weblate.kuma.pet>
Co-authored-by: Ivan M <ivan.meishutovich@gmail.com>
Co-authored-by: AlanFarley <alanfarley2010@live.com>
Co-authored-by: Mohammad Hesam Imani <hesamimany@gmail.com>
Co-authored-by: Shinwoo PARK <devpysweb@gmail.com>
Co-authored-by: Lucius Gaitán <lucius@luciusgaitan.com>
Co-authored-by: Ömer Faruk GÖL <omerfarukgol@hotmail.com>
Co-authored-by: Satveek Gupta <guptasatveek@gmail.com>
Co-authored-by: Aung ko <aungko.use@gmail.com>
2024-10-04 03:57:25 +02:00
Frank Elsinga
e262056512 Better description for shrink database button (#4814)
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-10-04 03:57:25 +02:00
Frank Elsinga
4bbd9430f3 made sure that CI is happy 2024-10-04 02:48:42 +02:00
Frank Elsinga
18d80b254b implemented a testcase for mqtt monitors 2024-10-04 02:30:06 +02:00
artshllaku
a309cf0e2c tests: improve playwright test readability (#5149) 2024-10-03 11:20:27 +02:00
Easy
0071775525 Add serverchan3 support for serverchan notification provider (#5145)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-10-01 10:28:47 +02:00
Louis Lam
46d90a6a99 1.23.14 changes merge to 2.0.0 (#5138) 2024-09-29 23:06:09 +08:00
Louis Lam
3479992302 Resolve conflict of package-lock.json 2024-09-29 22:13:05 +08:00
Louis Lam
030bb1c0b8 Merge branch 'master' into 1.23.14-to-2.0.0
# Conflicts:
#	package-lock.json
2024-09-29 22:12:25 +08:00
Louis Lam
7da401662f Merge branch '1.23.X' into 1.23.14-to-2.0.0
# Conflicts:
#	package-lock.json
#	package.json
2024-09-29 22:05:23 +08:00
Louis Lam
243726b03c Update to 1.23.14 2024-09-29 21:46:19 +08:00
Louis Lam
936665aac3 [1.23.X] Update dependencies (#5132) 2024-09-28 03:43:54 +08:00
Cassandra
b287a25de7 feat: structured logging (JSON) (#5118) 2024-09-24 17:51:21 +08:00
Ivan M.
dd75890364 Added a Belarusian language code (#5103) 2024-09-12 05:05:18 +08:00
David Luhmer
3c23a34fff Fix missing monitors in subgroups due to missing filterFunc (#5084)
Co-authored-by: David Luhmer <david.luhmer@wetteronline.de>
2024-09-04 14:33:53 +02:00
Jakob Lindskog
935194bca3 feat: implement very crude and bare-bones RSS feed (#5047) 2024-09-03 13:51:34 +02:00
Louis Lam
11f108b501 Remove .devcontainer (#5074) 2024-09-02 10:26:17 +08:00
Shaun
c567e8eb8e Status Page E2E spec (#5068) 2024-09-01 02:20:55 +02:00
Louis Lam
ba46945ea9 Fix weblate conflict and new translations (#5071) 2024-08-31 21:00:08 +08:00
Louis Lam
362a890bc3 Merge remote-tracking branch 'origin/master' into master-weblate
# Conflicts:
#	src/lang/pl.json
#	src/lang/uk-UA.json
2024-08-31 20:56:27 +08:00
Shaun
36f8be040d Monitor Conditions (#5048) 2024-08-30 21:48:13 +02:00
s3rgi
3346111090 Translated using Weblate (Catalan)
Currently translated at 22.4% (215 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
2024-08-29 13:48:41 +00:00
Frank Elsinga
032ac161f7 Fixed the url of the SIGNL4 docs as requested in a PR 2024-08-29 15:48:35 +02:00
Prabhsharan Singh
562de6abb4 fix: undefined in log while creating a new monitor (#5053)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-28 23:23:01 +02:00
Ron
716832b9f3 New Notification Provider SIGNL4 (#5058)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-28 23:18:25 +02:00
Shaun
3d9bbe1a62 Improve Playwright/E2E testing setup (#5056)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-28 23:18:04 +02:00
Aizat Faiz
01210ce88d Add url to pushover notification (#5055)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-28 03:24:29 +02:00
SIEEKUU
f8d34f22e3 Translated using Weblate (Polish)
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
2024-08-27 21:58:49 +00:00
Alone88
b059c19069 Add WPush Notification Provider (#5049)
Signed-off-by: Alone88 <anhaoyl@qq.com>
2024-08-26 04:57:56 +02:00
Dan Sullivan
dc3ad88fe1 Add option to pass cache bust param (#3525)
Co-authored-by: Dan Sullivan <dan@symbiosgroup.co.uk>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-24 22:57:31 +02:00
Cyril59310
4cb264afca Add translation keys (#5046) 2024-08-24 22:56:42 +02:00
Jay Bhanushali
3e88772e5d Fix: name column in the dashboad being squashed in some aspect ratios (#4193) 2024-08-24 19:44:51 +02:00
Frank Elsinga
9486fa22ee made sure that the socket handlers have consitent names (#4818) 2024-08-24 18:40:50 +02:00
Frank Elsinga
0ddf35e7b5 Removed now unnecessary version checking (#4815) 2024-08-24 18:38:50 +02:00
amyyeung17
4b68a86524 Extend length of status bars in status page (#4376)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: mobeicanyue <81098819+mobeicanyue@users.noreply.github.com>
2024-08-24 18:36:09 +02:00
Frank Elsinga
bde3d0e5ef Add translation key for time ago (#4813)
Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
2024-08-24 18:32:21 +02:00
Thomas Spalinger
643d28cebc Extend Prometheus Labels to include tags (requires restart for NEW labels on the monitor to be visible) (#4704)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-24 18:02:57 +02:00
Ilya
fe5035d9b3 Translated using Weblate (Russian)
Currently translated at 93.4% (894 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
2024-08-23 18:58:54 +00:00
vishalsabhaya
2a820ab16d marked library move devDependencies → dependencies (#5029) 2024-08-19 21:11:27 +08:00
15159617213
8e4b9dd9c1 Added translation using Weblate (Chinese (Literary)) 2024-08-14 20:03:49 +00:00
solidsniper
bab771df05 fix: !important in the prim-css editor is rendered incorrectly (#5018) 2024-08-14 16:58:33 +02:00
Leonardo Dimitrov
5fcc8cf34f Translated using Weblate (Macedonian)
Currently translated at 9.4% (90 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/mk/
2024-08-09 17:58:47 +00:00
Brook
895bf7c550 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
2024-08-08 16:41:55 +00:00
Leonardo Dimitrov
d6e254dfd6 Added translation using Weblate (Macedonian) 2024-08-08 16:41:44 +00:00
Dom Jasaitis
3a2385bc2b Translated using Weblate (Lithuanian)
Currently translated at 22.9% (220 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/lt/
2024-08-08 00:48:42 +00:00
Hasan Basri
92e982a910 Add OneSender Webhook notification (#4971)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-08-08 02:48:37 +02:00
Ikko Eltociear Ashimine
13f67462ad docs: fixed typo in the contribution guide (#4988) 2024-08-03 09:49:49 +02:00
pipemsn
f5f86e5524 Translated using Weblate (Thai)
Currently translated at 67.9% (650 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/th/
2024-08-03 04:56:45 +00:00
taisei
cd5644d6d2 Remove markdown with meta tags (#4968)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-07-31 17:58:13 +02:00
Dom Jasaitis
6dddf0319a Translated using Weblate (Lithuanian)
Currently translated at 12.5% (120 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/lt/
2024-07-30 04:56:31 +00:00
Dom Jasaitis
6e8d4e8d76 Added translation using Weblate (Lithuanian) 2024-07-30 01:31:05 +00:00
Ethan Figliola
74bb44e96e Translated using Weblate (Xhosa)
Currently translated at 0.5% (5 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/xh/
2024-07-29 05:34:11 +00:00
Ethan Figliola
e8c74457df Translated using Weblate (Spanish)
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
2024-07-29 05:34:10 +00:00
Marco Beretta
48539c4cce Translated using Weblate (Italian)
Currently translated at 69.2% (663 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/it/
2024-07-26 17:56:41 +00:00
Fabio Bassan
8138316b7d Translated using Weblate (Italian)
Currently translated at 69.2% (663 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/it/
2024-07-26 17:56:41 +00:00
Alex Javadi
8b096a5754 Translated using Weblate (Persian)
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
2024-07-25 14:24:06 +00:00
Phiton
36e7266fa4 Translated using Weblate (Slovak)
Currently translated at 58.3% (558 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sk/
2024-07-25 03:56:41 +00:00
Zandor Smith
50ec9e568c Translated using Weblate (Dutch)
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
2024-07-25 03:56:41 +00:00
J. Mulder
b690376750 Translated using Weblate (Dutch)
Currently translated at 100.0% (957 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
2024-07-25 03:56:41 +00:00
Harry Suryapambagya
58971335d0 Translated using Weblate (Indonesian)
Currently translated at 98.8% (946 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
2024-07-25 03:56:40 +00:00
Arian Ardeshiri
26ff2c3802 Translated using Weblate (Persian)
Currently translated at 92.2% (883 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
2024-07-25 03:56:40 +00:00
Phiton
da805ce097 Translated using Weblate (Slovak)
Currently translated at 36.2% (347 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sk/
2024-07-20 21:56:40 +00:00
SrIzan10
5ae45902e0 Translated using Weblate (Spanish)
Currently translated at 92.2% (883 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
2024-07-20 21:56:40 +00:00
Michal
ff37c68c1f Translated using Weblate (Czech)
Currently translated at 99.8% (956 of 957 strings)

Translation: Uptime Kuma/Uptime Kuma
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
2024-07-20 21:56:40 +00:00
Frank Elsinga
2c31f3a2ff Update Stale GitHub Action from v8 to v9 (#4950) 2024-07-18 08:42:31 +02:00
Ole
64073eaf71 Update Stale GitHub Action from v8 to v9 2024-07-18 02:40:56 +02:00
Frank Elsinga
ba448c765a Updated croner to 8.1.0 and fixed "recurring-interval" type maintenance (#4939) 2024-07-16 00:34:56 +02:00
Frank Elsinga
1822b55846 Merge branch 'master' into master 2024-07-16 00:30:48 +02:00
Frank Elsinga
e791c70c47 Fixed the package-lock.json 2024-07-16 00:29:56 +02:00
John Campbell
76deb6e0fa package.json - Update dependency version range for croner
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2024-07-15 17:21:27 -04:00
buzzinJohnnyBoi
30f7b48987 updated comments and ran linter on inferDuration 2024-07-15 17:04:16 -04:00
buzzinJohnnyBoi
4836f72cb8 added inferDuration to calculate cron duration 2024-07-15 16:56:41 -04:00
Frank Elsinga
4aef6780ed feat: Add SNMP Monitor (#4717) 2024-07-15 22:15:03 +02:00
Frank Elsinga
c82de20c7b fixed merge-typo 2024-07-15 22:07:13 +02:00
Frank Elsinga
71f9384c09 Merge branch 'master' into snmp-monitor 2024-07-15 21:57:12 +02:00
erphise
1d295441bb Translated using Weblate (Catalan)
Currently translated at 9.2% (89 of 957 strings)

Co-authored-by: erphise <github@erphise.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Michał Pietrzak
3bec59a205 Translated using Weblate (Polish)
Currently translated at 100.0% (957 of 957 strings)

Co-authored-by: Michał Pietrzak <michal.pietrzak@mphs.pl>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Zoltán Nagy
dab26b1cc8 Translated using Weblate (Hungarian)
Currently translated at 85.4% (818 of 957 strings)

Co-authored-by: Zoltán Nagy <pofagyalu@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Ivan Bratović
2bdd862340 Translated using Weblate (Croatian)
Currently translated at 100.0% (957 of 957 strings)

Co-authored-by: Ivan Bratović <ivanbratovic4@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hr/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Oton
921f1a2eb4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Portuguese)

Currently translated at 9.2% (89 of 957 strings)

Co-authored-by: Oton <oms.moreira@outlook.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Aindriú Mac Giolla Eoin
cc8c409489 Translated using Weblate (Irish)
Currently translated at 100.0% (957 of 957 strings)

Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ga/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
J. Mulder
3028d9b054 Translated using Weblate (Dutch)
Currently translated at 99.1% (949 of 957 strings)

Co-authored-by: J. Mulder <info@xoric.nl>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Leonardo Garcia
157e100bd0 Translated using Weblate (Portuguese)
Currently translated at 9.1% (87 of 956 strings)

Translated using Weblate (Xhosa)

Currently translated at 0.3% (3 of 956 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (956 of 956 strings)

Co-authored-by: Leonardo Garcia <leosgarcia@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/xh/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Yahor Haurylenka
6fe03585cc Translated using Weblate (Belarusian)
Currently translated at 97.5% (933 of 956 strings)

Added translation using Weblate (Belarusian)

Co-authored-by: Yahor Haurylenka <k1llo2810@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/be/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Max
73873d4580 Translated using Weblate (Chinese (Traditional))
Currently translated at 94.5% (904 of 956 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.3% (902 of 956 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 91.9% (879 of 956 strings)

Co-authored-by: Max <max.tsai@alfred.camera>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hant/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
AnnAngela
cada9aaef8 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (956 of 956 strings)

Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Lucas Elias Baccan
e08cee2dcb Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.9% (946 of 956 strings)

Co-authored-by: Lucas Elias Baccan <naccabbaccan@hotmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
KEINOS
a6980694bc Translated using Weblate (Japanese)
Currently translated at 70.9% (670 of 944 strings)

Co-authored-by: KEINOS <github@keinos.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
nazo6
032ba4a03e Translated using Weblate (Japanese)
Currently translated at 70.9% (670 of 944 strings)

Translated using Weblate (Japanese)

Currently translated at 69.9% (660 of 944 strings)

Co-authored-by: nazo6 <git@nazo6.dev>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Hugo Lee
ee54a3b843 Translated using Weblate (Chinese (Traditional, Hong Kong))
Currently translated at 75.8% (716 of 944 strings)

Co-authored-by: Hugo Lee <hohimster@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hant_HK/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Savvas Mantzouranidis
54acfaed2b Translated using Weblate (Greek)
Currently translated at 70.4% (665 of 944 strings)

Co-authored-by: Savvas Mantzouranidis <mohito6@hotmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/el/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Andy Chatziliadis
2c9782d1e8 Translated using Weblate (Greek)
Currently translated at 70.4% (665 of 944 strings)

Co-authored-by: Andy Chatziliadis <chatzeiliadis@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/el/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Nick Tzallas
71bb19dcba Translated using Weblate (Greek)
Currently translated at 70.4% (665 of 944 strings)

Co-authored-by: Nick Tzallas <tzallas@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/el/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Arian Ardeshiri
e3c4f6d9bd Translated using Weblate (Persian)
Currently translated at 93.5% (883 of 944 strings)

Co-authored-by: Arian Ardeshiri <arian.fawiki@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
ChobPT
c0ff5965ee Translated using Weblate (Portuguese (Portugal))
Currently translated at 48.9% (462 of 944 strings)

Co-authored-by: ChobPT <usteamchob@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_PT/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Navid Anindya
1cb6616f5c Translated using Weblate (Bengali)
Currently translated at 1.4% (14 of 944 strings)

Co-authored-by: Navid Anindya <navid@sayburgh.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bn/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
DevMirza
a2eee1f440 Translated using Weblate (Urdu)
Currently translated at 59.4% (561 of 944 strings)

Co-authored-by: DevMirza <pzhafeez@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ur/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
hamx01
504e8dbd09 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (944 of 944 strings)

Translated using Weblate (Russian)

Currently translated at 93.2% (880 of 944 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: hamx01 <asolianik2015@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
KEINOS
215c572988 Translated using Weblate (Japanese)
Currently translated at 69.9% (660 of 944 strings)

Translated using Weblate (Japanese)

Currently translated at 67.9% (641 of 944 strings)

Translated using Weblate (Japanese)

Currently translated at 67.7% (640 of 944 strings)

Co-authored-by: KEINOS <github@keinos.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Gerge
5113b3f22b Translated using Weblate (Hungarian)
Currently translated at 85.5% (808 of 944 strings)

Co-authored-by: Gerge <gerge.palfi@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Adam Stachowicz
f84aaf7576 Translated using Weblate (Polish)
Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Michal
96e7c13bb2 Translated using Weblate (Czech)
Currently translated at 99.5% (953 of 957 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (944 of 944 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: Michal <black23@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Ilkka Myller
9de3e7b803 Translated using Weblate (Finnish)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: Ilkka Myller <ilkka.myller@nodefield.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fi/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Gunnar Norin
7d728c23ed Translated using Weblate (Swedish)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: Gunnar Norin <gunnar.norin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sv/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Nicolas Verlhiac
fba3eac97a Translated using Weblate (French)
Currently translated at 100.0% (944 of 944 strings)

Co-authored-by: Nicolas Verlhiac <nicolas.verlhiac@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
kindercat
a87b836aa9 Translated using Weblate (Romanian)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (944 of 944 strings)

Translated using Weblate (Romanian)

Currently translated at 99.8% (944 of 945 strings)

Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ro/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
stanol
a5775df2cd Translated using Weblate (Ukrainian)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: stanol <stanol777@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Ömer Faruk Genç
ae4dc0678a Translated using Weblate (Turkish)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (944 of 944 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: Ömer Faruk Genç <omer@farukgenc.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/tr/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Frank Elsinga
74f8a2474f Translated using Weblate (Russian)
Currently translated at 87.5% (827 of 945 strings)

Co-authored-by: Frank Elsinga <CommanderStorm@users.noreply.weblate.kuma.pet>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Cyril59310
f19be09aae Translated using Weblate (French)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (French)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (French)

Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Marco
239c2693ca Translated using Weblate (German)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (German)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (German)

Currently translated at 100.0% (945 of 945 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de_CH/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
MrEddX
633a64043c Translated using Weblate (Bulgarian)
Currently translated at 100.0% (957 of 957 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (956 of 956 strings)

Translated using Weblate (Bulgarian)

Currently translated at 99.8% (943 of 944 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: MrEddX <mreddx@chatrix.one>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bg/
Translation: Uptime Kuma/Uptime Kuma
2024-07-15 00:06:18 +00:00
Frank Elsinga
6f1b8b469d Show URL for "real-browser" type monitor on details page (#4942) 2024-07-15 02:06:13 +02:00
persuasive0pest
4436b0ba8e Show URL for "real-browser" type monitor on details page 2024-07-14 19:38:48 -04:00
John Campbell
7f64badb06 Update comment on recurring-interval type maintenance
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-07-13 22:03:13 -04:00
buzzinJohnnyBoi
4750bd1e36 Fixed "recurring-interval" type maintenance - added comments 2024-07-13 14:08:53 -04:00
buzzinJohnnyBoi
d61688315d Updated croner to 8.1.0 and fixed "recurring-interval" type maintenance 2024-07-13 13:58:36 -04:00
Frank Elsinga
1a5a1a6e5d Fix uptime & ping badge duration (#4850) 2024-07-05 00:12:37 +02:00
Frank Elsinga
1ef61068c7 Merge branch 'master' into master 2024-07-04 03:13:43 +02:00
Frank Elsinga
ae224f9e18 Fix Slack notifications when URL is present (#4871) 2024-06-28 01:17:34 +02:00
Frank Elsinga
132f68a370 Merge branch 'master' into 4864-slack-button-broken 2024-06-28 01:14:46 +02:00
Brodie Davis
6eaf6b409c fix not utilizing fetched address 2024-06-27 14:04:03 -04:00
Brodie Davis
4b3ad53512 revert using url getter in monitor json 2024-06-27 14:03:34 -04:00
Louis Lam
8efbe95d62 1.23.14 to 2.0.0 (#4886) 2024-06-26 10:34:23 +08:00
Louis Lam
88ba9755a6 Resolve conflict 2024-06-26 10:02:39 +08:00
Louis Lam
cc52ee3feb Merge branch 'master' into 1.23.14-to-2.0.0
# Conflicts:
#	src/lang/en.json
#	src/util.js
#	src/util.ts
2024-06-26 10:00:30 +08:00
Louis Lam
1185b259c2 Fix dayjs issue on frontend (#4881) 2024-06-25 18:08:02 +08:00
Louis Lam
6e30f71947 Follow up the Apprise issue (#4880) 2024-06-24 19:49:37 +08:00
Louis Lam
953058c6a5 Drop out the testing repo from apt (#4878) 2024-06-24 16:20:12 +08:00
Louis Lam
85c67b6866 Revert "Switch back to TryGhost/node-sqlite3 from louislam/node-sqlite3" (#4879) 2024-06-24 15:52:07 +08:00
Brodie Davis
a19f417896 utilize extractAdress helper 2024-06-23 21:18:42 -04:00
Brodie Davis
0325c14d42 remove unneeded validation 2024-06-23 21:16:27 -04:00
Louis Lam
83969d2112 Update dependencies and embed axios-ntlm 1.3.0 into the project (#4877) 2024-06-24 02:08:39 +08:00
Louis Lam
dc15443716 Revert "Fix: Use retryInterval when a monitor is DOWN" (#4875) 2024-06-23 23:46:52 +08:00
Brodie Davis
2c8cefc784 use string url in json, not url object 2024-06-20 12:10:49 -04:00
Brodie Davis
94f75b2fbc invalidate url field for irrelevant monitor types 2024-06-20 12:05:15 -04:00
Brodie Davis
1488b1f17b prevent invalid url strings in monitorJSON 2024-06-20 11:54:20 -04:00
Qi Zhao
4941b17a46 perf: ️ revert some overengineer code 2024-06-20 10:50:35 +08:00
Qi Zhao
58533e8f06 Update server/routers/api-router.js
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-20 10:50:35 +08:00
Qi Zhao
3f425dc160 Update server/routers/api-router.js
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-20 10:50:35 +08:00
Qi Zhao
f63d36478d Update server/routers/api-router.js
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-20 10:50:35 +08:00
Qi Zhao
7270caccae Update server/routers/api-router.js
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-20 10:50:35 +08:00
Qi Zhao
53c4bba387 fix: 🐛 badge requestedDuration 2024-06-20 10:50:35 +08:00
Qi Zhao
e5ff86e6ac fix: 🐛 status & ping badge duration 2024-06-20 10:50:35 +08:00
Frank Elsinga
39c1283ba6 New notification provider: Threema Gateway (#4854) 2024-06-17 16:55:45 +02:00
booooza
add0ef7be0 Update threema provider translations to better match threema terminology 2024-06-17 10:16:38 +02:00
booooza
0960ec62b7 Adjust Threema notification view to conform with project standards 2024-06-17 08:46:08 +02:00
booooza
39b0c62c1d Refactor error handling to improve clarity and maintainability 2024-06-17 08:34:53 +02:00
Matt Visnovsky
8d8ce23f2b Robustness and edge-case handling 2024-06-14 16:45:51 -06:00
Matt Visnovsky
a0374487ce C&P typo from review 2024-06-14 16:44:36 -06:00
booooza
2e5e103434 New notification provider: Threema Gateway 2024-06-14 12:25:19 +02:00
Matt Visnovsky
e237d66bfc "Hostname or IP Address" back to "Hostname"
Revert change from "Hostname" to "Hostname or IP Address" from efb1642e3c
2024-06-13 12:26:05 -06:00
Matt Visnovsky
a1f31f9373 Remove jsonQueryDescription from lang files
jsonQueryDescription requires re-translation
2024-06-13 12:20:54 -06:00
Matt Visnovsky
46ecb822ba "Hostname or IP Address" back to "Hostname"
Revert change from "Hostname" to "Hostname or IP Address" from efb1642e3c
2024-06-13 12:17:49 -06:00
Matt Visnovsky
092688a5c8 ES Lint 2024-06-13 12:15:13 -06:00
Matt Visnovsky
6fc0cbf415 ES Lint 2024-06-13 12:12:17 -06:00
Matt Visnovsky
9820f57c64 Truncate long responses 2024-06-13 12:10:28 -06:00
Frank Elsinga
fbf7b77ceb improved zoho-cliq message format (#4848) 2024-06-13 17:10:46 +02:00
Frank Elsinga
9f563adc1a fixed formatting mistakes 2024-06-13 17:04:38 +02:00
Frank Elsinga
c9132adfc7 made sure that the address extraction is extracted into its own method 2024-06-13 17:00:11 +02:00
Frank Elsinga
c124f3a43e Formtting fix 2024-06-12 23:24:10 +02:00
Frank Elsinga
248aec8803 Formtting fix 2024-06-12 23:24:01 +02:00
Matt Visnovsky
b5a73e5ad7 Apply suggestions from code review
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-12 14:25:25 -06:00
Frank Elsinga
5dc4bb64d5 Merge branch 'master' into snmp-monitor 2024-06-12 19:55:31 +02:00
Matt Visnovsky
69c22edf23 Removed "Control Value"
No longer used.
2024-06-12 11:46:31 -06:00
Francesco M
bd95ccdc64 Update server/notification-providers/zoho-cliq.js
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-06-12 18:14:05 +02:00
Francesco M
82fb7b2816 improved zoho-cliq message format:
- show PORT (cloned switchcase from discord,squadcast,servenio)
- show monitorName in the first row to get on point also from messagge preview notification
- avoid duplication of monitorName and monitorName in the message content
2024-06-12 08:46:48 +02:00
Matt Visnovsky
43bd09be2c Update 2024-04-26-0000-snmp-monitor.js 2024-06-10 16:15:12 -06:00
Matt Visnovsky
e2e81091c3 Helpful error when query returns object or array 2024-06-10 12:49:41 -06:00
Matt Visnovsky
82352910bf Fix: Cast to string then eval 2024-06-10 12:25:55 -06:00
Matt Visnovsky
23f844d871 Error handling robustness
Cleanup of some things I missed yesterday...
2024-06-07 16:35:06 -06:00
Frank Elsinga
bc25b719db removed some monitor drift in terms of code comments (#4820) 2024-06-07 16:03:12 +02:00
Frank Elsinga
b6cd21c71a Removed where I was wrong about imports 2024-06-07 15:54:28 +02:00
Matt Visnovsky
fdc145bffd Added Robustness
There are a lot of changes here:
-Fixed a lot of issues encountered during my testing
-JSON path is evaluated BEFORE making comparisons (this was the true intended behavior by @chakflying)
-Variable name changes (cosmetic)
-Added != operator
-Changed jsonQueryDescription (again)
2024-06-06 18:52:33 -06:00
Matt Visnovsky
eaa935cba0 Also return result of the evaluation
-Maximum compatibility with @chakflying's existing json-query monitor code.
2024-06-06 10:09:35 -06:00
Matt Visnovsky
10d3188dd3 Query json directly rather than with $.value
-This is less abstract
-Maximum compatibility with @chakflying's existing json-query monitor code.
2024-06-06 10:08:07 -06:00
Matt Visnovsky
36dc94b8f2 Better type handling 2024-06-06 10:04:20 -06:00
Matt Visnovsky
efb1642e3c Blend json-query and snmp monitors
Utilizes the JSON Query library to handle comparison logic.
2024-06-05 16:09:53 -06:00
Matt Visnovsky
2d2c1866df Fix: a typo 2024-06-05 15:40:07 -06:00
Matt Visnovsky
b2d76bc60a Refactor line for conciseness 2024-06-05 15:39:55 -06:00
Matt Visnovsky
7eee5db4d2 Variable changes
-Reuse expected_value for snmpControlValue
-Create jsonPathOperator for snmpCondition
2024-06-05 15:37:47 -06:00
Frank Elsinga
d74facded6 removed some monitor drift in terms of imports and documentation 2024-06-04 05:01:53 +02:00
Frank Elsinga
4794f9eb0b Removed the last reminents of cypress (#4819) 2024-06-04 03:09:52 +02:00
Frank Elsinga
77d82ec30f removed the last reminents of cypress 2024-06-04 03:02:34 +02:00
Frank Elsinga
c7b83e729b Fixed smspartner not having fully working translations (#4816) 2024-06-04 01:28:33 +02:00
Frank Elsinga
f43fe53d28 Fixed smspartner not having fully working translations 2024-06-04 01:10:20 +02:00
Frank Elsinga
10ebdcacaa Fix: Use retryInterval when a monitor is DOWN (#4476) 2024-06-03 21:47:43 +02:00
Frank Elsinga
a00b4ed285 Feat: Add background color change on hover to nav links (#4494) 2024-06-03 21:06:23 +02:00
Frank Elsinga
a26d93ac52 Merge branch 'master' into nav-link-hover 2024-06-03 21:04:50 +02:00
DevMirza
c919d2c990 fix(ci): Prevent ARM runner from running in Forks (#4803) 2024-05-30 14:46:04 +08:00
Frank Elsinga
40d24e7837 feat: update feishu notification template (#4800) 2024-05-29 18:19:53 +02:00
nino
30bf7a5e23 fix: update Feishu notification provider configuration 2024-05-29 22:22:05 +08:00
nino
4e63d00007 feat: update feishu notification template 2024-05-29 18:20:33 +08:00
Frank Elsinga
9f35290c68 made sure that the Set up a Docker Builder section in the contribution guide is layouted like the rest 2024-05-29 11:03:37 +02:00
Frank Elsinga
11007823e7 feat: added HTTP method option for push monitor (#1991) 2024-05-24 10:26:58 +02:00
Stefan Ottosson
9ce0911e34 Merge branch 'master' into feature/http-method-push-monitor 2024-05-24 09:05:11 +02:00
Stefan Ottosson
df663a81cd Update src/pages/EditMonitor.vue
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-24 09:04:12 +02:00
Stefan Ottosson
8b648a3ed1 Revert "fix: hide body options for monitor type push"
This reverts commit e670b5f4cf.
2024-05-24 08:29:22 +02:00
Stefan Ottosson
9c7aa13190 fix: remove check that method is correct 2024-05-24 08:29:09 +02:00
Stefan Ottosson
823aeda9fe Revert "fix: update some examples"
This reverts commit b2ede38269.
2024-05-24 08:26:28 +02:00
Frank Elsinga
1590ca6c02 made sure that json-query for the mqtt monitor is translated properly (#4791) 2024-05-23 20:37:02 +02:00
Frank Elsinga
359e961aa7 Fixed flipflopping between having needs:resolve-merge-conflict and not having needs:resolve-merge-conflict 2024-05-23 20:16:37 +02:00
Stefan Ottosson
e670b5f4cf fix: hide body options for monitor type push 2024-05-23 17:49:18 +02:00
Stefan Ottosson
b2ede38269 fix: update some examples 2024-05-23 17:42:36 +02:00
Stefan Ottosson
1ac5696463 Merge branch 'master' into feature/http-method-push-monitor
# Conflicts:
#	src/pages/EditMonitor.vue
2024-05-23 17:34:45 +02:00
Frank Elsinga
63b8c52a65 made sure that json-query for the mqtt monitor is translated properly 2024-05-23 14:48:12 +02:00
Frank Elsinga
4df71af34f Update en.json: "setup" -> "set up" (#4436) 2024-05-23 14:13:53 +02:00
Frank Elsinga
07a0718272 Translation unpin incident -> delete incident (#4565) 2024-05-23 14:13:28 +02:00
Frank Elsinga
46e4b16a7e Translations Update from Weblate (#4394) 2024-05-23 14:10:39 +02:00
TheSkout001
5473bc9689 Translated using Weblate (Russian)
Currently translated at 89.4% (826 of 923 strings)

Co-authored-by: TheSkout001 <daniil.afg@yandex.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
dnlweijers
9f07011200 Translated using Weblate (Dutch)
Currently translated at 100.0% (923 of 923 strings)

Co-authored-by: dnlweijers <dnlweijers5@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Ilkka Myller
029944c655 Translated using Weblate (Finnish)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (923 of 923 strings)

Co-authored-by: Ilkka Myller <ilkka.myller@nodefield.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fi/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
glauder
b02a4055b8 Translated using Weblate (Russian)
Currently translated at 89.4% (826 of 923 strings)

Translated using Weblate (Russian)

Currently translated at 96.2% (888 of 923 strings)

Co-authored-by: glauder <glauder@mail.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Bram Van Mol
82c59da16a Translated using Weblate (Dutch)
Currently translated at 99.5% (919 of 923 strings)

Co-authored-by: Bram Van Mol <bram.vanmol@cawantwerpen.be>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
gclme
888585dcef Translated using Weblate (Malayalam)
Currently translated at 5.3% (49 of 923 strings)

Translated using Weblate (Malayalam)

Currently translated at 3.6% (34 of 923 strings)

Co-authored-by: gclme <github@cloudlink.media>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ml/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
TheSkout001
dd1eab1c6c Translated using Weblate (Russian)
Currently translated at 96.0% (887 of 923 strings)

Co-authored-by: TheSkout001 <daniil.afg@yandex.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Ilkka Myller
a56c4ffa36 Translated using Weblate (Finnish)
Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (English)

Currently translated at 100.0% (923 of 923 strings)

Co-authored-by: Ilkka Myller <ilkka@myller.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fi/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
eballester
4e49dccd15 Translated using Weblate (Catalan)
Currently translated at 7.8% (72 of 923 strings)

Co-authored-by: eballester <eduard.ballester@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Rumplin
96560fff20 Translated using Weblate (Slovenian)
Currently translated at 65.6% (606 of 923 strings)

Co-authored-by: Rumplin <rumplin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Karim guezlane
f5c59f403d Translated using Weblate (Arabic (ar_SY))
Currently translated at 71.7% (662 of 923 strings)

Co-authored-by: Karim guezlane <k.guezlane@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ar_SY/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:58 +00:00
Adrian \"Rootovsky\" Korzeniowski
798be2a4a8 Translated using Weblate (Polish)
Currently translated at 96.6% (891 of 922 strings)

Co-authored-by: Adrian \"Rootovsky\" Korzeniowski <adikorzeniowski@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Luc Dogge
ed69842939 Translated using Weblate (Dutch)
Currently translated at 99.1% (914 of 922 strings)

Co-authored-by: Luc Dogge <l.j.dogge@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Michal
5e1d2736eb Translated using Weblate (Czech)
Currently translated at 99.6% (919 of 922 strings)

Co-authored-by: Michal <black23@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
AnnAngela
9a83a9d6e7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (945 of 945 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (921 of 922 strings)

Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
凯观生活
325f3520e1 Translated using Weblate (Chinese (Simplified))
Currently translated at 99.8% (921 of 922 strings)

Co-authored-by: 凯观生活 <niujinkai@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Facundo Ortiz Gallego
52036394a5 Translated using Weblate (Spanish)
Currently translated at 97.5% (890 of 912 strings)

Co-authored-by: Facundo Ortiz Gallego <faq.ortiz@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Marco
b112afba9b Translated using Weblate (German)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (German)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (German)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (German)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (German)

Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de_CH/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Justman100
d0e1e40717 Translated using Weblate (German)
Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: Justman100 <justmegaliga10000@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
aditya wahyudi
fff47248f0 Translated using Weblate (Indonesian)
Currently translated at 99.4% (907 of 912 strings)

Co-authored-by: aditya wahyudi <aditbaco@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Zandor Smith
48a1314de1 Translated using Weblate (Dutch)
Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: Zandor Smith <info@zsinfo.nl>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Piet Jan
9a204fbfa9 Translated using Weblate (German)
Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Dutch)

Currently translated at 98.4% (898 of 912 strings)

Co-authored-by: Piet Jan <piet2001@fireman.net>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Buchtič
9e6e21243f Translated using Weblate (Czech)
Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: Buchtič <martin.buchta@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
gabichan
e355452b5c Translated using Weblate (Spanish)
Currently translated at 96.9% (884 of 912 strings)

Co-authored-by: gabichan <gabichan@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Lucas Elias Baccan
0279331326 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (English)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.7% (910 of 912 strings)

Co-authored-by: Lucas Elias Baccan <naccabbaccan@hotmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
gmesml
ec66dca19b Translated using Weblate (Albanian)
Currently translated at 27.8% (254 of 912 strings)

Co-authored-by: gmesml <gme@sml.at>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sq/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
stanol
d36e6b09ae Translated using Weblate (Ukrainian)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (917 of 917 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: stanol <stanol777@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
pashanchannel
f4e80dbd02 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (912 of 912 strings)

Co-authored-by: pashanchannel <pavlonimetrons@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
basmulder03
7dc17bd796 Translated using Weblate (Dutch)
Currently translated at 100.0% (889 of 889 strings)

Co-authored-by: basmulder03 <bas.d.mulder@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
amin Kazemi
68f9dfbcef Translated using Weblate (Persian)
Currently translated at 100.0% (889 of 889 strings)

Co-authored-by: amin Kazemi <amin.kazemi.ir@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Ömer Faruk Genç
f85c9b6b1d Translated using Weblate (Turkish)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: Ömer Faruk Genç <omer@farukgenc.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/tr/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Marco Beretta
75426ffbe7 Translated using Weblate (Italian)
Currently translated at 73.0% (648 of 887 strings)

Co-authored-by: Marco Beretta <marco13beretta@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/it/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:57 +00:00
Michal
05c67d07c3 Translated using Weblate (Czech)
Currently translated at 99.8% (911 of 912 strings)

Translated using Weblate (Czech)

Currently translated at 99.4% (884 of 889 strings)

Translated using Weblate (Czech)

Currently translated at 99.5% (883 of 887 strings)

Co-authored-by: Michal <black23@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
AnnAngela
e6c85d3afc Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
stanol
5d06378c11 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: stanol <stanol777@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Gunnar Norin
3c85d86cda Translated using Weblate (Swedish)
Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: Gunnar Norin <gunnar.norin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sv/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
René Vlugt
6fdcb1b909 Translated using Weblate (Dutch)
Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: René Vlugt <vashiru@pm.me>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
flyingfeng
1527a15dde Translated using Weblate (Japanese)
Currently translated at 62.9% (558 of 887 strings)

Co-authored-by: flyingfeng <2917268843@qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ja/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
MrEddX
5281dd47f7 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (917 of 917 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (887 of 887 strings)

Co-authored-by: MrEddX <mreddx@chatrix.one>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bg/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
DevMirza
43c10287a4 Translated using Weblate (Urdu)
Currently translated at 61.6% (546 of 885 strings)

Co-authored-by: DevMirza <pzhafeez@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ur/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Akashubham09
721234b5f6 Translated using Weblate (English)
Currently translated at 99.8% (884 of 885 strings)

Co-authored-by: Akashubham09 <Shubhamkumar56169@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
demonisius
ac4e1d890c Translated using Weblate (Russian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: demonisius <demonisius@mail.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Peter Paul
584c8b05ca Translated using Weblate (Afrikaans)
Currently translated at 6.8% (61 of 885 strings)

Added translation using Weblate (Afrikaans)

Co-authored-by: Peter Paul <henco@techguide.co.za>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/af/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
JC
25b17e5dde Translated using Weblate (Catalan)
Currently translated at 4.7% (42 of 885 strings)

Co-authored-by: JC <jjsuscc@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Ismael
17424f4e74 Translated using Weblate (Spanish)
Currently translated at 99.8% (884 of 885 strings)

Co-authored-by: Ismael <ismaellambertymuniz@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Marco
476d869321 Translated using Weblate (German (Switzerland))
Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (German)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (German)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (German)

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (German)

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (German)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (German)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (German)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de_CH/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Finn
c9a32bcdb2 Translated using Weblate (German)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Finn <git@finn.gg>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Aindriú Mac Giolla Eoin
ca28bee05f Translated using Weblate (Irish)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Irish)

Currently translated at 96.4% (890 of 923 strings)

Translated using Weblate (Irish)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Irish)

Currently translated at 80.0% (708 of 885 strings)

Translated using Weblate (Irish)

Currently translated at 47.4% (420 of 885 strings)

Translated using Weblate (Irish)

Currently translated at 7.7% (69 of 885 strings)

Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ga/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
John Doe
57c8656d32 Translated using Weblate (Bavarian)
Currently translated at 0.4% (4 of 885 strings)

Added translation using Weblate (Bavarian)

Co-authored-by: John Doe <weplaet@candymail.de>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bar/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:56 +00:00
Louis Lam
a7f7aa1ce9 Added translation using Weblate (Irish)
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2024-05-23 02:35:55 +00:00
letterlock
6bf5d97d66 Translated using Weblate (Danish)
Currently translated at 71.6% (634 of 885 strings)

Co-authored-by: letterlock <max@letterlock.eu>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/da/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Cosmo Abdon
1c438c91d5 Translated using Weblate (Portuguese (Portugal))
Currently translated at 50.8% (450 of 885 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Cosmo Abdon <cosmo.webp2@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_PT/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Adam Stachowicz
561d307fb8 Translated using Weblate (Polish)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Filip Rojek
49605cd061 Translated using Weblate (Czech)
Currently translated at 99.5% (881 of 885 strings)

Co-authored-by: Filip Rojek <filip@filiprojek.cz>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Kisem
320d9712cd Translated using Weblate (Hungarian)
Currently translated at 89.8% (795 of 885 strings)

Co-authored-by: Kisem <kiss.m.aron@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Mohannad Faihan Otaibi
b0a855e218 Translated using Weblate (Arabic)
Currently translated at 82.9% (734 of 885 strings)

Co-authored-by: Mohannad Faihan Otaibi <mohannad.otaibi@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ar/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Máté Tallósi
04d46b6a43 Translated using Weblate (Hungarian)
Currently translated at 89.8% (795 of 885 strings)

Translated using Weblate (Hungarian)

Currently translated at 87.7% (777 of 885 strings)

Co-authored-by: Máté Tallósi <tallosimate@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Jochem Pluim
50c8dd95c2 Translated using Weblate (Dutch)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Jochem Pluim <jochem@pluim.nu>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Jonne Saloranta
668aa49343 Translated using Weblate (Finnish)
Currently translated at 76.3% (676 of 885 strings)

Co-authored-by: Jonne Saloranta <saloranta.jonne@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fi/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
vahidxo
e2a2234504 Translated using Weblate (Persian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: vahidxo <s.v.morsali@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Aji Priyo Wibowo
8ccf536498 Translated using Weblate (Indonesian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Aji Priyo Wibowo <aji.wibowo@ionmobility.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Patrick van Halderen
f828fee27e Translated using Weblate (Dutch)
Currently translated at 99.6% (882 of 885 strings)

Co-authored-by: Patrick van Halderen <patrick@vanhalderen.net>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Ivan Bratović
5df75b37ab Translated using Weblate (Croatian)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Ivan Bratović <ivanbratovic4@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hr/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Dan Misener
b1f2e25188 Translated using Weblate (English)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Dan Misener <dan@misener.org>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
kindercat
c9eb483a58 Translated using Weblate (Romanian)
Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (English)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ro/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Alex Javadi
22d97318c2 Translated using Weblate (Persian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Alex Javadi <15309978+aljvdi@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Alejandro Vásquez Neira
1d75d7cace Translated using Weblate (Spanish)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Alejandro Vásquez Neira <alejandro.vasquez@neurotools.cl>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:55 +00:00
Cyril59310
5c72176ee4 Translated using Weblate (French)
Currently translated at 100.0% (940 of 940 strings)

Translated using Weblate (French)

Currently translated at 100.0% (936 of 936 strings)

Translated using Weblate (French)

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (French)

Currently translated at 100.0% (923 of 923 strings)

Translated using Weblate (French)

Currently translated at 100.0% (922 of 922 strings)

Translated using Weblate (French)

Currently translated at 100.0% (917 of 917 strings)

Translated using Weblate (French)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (French)

Currently translated at 100.0% (912 of 912 strings)

Translated using Weblate (French)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (French)

Currently translated at 100.0% (907 of 907 strings)

Translated using Weblate (French)

Currently translated at 100.0% (894 of 894 strings)

Translated using Weblate (French)

Currently translated at 100.0% (889 of 889 strings)

Translated using Weblate (French)

Currently translated at 100.0% (887 of 887 strings)

Translated using Weblate (French)

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (French)

Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-05-23 02:35:54 +00:00
Nicolas Verlhiac
b1e95968c0 New notification provider: SMS Partner API (#4769)
Co-authored-by: Nicolas Verlhiac <nicolas@novariom.com>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-22 22:53:49 +02:00
Louis Lam
88b7c047a8 Switch back to TryGhost/node-sqlite3 from louislam/node-sqlite3 (#4773) 2024-05-20 10:42:37 +08:00
Gauvino
533af0f56c feat: Add merge conflict labeler workflow (#4694)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-20 00:16:42 +02:00
HdroguettA
daca38a554 Ignore TLS/SSL error for Redis (#3878)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 23:34:01 +02:00
Furkan İ
55b2d4b907 Add Bitrix24 Webhook notification (#3620)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2024-05-19 23:33:32 +02:00
Damon
168d1ca24f refactor: upload check label when monitor empty (#3661)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 23:17:09 +02:00
Jed Fox
668e8bafed Gray out label for up/down/maintenance labels when they are zero (#3037)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 23:01:14 +02:00
Nelson Chan
a581a85633 Feat: Use UptimeCalculator for PingChart (#4264)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 22:03:32 +02:00
Sebastian Lang
a3ac954140 Add support for custom mongodb commands (#4445)
Co-authored-by: Sebastian Lang <sebastian.lang@damovo.com>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 21:59:57 +02:00
Laurent Aupse
e856cb6007 Make auto refresh interval customizable (#4260)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-19 21:56:55 +02:00
TheDanniCraft
4e24e96dab [discord] thread/forum channel support (#4099) 2024-05-19 21:14:13 +02:00
Louis Lam
6bfb1be71a Improve MariaDB's connection (#4728) 2024-05-19 22:46:22 +08:00
jmolnar-comparative
a81f949f98 chore: fixed a typo for internal, unused part of the file upload icon for status page (#4757) 2024-05-13 19:24:13 +02:00
jmolnar-comparative
237a6e8da9 chore: fixed a typo for internal, unused part of the file upload icon for status page (#4750) 2024-05-10 20:35:36 +02:00
Matt Visnovsky
d25ee8f128 Using JSON Query Expressions
Equivalent functionality as before, but we're now building json-query expressions for the user.
2024-05-10 10:56:38 -06:00
Matt Visnovsky
c4759948ec Fix ES Lint 2024-05-08 11:00:18 -06:00
Matt Visnovsky
1c4740748c Re-use monitor.radiusPassword for community string 2024-05-08 11:00:10 -06:00
Matt Visnovsky
da8f0d1c31 Apply suggestions from code review
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-05-08 10:06:20 -06:00
Matt Visnovsky
8b4b27f359 Final cleanup of changes to EditMonitor.vue
Reverts unintentional changes committed in: d92003e172
2024-05-07 10:05:36 -06:00
Matt Visnovsky
2015142b00 Maybe don't helptext all the things...
Addresses https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589866321
2024-05-07 09:23:23 -06:00
Matt Visnovsky
e5fb726160 Missed changes leftover from removal of getKey() 2024-05-07 09:16:23 -06:00
Matt Visnovsky
2b5d100cd3 Ensure SNMP session is closed properly
Addresses https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589858252

Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-06 15:56:41 -06:00
Matt Visnovsky
f4842ead68 Translation key for OID 2024-05-06 15:38:14 -06:00
Matt Visnovsky
56e7fa8bd5 Helptext ALL THE THINGS 2024-05-06 14:33:48 -06:00
Matt Visnovsky
19f21a9a39 SNMP OID helptext 2024-05-06 14:19:33 -06:00
Matt Visnovsky
4ef66b3760 SNMP version helptext 2024-05-06 14:17:46 -06:00
Matt Visnovsky
e9b52eb0e7 Separate error cases for SNMP varbind returns 2024-05-06 14:06:40 -06:00
Matt Visnovsky
c68b1c6274 Remove unnecessary func getKey 2024-05-06 12:05:12 -06:00
Matt Visnovsky
6037912085 Consistent placeholder text
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-06 09:58:39 -06:00
Matt Visnovsky
433e317eee Simplify error catch
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-06 09:57:13 -06:00
Matt Visnovsky
1fe1bb5864 Given that above throws, the else case is not nessesary
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-06 09:53:14 -06:00
Matt Visnovsky
997791bc78 Default: invalid condition error
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-06 09:51:37 -06:00
Matt Visnovsky
0384b34007 Remove unnecessary func getKey
Addresses:
- https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589856311
- https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589862733
2024-05-06 09:49:36 -06:00
Matt Visnovsky
86b997c664 Limit to <= SNMPv2c for now 2024-05-06 09:47:51 -06:00
Matt Visnovsky
0280b2ad3f A comment about varbinds[0] for clarification
Addresses https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589855126
2024-05-06 09:21:49 -06:00
Matt Visnovsky
4386d0afad Apply suggestions from code review
Addresses:
-https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589805237
-https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589806199
-https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589853470
-https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1589854032
2024-05-05 15:47:43 -06:00
Matt Visnovsky
9053b48030 Merge branch 'louislam:master' into snmp-monitor 2024-05-03 16:13:40 -06:00
Matt Visnovsky
407f7291b0 New dependency for net-snmp 2024-05-03 11:58:03 -06:00
Matt Visnovsky
09fd816aae Updated code comments 2024-05-03 11:52:02 -06:00
Matt Visnovsky
c87ac2f043 Move getKey() to util.ts 2024-05-03 11:39:14 -06:00
Matt Visnovsky
8e56a81ef1 Refactor how strings/numerics are parsed
Fixes issue `toString() radix argument must be between 2 and 36` due to `.toString("ascii")` conversion. This issue was introduced in 704ffd3f4b.
2024-05-02 15:11:03 -06:00
Matt Visnovsky
f059d54349 Use frontend timeout
Addresses https://github.com/louislam/uptime-kuma/pull/4717#discussion_r1585616669
2024-05-02 15:07:22 -06:00
Matt Visnovsky
d83c2b90c9 Revert unintentional changes to EditMonitor.vue
Reverts unintentional changes committed in: d92003e172
2024-05-02 09:50:09 -06:00
Frank Elsinga
dbbc79a05a Fixed a typo introduced in #3836 (#4729)
Co-authored-by: Nelson Chan <3271800+chakflying@users.noreply.github.com>
2024-05-02 12:11:49 +08:00
Matt Visnovsky
4699a1ccd8 ES Lint Compliant
Also changed line endings from 'LF' to 'CRLF'
2024-05-01 10:29:13 -06:00
Matt Visnovsky
ba84f01444 Delete .EditMonitor.vue.swp
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-05-01 09:15:35 -06:00
Matt Visnovsky
9ba0f68a86 Remove supurfluous log.debug
Co-Authored-By: Frank Elsinga <frank.elsinga@tum.de>
2024-04-30 18:19:18 -06:00
Matt Visnovsky
97a9094d7c ES Lint Compliant 2024-04-30 18:14:58 -06:00
Matt Visnovsky
e944492da8 Corrected down function
b4bd003626 (r1585590243)
2024-04-30 18:06:03 -06:00
Matt Visnovsky
7459654e11 ES Lint Compliant 2024-04-30 18:04:59 -06:00
Matt Visnovsky
ba47aca51f Apply suggestions from code review
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-30 17:54:29 -06:00
Frank Elsinga
b4bd003626 Merge branch 'master' into snmp-monitor 2024-05-01 00:01:03 +02:00
Matt Visnovsky
704ffd3f4b Finalized SNMP monitor 2024-04-30 15:18:25 -06:00
Matt Visnovsky
9848ce49f3 Minor frontend styling 2024-04-30 15:16:45 -06:00
Matt Visnovsky
4593afbdbb Frontend input validation 2024-04-30 15:15:37 -06:00
Matt Visnovsky
9d28fcff1a Update bean model backend
Updates appropriate values async when editing the SNMP monitor
2024-04-30 15:13:16 -06:00
Matt Visnovsky
9c8024c7fa Update db migration: down function
knex requires down function
2024-04-30 15:11:21 -06:00
Matt Visnovsky
138075a2af Update db migration: allow nulls
DB must allow nulls otherwise this will break other monitors.
2024-04-30 15:10:57 -06:00
Rakovskij Stanislav
9f2cf28a76 Making docker usage with localhost and external ip more clear (#3836)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-30 22:53:27 +02:00
Nelson Chan
59f10d542b Fix: Show API Keys disabled msg. when disabled Auth (#4723)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-30 22:11:09 +02:00
Ezhil Shanmugham
988ba79679 feat: keephq notification provider (#4722) 2024-04-30 16:17:34 +02:00
Matt Visnovsky
99dc4cfb46 Wrong variable used
Thanks for pointing it out @CommanderStorm!
2024-04-29 22:24:51 -06:00
Matt Visnovsky
4a882be6ba Further SNMP monitor development
Further testing of SNMP feat, however I'm running into the issue `Error in SNMP check: RequestTimedOutError: Request timed out` when the check function is called. I am unsure as to why since my local SNMP script works great with very similar code.
2024-04-29 15:59:59 -06:00
Matt Visnovsky
ff5890a11f Updated a comment 2024-04-29 15:59:21 -06:00
Matt Visnovsky
a3cdd69995 Use net-snmp instead of snmp-native
net-snmp over snmp-native is:
-more robust
-more popular
-better documented
-supports v3
2024-04-29 13:58:43 -06:00
Joschua Becker
19e8c75c3b SevenIO Notification Provider (#4219)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-27 23:40:59 +02:00
凯观生活
126d6cd912 Add the ability to notify @everyone in DingTalk notifications (#4718)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-27 17:16:39 +02:00
Matt Visnovsky
d92003e172 SNMP Initial Commits
This commit introduces a new SNMP monitor feature to the application, allowing users to monitor devices using SNMP (Simple Network Management Protocol).
2024-04-26 19:05:56 -06:00
Louis Lam
c86706f189 1.23.13 changes merge to 2.0.0 (#4708) 2024-04-26 15:11:26 +08:00
Louis Lam
1d091739c8 Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-26 15:01:03 +08:00
Louis Lam
27bcc968ec Update server/util-server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-25 23:45:30 +08:00
Louis Lam
c0db036187 Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-25 23:45:24 +08:00
Louis Lam
7da5b2311c Fix merge issue 2024-04-25 15:49:32 +08:00
Louis Lam
996db6281b Update dependencies and remove an empty file 2024-04-25 15:46:18 +08:00
Louis Lam
63a380326d Merge branch '1.23.X' into 1.23.13-to-2.0.0
# Conflicts:
#	.github/workflows/auto-test.yml
#	package-lock.json
#	package.json
#	server/database.js
#	server/model/monitor.js
#	server/monitor-types/real-browser-monitor-type.js
#	server/util-server.js
#	test/cypress/unit/i18n.spec.js
2024-04-25 15:42:53 +08:00
Louis Lam
2778929f74 Update to 1.23.13 2024-04-25 15:27:28 +08:00
Louis Lam
f71d35e53e Update dependencies 2024-04-25 15:26:49 +08:00
Nelson Chan
1490443618 Fix: Getting TLS certificate through proxy & prometheus update (#4700) 2024-04-24 14:37:17 +08:00
BigBoot
bab427f715 Add loginRequired SocketIO event (#4636) 2024-04-21 20:39:44 +02:00
Frank Elsinga
add5c128ce fix: Localisation-matching algorithm missing some edgecase (#4692) 2024-04-21 20:23:34 +08:00
Louis Lam
29575343ca Update CONTRIBUTING.md for how to set up a Docker builder (#4668)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-19 17:37:12 +08:00
Louis Lam
e797abd108 Update to 1.23.12 2024-04-19 01:17:13 +08:00
Louis Lam
7a9e2f5de6 Merge pull request from GHSA-23q2-5gf8-gjpp 2024-04-19 01:08:31 +08:00
Louis Lam
7b5d2a71ff Update dependencies 2024-04-18 20:48:07 +08:00
CoolCu
b25ac55a2f chore: fix some typos in comments (#4679)
Signed-off-by: CoolCu <coolcui@qq.com>
2024-04-16 16:59:07 +02:00
msrl2000
05606c69e7 Update i18n.js (#4666) 2024-04-10 21:49:19 +02:00
Nelson Chan
893278bd3d Feat: Use keylog event to obtain TLS certificate for better reliability [1.23.X] (#4630)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-06 18:43:08 +08:00
Adam Stachowicz
0e30ea830d fix: Update nodemailer to fix GHSA-9h6g-pr28-7cqp [1.23.X] (#4653) 2024-04-05 17:38:24 +02:00
Louis Lam
c67a2070b8 Update deps 2024-04-05 12:12:36 +08:00
Adam Stachowicz
9863a10321 fix: Update axios, @actions/github and dompurify [1.23.X] (#4652) 2024-04-05 11:47:46 +08:00
Edoardo Ridolfi
822ce5384b Add support for Whapi notification provider (#4323)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-03 02:22:01 +02:00
Merlin
937c8a9a7b New notification provider: CallMeBot API (#4605)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-02 21:39:45 +02:00
Daan Meijer
effd0197ac [Slack] restructure alert actions, add 'visit site' button (#3886)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Nelson Chan <3271800+chakflying@users.noreply.github.com>
2024-04-02 02:43:54 +02:00
Nelson Chan
ee7f8680c1 Fix: Add missing FK for monitor-tls-info table [1.23.X] (#4631) 2024-03-31 12:05:38 +08:00
Nelson Chan
08f75b0b99 Fix: Add missing FK for monitor-tls-info table (#4632) 2024-03-31 12:04:22 +08:00
Nelson Chan
c1301804d4 Fix: Fix CI on Windows Runner [1.23.X] (#4633) 2024-03-31 10:33:59 +08:00
Simon Nilsson
0923d05317 Cellsynt mobile services (#4625)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-03-29 16:27:54 +01:00
Chongyi Zheng
88187b66eb Drop Node.js 14 and 16 (#3747)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-03-28 07:30:33 +08:00
Nelson Chan
b8858f4605 Feat: Handle maintenance in UptimeCalculator (#4406)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-03-27 10:19:25 +08:00
Christoph Fichtmüller
49b6dacb4d Add gtxmessaging Notification Support (#4481)
Signed-off-by: Christoph Fichtmüller <cf@cfichtmueller.com>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-03-26 23:59:09 +01:00
Louis Lam
e927327bad Some minor changes for 2.0.0 (#4621) 2024-03-26 15:17:52 +08:00
DevMirza
45690a25a0 fix(ci): Fix for the Actions failing on Windows Runner (#4557) 2024-03-20 22:20:40 +08:00
Elliot Matson
5bc68fe0a7 Google chat cards (#3928)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-03-20 12:58:28 +01:00
OptimusGREEN
bc077cc536 add notification channel for home assistant (#4541)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Seb <144435+rmtsrc@users.noreply.github.com>
2024-03-18 23:31:37 +01:00
Frank Elsinga
975761b448 made sure to use Promise.allSettled instead of sleeping for 500ms after login (#4592) 2024-03-19 00:16:04 +08:00
Frank Elsinga
0e3b3a9ab8 Made sure that more of the async usages are awaited (#4574) 2024-03-15 15:02:55 +01:00
Frank Elsinga
a9a1cf1353 Chore: General notification reformatting (#3182)
- I unified where in file the name of `NotificationProvider.name` is placed
- I made sure that all the providers adhere to the signature of `NotificationProvider.send()`
- I made sure that all the providers use `okMsg` if returning success messages directly from the function.
  Here a discussion should be had:
  Should this be refactored into a constant of `NotificationProvider`? I could imagine that `NotificationProvider.SENDING_SUCCESSFULL`  could be a suitable alternative.
- I made sure all providers have the URL they `POST`/`GET` to be extraced into a variable.
  => refactored this way due to Nelsons suggestion
2024-03-14 14:21:15 +01:00
Frank Elsinga
bfd65ab6e3 Add Heii On-Call Notification Provider (#4485) 2024-03-11 20:51:34 +01:00
Frank Elsinga
1db750a2e1 Merge branch 'master' into hevans/add-heii-on-call-notification-provider 2024-03-11 20:49:42 +01:00
LeoThies
abd62a12e4 edit unpin to delete 2024-03-11 11:52:22 +01:00
Frank Elsinga
ca14c34977 Set mqtt-clientId to uptime-kuma_.. instead of mqttjs_.. (#4503) 2024-03-10 15:16:07 +01:00
Frank Elsinga
2a3a2201e6 make monitor start() and stop() async (#2830) 2024-03-09 23:36:00 +01:00
Frank Elsinga
6eef2192b0 Refactor MS-Teams notification to use AdaptiveCards (#4538) 2024-03-07 15:36:32 +01:00
taschenuhr
9789931edf Merge branch 'master' into feature/msteams-adaptivecards 2024-03-07 15:30:26 +01:00
taschenuhr
4aaa0b92fb fix test notification after method refactoring 2024-03-07 15:11:19 +01:00
Frank Elsinga
fdf562fe39 Test for db file existance instead of creating a new one in password-restet.js (#4519)
Test for db file existance instead of creating a new one in `password-restet.js`
2024-03-06 13:03:07 +01:00
Frank Elsinga
b385e81608 Improved helptext of how to send mail via the systems mail subsystem (#4477) 2024-03-05 19:40:45 +01:00
Frank Elsinga
e2239bc7b7 Merge branch 'master' into hevans/add-heii-on-call-notification-provider 2024-03-05 19:20:14 +01:00
Frank Elsinga
da92400401 [Ntfy] Only include action link if monitor url is defined #3274 (#4411) 2024-03-05 19:19:18 +01:00
Humberto Evans
daa59b812c Update en.json
Fix misspelled "Trigger"
2024-03-04 14:54:17 -08:00
Frank Elsinga
574705a4c3 Updated strings for LINE Messenger as changed in their developer portal (#4527) 2024-03-03 18:29:24 +01:00
Andi Pätzold
a0c62d8ab0 Fix: handle monitor names with slashes (#4472)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-03-03 17:59:38 +08:00
Huzaifa Azim
8fd713d642 migrate timezones-list lib to @vvo/tzdb issue #4479 (#4521) 2024-03-03 17:54:14 +08:00
Frank Elsinga
4c683da0dd Refactored the Can I create a pull request for Uptime Kuma section (#4545) 2024-03-03 17:45:35 +08:00
Frank Elsinga
507ff76728 More readable Contribution guide (#4525)
Co-authored-by: Nelson Chan <3271800+chakflying@users.noreply.github.com>
2024-03-03 17:44:53 +08:00
taschenuhr
8bd69d78a8 some more tweaking of the card content and notification summary 2024-02-28 17:41:00 +01:00
taschenuhr
cc09147b4b fix adding optional ActionSet 2024-02-28 16:31:47 +01:00
taschenuhr
b8941403d1 code cleanup 2024-02-28 16:30:48 +01:00
taschenuhr
5b87da94b3 push notificationMessage to card header and add action button to dashboard 2024-02-28 14:19:32 +01:00
Vista2003
9209210e7c Merge branch 'master' into master 2024-02-28 13:00:03 +00:00
Nelson Chan
c7b8bb9e4a Fix: Incorrect handling of status page not found (#4537) 2024-02-28 18:57:58 +08:00
taschenuhr
7756070c57 refactor MS-Teams notification to use adaptive cards 2024-02-28 09:47:14 +01:00
Louis Lam
d88e7702b9 Update axios from 0.27.x to 0.28.x (#4532) 2024-02-27 22:07:43 +08:00
Frank Elsinga
ee99ee9460 changed which translation we are using 2024-02-27 00:09:56 +01:00
Vista2003
800847d0df Correcting label to fit what it says on the page 2024-02-26 20:23:57 +00:00
Vista2003
3fd0c822d6 Removed trailing space 2024-02-25 16:31:25 +00:00
Vista2003
e74d6a7471 Fixing strings as reported on issue #4526 2024-02-25 10:35:32 +00:00
Huzaifa Azim
99dccd9e90 reset-password-issue-4518 2024-02-23 18:10:09 +05:00
Louis Lam
e61589d02e Add Vue DevTools (#4509) 2024-02-21 09:13:49 +08:00
NihadBadalov
17dfdacd93 Fix: Correct colors 2024-02-20 17:52:59 +01:00
Stefan Heine
6bfc58674a MQTT monitor, set the MQTT clientId to make it easier in the MQTT broker to identify where the connections are coming from 2024-02-19 07:57:34 +01:00
Stefan Heine
1b293f2754 MQTT monitor, set the MQTT clientId to make it easier in the MQTT broker to identify where the connections are coming from 2024-02-19 07:42:37 +01:00
Humberto Evans
9a52a8761c Apply suggestions from code review
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-02-16 15:08:59 -07:00
NihadBadalov
94154c49aa Feat: Change hover colors to shades of gray 2024-02-15 08:21:54 +01:00
Frank Elsinga
1fdb7e48ed Formatting fixes 2024-02-14 23:45:23 +01:00
Frank Elsinga
cc000117ca Updated the code to look more like the other notification providers 2024-02-14 23:41:35 +01:00
NihadBadalov
4d6ea433e6 Feat: Add background color change on hover to nav links 2024-02-14 22:51:06 +01:00
Frank Elsinga
cf2d603e27 Stale, incorrect issue template and cannot-reproduce Comments (#4490) 2024-02-14 13:43:31 +08:00
Humberto Evans
5a9c3ad353 move try/catch into main control flow and more from code review comments 2024-02-13 17:27:59 -08:00
Humberto Evans
4cdc8f344b Apply suggestions from code review
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-02-13 16:54:52 -08:00
Aindriú Mac Giolla Eoin
0efdfc4497 Added Irish language (#4487) 2024-02-13 19:44:53 +08:00
Humberto Evans
673f9a7744 get the linter to be happy with my vue file 2024-02-12 18:12:07 -08:00
Humberto Evans
f254940b64 spell things correctly 2024-02-12 18:01:06 -08:00
Humberto Evans
71dd5e2369 do not assume missing heartbeat is a test 2024-02-12 17:54:36 -08:00
Humberto Evans
09d249ca36 add form to NotificationDialog 2024-02-12 17:48:53 -08:00
Humberto Evans
5f25a75f65 remove line at end of file 2024-02-12 17:39:41 -08:00
Humberto Evans
92488290fa add newline at end of file 2024-02-12 17:38:50 -08:00
Humberto Evans
246316f55b Undo my overzelous linter 2024-02-12 17:24:42 -08:00
Humberto Evans
10659d07b2 add link to documentation explaining how to get id and token 2024-02-12 16:56:41 -08:00
Humberto Evans
80bdc455da lint 2024-02-12 16:31:58 -08:00
Humberto Evans
e8fada9386 set url to heii on call prod 2024-02-12 16:14:41 -08:00
Humberto Evans
93ac212aef Add Heii On-Call 2024-02-12 15:58:54 -08:00
Frank Elsinga
f37f55e06c Fixed lining issues introduced by code reivew 2024-02-11 22:44:57 +01:00
Frank Elsinga
87d7a780e3 changed the helptext a bit to make it more usefull for novice users 2024-02-11 22:40:47 +01:00
apio-sys
0fc372f558 #2793 2024-02-11 20:20:52 +01:00
Joris Le Blansch
67a13e1259 #2793 2024-02-11 20:03:17 +01:00
Frank Elsinga
81e465f418 Removed debug statement 2024-02-11 20:02:09 +01:00
Neel Bhanushali
a5d0f7a7db console added with double quotes and semicolons. console added with username so i can remove later 2024-02-11 21:42:20 +05:30
Neel Bhanushali
a16b42f98c poc done 2024-02-11 21:33:58 +05:30
Neel Bhanushali
65ff08b38e lower heartbeat interval when monitor is down #4025 2024-02-11 20:19:56 +05:30
Nelson Chan
56aa81e337 UI: Improve maintenace actions design (#4407) 2024-02-03 06:16:14 +08:00
Frank Elsinga
dc3abc68f0 Fixed type anotation 2024-01-30 17:54:04 +01:00
Frank Elsinga
89158be7de Merge branch 'master' into monitor_async_start_stop 2024-01-30 17:53:21 +01:00
Robert Martin
ac4355114a Update en.json
Grammar fix: "set up" should be two words when used as a verb
2024-01-29 22:14:06 -06:00
Louis Lam
3cea7d4eb2 Update dependencies 2024-01-30 01:38:45 +08:00
Nelson Chan
2bccae415f Feat: Show monitor ID in Details (#4331) 2024-01-28 18:15:29 +08:00
Nelson Chan
2b8f55194f Fix: [JSON-Query] Prevent parsing string-only JSON (#4425) 2024-01-28 03:18:24 +08:00
Frank Elsinga
538f83205d chore/issue template modification for v2 (#4240)
Co-authored-by: Nelson Chan <3271800+chakflying@users.noreply.github.com>
2024-01-26 06:57:36 +08:00
Nelson Chan
cf075a8793 Fix: missing await in getRemoteBrowser() (#4418) 2024-01-26 06:53:15 +08:00
Nelson Chan
288cab6dd7 Fix: Make sure browser is connected before returning (#4417) 2024-01-25 07:59:42 +08:00
Philip Klostermann
95125cc417 [Ntfy] don't include url action with defaut URL value 2024-01-23 11:16:10 -05:00
Philip Klostermann
20b69acde2 [Ntfy] Only include action link if monitor url is defined #3274 2024-01-23 10:10:31 -05:00
AnnAngela
b4e45c7ce8 fix(notification-dingding): throw error when failed (#3598) 2024-01-20 03:29:13 +08:00
Louis Lam
36196f632d Translations Update from Weblate (#4386) 2024-01-18 03:48:14 +08:00
Paco Culebras
c31475e020 Translated using Weblate (Catalan)
Currently translated at 3.7% (33 of 885 strings)

Co-authored-by: Paco Culebras <pculebras@me.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:32:38 +08:00
kindercat
9e21c7cb02 Translated using Weblate (Romanian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ro/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:32:38 +08:00
Nima Shayanfar
669fc02baa Translated using Weblate (Persian)
Currently translated at 96.8% (857 of 885 strings)

Co-authored-by: Nima Shayanfar <nsh20100@yahoo.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fa/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:32:38 +08:00
kindercat
371eb38198 Translated using Weblate (Romanian)
Currently translated at 87.2% (772 of 885 strings)

Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ro/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:32:37 +08:00
Белич Дмитрий
64fe00f9f2 Translated using Weblate (Russian)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Белич Дмитрий <belicdima8@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:28:33 +08:00
Kisem
2fbdf4886e Translated using Weblate (Hungarian)
Currently translated at 84.5% (748 of 885 strings)

Co-authored-by: Kisem <kiss.m.aron@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:28:33 +08:00
ItzAndriss
5ebb160597 Translated using Weblate (Hungarian)
Currently translated at 84.5% (748 of 885 strings)

Co-authored-by: ItzAndriss <itzandriss@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hu/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:28:33 +08:00
Louis Lam
e47e3cf479 Translated using Weblate (Yue)
Currently translated at 11.8% (105 of 885 strings)

Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/yue/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:28:33 +08:00
kindercat
ef573d057b Translated using Weblate (Romanian)
Currently translated at 66.7% (590 of 884 strings)

Co-authored-by: kindercat <156439718+kindercat@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ro/
Translation: Uptime Kuma/Uptime Kuma
2024-01-18 03:28:32 +08:00
alejandro vasquez
2d64204ff1 Translated using Weblate (Spanish)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: alejandro vasquez <rootltsm@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Zandor Smith
2afd743a6d Translated using Weblate (Dutch)
Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (English)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Zandor Smith <info@zsinfo.nl>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nl/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
ITQ
08da5ad5d8 Translated using Weblate (Russian)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: ITQ <itq.dev@ya.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
JC
83c7b593da Translated using Weblate (Catalan)
Currently translated at 3.5% (31 of 884 strings)

Co-authored-by: JC <jjsuscc@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ca/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Adam Stachowicz
a2905b0686 Translated using Weblate (Polish)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pl/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Fabien BERNARD
7f4a23a5ac Translated using Weblate (French)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Fabien BERNARD <fabien.bernard@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Markus Fenes
465531ad98 Translated using Weblate (Norwegian Bokmål)
Currently translated at 61.3% (542 of 884 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 60.4% (534 of 884 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 54.9% (486 of 884 strings)

Co-authored-by: Markus Fenes <mafen@users.noreply.github.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/nb_NO/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Itay-Asudi
f491879214 Translated using Weblate (Hebrew)
Currently translated at 0.3% (3 of 884 strings)

Co-authored-by: Itay-Asudi <itay.asudi@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/he/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Lê Huy Mạnh Tân
5ea712e826 Translated using Weblate (Vietnamese)
Currently translated at 56.2% (497 of 884 strings)

Co-authored-by: Lê Huy Mạnh Tân <tanmanh350@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/vi/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
dng-nguyn
423e66d609 Translated using Weblate (Vietnamese)
Currently translated at 56.2% (497 of 884 strings)

Co-authored-by: dng-nguyn <dungnguyen@dinhcap.dev>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/vi/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Elia Ronchetti
05f9f35cb3 Translated using Weblate (Italian)
Currently translated at 72.5% (641 of 884 strings)

Co-authored-by: Elia Ronchetti <e.ronchetti@campus.unimib.it>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/it/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Salvatore Cahyo
3b0800b78e Translated using Weblate (Indonesian)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Salvatore Cahyo <salvacmp18@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
renph
c33840fc9f Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: renph <renph96@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Michal
44f8a8f8e9 Translated using Weblate (Czech)
Currently translated at 99.5% (881 of 885 strings)

Translated using Weblate (Czech)

Currently translated at 99.5% (880 of 884 strings)

Translated using Weblate (Czech)

Currently translated at 97.7% (864 of 884 strings)

Co-authored-by: Michal <black23@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/cs/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
AnnAngela
ca0385c449 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: AnnAngela <naganjue@vip.qq.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/zh_Hans/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Abner Santana
ceb1d90095 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.8% (883 of 884 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Abner Santana <abnerss@outlook.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/pt_BR/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Jong Ryul Oh
b6a75e198c Translated using Weblate (Korean)
Currently translated at 84.6% (748 of 884 strings)

Co-authored-by: Jong Ryul Oh <hydromix@hotmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ko/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:36 +00:00
Alanimdeo
999ed525e5 Translated using Weblate (Korean)
Currently translated at 84.6% (748 of 884 strings)

Co-authored-by: Alanimdeo <alan@imdeo.kr>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ko/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
MrEddX
8773eed790 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: MrEddX <mreddx@chatrix.one>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/bg/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Marco
f9b3f9766e Translated using Weblate (German)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (German)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (German)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (German (Switzerland))

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Marco <marco@nanoweb.ch>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de_CH/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
demonisius
eaa8939098 Translated using Weblate (Russian)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: demonisius <demonisius@mail.ru>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ru/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Aji Priyo Wibowo
055aa8d2da Translated using Weblate (Indonesian)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Aji Priyo Wibowo <aji.wibowo@ionmobility.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/id/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Ivan Bratović
e815ee9192 Translated using Weblate (Croatian)
Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Ivan Bratović <ivanbratovic4@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/hr/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
stanol
3b7e3e093b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: stanol <stanol777@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/uk/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Thanassis Zografos
8cce068224 Translated using Weblate (Greek)
Currently translated at 76.0% (672 of 884 strings)

Co-authored-by: Thanassis Zografos <tzografos@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/el/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Ömer Faruk Genç
f8650239a6 Translated using Weblate (Turkish)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Ömer Faruk Genç <omer@farukgenc.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/tr/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Rumplin
2a001d1aae Translated using Weblate (Slovenian)
Currently translated at 59.1% (523 of 884 strings)

Co-authored-by: Rumplin <rumplin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sl/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
leonsk29
0eca97f690 Translated using Weblate (Spanish)
Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: leonsk29 <leonsk29@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/es/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
ConfusedAlex
f296d8f533 Translated using Weblate (German)
Currently translated at 99.5% (880 of 884 strings)

Co-authored-by: ConfusedAlex <alex@epostnet.de>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/de/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Gunnar Norin
8150700906 Translated using Weblate (Swedish)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (Swedish)

Currently translated at 97.0% (858 of 884 strings)

Translated using Weblate (English)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Gunnar Norin <gunnar.norin@gmail.com>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/en/
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/sv/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
Cyril59310
0ffa150c5d Translated using Weblate (French)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (French)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (French)

Currently translated at 100.0% (884 of 884 strings)

Translated using Weblate (French)

Currently translated at 100.0% (884 of 884 strings)

Co-authored-by: Cyril59310 <archas.cyril@hotmail.fr>
Translate-URL: https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/fr/
Translation: Uptime Kuma/Uptime Kuma
2024-01-17 19:01:35 +00:00
James Cocker
8456912ae3 Corrected "Login" & "Logout" to verbs (EN) (#4320)
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2024-01-14 05:35:11 +08:00
Cyril59310
17e284f011 Missing translation key (#4363) 2024-01-14 04:59:13 +08:00
Louis Lam
d4c21a2259 [GitHub] Minor issue template wording 2024-01-09 01:35:10 +08:00
Frank Elsinga
7635ab54a0 made sure that the i18n does use navigator.languages instead of navigator.language for automatic language detection (#4244) 2024-01-07 23:55:10 +08:00
Louis Lam
f3dcdb6332 Update the demo server (#4296) 2024-01-07 23:24:28 +08:00
Adam Stachowicz
458cdf9f9b Fix encodeBase64 for empty password or user in HTTP Basic Authentication (#4326) 2024-01-07 02:06:06 +08:00
Louis Lam
9356e7dd4f Update clear stats (#4324) 2024-01-05 20:51:05 +08:00
Ben Scobie
25cb78796a Fix incorrect ping log (#4322) 2024-01-05 20:43:03 +08:00
Nelson Chan
bf1e3a3d5e Feat: Add stat_hourly & min. max. ping (#4267)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-01-05 20:42:24 +08:00
Louis Lam
0060e46b91 [README.md] Fix star badge 2024-01-04 03:13:31 +08:00
Abhishek Srinivasan
23e80882c6 Changed the color of delete button in dashboard from red to grey (#4307) 2024-01-04 00:29:38 +08:00
Louis Lam
5ebea3134a Merge 1.23.11 to master (#4306) 2024-01-03 03:04:12 +08:00
Louis Lam
2a315d4c84 Merge branch '1.23.X' into merge
# Conflicts:
#	package-lock.json
#	package.json
2024-01-03 02:10:01 +08:00
Louis Lam
f1e2ee74ea Update to 1.23.11 2023-12-31 05:46:54 +08:00
Louis Lam
8d847abf35 Update dependencies 2023-12-31 05:09:45 +08:00
Louis Lam
d7d57eafe3 Update Vue to 3.4 (#4300) 2023-12-30 23:13:21 +08:00
Louis Lam
329e2042bc Update to respect docker compose v2 (#4289) 2023-12-27 22:39:04 +08:00
Louis Lam
8151ac0e25 Fix Async child process output issue (#4231) 2023-12-14 04:54:34 +08:00
Thomas Spalinger
7f88aacbe7 make monitor start() and stop() async 2023-02-23 16:16:49 +00:00
Stefan Ottosson
f89ed0a3a4 feat: added HTTP method option for push monitor 2022-08-12 18:52:23 +02:00
266 changed files with 24735 additions and 7726 deletions

View File

@@ -1,28 +0,0 @@
# Codespaces
You can modifiy Uptime Kuma in your browser without setting up a local development.
![image](https://github.com/louislam/uptime-kuma/assets/1336778/31d9f06d-dd0b-4405-8e0d-a96586ee4595)
1. Click `Code` -> `Create codespace on master`
2. Wait a few minutes until you see there are two exposed ports
3. Go to the `3000` url, see if it is working
![image](https://github.com/louislam/uptime-kuma/assets/1336778/909b2eb4-4c5e-44e4-ac26-6d20ed856e7f)
## Frontend
Since the frontend is using [Vite.js](https://vitejs.dev/), all changes in this area will be hot-reloaded.
You don't need to restart the frontend, unless you try to add a new frontend dependency.
## Backend
The backend does not automatically hot-reload.
You will need to restart the backend after changing something using these steps:
1. Click `Terminal`
2. Click `Codespaces: server-dev` in the right panel
3. Press `Ctrl + C` to stop the server
4. Press `Up` to run `npm run start-server-dev`
![image](https://github.com/louislam/uptime-kuma/assets/1336778/e0c0a350-fe46-4588-9f37-e053c85834d1)

View File

@@ -1,23 +0,0 @@
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:dev-18-bookworm",
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"updateContentCommand": "npm ci",
"postCreateCommand": "",
"postAttachCommand": {
"frontend-dev": "npm run start-frontend-devcontainer",
"server-dev": "npm run start-server-dev",
"open-port": "gh codespace ports visibility 3001:public -c $CODESPACE_NAME"
},
"customizations": {
"vscode": {
"extensions": [
"streetsidesoftware.code-spell-checker",
"dbaeumer.vscode-eslint",
"GitHub.copilot-chat"
]
}
},
"forwardPorts": [3000, 3001]
}

View File

@@ -1,7 +1,6 @@
/.idea
/node_modules
/data*
/cypress
/out
/test
/kubernetes
@@ -18,7 +17,6 @@ README.md
.vscode
.eslint*
.stylelint*
/.devcontainer
/.github
yarn.lock
app.json

View File

@@ -1,8 +1,7 @@
module.exports = {
ignorePatterns: [
"test/*.js",
"test/cypress",
"server/modules/apicache/*",
"server/modules/*",
"src/util.js"
],
root: true,

View File

@@ -6,7 +6,7 @@ body:
- type: checkboxes
id: no-duplicate-issues
attributes:
label: "⚠️ Please verify that this bug has NOT been raised before."
label: "⚠️ Please verify that this question has NOT been raised before."
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
options:
- label: "I checked and didn't find similar issue"
@@ -24,7 +24,7 @@ body:
required: true
attributes:
label: "📝 Describe your problem"
description: "Please walk us through it step by step."
description: "Please walk us through it step by step. Include all important details and add screenshots where appropriate"
placeholder: "Describe what are you asking for..."
- type: textarea
id: error-msg
@@ -56,19 +56,20 @@ body:
placeholder: "Ex. Google Chrome 95.0.4638.69"
validations:
required: true
- type: input
id: docker-version
- type: textarea
id: deployment-info
attributes:
label: "🐋 Docker Version"
description: "If running with Docker, which version are you running?"
placeholder: "Ex. Docker 20.10.9 / K8S / Podman"
label: "🖥️ Deployment Environment"
description: |
examples:
- **Runtime**: Docker 20.10.9 / nodejs 14.18.0 / K8S via ... v1.3.3 / ..
- **Database**: sqlite/embedded mariadb/external mariadb
- **Filesystem used to store the database on**: Windows/ZFS/btrfs/NFSv3 on a SSD/HDD/eMMC
- **number of monitors**: 42
value: |
- Runtime:
- Database:
- Filesystem used to store the database on:
- number of monitors:
validations:
required: false
- type: input
id: nodejs-version
attributes:
label: "🟩 NodeJS Version"
description: "If running with Node.js? which version are you running?"
placeholder: "Ex. 14.18.0"
validations:
required: false
required: true

View File

@@ -3,14 +3,14 @@ description: "Submit a bug report to help us improve"
#title: "[Bug] "
labels: [bug]
body:
- type: checkboxes
id: no-duplicate-issues
- type: textarea
id: related-issues
validations:
required: true
attributes:
label: "⚠️ Please verify that this bug has NOT been raised before."
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
options:
- label: "I checked and didn't find similar issue"
required: true
label: "📑 I have found these related issues/pull requests"
description: "Search related issues by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=) and explain what the difference between them or explain that you are unable to find any related issues"
placeholder: "Related to #1 by also touching the ... system. They should not be merged because ..."
- type: checkboxes
attributes:
label: "🛡️ Security Policy"
@@ -31,7 +31,7 @@ body:
required: true
attributes:
label: "👟 Reproduction steps"
description: "How do you trigger this bug? Please walk us through it step by step."
description: "How do you trigger this bug? Please walk us through it step by step. Include all important details and add screenshots where appropriate"
placeholder: "..."
- type: textarea
id: expected-behavior
@@ -73,22 +73,23 @@ body:
placeholder: "Ex. Google Chrome 95.0.4638.69"
validations:
required: true
- type: input
id: docker-version
- type: textarea
id: deployment-info
attributes:
label: "🐋 Docker Version"
description: "If running with Docker, which version are you running?"
placeholder: "Ex. Docker 20.10.9 / K8S / Podman"
label: "🖥️ Deployment Environment"
description: |
examples:
- **Runtime**: Docker 20.10.9 / nodejs 18.17.1 / K8S via ... v1.3.3 / ..
- **Database**: sqlite/embedded mariadb/external mariadb
- **Filesystem used to store the database on**: Windows/ZFS/btrfs/NFSv3 on a SSD/HDD/eMMC
- **number of monitors**: 42
value: |
- Runtime:
- Database:
- Filesystem used to store the database on:
- number of monitors:
validations:
required: false
- type: input
id: nodejs-version
attributes:
label: "🟩 NodeJS Version"
description: "If running with Node.js? which version are you running?"
placeholder: "Ex. 14.18.0"
validations:
required: false
required: true
- type: textarea
id: logs
attributes:

View File

@@ -3,14 +3,14 @@ description: "Submit a proposal for a new feature"
#title: "[Feature] "
labels: [feature-request]
body:
- type: checkboxes
id: no-duplicate-issues
- type: textarea
id: related-issues
validations:
required: true
attributes:
label: "⚠️ Please verify that this feature request has NOT been suggested before."
description: "Search in the issues sections by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=)"
options:
- label: "I checked and didn't find similar feature request"
required: true
label: "📑 I have found these related issues/pull requests"
description: "Search related issues by clicking [HERE](https://github.com/louislam/uptime-kuma/issues?q=) and explain what the difference between them or explain that you are unable to find any related issues"
placeholder: "Related to #1 by also touching the ... system. They should not be merged because ..."
- type: dropdown
id: feature-area
attributes:
@@ -18,10 +18,17 @@ body:
description: "What kind of feature request is this?"
multiple: true
options:
- API
- New Notification
- New Monitor
- UI Feature
- API / automation options
- New notification-provider
- Change to existing notification-provider
- New monitor
- Change to existing monitor
- Dashboard
- Status-page
- Maintenance
- Deployment
- Certificate expiry
- Settings
- Other
validations:
required: true

View File

@@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest, ARM64]
node: [ 14, 20.5 ]
node: [ 18, 20.5 ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
@@ -33,7 +33,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm install npm@9 -g
- run: npm install
- run: npm run build
- run: npm run test-backend
@@ -46,11 +45,11 @@ jobs:
needs: [ check-linters ]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
if: ${{ github.repository == 'louislam/uptime-kuma' }}
strategy:
matrix:
os: [ ARMv7 ]
node: [ 14, 20 ]
node: [ 18, 20 ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
@@ -61,7 +60,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm install npm@9 -g
- run: npm ci --production
check-linters:

View File

@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node-version: [16]
node-version: [18]
steps:
- uses: actions/checkout@v4

25
.github/workflows/conflict_labeler.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Merge Conflict Labeler
on:
push:
branches:
- master
pull_request_target:
branches:
- master
types: [synchronize]
jobs:
label:
name: Labeling
runs-on: ubuntu-latest
if: ${{ github.repository == 'louislam/uptime-kuma' }}
permissions:
contents: read
pull-requests: write
steps:
- name: Apply label
uses: eps1lon/actions-label-merge-conflict@v3
with:
dirtyLabel: 'needs:resolve-merge-conflict'
repoToken: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -9,14 +9,34 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@v9
with:
stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 3 months with no activity. Remove stale label or comment or this will be closed in 2 days.'
close-issue-message: 'This issue was closed because it has been stalled for 2 days with no activity.'
days-before-stale: 90
days-before-close: 2
stale-issue-message: |-
We are clearing up our old `help`-issues and your issue has been open for 60 days with no activity.
If no comment is made and the stale label is not removed, this issue will be closed in 7 days.
days-before-stale: 60
days-before-close: 7
days-before-pr-stale: -1
days-before-pr-close: -1
exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,feature-request'
exempt-issue-assignees: 'louislam'
operations-per-run: 200
- uses: actions/stale@v9
with:
stale-issue-message: |-
This issue was marked as `cannot-reproduce` by a maintainer.
If an issue is non-reproducible, we cannot fix it, as we do not know what the underlying issue is.
If you have any ideas how we can reproduce this issue, we would love to hear them.
We don't have a good way to deal with truely unreproducible issues and are going to close this issue in a month.
If think there might be other differences in our environment or in how we tried to reproduce this, we would appreciate any ideas.
close-issue-message: |-
This issue will be closed as no way to reproduce it has been found.
If you/somebody finds a way how to (semi-reliably) reproduce this, we can reopen this issue. ^^
days-before-stale: 180
days-before-close: 30
days-before-pr-stale: -1
days-before-pr-close: -1
any-of-issue-labels: 'cannot-reproduce'
operations-per-run: 200

View File

@@ -1,76 +1,193 @@
# Project Info
First of all, I want to thank everyone who have made pull requests for Uptime Kuma. I never thought the GitHub community would be so nice! Because of this, I also never thought that other people would actually read and edit my code. It is not very well structured or commented, sorry about that.
First of all, I want to thank everyone who have wrote issues or shared pull requests for Uptime Kuma.
I never thought the GitHub community would be so nice!
Because of this, I also never thought that other people would actually read and edit my code.
Parts of the code are not very well-structured or commented, sorry about that.
The project was created with vite.js (vue3). Then I created a subdirectory called "server" for the server part. Both frontend and backend share the same `package.json`.
The project was created with `vite.js` and is written in `vue3`.
Our backend lives in the `server`-directory and mostly communicates via websockets.
Both frontend and backend share the same `package.json`.
The frontend code builds into "dist" directory. The server (express.js) exposes the "dist" directory as the root of the endpoint. This is how production is working.
## Key Technical Skills
- Node.js (You should know about promises, async/await, arrow functions, etc.)
- Socket.io
- SCSS
- Vue.js
- Bootstrap
- SQLite
For production, the frontend is build into `dist`-directory and the server (`express.js`) exposes the `dist` directory as the root of the endpoint.
For development, we run vite in development mode on another port.
## Directories
- config (dev config files)
- data (App data)
- db (Base database and migration scripts)
- dist (Frontend build)
- docker (Dockerfiles)
- extra (Extra useful scripts)
- public (Frontend resources for dev only)
- server (Server source code)
- src (Frontend source code)
- test (unit test)
- `config` (dev config files)
- `data` (App data)
- `db` (Base database and migration scripts)
- `dist` (Frontend build)
- `docker` (Dockerfiles)
- `extra` (Extra useful scripts)
- `public` (Frontend resources for dev only)
- `server` (Server source code)
- `src` (Frontend source code)
- `test` (unit test)
## Can I create a pull request for Uptime Kuma?
Yes or no, it depends on what you will try to do. Since I don't want to waste your time, be sure to **create an empty draft pull request or open an issue, so we can have a discussion first**. Especially for a large pull request or you don't know if it will be merged or not.
Yes or no, it depends on what you will try to do.
Both your and our maintainers time is precious, and we don't want to waste both time.
Here are some references:
If you have any questions about any process/.. is not clear, you are likely not alone => please ask them ^^
### ✅ Usually accepted
Different guidelines exist for different types of pull requests (PRs):
- <details><summary><b>security fixes</b></summary>
<p>
Submitting security fixes is something that may put the community at risk.
Please read through our [security policy](SECURITY.md) and submit vulnerabilities via an [advisory](https://github.com/louislam/uptime-kuma/security/advisories/new) + [issue](https://github.com/louislam/uptime-kuma/issues/new?assignees=&labels=help&template=security.md) instead.
We encourage you to submit how to fix a vulnerability if you know how to, this is not required.
Following the security policy allows us to properly test, fix bugs.
This review allows us to notice, if there are any changes necessary to unrelated parts like the documentation.
[**PLEASE SEE OUR SECURITY POLICY.**](SECURITY.md)
</p>
</details>
- <details><summary><b>small, non-breaking bug fixes</b></summary>
<p>
If you come across a bug and think you can solve, we appreciate your work.
Please make sure that you follow by these rules:
- keep the PR as small as possible, fix only one thing at a time => keeping it reviewable
- test that your code does what you came it does.
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
</p>
</details>
- <details><summary><b>translations / internationalisation (i18n)</b></summary>
<p>
We use weblate to localise this project into many languages.
If you are unhappy with a translation this is the best start.
On how to translate using weblate, please see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md).
There are two cases in which a change cannot be done in weblate and requires a PR:
- A text may not be currently localisable. In this case, **adding a new language key** via `$t("languageKey")` might be nessesary
- language keys need to be **added to `en.json`** to be visible in weblate. If this has not happened, a PR is appreciated.
- **Adding a new language** requires a new file see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md)
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
</p>
</details>
- <details><summary><b>new notification providers</b></summary>
<p>
To set up a new notification provider these files need to be modified/created:
- `server/notification-providers/PROVIDER_NAME.js` is where the heart of the notification provider lives.
- Both `monitorJSON` and `heartbeatJSON` can be `null` for some events.
If both are `null`, this is a general testing message, but if just `heartbeatJSON` is `null` this is a certificate expiry.
- Please wrap the axios call into a
```js
try {
let result = await axios.post(...);
if (result.status === ...) ...
} catch (error) {
this.throwGeneralAxiosError(error);
}
```
- `server/notification.js` is where the backend of the notification provider needs to be registered.
*If you have an idea how we can skip this step, we would love to hear about it ^^*
- `src/components/NotificationDialog.vue` you need to decide if the provider is a regional or a global one and add it with a name to the respective list
- `src/components/notifications/PROVIDER_NAME.vue` is where the frontend of each provider lives.
Please make sure that you have:
- used `HiddenInput` for secret credentials
- included all the necessary helptexts/placeholder/.. to make sure the notification provider is simple to setup for new users.
- include all translations (`{{ $t("Translation key") }}`, [`i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html)) in `src/lang/en.json` to enable our translators to translate this
- `src/components/notifications/index.js` is where the frontend of the provider needs to be registered.
*If you have an idea how we can skip this step, we would love to hear about it ^^*
- Bug fix
- Security fix
- Adding notification providers
- Adding new language files (see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md))
- Adding new language keys: `$t("...")`
Offering notifications is close to the core of what we are as an uptime monitor.
Therefore, making sure that they work is also really important.
Because testing notification providers is quite time intensive, we mostly offload this onto the person contributing a notification provider.
To make shure you have tested the notification provider, please include screenshots of the following events in the pull-request description:
- `UP`/`DOWN`
- Certificate Expiry via https://expired.badssl.com/
- Testing (the test button on the notification provider setup page)
Using the following way to format this is encouraged:
```md
| Event | Before | After |
------------------
| `UP` | paste-image-here | paste-image-here |
| `DOWN` | paste-image-here | paste-image-here |
| Certificate-expiry | paste-image-here | paste-image-here |
| Testing | paste-image-here | paste-image-here |
```
### ⚠️ Discussion required
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
</p>
</details>
- <details><summary><b>new monitoring types</b></summary>
<p>
- Large pull requests
- New features
To set up a new notification provider these files need to be modified/created:
- `server/monitor-types/MONITORING_TYPE.js` is the core of each monitor.
the `async check(...)`-function should:
- throw an error for each fault that is detected with an actionable error message
- in the happy-path, you should set `heartbeat.msg` to a successful message and set `heartbeat.status = UP`
- `server/uptime-kuma-server.js` is where the monitoring backend needs to be registered.
*If you have an idea how we can skip this step, we would love to hear about it ^^*
- `src/pages/EditMonitor.vue` is the shared frontend users interact with.
Please make sure that you have:
- used `HiddenInput` for secret credentials
- included all the necessary helptexts/placeholder/.. to make sure the notification provider is simple to setup for new users.
- include all translations (`{{ $t("Translation key") }}`, [`i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html)) in `src/lang/en.json` to enable our translators to translate this
-
### ❌ Won't be merged
- A dedicated PR for translating existing languages (see [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md))
- Do not pass the auto-test
- Any breaking changes
- Duplicated pull requests
- Buggy
- UI/UX is not close to Uptime Kuma
- Modifications or deletions of existing logic without a valid reason.
- Adding functions that is completely out of scope
- Converting existing code into other programming languages
- Unnecessarily large code changes that are hard to review and cause conflicts with other PRs.
<sub>Because maintainer time is precious junior maintainers may merge uncontroversial PRs in this area.</sub>
</p>
</details>
- <details><summary><b>new features/ major changes / breaking bugfixes</b></summary>
<p>
be sure to **create an empty draft pull request or open an issue, so we can have a discussion first**.
This is especially important for a large pull request or you don't know if it will be merged or not.
<sub>Because of the large impact of this work, only senior maintainers may merge PRs in this area.</sub>
</p>
</details>
The above cases may not cover all possible situations.
The following rules are essential for making your PR mergable:
- Merging multiple issues by a huge PR is more difficult to review and causes conflicts with other PRs. Please
- (if possible) **create one PR for one issue** or
- (if not possible) **explain which issues a PR addresses and why this PR should not be broken apart**
- Make sure your **PR passes our continuous integration**.
PRs will not be merged unless all CI-Checks are green.
- **Breaking changes** (unless for a good reason and discussed beforehand) will not get merged / not get merged quickly.
Such changes require a major version release.
- **Test your code** before submitting a PR.
Buggy PRs will not be merged.
- Make sure the **UI/UX is close to Uptime Kuma**.
- **Think about the maintainability**:
Don't add functionality that is completely **out of scope**.
Keep in mind that we need to be able to maintain the functionality.
- Don't modify or delete existing logic without a valid reason.
- Don't convert existing code into other programming languages for no reason.
I ([@louislam](https://github.com/louislam)) have the final say. If your pull request does not meet my expectations, I will reject it, no matter how much time you spent on it. Therefore, it is essential to have a discussion beforehand.
I ([@louislam](https://github.com/louislam)) have the final say.
If your pull request does not meet my expectations, I will reject it, no matter how much time you spent on it.
Therefore, it is essential to have a discussion beforehand.
I will assign your pull request to a [milestone](https://github.com/louislam/uptime-kuma/milestones), if I plan to review and merge it.
Also, please don't rush or ask for an ETA, because I have to understand the pull request, make sure it is no breaking changes and stick to my vision of this project, especially for large pull requests.
Please don't rush or ask for an ETA.
We have to understand the pull request, make sure it has no breaking changes and stick to the vision of this project, especially for large pull requests.
## I'd like to work on an issue. How do I do that?
We have found that assigning people to issues is management-overhead that we don't need.
A short comment that you want to try your hand at this issue is appreciated to save other devs time.
If you come across any problem during development, feel free to leave a comment with what you are stuck on.
### Recommended Pull Request Guideline
Before deep into coding, discussion first is preferred. Creating an empty pull request for discussion would be recommended.
Before diving deep into coding, having a discussion first by creating an empty pull request for discussion is preferred.
The rationale behind this is that we can align the direction and scope of the feature to eliminate any conflicts with existing and planned work, and can help by pointing out any potential pitfalls.
1. Fork the project
2. Clone your fork repo to local
@@ -84,10 +201,16 @@ Before deep into coding, discussion first is preferred. Creating an empty pull r
## Project Styles
I personally do not like something that requires so many configurations before you can finally start the app. I hope Uptime Kuma installation will be as easy as like installing a mobile app.
I personally do not like something that requires so many configurations before you can finally start the app.
The goal is to make the Uptime Kuma installation as easy as installing a mobile app.
- Easy to install for non-Docker users, no native build dependency is needed (for x86_64/armv7/arm64), no extra config, and no extra effort required to get it running
- Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go
- Easy to install for non-Docker users
- no native build dependency is needed (for `x86_64`/`armv7`/`arm64`)
- no extra configuration and
- no extra effort required to get it running
- Single container for Docker users
- no complex docker-compose file
- mapping the volume and exposing the port should be the only requirements
- Settings should be configurable in the frontend. Environment variables are discouraged, unless it is related to startup such as `DATA_DIR`
- Easy to use
- The web UI styling should be consistent and nice
@@ -107,18 +230,12 @@ I personally do not like something that requires so many configurations before y
## Tools
- [`Node.js`](https://nodejs.org/) >= 14
- [`npm`](https://www.npmjs.com/) >= 8.5
- [`Node.js`](https://nodejs.org/) >= 18
- [`npm`](https://www.npmjs.com/) >= 9.3
- [`git`](https://git-scm.com/)
- IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using [`IntelliJ IDEA`](https://www.jetbrains.com/idea/))
- A SQLite GUI tool (f.ex. [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or [`DBeaver Community`](https://dbeaver.io/download/))
### GitHub Codespaces
If you don't want to setup an local environment, you can now develop on GitHub Codespaces, read more:
https://github.com/louislam/uptime-kuma/tree/master/.devcontainer
## Git Branches
- `master`: 2.X.X development. If you want to add a new feature, your pull request should base on this.
@@ -154,25 +271,25 @@ npm run start-server-dev
It binds to `0.0.0.0:3001` by default.
It is mainly a socket.io app + express.js.
The backend is an `express.js` server with `socket.io` integrated.
It uses `socket.io` to communicate with clients, and most server logic is encapsulated in the `socket.io` handlers.
`express.js` is also used to serve:
express.js is used for:
- as an entry point for redirecting to a status page or the dashboard
- the frontend built files (`index.html`, `*.js`, `*.css`, etc.)
- internal APIs of the status page
- entry point such as redirecting to a status page or the dashboard
- serving the frontend built files (index.html, .js and .css etc.)
- serving internal APIs of the status page
### Structure in `/server/`
### Structure in /server/
- jobs/ (Jobs that are running in another process)
- model/ (Object model, auto-mapping to the database table name)
- modules/ (Modified 3rd-party modules)
- monitor_types (Monitor Types)
- notification-providers/ (individual notification logic)
- routers/ (Express Routers)
- socket-handler (Socket.io Handlers)
- server.js (Server entry point)
- uptime-kuma-server.js (UptimeKumaServer class, main logic should be here, but some still in `server.js`)
- `jobs/` (Jobs that are running in another process)
- `model/` (Object model, auto-mapping to the database table name)
- `modules/` (Modified 3rd-party modules)
- `monitor_types/` (Monitor Types)
- `notification-providers/` (individual notification logic)
- `routers/` (Express Routers)
- `socket-handler/` (Socket.io Handlers)
- `server.js` (Server entry point)
- `uptime-kuma-server.js` (UptimeKumaServer class, main logic should be here, but some still in `server.js`)
## Frontend Dev Server
@@ -211,14 +328,15 @@ npm test
## Dependencies
Both frontend and backend share the same package.json. However, the frontend dependencies are eventually not used in the production environment, because it is usually also baked into dist files. So:
Both frontend and backend share the same `package.json`.
However, the frontend dependencies are eventually not used in the production environment, because it is usually also baked into `dist` files. So:
- Frontend dependencies = "devDependencies"
- Examples: vue, chart.js
- Examples: `vue`, `chart.js`
- Backend dependencies = "dependencies"
- Examples: socket.io, sqlite3
- Examples: `socket.io`, `sqlite3`
- Development dependencies = "devDependencies"
- Examples: eslint, sass
- Examples: `eslint`, `sass`
### Update Dependencies
@@ -289,54 +407,106 @@ https://github.com/louislam/uptime-kuma-wiki
Check the latest issues and pull requests:
https://github.com/louislam/uptime-kuma/issues?q=sort%3Aupdated-desc
### Release Procedures
### What is a maintainer and what are their roles?
1. Draft a release note
2. Make sure the repo is cleared
3. If the healthcheck is updated, remember to re-compile it: `npm run build-docker-builder-go`
4. `npm run release-final` with env vars: `VERSION` and `GITHUB_TOKEN`
5. Wait until the `Press any key to continue`
6. `git push`
7. Publish the release note as 1.X.X
8. Press any key to continue
9. Deploy to the demo server: `npm run deploy-demo-server`
This project has multiple maintainers which specialise in different areas.
Currently, there are 3 maintainers:
Checking:
| Person | Role | Main Area |
|-------------------|-------------------|------------------|
| `@louislam` | senior maintainer | major features |
| `@chakflying` | junior maintainer | fixing bugs |
| `@commanderstorm` | junior maintainer | issue-management |
- Check all tags is fine on https://hub.docker.com/r/louislam/uptime-kuma/tags
- Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 / armv7)
- Try clean installation with Node.js
### Procedures
### Release Beta Procedures
We have a few procedures we follow. These are documented here:
- <details><summary>Set up a Docker Builder</summary>
<p>
1. Draft a release note, check "This is a pre-release"
2. Make sure the repo is cleared
3. `npm run release-beta` with env vars: `VERSION` and `GITHUB_TOKEN`
4. Wait until the `Press any key to continue`
5. Publish the release note as 1.X.X-beta.X
6. Press any key to continue
- amd64, armv7 using local.
- arm64 using remote arm64 cpu, as the emulator is too slow and can no longer pass the `npm ci` command.
1. Add the public key to the remote server.
2. Add the remote context. The remote machine must be arm64 and installed Docker CE.
```
docker context create oracle-arm64-jp --docker "host=ssh://root@100.107.174.88"
```
3. Create a new builder.
```
docker buildx create --name kuma-builder --platform linux/amd64,linux/arm/v7
docker buildx use kuma-builder
docker buildx inspect --bootstrap
```
4. Append the remote context to the builder.
```
docker buildx create --append --name kuma-builder --platform linux/arm64 oracle-arm64-jp
```
5. Verify the builder and check if the builder is using `kuma-builder`.
```
docker buildx inspect kuma-builder
docker buildx ls
```
</p>
</details>
- <details><summary>Release</summary>
<p>
### Release Wiki
1. Draft a release note
2. Make sure the repo is cleared
3. If the healthcheck is updated, remember to re-compile it: `npm run build-docker-builder-go`
4. `npm run release-final` with env vars: `VERSION` and `GITHUB_TOKEN`
5. Wait until the `Press any key to continue`
6. `git push`
7. Publish the release note as `1.X.X`
8. Press any key to continue
9. Deploy to the demo server: `npm run deploy-demo-server`
#### Setup Repo
These Items need to be checked:
```bash
git clone https://github.com/louislam/uptime-kuma-wiki.git
cd uptime-kuma-wiki
git remote add production https://github.com/louislam/uptime-kuma.wiki.git
```
- [ ] Check all tags is fine on https://hub.docker.com/r/louislam/uptime-kuma/tags
- [ ] Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 / armv7)
- [ ] Try clean installation with Node.js
</p>
</details>
- <details><summary>Release Beta</summary>
<p>
#### Push to Production Wiki
1. Draft a release note, check `This is a pre-release`
2. Make sure the repo is cleared
3. `npm run release-beta` with env vars: `VERSION` and `GITHUB_TOKEN`
4. Wait until the `Press any key to continue`
5. Publish the release note as `1.X.X-beta.X`
6. Press any key to continue
</p>
</details>
- <details><summary>Release Wiki</summary>
<p>
```bash
git pull
git push production master
```
## Useful Commands
Change the base of a pull request such as `master` to `1.23.X`
```bash
git rebase --onto <new parent> <old parent>
```
**Setup Repo**
```bash
git clone https://github.com/louislam/uptime-kuma-wiki.git
cd uptime-kuma-wiki
git remote add production https://github.com/louislam/uptime-kuma.wiki.git
```
**Push to Production Wiki**
```bash
git pull
git push production master
```
</p>
</details>
- <details><summary>Change the base of a pull request such as <code>master</code> to <code>1.23.X</code></summary>
<p>
```bash
git rebase --onto <new parent> <old parent>
```
</p>
</details>

View File

@@ -6,7 +6,7 @@
Uptime Kuma is an easy-to-use self-hosted monitoring tool.
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a>
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma?style=flat" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a>
[![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) <a href="https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/">
<img src="https://weblate.kuma.pet/widgets/uptime-kuma/-/svg-badge.svg" alt="Translation status" />
</a>
@@ -17,9 +17,9 @@ Uptime Kuma is an easy-to-use self-hosted monitoring tool.
Try it!
- Tokyo Demo Server: https://demo.uptime.kuma.pet (Sponsored by [Uptime Kuma Sponsors](https://github.com/louislam/uptime-kuma#%EF%B8%8F-sponsors))
Demo Server (Location: Frankfurt - Germany): https://demo.kuma.pet/start-demo
It is a temporary live demo, all data will be deleted after 10 minutes. Use the one that is closer to you, but I suggest that you should install and try it out for the best demo experience.
It is a temporary live demo, all data will be deleted after 10 minutes. Sponsored by [Uptime Kuma Sponsors](https://github.com/louislam/uptime-kuma#%EF%B8%8F-sponsors).
## ⭐ Features
@@ -43,11 +43,18 @@ It is a temporary live demo, all data will be deleted after 10 minutes. Use the
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
```
Uptime Kuma is now running on http://localhost:3001
Uptime Kuma is now running on <http://0.0.0.0:3001>.
> [!WARNING]
> File Systems like **NFS** (Network File System) are **NOT** supported. Please map to a local directory or volume.
> [!NOTE]
> If you want to limit exposure to localhost (without exposing port for other users or to use a [reverse proxy](https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy)), you can expose the port like this:
>
> ```bash
> docker run -d --restart=always -p 127.0.0.1:3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
> ```
### 💪🏻 Non-Docker
Requirements:
@@ -56,15 +63,12 @@ Requirements:
- ✅ Major Linux distros such as Debian, Ubuntu, CentOS, Fedora and ArchLinux etc.
- ✅ Windows 10 (x64), Windows Server 2012 R2 (x64) or higher
- ❌ Replit / Heroku
- [Node.js](https://nodejs.org/en/download/) 14 / 16 / 18 / 20.4
- [Node.js](https://nodejs.org/en/download/) 18 / 20.4
- [npm](https://docs.npmjs.com/cli/) 9
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) - For running Uptime Kuma in the background
```bash
# Update your npm
npm install npm@9 -g
git clone https://github.com/louislam/uptime-kuma.git
cd uptime-kuma
npm run setup

9
compose.yaml Normal file
View File

@@ -0,0 +1,9 @@
services:
uptime-kuma:
image: louislam/uptime-kuma:1
volumes:
- ./data:/app/data
ports:
# <Host Port>:<Container Port>
- 3001:3001
restart: unless-stopped

View File

@@ -1,28 +0,0 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
projectId: "vyjuem",
e2e: {
experimentalStudio: true,
setupNodeEvents(on, config) {
},
fixturesFolder: "test/cypress/fixtures",
screenshotsFolder: "test/cypress/screenshots",
videosFolder: "test/cypress/videos",
downloadsFolder: "test/cypress/downloads",
supportFile: "test/cypress/support/e2e.js",
baseUrl: "http://localhost:3002",
defaultCommandTimeout: 10000,
pageLoadTimeout: 60000,
viewportWidth: 1920,
viewportHeight: 1080,
specPattern: [
"test/cypress/e2e/setup.cy.js",
"test/cypress/e2e/**/*.js"
],
},
env: {
baseUrl: "http://localhost:3002",
},
});

View File

@@ -1,10 +0,0 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
supportFile: false,
specPattern: [
"test/cypress/unit/**/*.js"
],
}
});

View File

@@ -1,11 +1,11 @@
import { defineConfig, devices } from "@playwright/test";
const port = 30001;
const url = `http://localhost:${port}`;
export const url = `http://localhost:${port}`;
export default defineConfig({
// Look for test files in the "tests" directory, relative to this configuration file.
testDir: "../test/e2e",
testDir: "../test/e2e/specs",
outputDir: "../private/playwright-test-results",
fullyParallel: false,
locale: "en-US",
@@ -40,9 +40,15 @@ export default defineConfig({
// Configure projects for major browsers.
projects: [
{
name: "chromium",
name: "run-once setup",
testMatch: /setup-process\.once\.js/,
use: { ...devices["Desktop Chrome"] },
},
{
name: "specs",
use: { ...devices["Desktop Chrome"] },
dependencies: [ "run-once setup" ],
},
/*
{
name: "firefox",
@@ -52,7 +58,7 @@ export default defineConfig({
// Run your local dev server before starting the tests.
webServer: {
command: `node extra/remove-playwright-test-data.js && node server/server.js --port=${port} --data-dir=./data/playwright-test`,
command: `node extra/remove-playwright-test-data.js && cross-env NODE_ENV=development node server/server.js --port=${port} --data-dir=./data/playwright-test`,
url,
reuseExistingServer: false,
cwd: "../",

View File

@@ -2,6 +2,7 @@ import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import visualizer from "rollup-plugin-visualizer";
import viteCompression from "vite-plugin-compression";
import VueDevTools from "vite-plugin-vue-devtools";
const postCssScss = require("postcss-scss");
const postcssRTLCSS = require("postcss-rtlcss");
@@ -32,6 +33,7 @@ export default defineConfig({
algorithm: "brotliCompress",
filter: viteCompressionFilter,
}),
VueDevTools(),
],
css: {
postcss: {

View File

@@ -318,7 +318,10 @@ async function createTables() {
// monitor_tls_info
await knex.schema.createTable("monitor_tls_info", (table) => {
table.increments("id");
table.integer("monitor_id").unsigned().notNullable(); //TODO: no fk ?
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.text("info_json");
});

View File

@@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema
.alterTable("status_page", function (table) {
table.integer("auto_refresh_interval").defaultTo(300).unsigned();
});
};
exports.down = function (knex) {
return knex.schema.alterTable("status_page", function (table) {
table.dropColumn("auto_refresh_interval");
});
};

View File

@@ -0,0 +1,24 @@
exports.up = function (knex) {
return knex.schema
.alterTable("stat_daily", function (table) {
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds");
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds");
})
.alterTable("stat_minutely", function (table) {
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds");
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds");
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("stat_daily", function (table) {
table.dropColumn("ping_min");
table.dropColumn("ping_max");
})
.alterTable("stat_minutely", function (table) {
table.dropColumn("ping_min");
table.dropColumn("ping_max");
});
};

View File

@@ -0,0 +1,26 @@
exports.up = function (knex) {
return knex.schema
.createTable("stat_hourly", function (table) {
table.increments("id");
table.comment("This table contains the hourly aggregate statistics for each monitor");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("timestamp")
.notNullable()
.comment("Unix timestamp rounded down to the nearest hour");
table.float("ping").notNullable().comment("Average ping in milliseconds");
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds");
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds");
table.smallint("up").notNullable();
table.smallint("down").notNullable();
table.unique([ "monitor_id", "timestamp" ]);
});
};
exports.down = function (knex) {
return knex.schema
.dropTable("stat_hourly");
};

View File

@@ -0,0 +1,26 @@
exports.up = function (knex) {
return knex.schema
.alterTable("stat_daily", function (table) {
table.text("extras").defaultTo(null).comment("Extra statistics during this time period");
})
.alterTable("stat_minutely", function (table) {
table.text("extras").defaultTo(null).comment("Extra statistics during this time period");
})
.alterTable("stat_hourly", function (table) {
table.text("extras").defaultTo(null).comment("Extra statistics during this time period");
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("stat_daily", function (table) {
table.dropColumn("extras");
})
.alterTable("stat_minutely", function (table) {
table.dropColumn("extras");
})
.alterTable("stat_hourly", function (table) {
table.dropColumn("extras");
});
};

View File

@@ -0,0 +1,16 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("snmp_oid").defaultTo(null);
table.enum("snmp_version", [ "1", "2c", "3" ]).defaultTo("2c");
table.string("json_path_operator").defaultTo(null);
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("snmp_oid");
table.dropColumn("snmp_version");
table.dropColumn("json_path_operator");
});
};

View File

@@ -0,0 +1,13 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.boolean("cache_bust").notNullable().defaultTo(false);
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.dropColumn("cache_bust");
});
};

View File

@@ -0,0 +1,12 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.text("conditions").notNullable().defaultTo("[]");
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("conditions");
});
};

View File

@@ -0,0 +1,18 @@
BEGIN TRANSACTION;
PRAGMA writable_schema = TRUE;
UPDATE
SQLITE_MASTER
SET
sql = replace(sql,
'monitor_id INTEGER NOT NULL',
'monitor_id INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE'
)
WHERE
name = 'monitor_tls_info'
AND type = 'table';
PRAGMA writable_schema = RESET;
COMMIT;

View File

@@ -0,0 +1,18 @@
BEGIN TRANSACTION;
PRAGMA writable_schema = TRUE;
UPDATE
SQLITE_MASTER
SET
sql = replace(sql,
'monitor_id INTEGER NOT NULL',
'monitor_id INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE'
)
WHERE
name = 'monitor_tls_info'
AND type = 'table';
PRAGMA writable_schema = RESET;
COMMIT;

View File

@@ -3,7 +3,6 @@ FROM node:20-bookworm-slim AS base2-slim
ARG TARGETPLATFORM
# Specify --no-install-recommends to skip unused dependencies, make the base much smaller!
# apprise = for notifications (From testing repo)
# sqlite3 = for debugging
# iputils-ping = for ping
# util-linux = for setpriv (Should be dropped in 2.0.0?)
@@ -12,10 +11,10 @@ ARG TARGETPLATFORM
# ca-certificates = keep the cert up-to-date
# sudo = for start service nscd with non-root user
# nscd = for better DNS caching
RUN echo "deb http://deb.debian.org/debian testing main" >> /etc/apt/sources.list && \
apt update && \
apt --yes --no-install-recommends -t testing install apprise sqlite3 ca-certificates && \
apt --yes --no-install-recommends -t stable install \
RUN apt update && \
apt --yes --no-install-recommends install \
sqlite3 \
ca-certificates \
iputils-ping \
util-linux \
dumb-init \
@@ -25,6 +24,15 @@ RUN echo "deb http://deb.debian.org/debian testing main" >> /etc/apt/sources.lis
rm -rf /var/lib/apt/lists/* && \
apt --yes autoremove
# apprise = for notifications (Install from the deb package, as the stable one is too old) (workaround for #4867)
# Switching to testing repo is no longer working, as the testing repo is not bookworm anymore.
# python3-paho-mqtt (#4859)
RUN curl http://ftp.debian.org/debian/pool/main/a/apprise/apprise_1.8.0-2_all.deb --output apprise.deb && \
apt update && \
apt --yes --no-install-recommends install ./apprise.deb python3-paho-mqtt && \
rm -rf /var/lib/apt/lists/* && \
rm -f apprise.deb && \
apt --yes autoremove
# Install cloudflared
RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \
@@ -42,7 +50,9 @@ COPY ./docker/etc/sudoers /etc/sudoers
# Full Base Image
# MariaDB, Chromium and fonts
FROM base2-slim AS base2
# Make sure to reuse the slim image here. Uncomment the above line if you want to build it from scratch.
# FROM base2-slim AS base2
FROM louislam/uptime-kuma:base2-slim AS base2
ENV UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1
RUN apt update && \
apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk mariadb-server && \

View File

@@ -1,15 +0,0 @@
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- uptime-kuma:/app/data
ports:
- "3001:3001" # <Host Port>:<Container Port>
restart: always
volumes:
uptime-kuma:

View File

@@ -37,7 +37,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please DO NOT open a blank issue.`
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please **DO NOT open blank issues and use our [issue-templates](https://github.com/louislam/uptime-kuma/issues/new/choose) instead**.\nBlank Issues do not contain the context nessesary for a good discussions.`
});
// Close the issue

View File

@@ -8,6 +8,7 @@ const User = require("../server/model/user");
const { io } = require("socket.io-client");
const { localWebSocketURL } = require("../server/config");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
@@ -19,10 +20,10 @@ const main = async () => {
}
console.log("Connecting the database");
Database.initDataDir(args);
await Database.connect(false, false, true);
try {
Database.initDataDir(args);
await Database.connect(false, false, true);
// 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");

13761
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"url": "https://github.com/louislam/uptime-kuma.git"
},
"engines": {
"node": "14 || 16 || 18 || >= 20.4.0"
"node": "18 || >= 20.4.0"
},
"scripts": {
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
@@ -27,9 +27,7 @@
"build": "vite build --config ./config/vite.config.js",
"test": "npm run test-backend && npm run test-e2e",
"test-with-build": "npm run build && npm test",
"test-backend": "node test/backend-test-entry.js",
"test-backend:14": "cross-env TEST_BACKEND=1 NODE_OPTIONS=\"--experimental-abortcontroller --no-warnings\" node--test test/backend-test",
"test-backend:18": "cross-env TEST_BACKEND=1 node --test test/backend-test",
"test-backend": "cross-env TEST_BACKEND=1 node --test test/backend-test",
"test-e2e": "playwright test --config ./config/playwright.config.js",
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
@@ -49,7 +47,7 @@
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.23.10 && npm ci --production && npm run download-dist",
"setup": "git checkout 1.23.14 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@@ -74,12 +72,12 @@
"start-server-node14-win": "private\\node14\\node.exe server/server.js"
},
"dependencies": {
"@grpc/grpc-js": "~1.7.3",
"@grpc/grpc-js": "~1.8.22",
"@louislam/ping": "~0.4.4-mod.1",
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "^6.125.0",
"args-parser": "~1.3.0",
"axios": "~0.27.0",
"axios-ntlm": "1.3.0",
"axios": "~0.28.1",
"badge-maker": "~3.3.1",
"bcryptjs": "~2.4.3",
"chardet": "~1.4.0",
@@ -89,12 +87,14 @@
"command-exists": "~1.2.9",
"compare-versions": "~3.6.0",
"compression": "~1.7.4",
"croner": "~6.0.5",
"croner": "~8.1.0",
"dayjs": "~1.11.5",
"dev-null": "^0.1.1",
"dotenv": "~16.0.3",
"express": "~4.17.3",
"express": "~4.21.0",
"express-basic-auth": "~1.2.1",
"express-static-gzip": "~2.1.7",
"feed": "^4.2.2",
"form-data": "~4.0.0",
"gamedig": "^4.2.0",
"html-escaper": "^3.0.3",
@@ -112,15 +112,17 @@
"knex": "^2.4.2",
"limiter": "~2.1.0",
"liquidjs": "^10.7.0",
"marked": "^14.0.0",
"mitt": "~3.0.1",
"mongodb": "~4.17.1",
"mqtt": "~4.3.7",
"mssql": "~8.1.4",
"mysql2": "~3.6.2",
"mssql": "~11.0.0",
"mysql2": "~3.9.6",
"nanoid": "~3.3.4",
"net-snmp": "^3.11.2",
"node-cloudflared-tunnel": "~1.0.9",
"node-radius-client": "~1.0.0",
"nodemailer": "~6.6.5",
"nodemailer": "~6.9.13",
"nostr-tools": "^1.13.1",
"notp": "~2.0.3",
"openid-client": "^5.4.2",
@@ -136,29 +138,30 @@
"redbean-node": "~0.3.0",
"redis": "~4.5.1",
"semver": "~7.5.4",
"socket.io": "~4.6.1",
"socket.io-client": "~4.6.1",
"socket.io": "~4.8.0",
"socket.io-client": "~4.8.0",
"socks-proxy-agent": "6.1.1",
"tar": "~6.1.11",
"tar": "~6.2.1",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
"tough-cookie": "~4.1.3",
"ws": "^8.13.0"
},
"devDependencies": {
"@actions/github": "~5.0.1",
"@actions/github": "~5.1.1",
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-5",
"@playwright/test": "~1.39.0",
"@popperjs/core": "~2.10.2",
"@testcontainers/hivemq": "^10.13.1",
"@types/bootstrap": "~5.1.9",
"@types/node": "^20.8.6",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"@vitejs/plugin-vue": "~4.2.3",
"@vue/compiler-sfc": "~3.3.4",
"@vitejs/plugin-vue": "~5.0.1",
"@vue/compiler-sfc": "~3.4.2",
"@vuepic/vue-datepicker": "~3.4.8",
"aedes": "^0.46.3",
"bootstrap": "5.1.3",
@@ -170,13 +173,12 @@
"cross-env": "~7.0.3",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"dompurify": "~3.1.7",
"eslint": "~8.14.0",
"eslint-plugin-jsdoc": "~46.4.6",
"eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10",
"get-port-please": "^3.1.1",
"marked": "~4.2.5",
"node-ssh": "~13.1.0",
"postcss-html": "~1.5.0",
"postcss-rtlcss": "~3.7.2",
@@ -189,12 +191,13 @@
"stylelint-config-standard": "~25.0.0",
"terser": "~5.15.0",
"test": "~3.3.0",
"timezones-list": "~3.0.1",
"testcontainers": "^10.13.1",
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7",
"vite": "~4.4.1",
"vite": "~5.2.8",
"vite-plugin-compression": "^0.5.1",
"vue": "~3.3.4",
"vite-plugin-vue-devtools": "^7.0.15",
"vue": "~3.4.2",
"vue-chartjs": "~5.2.0",
"vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4",
@@ -203,7 +206,7 @@
"vue-multiselect": "~3.0.0-alpha.2",
"vue-prism-editor": "~2.0.0-alpha.2",
"vue-qrcode": "~1.0.0",
"vue-router": "~4.0.14",
"vue-router": "~4.2.5",
"vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0",
"wait-on": "^7.2.0",

View File

@@ -1,7 +1,6 @@
const basicAuth = require("express-basic-auth");
const passwordHash = require("./password-hash");
const { R } = require("redbean-node");
const { setting } = require("./util-server");
const { log } = require("../src/util");
const { loginRateLimiter, apiRateLimiter } = require("./rate-limiter");
const { Settings } = require("./settings");
@@ -130,7 +129,7 @@ function userAuthorizer(username, password, callback) {
* @param {express.Request} req Express request object
* @param {express.Response} res Express response object
* @param {express.NextFunction} next Next handler in chain
* @returns {void}
* @returns {Promise<void>}
*/
exports.basicAuth = async function (req, res, next) {
const middleware = basicAuth({
@@ -139,7 +138,7 @@ exports.basicAuth = async function (req, res, next) {
challenge: true,
});
const disabledAuth = await setting("disableAuth");
const disabledAuth = await Settings.get("disableAuth");
if (!disabledAuth) {
middleware(req, res, next);
@@ -153,7 +152,7 @@ exports.basicAuth = async function (req, res, next) {
* @param {express.Request} req Express request object
* @param {express.Response} res Express response object
* @param {express.NextFunction} next Next handler in chain
* @returns {void}
* @returns {Promise<void>}
*/
exports.apiAuth = async function (req, res, next) {
if (!await Settings.get("disableAuth")) {

View File

@@ -1,7 +1,7 @@
const { setSetting, setting } = require("./util-server");
const axios = require("axios");
const compareVersions = require("compare-versions");
const { log } = require("../src/util");
const { Settings } = require("./settings");
exports.version = require("../package.json").version;
exports.latestVersion = null;
@@ -14,7 +14,7 @@ let interval;
exports.startInterval = () => {
let check = async () => {
if (await setting("checkUpdate") === false) {
if (await Settings.get("checkUpdate") === false) {
return;
}
@@ -28,7 +28,7 @@ exports.startInterval = () => {
res.data.slow = "1000.0.0";
}
let checkBeta = await setting("checkBeta");
let checkBeta = await Settings.get("checkBeta");
if (checkBeta && res.data.beta) {
if (compareVersions.compare(res.data.beta, res.data.slow, ">")) {
@@ -57,7 +57,7 @@ exports.startInterval = () => {
* @returns {Promise<void>}
*/
exports.enableCheckUpdate = async (value) => {
await setSetting("checkUpdate", value);
await Settings.set("checkUpdate", value);
clearInterval(interval);

View File

@@ -6,8 +6,9 @@ const { R } = require("redbean-node");
const { UptimeKumaServer } = require("./uptime-kuma-server");
const server = UptimeKumaServer.getInstance();
const io = server.io;
const { setting } = require("./util-server");
const checkVersion = require("./check-version");
const { Settings } = require("./settings");
const Database = require("./database");
/**
* Send list of notification providers to client
@@ -144,18 +145,21 @@ async function sendInfo(socket, hideVersion = false) {
let version;
let latestVersion;
let isContainer;
let dbType;
if (!hideVersion) {
version = checkVersion.version;
latestVersion = checkVersion.latestVersion;
isContainer = (process.env.UPTIME_KUMA_IS_CONTAINER === "1");
dbType = Database.dbConfig.type;
}
socket.emit("info", {
version,
latestVersion,
isContainer,
primaryBaseURL: await setting("primaryBaseURL"),
primaryBaseURL: await Settings.get("primaryBaseURL"),
dbType,
serverTimezone: await server.getTimezone(),
serverTimezoneOffset: server.getTimezoneOffset(),
});
@@ -209,6 +213,32 @@ async function sendRemoteBrowserList(socket) {
return list;
}
/**
* Send list of monitor types to client
* @param {Socket} socket Socket.io socket instance
* @returns {Promise<void>}
*/
async function sendMonitorTypeList(socket) {
const result = Object.entries(UptimeKumaServer.monitorTypeList).map(([ key, type ]) => {
return [ key, {
supportsConditions: type.supportsConditions,
conditionVariables: type.conditionVariables.map(v => {
return {
id: v.id,
operators: v.operators.map(o => {
return {
id: o.id,
caption: o.caption,
};
}),
};
}),
}];
});
io.to(socket.userID).emit("monitorTypeList", Object.fromEntries(result));
}
module.exports = {
sendNotificationList,
sendImportantHeartbeatList,
@@ -218,4 +248,5 @@ module.exports = {
sendInfo,
sendDockerHostList,
sendRemoteBrowserList,
sendMonitorTypeList,
};

View File

@@ -1,11 +1,11 @@
const fs = require("fs");
const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server");
const { log, sleep } = require("../src/util");
const knex = require("knex");
const path = require("path");
const { EmbeddedMariaDB } = require("./embedded-mariadb");
const mysql = require("mysql2/promise");
const { Settings } = require("./settings");
/**
* Database & App Data Folder
@@ -105,7 +105,8 @@ class Database {
"patch-add-gamedig-given-port.sql": true,
"patch-notification-config.sql": true,
"patch-fix-kafka-producer-booleans.sql": true,
"patch-timeout.sql": true, // The last file so far converted to a knex migration file
"patch-timeout.sql": true,
"patch-monitor-tls-info-add-fk.sql": true, // The last file so far converted to a knex migration file
};
/**
@@ -208,9 +209,9 @@ class Database {
let config = {};
let mariadbPoolConfig = {
afterCreate: function (conn, done) {
}
min: 0,
max: 10,
idleTimeoutMillis: 30000,
};
log.info("db", `Database Type: ${dbConfig.type}`);
@@ -378,7 +379,7 @@ class Database {
/**
* Patch the database
* @returns {void}
* @returns {Promise<void>}
*/
static async patch() {
// Still need to keep this for old versions of Uptime Kuma
@@ -419,7 +420,7 @@ class Database {
* @deprecated
*/
static async patchSqlite() {
let version = parseInt(await setting("database_version"));
let version = parseInt(await Settings.get("database_version"));
if (! version) {
version = 0;
@@ -444,7 +445,7 @@ class Database {
log.info("db", `Patching ${sqlFile}`);
await Database.importSQLFile(sqlFile);
log.info("db", `Patched ${sqlFile}`);
await setSetting("database_version", i);
await Settings.set("database_version", i);
}
} catch (ex) {
await Database.close();
@@ -470,7 +471,7 @@ class Database {
*/
static async patchSqlite2() {
log.debug("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles");
let databasePatchedFiles = await Settings.get("databasePatchedFiles");
if (! databasePatchedFiles) {
databasePatchedFiles = {};
@@ -498,7 +499,7 @@ class Database {
process.exit(1);
}
await setSetting("databasePatchedFiles", databasePatchedFiles);
await Settings.set("databasePatchedFiles", databasePatchedFiles);
}
/**
@@ -511,27 +512,27 @@ class Database {
// Fix 1.13.0 empty slug bug
await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''");
let title = await setting("title");
let title = await Settings.get("title");
if (title) {
console.log("Migrating Status Page");
log.info("database", "Migrating Status Page");
let statusPageCheck = await R.findOne("status_page", " slug = 'default' ");
if (statusPageCheck !== null) {
console.log("Migrating Status Page - Skip, default slug record is already existing");
log.info("database", "Migrating Status Page - Skip, default slug record is already existing");
return;
}
let statusPage = R.dispense("status_page");
statusPage.slug = "default";
statusPage.title = title;
statusPage.description = await setting("description");
statusPage.icon = await setting("icon");
statusPage.theme = await setting("statusPageTheme");
statusPage.published = !!await setting("statusPagePublished");
statusPage.search_engine_index = !!await setting("searchEngineIndex");
statusPage.show_tags = !!await setting("statusPageTags");
statusPage.description = await Settings.get("description");
statusPage.icon = await Settings.get("icon");
statusPage.theme = await Settings.get("statusPageTheme");
statusPage.published = !!await Settings.get("statusPagePublished");
statusPage.search_engine_index = !!await Settings.get("searchEngineIndex");
statusPage.show_tags = !!await Settings.get("statusPageTags");
statusPage.password = null;
if (!statusPage.title) {
@@ -559,13 +560,13 @@ class Database {
await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
// Migrate Entry Page if it is status page
let entryPage = await setting("entryPage");
let entryPage = await Settings.get("entryPage");
if (entryPage === "statusPage") {
await setSetting("entryPage", "statusPage-default", "general");
await Settings.set("entryPage", "statusPage-default", "general");
}
console.log("Migrating Status Page - Done");
log.info("database", "Migrating Status Page - Done");
}
}

View File

@@ -65,7 +65,7 @@ class DockerHost {
/**
* Fetches the amount of containers on the Docker host
* @param {object} dockerHost Docker host to check for
* @returns {number} Total amount of containers on the host
* @returns {Promise<number>} Total amount of containers on the host
*/
static async testDockerHost(dockerHost) {
const options = {

View File

@@ -1,7 +1,7 @@
const { R } = require("redbean-node");
const { log } = require("../../src/util");
const { setSetting, setting } = require("../util-server");
const Database = require("../database");
const { Settings } = require("../settings");
const DEFAULT_KEEP_PERIOD = 180;
@@ -11,11 +11,11 @@ const DEFAULT_KEEP_PERIOD = 180;
*/
const clearOldData = async () => {
let period = await setting("keepDataPeriodDays");
let period = await Settings.get("keepDataPeriodDays");
// Set Default Period
if (period == null) {
await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
period = DEFAULT_KEEP_PERIOD;
}
@@ -25,7 +25,7 @@ const clearOldData = async () => {
parsedPeriod = parseInt(period);
} catch (_) {
log.warn("clearOldData", "Failed to parse setting, resetting to default..");
await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general");
parsedPeriod = DEFAULT_KEEP_PERIOD;
}

View File

@@ -9,7 +9,7 @@ class Group extends BeanModel {
* @param {boolean} showTags Should the JSON include monitor tags
* @param {boolean} certExpiry Should JSON include info about
* certificate expiry?
* @returns {object} Object ready to parse
* @returns {Promise<object>} Object ready to parse
*/
async toPublicJSON(showTags = false, certExpiry = false) {
let monitorBeanList = await this.getMonitorList();
@@ -29,7 +29,7 @@ class Group extends BeanModel {
/**
* Get all monitors
* @returns {Bean[]} List of monitors
* @returns {Promise<Bean[]>} List of monitors
*/
async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(`

View File

@@ -11,7 +11,7 @@ class Maintenance extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {object} Object ready to parse
* @returns {Promise<object>} Object ready to parse
*/
async toPublicJSON() {
@@ -98,7 +98,7 @@ class Maintenance extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @param {string} timezone If not specified, the timeRange will be in UTC
* @returns {object} Object ready to parse
* @returns {Promise<object>} Object ready to parse
*/
async toJSON(timezone = null) {
return this.toPublicJSON(timezone);
@@ -143,7 +143,7 @@ class Maintenance extends BeanModel {
* Convert data from socket to bean
* @param {Bean} bean Bean to fill in
* @param {object} obj Data to fill bean with
* @returns {Bean} Filled bean
* @returns {Promise<Bean>} Filled bean
*/
static async jsonToBean(bean, obj) {
if (obj.id) {
@@ -189,9 +189,9 @@ class Maintenance extends BeanModel {
/**
* Throw error if cron is invalid
* @param {string|Date} cron Pattern or date
* @returns {Promise<void>}
* @returns {void}
*/
static async validateCron(cron) {
static validateCron(cron) {
let job = new Cron(cron, () => {});
job.stop();
}
@@ -239,19 +239,7 @@ class Maintenance extends BeanModel {
this.beanMeta.status = "under-maintenance";
clearTimeout(this.beanMeta.durationTimeout);
// Check if duration is still in the window. If not, use the duration from the current time to the end of the window
let duration;
if (customDuration > 0) {
duration = customDuration;
} else if (this.end_date) {
let d = dayjs(this.end_date).diff(dayjs(), "second");
if (d < this.duration) {
duration = d * 1000;
}
} else {
duration = this.duration * 1000;
}
let duration = this.inferDuration(customDuration);
UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id);
@@ -263,9 +251,21 @@ class Maintenance extends BeanModel {
};
// Create Cron
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
}, startEvent);
if (this.strategy === "recurring-interval") {
// For recurring-interval, Croner needs to have interval and startAt
const startDate = dayjs(this.startDate);
const [ hour, minute ] = this.startTime.split(":");
const startDateTime = startDate.hour(hour).minute(minute);
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
interval: this.interval_day * 24 * 60 * 60,
startAt: startDateTime.toISOString(),
}, startEvent);
} else {
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
}, startEvent);
}
// Continue if the maintenance is still in the window
let runningTimeslot = this.getRunningTimeslot();
@@ -311,6 +311,24 @@ class Maintenance extends BeanModel {
}
}
/**
* Calculate the maintenance duration
* @param {number} customDuration - The custom duration in milliseconds.
* @returns {number} The inferred duration in milliseconds.
*/
inferDuration(customDuration) {
// Check if duration is still in the window. If not, use the duration from the current time to the end of the window
if (customDuration > 0) {
return customDuration;
} else if (this.end_date) {
let d = dayjs(this.end_date).diff(dayjs(), "second");
if (d < this.duration) {
return d * 1000;
}
}
return this.duration * 1000;
}
/**
* Stop the maintenance
* @returns {void}
@@ -324,7 +342,7 @@ class Maintenance extends BeanModel {
/**
* Is this maintenance currently active
* @returns {boolean} The maintenance is active?
* @returns {Promise<boolean>} The maintenance is active?
*/
async isUnderMaintenance() {
return (await this.getStatus()) === "under-maintenance";
@@ -332,7 +350,7 @@ class Maintenance extends BeanModel {
/**
* Get the timezone of the maintenance
* @returns {string} timezone
* @returns {Promise<string>} timezone
*/
async getTimezone() {
if (!this.timezone || this.timezone === "SAME_AS_SERVER") {
@@ -343,7 +361,7 @@ class Maintenance extends BeanModel {
/**
* Get offset for timezone
* @returns {string} offset
* @returns {Promise<string>} offset
*/
async getTimezoneOffset() {
return dayjs.tz(dayjs(), await this.getTimezone()).format("Z");
@@ -351,7 +369,7 @@ class Maintenance extends BeanModel {
/**
* Get the current status of the maintenance
* @returns {string} Current status
* @returns {Promise<string>} Current status
*/
async getStatus() {
if (!this.active) {
@@ -395,10 +413,8 @@ class Maintenance extends BeanModel {
} else if (!this.strategy.startsWith("recurring-")) {
this.cron = "";
} else if (this.strategy === "recurring-interval") {
let array = this.start_time.split(":");
let hour = parseInt(array[0]);
let minute = parseInt(array[1]);
this.cron = minute + " " + hour + " */" + this.interval_day + " * *";
// For intervals, the pattern is calculated in the run function as the interval-option is set
this.cron = "* * * * *";
this.duration = this.calcDuration();
log.debug("maintenance", "Cron: " + this.cron);
log.debug("maintenance", "Duration: " + this.duration);

View File

@@ -2,10 +2,10 @@ const dayjs = require("dayjs");
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
SQL_DATETIME_FORMAT
SQL_DATETIME_FORMAT, evaluateJsonQuery
} = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, mssqlQuery, postgresQuery, mysqlQuery, httpNtlm, radius, grpcQuery,
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
} = require("../util-server");
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
@@ -17,7 +17,6 @@ const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const { DockerHost } = require("../docker");
const Gamedig = require("gamedig");
const jsonata = require("jsonata");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const { UptimeCalculator } = require("../uptime-calculator");
@@ -25,6 +24,7 @@ const { CookieJar } = require("tough-cookie");
const { HttpsCookieAgent } = require("http-cookie-agent/http");
const https = require("https");
const http = require("http");
const { Settings } = require("../settings");
const rootCertificates = rootCertificatesFingerprints();
@@ -43,7 +43,7 @@ class Monitor extends BeanModel {
* @param {boolean} showTags Include tags in JSON
* @param {boolean} certExpiry Include certificate expiry info in
* JSON
* @returns {object} Object ready to parse
* @returns {Promise<object>} Object ready to parse
*/
async toPublicJSON(showTags = false, certExpiry = false) {
let obj = {
@@ -74,7 +74,7 @@ class Monitor extends BeanModel {
* Return an object that ready to parse to JSON
* @param {boolean} includeSensitiveData Include sensitive data in
* JSON
* @returns {object} Object ready to parse
* @returns {Promise<object>} Object ready to parse
*/
async toJSON(includeSensitiveData = true) {
@@ -96,11 +96,15 @@ class Monitor extends BeanModel {
screenshot = "/screenshots/" + jwt.sign(this.id, UptimeKumaServer.getInstance().jwtSecret) + ".png";
}
const path = await this.getPath();
const pathName = path.join(" / ");
let data = {
id: this.id,
name: this.name,
description: this.description,
pathName: await this.getPathName(),
path,
pathName,
parent: this.parent,
childrenIDs: await Monitor.getAllChildrenIDs(this.id),
url: this.url,
@@ -156,7 +160,12 @@ class Monitor extends BeanModel {
kafkaProducerAllowAutoTopicCreation: this.getKafkaProducerAllowAutoTopicCreation(),
kafkaProducerMessage: this.kafkaProducerMessage,
screenshot,
cacheBust: this.getCacheBust(),
remote_browser: this.remote_browser,
snmpOid: this.snmpOid,
jsonPathOperator: this.jsonPathOperator,
snmpVersion: this.snmpVersion,
conditions: JSON.parse(this.conditions),
};
if (includeSensitiveData) {
@@ -241,12 +250,12 @@ class Monitor extends BeanModel {
/**
* Encode user and password to Base64 encoding
* for HTTP "basic" auth, as per RFC-7617
* @param {string} user Username to encode
* @param {string} pass Password to encode
* @returns {string} Encoded username:password
* @param {string|null} user - The username (nullable if not changed by a user)
* @param {string|null} pass - The password (nullable if not changed by a user)
* @returns {string} Encoded Base64 string
*/
encodeBase64(user, pass) {
return Buffer.from(user + ":" + pass).toString("base64");
return Buffer.from(`${user || ""}:${pass || ""}`).toString("base64");
}
/**
@@ -289,6 +298,14 @@ class Monitor extends BeanModel {
return Boolean(this.grpcEnableTls);
}
/**
* Parse to boolean
* @returns {boolean} if cachebusting is enabled
*/
getCacheBust() {
return Boolean(this.cacheBust);
}
/**
* Get accepted status codes
* @returns {object} Accepted status codes
@@ -324,13 +341,13 @@ class Monitor extends BeanModel {
/**
* Start monitor
* @param {Server} io Socket server instance
* @returns {void}
* @returns {Promise<void>}
*/
start(io) {
async start(io) {
let previousBeat = null;
let retries = 0;
this.prometheus = new Prometheus(this);
this.prometheus = await Prometheus.createAndInitMetrics(this);
const beat = async () => {
@@ -494,6 +511,14 @@ class Monitor extends BeanModel {
options.data = bodyValue;
}
if (this.cacheBust) {
const randomFloatString = Math.random().toString(36);
const cacheBust = randomFloatString.substring(2);
options.params = {
uptime_kuma_cachebuster: cacheBust,
};
}
if (this.proxy_id) {
const proxy = await R.load("proxy", this.proxy_id);
@@ -529,6 +554,18 @@ class Monitor extends BeanModel {
}
}
let tlsInfo = {};
// Store tlsInfo when secureConnect event is emitted
// The keylog event listener is a workaround to access the tlsSocket
options.httpsAgent.once("keylog", async (line, tlsSocket) => {
tlsSocket.once("secureConnect", async () => {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;
await this.handleTlsInfo(tlsInfo);
});
});
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
log.debug("monitor", `[${this.name}] Axios Request`);
@@ -538,31 +575,19 @@ class Monitor extends BeanModel {
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
// Check certificate if https is used
let certInfoStartTime = dayjs().valueOf();
if (this.getUrl()?.protocol === "https:") {
log.debug("monitor", `[${this.name}] Check cert`);
try {
let tlsInfoObject = checkCertificate(res);
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
// fallback for if kelog event is not emitted, but we may still have tlsInfo,
// e.g. if the connection is made through a proxy
if (this.getUrl()?.protocol === "https:" && tlsInfo.valid === undefined) {
const tlsSocket = res.request.res.socket;
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfoObject);
}
if (tlsSocket) {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;
} catch (e) {
if (e.message !== "No TLS certificate in response") {
log.error("monitor", "Caught error");
log.error("monitor", e.message);
}
await this.handleTlsInfo(tlsInfo);
}
}
if (process.env.TIMELOGGER === "1") {
log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID === this.id) {
log.info("monitor", res.data);
}
@@ -594,21 +619,15 @@ class Monitor extends BeanModel {
} else if (this.type === "json-query") {
let data = res.data;
// convert data to object
if (typeof data === "string") {
data = JSON.parse(data);
}
const { status, response } = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue);
let expression = jsonata(this.jsonPath);
let result = await expression.evaluate(data);
if (result.toString() === this.expectedValue) {
bean.msg += ", expected value is found";
if (status) {
bean.status = UP;
bean.msg = `JSON query passes (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})`;
} else {
throw new Error(bean.msg + ", but value is not equal to expected value, value was: [" + result + "]");
throw new Error(`JSON query does not pass (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})`);
}
}
} else if (this.type === "port") {
@@ -654,7 +673,7 @@ class Monitor extends BeanModel {
} else if (this.type === "steam") {
const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/";
const steamAPIKey = await setting("steamAPIKey");
const steamAPIKey = await Settings.get("steamAPIKey");
const filter = `addr\\${this.hostname}:${this.port}`;
if (!steamAPIKey) {
@@ -814,15 +833,6 @@ class Monitor extends BeanModel {
bean.msg = await mysqlQuery(this.databaseConnectionString, this.databaseQuery || "SELECT 1", mysqlPassword);
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
} else if (this.type === "mongodb") {
let startTime = dayjs().valueOf();
await mongodbPing(this.databaseConnectionString);
bean.msg = "";
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
} else if (this.type === "radius") {
let startTime = dayjs().valueOf();
@@ -853,7 +863,7 @@ class Monitor extends BeanModel {
} else if (this.type === "redis") {
let startTime = dayjs().valueOf();
bean.msg = await redisPingAsync(this.databaseConnectionString);
bean.msg = await redisPingAsync(this.databaseConnectionString, !this.ignoreTls);
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
@@ -943,7 +953,7 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] apicache clear`);
apicache.clear();
UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id);
await UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id);
} else {
bean.important = false;
@@ -989,7 +999,7 @@ class Monitor extends BeanModel {
await R.store(bean);
log.debug("monitor", `[${this.name}] prometheus.update`);
this.prometheus?.update(bean, tlsInfo);
await this.prometheus?.update(bean, tlsInfo);
previousBeat = bean;
@@ -1098,9 +1108,9 @@ class Monitor extends BeanModel {
/**
* Stop monitor
* @returns {void}
* @returns {Promise<void>}
*/
stop() {
async stop() {
clearTimeout(this.heartbeatInterval);
this.isStop = true;
@@ -1370,11 +1380,12 @@ class Monitor extends BeanModel {
return;
}
let notifyDays = await setting("tlsExpiryNotifyDays");
let notifyDays = await Settings.get("tlsExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default
setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general");
await Settings.set("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general");
notifyDays = [ 7, 14, 21 ];
await Settings.set("tlsExpiryNotifyDays", notifyDays, "general");
}
if (Array.isArray(notifyDays)) {
@@ -1527,11 +1538,11 @@ class Monitor extends BeanModel {
}
/**
* Gets Full Path-Name (Groups and Name)
* @returns {Promise<string>} Full path name of this monitor
* Gets the full path
* @returns {Promise<string[]>} Full path (includes groups and the name) of the monitor
*/
async getPathName() {
let path = this.name;
async getPath() {
const path = [ this.name ];
if (this.parent === null) {
return path;
@@ -1539,7 +1550,7 @@ class Monitor extends BeanModel {
let parent = await Monitor.getParent(this.id);
while (parent !== null) {
path = `${parent.name} / ${path}`;
path.unshift(parent.name);
parent = await Monitor.getParent(parent.id);
}
@@ -1569,7 +1580,7 @@ class Monitor extends BeanModel {
}
/**
* Unlinks all children of the the group monitor
* Unlinks all children of the group monitor
* @param {number} groupID ID of group to remove children of
* @returns {Promise<void>}
*/
@@ -1611,6 +1622,20 @@ class Monitor extends BeanModel {
return oAuthAccessToken;
}
/**
* Store TLS certificate information and check for expiry
* @param {object} tlsInfo Information about the TLS connection
* @returns {Promise<void>}
*/
async handleTlsInfo(tlsInfo) {
await this.updateTlsInfo(tlsInfo);
this.prometheus?.update(null, tlsInfo);
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfo);
}
}
}
module.exports = Monitor;

View File

@@ -4,6 +4,11 @@ const cheerio = require("cheerio");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const jsesc = require("jsesc");
const googleAnalytics = require("../google-analytics");
const { marked } = require("marked");
const { Feed } = require("feed");
const config = require("../config");
const { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE, DOWN } = require("../../src/util");
class StatusPage extends BeanModel {
@@ -13,12 +18,30 @@ class StatusPage extends BeanModel {
*/
static domainMappingList = { };
/**
* Handle responses to RSS pages
* @param {Response} response Response object
* @param {string} slug Status page slug
* @returns {Promise<void>}
*/
static async handleStatusPageRSSResponse(response, slug) {
let statusPage = await R.findOne("status_page", " slug = ? ", [
slug
]);
if (statusPage) {
response.send(await StatusPage.renderRSS(statusPage, slug));
} else {
response.status(404).send(UptimeKumaServer.getInstance().indexHTML);
}
}
/**
* Handle responses to status page
* @param {Response} response Response object
* @param {string} indexHTML HTML to render
* @param {string} slug Status page slug
* @returns {void}
* @returns {Promise<void>}
*/
static async handleStatusPageResponse(response, indexHTML, slug) {
// Handle url with trailing slash (http://localhost:3001/status/)
@@ -38,15 +61,51 @@ class StatusPage extends BeanModel {
}
}
/**
* SSR for RSS feed
* @param {statusPage} statusPage object
* @param {slug} slug from router
* @returns {Promise<string>} the rendered html
*/
static async renderRSS(statusPage, slug) {
const { heartbeats, statusDescription } = await StatusPage.getRSSPageData(statusPage);
let proto = config.isSSL ? "https" : "http";
let host = `${proto}://${config.hostname || "localhost"}:${config.port}/status/${slug}`;
const feed = new Feed({
title: "uptime kuma rss feed",
description: `current status: ${statusDescription}`,
link: host,
language: "en", // optional, used only in RSS 2.0, possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
updated: new Date(), // optional, default = today
});
heartbeats.forEach(heartbeat => {
feed.addItem({
title: `${heartbeat.name} is down`,
description: `${heartbeat.name} has been down since ${heartbeat.time}`,
id: heartbeat.monitorID,
date: new Date(heartbeat.time),
});
});
return feed.rss2();
}
/**
* SSR for status pages
* @param {string} indexHTML HTML page to render
* @param {StatusPage} statusPage Status page populate HTML with
* @returns {void}
* @returns {Promise<string>} the rendered html
*/
static async renderHTML(indexHTML, statusPage) {
const $ = cheerio.load(indexHTML);
const description155 = statusPage.description?.substring(0, 155) ?? "";
const description155 = marked(statusPage.description ?? "")
.replace(/<[^>]+>/gm, "")
.trim()
.substring(0, 155);
$("title").text(statusPage.title);
$("meta[name=description]").attr("content", description155);
@@ -93,6 +152,109 @@ class StatusPage extends BeanModel {
return $.root().html();
}
/**
* @param {heartbeats} heartbeats from getRSSPageData
* @returns {number} status_page constant from util.ts
*/
static overallStatus(heartbeats) {
if (heartbeats.length === 0) {
return -1;
}
let status = STATUS_PAGE_ALL_UP;
let hasUp = false;
for (let beat of heartbeats) {
if (beat.status === MAINTENANCE) {
return STATUS_PAGE_MAINTENANCE;
} else if (beat.status === UP) {
hasUp = true;
} else {
status = STATUS_PAGE_PARTIAL_DOWN;
}
}
if (! hasUp) {
status = STATUS_PAGE_ALL_DOWN;
}
return status;
}
/**
* @param {number} status from overallStatus
* @returns {string} description
*/
static getStatusDescription(status) {
if (status === -1) {
return "No Services";
}
if (status === STATUS_PAGE_ALL_UP) {
return "All Systems Operational";
}
if (status === STATUS_PAGE_PARTIAL_DOWN) {
return "Partially Degraded Service";
}
if (status === STATUS_PAGE_ALL_DOWN) {
return "Degraded Service";
}
// TODO: show the real maintenance information: title, description, time
if (status === MAINTENANCE) {
return "Under maintenance";
}
return "?";
}
/**
* Get all data required for RSS
* @param {StatusPage} statusPage Status page to get data for
* @returns {object} Status page data
*/
static async getRSSPageData(statusPage) {
// get all heartbeats that correspond to this statusPage
const config = await statusPage.toPublicJSON();
// Public Group List
const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id
]);
let heartbeats = [];
for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
for (const monitor of monitorGroup.monitorList) {
const heartbeat = await R.findOne("heartbeat", "monitor_id = ? ORDER BY time DESC", [ monitor.id ]);
if (heartbeat) {
heartbeats.push({
...monitor,
status: heartbeat.status,
time: heartbeat.time
});
}
}
}
// calculate RSS feed description
let status = StatusPage.overallStatus(heartbeats);
let statusDescription = StatusPage.getStatusDescription(status);
// keep only DOWN heartbeats in the RSS feed
heartbeats = heartbeats.filter(heartbeat => heartbeat.status === DOWN);
return {
heartbeats,
statusDescription
};
}
/**
* Get all status page data in one call
* @param {StatusPage} statusPage Status page to get data for
@@ -238,6 +400,7 @@ class StatusPage extends BeanModel {
description: this.description,
icon: this.getIcon(),
theme: this.theme,
autoRefreshInterval: this.autoRefreshInterval,
published: !!this.published,
showTags: !!this.show_tags,
domainNameList: this.getDomainNameList(),
@@ -260,6 +423,7 @@ class StatusPage extends BeanModel {
title: this.title,
description: this.description,
icon: this.getIcon(),
autoRefreshInterval: this.autoRefreshInterval,
theme: this.theme,
published: !!this.published,
showTags: !!this.show_tags,

View File

@@ -1,5 +1,6 @@
let url = require("url");
let MemoryCache = require("./memory-cache");
const { log } = require("../../../src/util");
let t = {
ms: 1,
@@ -90,24 +91,6 @@ function ApiCache() {
instances.push(this);
this.id = instances.length;
/**
* Logs a message to the console if the `DEBUG` environment variable is set.
* @param {string} a The first argument to log.
* @param {string} b The second argument to log.
* @param {string} c The third argument to log.
* @param {string} d The fourth argument to log, and so on... (optional)
*
* Generated by Trelent
*/
function debug(a, b, c, d) {
let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) {
return arg !== undefined;
});
let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1;
return (globalOptions.debug || debugEnv) && console.log.apply(null, arr);
}
/**
* Returns true if the given request and response should be logged.
* @param {Object} request The HTTP request object.
@@ -146,7 +129,7 @@ function ApiCache() {
let groupName = req.apicacheGroup;
if (groupName) {
debug("group detected \"" + groupName + "\"");
log.debug("apicache", `group detected "${groupName}"`);
let group = (index.groups[groupName] = index.groups[groupName] || []);
group.unshift(key);
}
@@ -212,7 +195,7 @@ function ApiCache() {
redis.hset(key, "duration", duration);
redis.expire(key, duration / 1000, expireCallback || function () {});
} catch (err) {
debug("[apicache] error in redis.hset()");
log.debug("apicache", `error in redis.hset(): ${err}`);
}
} else {
memCache.add(key, value, duration, expireCallback);
@@ -320,10 +303,10 @@ function ApiCache() {
// display log entry
let elapsed = new Date() - req.apicacheTimer;
debug("adding cache entry for \"" + key + "\" @ " + strDuration, logDuration(elapsed));
debug("_apicache.headers: ", res._apicache.headers);
debug("res.getHeaders(): ", getSafeHeaders(res));
debug("cacheObject: ", cacheObject);
log.debug("apicache", `adding cache entry for "${key}" @ ${strDuration} ${logDuration(elapsed)}`);
log.debug("apicache", `_apicache.headers: ${JSON.stringify(res._apicache.headers)}`);
log.debug("apicache", `res.getHeaders(): ${JSON.stringify(getSafeHeaders(res))}`);
log.debug("apicache", `cacheObject: ${JSON.stringify(cacheObject)}`);
}
}
@@ -402,10 +385,10 @@ function ApiCache() {
let redis = globalOptions.redisClient;
if (group) {
debug("clearing group \"" + target + "\"");
log.debug("apicache", `clearing group "${target}"`);
group.forEach(function (key) {
debug("clearing cached entry for \"" + key + "\"");
log.debug("apicache", `clearing cached entry for "${key}"`);
clearTimeout(timers[key]);
delete timers[key];
if (!globalOptions.redisClient) {
@@ -414,7 +397,7 @@ function ApiCache() {
try {
redis.del(key);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")");
log.info("apicache", "error in redis.del(\"" + key + "\")");
}
}
index.all = index.all.filter(doesntMatch(key));
@@ -422,7 +405,7 @@ function ApiCache() {
delete index.groups[target];
} else if (target) {
debug("clearing " + (isAutomatic ? "expired" : "cached") + " entry for \"" + target + "\"");
log.debug("apicache", `clearing ${isAutomatic ? "expired" : "cached"} entry for "${target}"`);
clearTimeout(timers[target]);
delete timers[target];
// clear actual cached entry
@@ -432,7 +415,7 @@ function ApiCache() {
try {
redis.del(target);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + target + "\")");
log.error("apicache", "error in redis.del(\"" + target + "\")");
}
}
@@ -449,7 +432,7 @@ function ApiCache() {
}
});
} else {
debug("clearing entire index");
log.debug("apicache", "clearing entire index");
if (!redis) {
memCache.clear();
@@ -461,7 +444,7 @@ function ApiCache() {
try {
redis.del(key);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")");
log.error("apicache", `error in redis.del("${key}"): ${err}`);
}
});
}
@@ -528,7 +511,7 @@ function ApiCache() {
/**
* Get index of a group
* @param {string} group
* @param {string} group
* @returns {number}
*/
this.getIndex = function (group) {
@@ -543,9 +526,9 @@ function ApiCache() {
* Express middleware
* @param {(string|number)} strDuration Duration to cache responses
* for.
* @param {function(Object, Object):boolean} middlewareToggle
* @param {function(Object, Object):boolean} middlewareToggle
* @param {Object} localOptions Options for APICache
* @returns
* @returns
*/
this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
let duration = instance.getDuration(strDuration);
@@ -752,7 +735,7 @@ function ApiCache() {
*/
let cache = function (req, res, next) {
function bypass() {
debug("bypass detected, skipping cache.");
log.debug("apicache", "bypass detected, skipping cache.");
return next();
}
@@ -805,7 +788,7 @@ function ApiCache() {
// send if cache hit from memory-cache
if (cached) {
let elapsed = new Date() - req.apicacheTimer;
debug("sending cached (memory-cache) version of", key, logDuration(elapsed));
log.debug("apicache", `sending cached (memory-cache) version of ${key} ${logDuration(elapsed)}`);
perf.hit(key);
return sendCachedResponse(req, res, cached, middlewareToggle, next, duration);
@@ -817,7 +800,7 @@ function ApiCache() {
redis.hgetall(key, function (err, obj) {
if (!err && obj && obj.response) {
let elapsed = new Date() - req.apicacheTimer;
debug("sending cached (redis) version of", key, logDuration(elapsed));
log.debug("apicache", "sending cached (redis) version of "+ key+" "+ logDuration(elapsed));
perf.hit(key);
return sendCachedResponse(
@@ -859,7 +842,7 @@ function ApiCache() {
/**
* Process options
* @param {Object} options
* @param {Object} options
* @returns {Object}
*/
this.options = function (options) {
@@ -873,7 +856,7 @@ function ApiCache() {
}
if (globalOptions.trackPerformance) {
debug("WARNING: using trackPerformance flag can cause high memory usage!");
log.debug("apicache", "WARNING: using trackPerformance flag can cause high memory usage!");
}
return this;

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 CatButtes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,77 @@
'use strict';
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
/* Indicates that Unicode strings are supported for use in security buffer
data. */
module.exports.NTLMFLAG_NEGOTIATE_OEM = 1 << 1;
/* Indicates that OEM strings are supported for use in security buffer data. */
module.exports.NTLMFLAG_REQUEST_TARGET = 1 << 2;
/* Requests that the server's authentication realm be included in the Type 2
message. */
/* unknown (1<<3) */
module.exports.NTLMFLAG_NEGOTIATE_SIGN = 1 << 4;
/* Specifies that authenticated communication between the client and server
should carry a digital signature (message integrity). */
module.exports.NTLMFLAG_NEGOTIATE_SEAL = 1 << 5;
/* Specifies that authenticated communication between the client and server
should be encrypted (message confidentiality). */
module.exports.NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE = 1 << 6;
/* Indicates that datagram authentication is being used. */
module.exports.NTLMFLAG_NEGOTIATE_LM_KEY = 1 << 7;
/* Indicates that the LAN Manager session key should be used for signing and
sealing authenticated communications. */
module.exports.NTLMFLAG_NEGOTIATE_NETWARE = 1 << 8;
/* unknown purpose */
module.exports.NTLMFLAG_NEGOTIATE_NTLM_KEY = 1 << 9;
/* Indicates that NTLM authentication is being used. */
/* unknown (1<<10) */
module.exports.NTLMFLAG_NEGOTIATE_ANONYMOUS = 1 << 11;
/* Sent by the client in the Type 3 message to indicate that an anonymous
context has been established. This also affects the response fields. */
module.exports.NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED = 1 << 12;
/* Sent by the client in the Type 1 message to indicate that a desired
authentication realm is included in the message. */
module.exports.NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED = 1 << 13;
/* Sent by the client in the Type 1 message to indicate that the client
workstation's name is included in the message. */
module.exports.NTLMFLAG_NEGOTIATE_LOCAL_CALL = 1 << 14;
/* Sent by the server to indicate that the server and client are on the same
machine. Implies that the client may use a pre-established local security
context rather than responding to the challenge. */
module.exports.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN = 1 << 15;
/* Indicates that authenticated communication between the client and server
should be signed with a "dummy" signature. */
module.exports.NTLMFLAG_TARGET_TYPE_DOMAIN = 1 << 16;
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a domain. */
module.exports.NTLMFLAG_TARGET_TYPE_SERVER = 1 << 17;
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a server. */
module.exports.NTLMFLAG_TARGET_TYPE_SHARE = 1 << 18;
/* Sent by the server in the Type 2 message to indicate that the target
authentication realm is a share. Presumably, this is for share-level
authentication. Usage is unclear. */
module.exports.NTLMFLAG_NEGOTIATE_NTLM2_KEY = 1 << 19;
/* Indicates that the NTLM2 signing and sealing scheme should be used for
protecting authenticated communications. */
module.exports.NTLMFLAG_REQUEST_INIT_RESPONSE = 1 << 20;
/* unknown purpose */
module.exports.NTLMFLAG_REQUEST_ACCEPT_RESPONSE = 1 << 21;
/* unknown purpose */
module.exports.NTLMFLAG_REQUEST_NONNT_SESSION_KEY = 1 << 22;
/* unknown purpose */
module.exports.NTLMFLAG_NEGOTIATE_TARGET_INFO = 1 << 23;
/* Sent by the server in the Type 2 message to indicate that it is including a
Target Information block in the message. */
/* unknown (1<24) */
/* unknown (1<25) */
/* unknown (1<26) */
/* unknown (1<27) */
/* unknown (1<28) */
module.exports.NTLMFLAG_NEGOTIATE_128 = 1 << 29;
/* Indicates that 128-bit encryption is supported. */
module.exports.NTLMFLAG_NEGOTIATE_KEY_EXCHANGE = 1 << 30;
/* Indicates that the client will provide an encrypted master key in
the "Session Key" field of the Type 3 message. */
module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31;
//# sourceMappingURL=flags.js.map

View File

@@ -0,0 +1,122 @@
'use strict';
// Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js
var crypto = require('crypto');
function createLMResponse(challenge, lmhash) {
var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0);
lmhash.copy(pwBuffer);
calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8);
calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16);
return buf;
}
function createLMHash(password) {
var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from('KGS!@#$%', 'ascii');
if (password.length > 14) {
buf.fill(0);
return buf;
}
pwBuffer.fill(0);
pwBuffer.write(password.toUpperCase(), 0, 'ascii');
return Buffer.concat([
calculateDES(pwBuffer.slice(0, 7), magicKey),
calculateDES(pwBuffer.slice(7), magicKey)
]);
}
function calculateDES(key, message) {
var desKey = new Buffer.alloc(8);
desKey[0] = key[0] & 0xFE;
desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1);
desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2);
desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3);
desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4);
desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5);
desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6);
desKey[7] = (key[6] << 1) & 0xFF;
for (var i = 0; i < 8; i++) {
var parity = 0;
for (var j = 1; j < 8; j++) {
parity += (desKey[i] >> j) % 2;
}
desKey[i] |= (parity % 2) === 0 ? 1 : 0;
}
var des = crypto.createCipheriv('DES-ECB', desKey, '');
return des.update(message);
}
function createNTLMResponse(challenge, ntlmhash) {
var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0);
ntlmhash.copy(ntlmBuffer);
calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8);
calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16);
return buf;
}
function createNTLMHash(password) {
var md4sum = crypto.createHash('md4');
md4sum.update(new Buffer.from(password, 'ucs2'));
return md4sum.digest();
}
function createNTLMv2Hash(ntlmhash, username, authTargetName) {
var hmac = crypto.createHmac('md5', ntlmhash);
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2'));
return hmac.digest();
}
function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash);
//server challenge
type2message.challenge.copy(buf, 8);
//client nonce
buf.write(nonce || createPseudoRandomValue(16), 16, 'hex');
//create hash
hmac.update(buf.slice(8));
var hashedBuffer = hmac.digest();
hashedBuffer.copy(buf);
return buf;
}
function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash);
//the first 8 bytes are spare to store the hashed value before the blob
//server challenge
type2message.challenge.copy(buf, 8);
//blob signature
buf.writeUInt32BE(0x01010000, 16);
//reserved
buf.writeUInt32LE(0, 20);
//timestamp
//TODO: we are loosing precision here since js is not able to handle those large integers
// maybe think about a different solution here
// 11644473600000 = diff between 1970 and 1601
var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
var timestampLow = Number('0x' + timestamp.substring(Math.max(0, timestamp.length - 8)));
var timestampHigh = Number('0x' + timestamp.substring(0, Math.max(0, timestamp.length - 8)));
buf.writeUInt32LE(timestampLow, 24, false);
buf.writeUInt32LE(timestampHigh, 28, false);
//random client nonce
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex');
//zero
buf.writeUInt32LE(0, 40);
//complete target information block from type 2 message
type2message.targetInfo.buffer.copy(buf, 44);
//zero
buf.writeUInt32LE(0, 44 + type2message.targetInfo.buffer.length);
hmac.update(buf.slice(8));
var hashedBuffer = hmac.digest();
hashedBuffer.copy(buf);
return buf;
}
function createPseudoRandomValue(length) {
var str = '';
while (str.length < length) {
str += Math.floor(Math.random() * 16).toString(16);
}
return str;
}
module.exports = {
createLMHash: createLMHash,
createNTLMHash: createNTLMHash,
createLMResponse: createLMResponse,
createNTLMResponse: createNTLMResponse,
createLMv2Response: createLMv2Response,
createNTLMv2Response: createNTLMv2Response,
createPseudoRandomValue: createPseudoRandomValue
};
//# sourceMappingURL=hash.js.map

View File

@@ -0,0 +1,220 @@
'use strict';
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js
var os = require('os'), flags = require('./flags'), hash = require('./hash');
var NTLMSIGNATURE = "NTLMSSP\0";
function createType1Message(workstation, target) {
var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024);
workstation = workstation === undefined ? os.hostname() : workstation;
target = target === undefined ? '' : target;
//signature
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii');
pos += NTLMSIGNATURE.length;
//message type
buf.writeUInt32LE(1, pos);
pos += 4;
//flags
buf.writeUInt32LE(flags.NTLMFLAG_NEGOTIATE_OEM |
flags.NTLMFLAG_REQUEST_TARGET |
flags.NTLMFLAG_NEGOTIATE_NTLM_KEY |
flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY |
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN, pos);
pos += 4;
//domain security buffer
buf.writeUInt16LE(target.length, pos);
pos += 2;
buf.writeUInt16LE(target.length, pos);
pos += 2;
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
pos += 4;
if (target.length > 0) {
dataPos += buf.write(target, dataPos, 'ascii');
}
//workstation security buffer
buf.writeUInt16LE(workstation.length, pos);
pos += 2;
buf.writeUInt16LE(workstation.length, pos);
pos += 2;
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
pos += 4;
if (workstation.length > 0) {
dataPos += buf.write(workstation, dataPos, 'ascii');
}
return 'NTLM ' + buf.toString('base64', 0, dataPos);
}
function decodeType2Message(str) {
if (str === undefined) {
throw new Error('Invalid argument');
}
//convenience
if (Object.prototype.toString.call(str) !== '[object String]') {
if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) {
str = str.headers['www-authenticate'];
}
else {
throw new Error('Invalid argument');
}
}
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
if (ntlmMatch) {
str = ntlmMatch[1];
}
var buf = new Buffer.from(str, 'base64'), obj = {};
//check signature
if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
throw new Error('Invalid message signature: ' + str);
}
//check message type
if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) {
throw new Error('Invalid message type (no type 2)');
}
//read flags
obj.flags = buf.readUInt32LE(20);
obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? 'ascii' : 'ucs2';
obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1;
obj.challenge = buf.slice(24, 32);
//read target name
obj.targetName = (function () {
var length = buf.readUInt16LE(12);
//skipping allocated space
var offset = buf.readUInt32LE(16);
if (length === 0) {
return '';
}
if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message');
}
return buf.toString(obj.encoding, offset, offset + length);
})();
//read target info
if (obj.flags & flags.NTLMFLAG_NEGOTIATE_TARGET_INFO) {
obj.targetInfo = (function () {
var info = {};
var length = buf.readUInt16LE(40);
//skipping allocated space
var offset = buf.readUInt32LE(44);
var targetInfoBuffer = new Buffer.alloc(length);
buf.copy(targetInfoBuffer, 0, offset, offset + length);
if (length === 0) {
return info;
}
if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message');
}
var pos = offset;
while (pos < (offset + length)) {
var blockType = buf.readUInt16LE(pos);
pos += 2;
var blockLength = buf.readUInt16LE(pos);
pos += 2;
if (blockType === 0) {
//reached the terminator subblock
break;
}
var blockTypeStr = void 0;
switch (blockType) {
case 1:
blockTypeStr = 'SERVER';
break;
case 2:
blockTypeStr = 'DOMAIN';
break;
case 3:
blockTypeStr = 'FQDN';
break;
case 4:
blockTypeStr = 'DNS';
break;
case 5:
blockTypeStr = 'PARENT_DNS';
break;
default:
blockTypeStr = '';
break;
}
if (blockTypeStr) {
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength);
}
pos += blockLength;
}
return {
parsed: info,
buffer: targetInfoBuffer
};
})();
}
return obj;
}
function createType3Message(type2Message, username, password, workstation, target) {
var dataPos = 52, buf = new Buffer.alloc(1024);
if (workstation === undefined) {
workstation = os.hostname();
}
if (target === undefined) {
target = type2Message.targetName;
}
//signature
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii');
//message type
buf.writeUInt32LE(3, 8);
if (type2Message.version === 2) {
dataPos = 64;
var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16), lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target), ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target);
//lmv2 security buffer
buf.writeUInt16LE(lmv2.length, 12);
buf.writeUInt16LE(lmv2.length, 14);
buf.writeUInt32LE(dataPos, 16);
lmv2.copy(buf, dataPos);
dataPos += lmv2.length;
//ntlmv2 security buffer
buf.writeUInt16LE(ntlmv2.length, 20);
buf.writeUInt16LE(ntlmv2.length, 22);
buf.writeUInt32LE(dataPos, 24);
ntlmv2.copy(buf, dataPos);
dataPos += ntlmv2.length;
}
else {
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
//lm security buffer
buf.writeUInt16LE(lm.length, 12);
buf.writeUInt16LE(lm.length, 14);
buf.writeUInt32LE(dataPos, 16);
lm.copy(buf, dataPos);
dataPos += lm.length;
//ntlm security buffer
buf.writeUInt16LE(ntlm.length, 20);
buf.writeUInt16LE(ntlm.length, 22);
buf.writeUInt32LE(dataPos, 24);
ntlm.copy(buf, dataPos);
dataPos += ntlm.length;
}
//target name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 28);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 30);
buf.writeUInt32LE(dataPos, 32);
dataPos += buf.write(target, dataPos, type2Message.encoding);
//user name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 36);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 38);
buf.writeUInt32LE(dataPos, 40);
dataPos += buf.write(username, dataPos, type2Message.encoding);
//workstation name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 44);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 46);
buf.writeUInt32LE(dataPos, 48);
dataPos += buf.write(workstation, dataPos, type2Message.encoding);
if (type2Message.version === 2) {
//session key security buffer
buf.writeUInt16LE(0, 52);
buf.writeUInt16LE(0, 54);
buf.writeUInt32LE(0, 56);
//flags
buf.writeUInt32LE(type2Message.flags, 60);
}
return 'NTLM ' + buf.toString('base64', 0, dataPos);
}
module.exports = {
createType1Message: createType1Message,
decodeType2Message: decodeType2Message,
createType3Message: createType3Message
};
//# sourceMappingURL=ntlm.js.map

View File

@@ -0,0 +1,127 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NtlmClient = void 0;
var axios_1 = __importDefault(require("axios"));
var ntlm = __importStar(require("./ntlm"));
var https = __importStar(require("https"));
var http = __importStar(require("http"));
var dev_null_1 = __importDefault(require("dev-null"));
/**
* @param credentials An NtlmCredentials object containing the username and password
* @param AxiosConfig The Axios config for the instance you wish to create
*
* @returns This function returns an axios instance configured to use the provided credentials
*/
function NtlmClient(credentials, AxiosConfig) {
var _this = this;
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
if (!config.httpAgent) {
config.httpAgent = new http.Agent({ keepAlive: true });
}
if (!config.httpsAgent) {
config.httpsAgent = new https.Agent({ keepAlive: true });
}
var client = axios_1.default.create(config);
client.interceptors.response.use(function (response) {
return response;
}, function (err) { return __awaiter(_this, void 0, void 0, function () {
var error, t1Msg, t2Msg, t3Msg, stream_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
error = err.response;
if (!(error && error.status === 401
&& error.headers['www-authenticate']
&& error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3];
// This length check is a hack because SharePoint is awkward and will
// include the Negotiate option when responding with the T2 message
// There is nore we could do to ensure we are processing correctly,
// but this is the easiest option for now
if (error.headers['www-authenticate'].length < 50) {
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
error.config.headers["Authorization"] = t1Msg;
}
else {
t2Msg = ntlm.decodeType2Message((error.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
error.config.headers["X-retry"] = "false";
error.config.headers["Authorization"] = t3Msg;
}
if (!(error.config.responseType === "stream")) return [3 /*break*/, 2];
stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
if (!(stream_1 && !stream_1.readableEnded)) return [3 /*break*/, 2];
return [4 /*yield*/, new Promise(function (resolve) {
stream_1.pipe((0, dev_null_1.default)());
stream_1.once('close', resolve);
})];
case 1:
_b.sent();
_b.label = 2;
case 2: return [2 /*return*/, client(error.config)];
case 3: throw err;
}
});
}); });
return client;
}
exports.NtlmClient = NtlmClient;
//# sourceMappingURL=ntlmClient.js.map

View File

@@ -0,0 +1,71 @@
const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("./expression");
const { operatorMap } = require("./operators");
/**
* @param {ConditionExpression} expression Expression to evaluate
* @param {object} context Context to evaluate against; These are values for variables in the expression
* @returns {boolean} Whether the expression evaluates true or false
* @throws {Error}
*/
function evaluateExpression(expression, context) {
/**
* @type {import("./operators").ConditionOperator|null}
*/
const operator = operatorMap.get(expression.operator) || null;
if (operator === null) {
throw new Error("Unexpected expression operator ID '" + expression.operator + "'. Expected one of [" + operatorMap.keys().join(",") + "]");
}
if (!Object.prototype.hasOwnProperty.call(context, expression.variable)) {
throw new Error("Variable missing in context: " + expression.variable);
}
return operator.test(context[expression.variable], expression.value);
}
/**
* @param {ConditionExpressionGroup} group Group of expressions to evaluate
* @param {object} context Context to evaluate against; These are values for variables in the expression
* @returns {boolean} Whether the group evaluates true or false
* @throws {Error}
*/
function evaluateExpressionGroup(group, context) {
if (!group.children.length) {
throw new Error("ConditionExpressionGroup must contain at least one child.");
}
let result = null;
for (const child of group.children) {
let childResult;
if (child instanceof ConditionExpression) {
childResult = evaluateExpression(child, context);
} else if (child instanceof ConditionExpressionGroup) {
childResult = evaluateExpressionGroup(child, context);
} else {
throw new Error("Invalid child type in ConditionExpressionGroup. Expected ConditionExpression or ConditionExpressionGroup");
}
if (result === null) {
result = childResult; // Initialize result with the first child's result
} else if (child.andOr === LOGICAL.OR) {
result = result || childResult;
} else if (child.andOr === LOGICAL.AND) {
result = result && childResult;
} else {
throw new Error("Invalid logical operator in child of ConditionExpressionGroup. Expected 'and' or 'or'. Got '" + group.andOr + "'");
}
}
if (result === null) {
throw new Error("ConditionExpressionGroup did not result in a boolean.");
}
return result;
}
module.exports = {
evaluateExpression,
evaluateExpressionGroup,
};

View File

@@ -0,0 +1,111 @@
/**
* @readonly
* @enum {string}
*/
const LOGICAL = {
AND: "and",
OR: "or",
};
/**
* Recursively processes an array of raw condition objects and populates the given parent group with
* corresponding ConditionExpression or ConditionExpressionGroup instances.
* @param {Array} conditions Array of raw condition objects, where each object represents either a group or an expression.
* @param {ConditionExpressionGroup} parentGroup The parent group to which the instantiated ConditionExpression or ConditionExpressionGroup objects will be added.
* @returns {void}
*/
function processMonitorConditions(conditions, parentGroup) {
conditions.forEach(condition => {
const andOr = condition.andOr === LOGICAL.OR ? LOGICAL.OR : LOGICAL.AND;
if (condition.type === "group") {
const group = new ConditionExpressionGroup([], andOr);
// Recursively process the group's children
processMonitorConditions(condition.children, group);
parentGroup.children.push(group);
} else if (condition.type === "expression") {
const expression = new ConditionExpression(condition.variable, condition.operator, condition.value, andOr);
parentGroup.children.push(expression);
}
});
}
class ConditionExpressionGroup {
/**
* @type {ConditionExpressionGroup[]|ConditionExpression[]} Groups and/or expressions to test
*/
children = [];
/**
* @type {LOGICAL} Connects group result with previous group/expression results
*/
andOr;
/**
* @param {ConditionExpressionGroup[]|ConditionExpression[]} children Groups and/or expressions to test
* @param {LOGICAL} andOr Connects group result with previous group/expression results
*/
constructor(children = [], andOr = LOGICAL.AND) {
this.children = children;
this.andOr = andOr;
}
/**
* @param {Monitor} monitor Monitor instance
* @returns {ConditionExpressionGroup|null} A ConditionExpressionGroup with the Monitor's conditions
*/
static fromMonitor(monitor) {
const conditions = JSON.parse(monitor.conditions);
if (conditions.length === 0) {
return null;
}
const root = new ConditionExpressionGroup();
processMonitorConditions(conditions, root);
return root;
}
}
class ConditionExpression {
/**
* @type {string} ID of variable
*/
variable;
/**
* @type {string} ID of operator
*/
operator;
/**
* @type {string} Value to test with the operator
*/
value;
/**
* @type {LOGICAL} Connects expression result with previous group/expression results
*/
andOr;
/**
* @param {string} variable ID of variable to test against
* @param {string} operator ID of operator to test the variable with
* @param {string} value Value to test with the operator
* @param {LOGICAL} andOr Connects expression result with previous group/expression results
*/
constructor(variable, operator, value, andOr = LOGICAL.AND) {
this.variable = variable;
this.operator = operator;
this.value = value;
this.andOr = andOr;
}
}
module.exports = {
LOGICAL,
ConditionExpressionGroup,
ConditionExpression,
};

View File

@@ -0,0 +1,318 @@
class ConditionOperator {
id = undefined;
caption = undefined;
/**
* @type {mixed} variable
* @type {mixed} value
*/
test(variable, value) {
throw new Error("You need to override test()");
}
}
const OP_STR_EQUALS = "equals";
const OP_STR_NOT_EQUALS = "not_equals";
const OP_CONTAINS = "contains";
const OP_NOT_CONTAINS = "not_contains";
const OP_STARTS_WITH = "starts_with";
const OP_NOT_STARTS_WITH = "not_starts_with";
const OP_ENDS_WITH = "ends_with";
const OP_NOT_ENDS_WITH = "not_ends_with";
const OP_NUM_EQUALS = "num_equals";
const OP_NUM_NOT_EQUALS = "num_not_equals";
const OP_LT = "lt";
const OP_GT = "gt";
const OP_LTE = "lte";
const OP_GTE = "gte";
/**
* Asserts a variable is equal to a value.
*/
class StringEqualsOperator extends ConditionOperator {
id = OP_STR_EQUALS;
caption = "equals";
/**
* @inheritdoc
*/
test(variable, value) {
return variable === value;
}
}
/**
* Asserts a variable is not equal to a value.
*/
class StringNotEqualsOperator extends ConditionOperator {
id = OP_STR_NOT_EQUALS;
caption = "not equals";
/**
* @inheritdoc
*/
test(variable, value) {
return variable !== value;
}
}
/**
* Asserts a variable contains a value.
* Handles both Array and String variable types.
*/
class ContainsOperator extends ConditionOperator {
id = OP_CONTAINS;
caption = "contains";
/**
* @inheritdoc
*/
test(variable, value) {
if (Array.isArray(variable)) {
return variable.includes(value);
}
return variable.indexOf(value) !== -1;
}
}
/**
* Asserts a variable does not contain a value.
* Handles both Array and String variable types.
*/
class NotContainsOperator extends ConditionOperator {
id = OP_NOT_CONTAINS;
caption = "not contains";
/**
* @inheritdoc
*/
test(variable, value) {
if (Array.isArray(variable)) {
return !variable.includes(value);
}
return variable.indexOf(value) === -1;
}
}
/**
* Asserts a variable starts with a value.
*/
class StartsWithOperator extends ConditionOperator {
id = OP_STARTS_WITH;
caption = "starts with";
/**
* @inheritdoc
*/
test(variable, value) {
return variable.startsWith(value);
}
}
/**
* Asserts a variable does not start with a value.
*/
class NotStartsWithOperator extends ConditionOperator {
id = OP_NOT_STARTS_WITH;
caption = "not starts with";
/**
* @inheritdoc
*/
test(variable, value) {
return !variable.startsWith(value);
}
}
/**
* Asserts a variable ends with a value.
*/
class EndsWithOperator extends ConditionOperator {
id = OP_ENDS_WITH;
caption = "ends with";
/**
* @inheritdoc
*/
test(variable, value) {
return variable.endsWith(value);
}
}
/**
* Asserts a variable does not end with a value.
*/
class NotEndsWithOperator extends ConditionOperator {
id = OP_NOT_ENDS_WITH;
caption = "not ends with";
/**
* @inheritdoc
*/
test(variable, value) {
return !variable.endsWith(value);
}
}
/**
* Asserts a numeric variable is equal to a value.
*/
class NumberEqualsOperator extends ConditionOperator {
id = OP_NUM_EQUALS;
caption = "equals";
/**
* @inheritdoc
*/
test(variable, value) {
return variable === Number(value);
}
}
/**
* Asserts a numeric variable is not equal to a value.
*/
class NumberNotEqualsOperator extends ConditionOperator {
id = OP_NUM_NOT_EQUALS;
caption = "not equals";
/**
* @inheritdoc
*/
test(variable, value) {
return variable !== Number(value);
}
}
/**
* Asserts a variable is less than a value.
*/
class LessThanOperator extends ConditionOperator {
id = OP_LT;
caption = "less than";
/**
* @inheritdoc
*/
test(variable, value) {
return variable < Number(value);
}
}
/**
* Asserts a variable is greater than a value.
*/
class GreaterThanOperator extends ConditionOperator {
id = OP_GT;
caption = "greater than";
/**
* @inheritdoc
*/
test(variable, value) {
return variable > Number(value);
}
}
/**
* Asserts a variable is less than or equal to a value.
*/
class LessThanOrEqualToOperator extends ConditionOperator {
id = OP_LTE;
caption = "less than or equal to";
/**
* @inheritdoc
*/
test(variable, value) {
return variable <= Number(value);
}
}
/**
* Asserts a variable is greater than or equal to a value.
*/
class GreaterThanOrEqualToOperator extends ConditionOperator {
id = OP_GTE;
caption = "greater than or equal to";
/**
* @inheritdoc
*/
test(variable, value) {
return variable >= Number(value);
}
}
const operatorMap = new Map([
[ OP_STR_EQUALS, new StringEqualsOperator ],
[ OP_STR_NOT_EQUALS, new StringNotEqualsOperator ],
[ OP_CONTAINS, new ContainsOperator ],
[ OP_NOT_CONTAINS, new NotContainsOperator ],
[ OP_STARTS_WITH, new StartsWithOperator ],
[ OP_NOT_STARTS_WITH, new NotStartsWithOperator ],
[ OP_ENDS_WITH, new EndsWithOperator ],
[ OP_NOT_ENDS_WITH, new NotEndsWithOperator ],
[ OP_NUM_EQUALS, new NumberEqualsOperator ],
[ OP_NUM_NOT_EQUALS, new NumberNotEqualsOperator ],
[ OP_LT, new LessThanOperator ],
[ OP_GT, new GreaterThanOperator ],
[ OP_LTE, new LessThanOrEqualToOperator ],
[ OP_GTE, new GreaterThanOrEqualToOperator ],
]);
const defaultStringOperators = [
operatorMap.get(OP_STR_EQUALS),
operatorMap.get(OP_STR_NOT_EQUALS),
operatorMap.get(OP_CONTAINS),
operatorMap.get(OP_NOT_CONTAINS),
operatorMap.get(OP_STARTS_WITH),
operatorMap.get(OP_NOT_STARTS_WITH),
operatorMap.get(OP_ENDS_WITH),
operatorMap.get(OP_NOT_ENDS_WITH)
];
const defaultNumberOperators = [
operatorMap.get(OP_NUM_EQUALS),
operatorMap.get(OP_NUM_NOT_EQUALS),
operatorMap.get(OP_LT),
operatorMap.get(OP_GT),
operatorMap.get(OP_LTE),
operatorMap.get(OP_GTE)
];
module.exports = {
OP_STR_EQUALS,
OP_STR_NOT_EQUALS,
OP_CONTAINS,
OP_NOT_CONTAINS,
OP_STARTS_WITH,
OP_NOT_STARTS_WITH,
OP_ENDS_WITH,
OP_NOT_ENDS_WITH,
OP_NUM_EQUALS,
OP_NUM_NOT_EQUALS,
OP_LT,
OP_GT,
OP_LTE,
OP_GTE,
operatorMap,
defaultStringOperators,
defaultNumberOperators,
ConditionOperator,
};

View File

@@ -0,0 +1,31 @@
/**
* Represents a variable used in a condition and the set of operators that can be applied to this variable.
*
* A `ConditionVariable` holds the ID of the variable and a list of operators that define how this variable can be evaluated
* in conditions. For example, if the variable is a request body or a specific field in a request, the operators can include
* operations such as equality checks, comparisons, or other custom evaluations.
*/
class ConditionVariable {
/**
* @type {string}
*/
id;
/**
* @type {import("./operators").ConditionOperator[]}
*/
operators = {};
/**
* @param {string} id ID of variable
* @param {import("./operators").ConditionOperator[]} operators Operators the condition supports
*/
constructor(id, operators = []) {
this.id = id;
this.operators = operators;
}
}
module.exports = {
ConditionVariable,
};

View File

@@ -1,13 +1,22 @@
const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");
const { UP, DOWN } = require("../../src/util");
const dayjs = require("dayjs");
const { dnsResolve } = require("../util-server");
const { R } = require("redbean-node");
const { ConditionVariable } = require("../monitor-conditions/variables");
const { defaultStringOperators } = require("../monitor-conditions/operators");
const { ConditionExpressionGroup } = require("../monitor-conditions/expression");
const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator");
class DnsMonitorType extends MonitorType {
name = "dns";
supportsConditions = true;
conditionVariables = [
new ConditionVariable("record", defaultStringOperators ),
];
/**
* @inheritdoc
*/
@@ -18,28 +27,48 @@ class DnsMonitorType extends MonitorType {
let dnsRes = await dnsResolve(monitor.hostname, monitor.dns_resolve_server, monitor.port, monitor.dns_resolve_type);
heartbeat.ping = dayjs().valueOf() - startTime;
if (monitor.dns_resolve_type === "A" || monitor.dns_resolve_type === "AAAA" || monitor.dns_resolve_type === "TXT" || monitor.dns_resolve_type === "PTR") {
dnsMessage += "Records: ";
dnsMessage += dnsRes.join(" | ");
} else if (monitor.dns_resolve_type === "CNAME" || monitor.dns_resolve_type === "PTR") {
dnsMessage += dnsRes[0];
} else if (monitor.dns_resolve_type === "CAA") {
dnsMessage += dnsRes[0].issue;
} else if (monitor.dns_resolve_type === "MX") {
dnsRes.forEach(record => {
dnsMessage += `Hostname: ${record.exchange} - Priority: ${record.priority} | `;
});
dnsMessage = dnsMessage.slice(0, -2);
} else if (monitor.dns_resolve_type === "NS") {
dnsMessage += "Servers: ";
dnsMessage += dnsRes.join(" | ");
} else if (monitor.dns_resolve_type === "SOA") {
dnsMessage += `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`;
} else if (monitor.dns_resolve_type === "SRV") {
dnsRes.forEach(record => {
dnsMessage += `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight} | `;
});
dnsMessage = dnsMessage.slice(0, -2);
const conditions = ConditionExpressionGroup.fromMonitor(monitor);
let conditionsResult = true;
const handleConditions = (data) => conditions ? evaluateExpressionGroup(conditions, data) : true;
switch (monitor.dns_resolve_type) {
case "A":
case "AAAA":
case "TXT":
case "PTR":
dnsMessage = `Records: ${dnsRes.join(" | ")}`;
conditionsResult = dnsRes.some(record => handleConditions({ record }));
break;
case "CNAME":
dnsMessage = dnsRes[0];
conditionsResult = handleConditions({ record: dnsRes[0] });
break;
case "CAA":
dnsMessage = dnsRes[0].issue;
conditionsResult = handleConditions({ record: dnsRes[0].issue });
break;
case "MX":
dnsMessage = dnsRes.map(record => `Hostname: ${record.exchange} - Priority: ${record.priority}`).join(" | ");
conditionsResult = dnsRes.some(record => handleConditions({ record: record.exchange }));
break;
case "NS":
dnsMessage = `Servers: ${dnsRes.join(" | ")}`;
conditionsResult = dnsRes.some(record => handleConditions({ record }));
break;
case "SOA":
dnsMessage = `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`;
conditionsResult = handleConditions({ record: dnsRes.nsname });
break;
case "SRV":
dnsMessage = dnsRes.map(record => `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight}`).join(" | ");
conditionsResult = dnsRes.some(record => handleConditions({ record: record.name }));
break;
}
if (monitor.dns_last_result !== dnsMessage && dnsMessage !== undefined) {
@@ -47,7 +76,7 @@ class DnsMonitorType extends MonitorType {
}
heartbeat.msg = dnsMessage;
heartbeat.status = UP;
heartbeat.status = conditionsResult ? UP : DOWN;
}
}

View File

@@ -0,0 +1,63 @@
const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");
const { MongoClient } = require("mongodb");
const jsonata = require("jsonata");
class MongodbMonitorType extends MonitorType {
name = "mongodb";
/**
* @inheritdoc
*/
async check(monitor, heartbeat, _server) {
let command = { "ping": 1 };
if (monitor.databaseQuery) {
command = JSON.parse(monitor.databaseQuery);
}
let result = await this.runMongodbCommand(monitor.databaseConnectionString, command);
if (result["ok"] !== 1) {
throw new Error("MongoDB command failed");
} else {
heartbeat.msg = "Command executed successfully";
}
if (monitor.jsonPath) {
let expression = jsonata(monitor.jsonPath);
result = await expression.evaluate(result);
if (result) {
heartbeat.msg = "Command executed successfully and the jsonata expression produces a result.";
} else {
throw new Error("Queried value not found.");
}
}
if (monitor.expectedValue) {
if (result.toString() === monitor.expectedValue) {
heartbeat.msg = "Command executed successfully and expected value was found";
} else {
throw new Error("Query executed, but value is not equal to expected value, value was: [" + JSON.stringify(result) + "]");
}
}
heartbeat.status = UP;
}
/**
* Connect to and run MongoDB command on a MongoDB database
* @param {string} connectionString The database connection string
* @param {object} command MongoDB command to run on the database
* @returns {Promise<(string[] | object[] | object)>} Response from server
*/
async runMongodbCommand(connectionString, command) {
let client = await MongoClient.connect(connectionString);
let result = await client.db().command(command);
await client.close();
return result;
}
}
module.exports = {
MongodbMonitorType,
};

View File

@@ -1,7 +1,19 @@
class MonitorType {
name = undefined;
/**
* Whether or not this type supports monitor conditions. Controls UI visibility in monitor form.
* @type {boolean}
*/
supportsConditions = false;
/**
* Variables supported by this type. e.g. an HTTP type could have a "response_code" variable to test against.
* This property controls the choices displayed in the monitor edit form.
* @type {import("../monitor-conditions/variables").ConditionVariable[]}
*/
conditionVariables = [];
/**
* Run the monitoring check on the given monitor
* @param {Monitor} monitor Monitor to check
@@ -12,7 +24,6 @@ class MonitorType {
async check(monitor, heartbeat, server) {
throw new Error("You need to override check()");
}
}
module.exports = {

View File

@@ -4,15 +4,10 @@ const mqtt = require("mqtt");
const jsonata = require("jsonata");
class MqttMonitorType extends MonitorType {
name = "mqtt";
/**
* Run the monitoring check on the MQTT monitor
* @param {Monitor} monitor Monitor to check
* @param {Heartbeat} heartbeat Monitor heartbeat to update
* @param {UptimeKumaServer} server Uptime Kuma server
* @returns {Promise<void>}
* @inheritdoc
*/
async check(monitor, heartbeat, server) {
const receivedMessage = await this.mqttAsync(monitor.hostname, monitor.mqttTopic, {
@@ -81,7 +76,8 @@ class MqttMonitorType extends MonitorType {
let client = mqtt.connect(mqttUrl, {
username,
password
password,
clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8)
});
client.on("connect", () => {

View File

@@ -10,6 +10,10 @@ const jwt = require("jsonwebtoken");
const config = require("../config");
const { RemoteBrowser } = require("../remote-browser");
/**
* Cached instance of a browser
* @type {import ("playwright-core").Browser}
*/
let browser = null;
let allowedList = [];
@@ -59,7 +63,7 @@ if (process.platform === "win32") {
* @returns {Promise<boolean>} The executable is allowed?
*/
async function isAllowedChromeExecutable(executablePath) {
console.log(config.args);
log.info("Chromium", config.args);
if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") {
return true;
}
@@ -71,10 +75,12 @@ async function isAllowedChromeExecutable(executablePath) {
/**
* Get the current instance of the browser. If there isn't one, create
* it.
* @returns {Promise<Browser>} The browser
* @returns {Promise<import ("playwright-core").Browser>} The browser
*/
async function getBrowser() {
if (!browser) {
if (browser && browser.isConnected()) {
return browser;
} else {
let executablePath = await Settings.get("chromeExecutable");
executablePath = await prepareChromeExecutable(executablePath);
@@ -83,8 +89,9 @@ async function getBrowser() {
//headless: false,
executablePath,
});
return browser;
}
return browser;
}
/**
@@ -95,8 +102,9 @@ async function getBrowser() {
*/
async function getRemoteBrowser(remoteBrowserID, userId) {
let remoteBrowser = await RemoteBrowser.get(remoteBrowserID, userId);
log.debug("MONITOR", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`);
log.debug("Chromium", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`);
browser = chromium.connect(remoteBrowser.url);
browser = await chromium.connect(remoteBrowser.url);
return browser;
}

View File

@@ -0,0 +1,63 @@
const { MonitorType } = require("./monitor-type");
const { UP, log, evaluateJsonQuery } = require("../../src/util");
const snmp = require("net-snmp");
class SNMPMonitorType extends MonitorType {
name = "snmp";
/**
* @inheritdoc
*/
async check(monitor, heartbeat, _server) {
let session;
try {
const sessionOptions = {
port: monitor.port || "161",
retries: monitor.maxretries,
timeout: monitor.timeout * 1000,
version: snmp.Version[monitor.snmpVersion],
};
session = snmp.createSession(monitor.hostname, monitor.radiusPassword, sessionOptions);
// Handle errors during session creation
session.on("error", (error) => {
throw new Error(`Error creating SNMP session: ${error.message}`);
});
const varbinds = await new Promise((resolve, reject) => {
session.get([ monitor.snmpOid ], (error, varbinds) => {
error ? reject(error) : resolve(varbinds);
});
});
log.debug("monitor", `SNMP: Received varbinds (Type: ${snmp.ObjectType[varbinds[0].type]} Value: ${varbinds[0].value})`);
if (varbinds.length === 0) {
throw new Error(`No varbinds returned from SNMP session (OID: ${monitor.snmpOid})`);
}
if (varbinds[0].type === snmp.ObjectType.NoSuchInstance) {
throw new Error(`The SNMP query returned that no instance exists for OID ${monitor.snmpOid}`);
}
// We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in.
const value = varbinds[0].value;
const { status, response } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
if (status) {
heartbeat.status = UP;
heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`;
} else {
throw new Error(`JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`);
}
} finally {
if (session) {
session.close();
}
}
}
}
module.exports = {
SNMPMonitorType,
};

View File

@@ -2,23 +2,13 @@ const { MonitorType } = require("./monitor-type");
const { UP } = require("../../src/util");
const childProcessAsync = require("promisify-child-process");
/**
* A TailscalePing class extends the MonitorType.
* It runs Tailscale ping to monitor the status of a specific node.
*/
class TailscalePing extends MonitorType {
name = "tailscale-ping";
/**
* Checks the ping status of the URL associated with the monitor.
* It then parses the Tailscale ping command output to update the heatrbeat.
* @param {object} monitor The monitor object associated with the check.
* @param {object} heartbeat The heartbeat object to update.
* @returns {Promise<void>}
* @throws Will throw an error if checking Tailscale ping encounters any error
* @inheritdoc
*/
async check(monitor, heartbeat) {
async check(monitor, heartbeat, _server) {
try {
let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval);
this.parseTailscaleOutput(tailscaleOutput, heartbeat);
@@ -38,7 +28,8 @@ class TailscalePing extends MonitorType {
async runTailscalePing(hostname, interval) {
let timeout = interval * 1000 * 0.8;
let res = await childProcessAsync.spawn("tailscale", [ "ping", "--c", "1", hostname ], {
timeout: timeout
timeout: timeout,
encoding: "utf8",
});
if (res.stderr && res.stderr.toString()) {
throw new Error(`Error in output: ${res.stderr.toString()}`);

View File

@@ -3,17 +3,15 @@ const { DOWN, UP } = require("../../src/util");
const axios = require("axios");
class Alerta extends NotificationProvider {
name = "alerta";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
let alertaUrl = `${notification.alertaApiEndpoint}`;
let config = {
headers: {
"Content-Type": "application/json;charset=UTF-8",
@@ -40,7 +38,7 @@ class Alerta extends NotificationProvider {
resource: "Message",
}, data);
await axios.post(alertaUrl, postData, config);
await axios.post(notification.alertaApiEndpoint, postData, config);
} else {
let datadup = Object.assign( {
correlate: [ "service_up", "service_down" ],
@@ -52,11 +50,11 @@ class Alerta extends NotificationProvider {
if (heartbeatJSON["status"] === DOWN) {
datadup.severity = notification.alertaAlertState; // critical
datadup.text = "Service " + monitorJSON["type"] + " is down.";
await axios.post(alertaUrl, datadup, config);
await axios.post(notification.alertaApiEndpoint, datadup, config);
} else if (heartbeatJSON["status"] === UP) {
datadup.severity = notification.alertaRecoverState; // cleaned
datadup.text = "Service " + monitorJSON["type"] + " is up.";
await axios.post(alertaUrl, datadup, config);
await axios.post(notification.alertaApiEndpoint, datadup, config);
}
}
return okMsg;

View File

@@ -1,17 +1,17 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
const { Settings } = require("../settings");
class AlertNow extends NotificationProvider {
name = "AlertNow";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
let textMsg = "";
let status = "open";
@@ -29,7 +29,7 @@ class AlertNow extends NotificationProvider {
textMsg += ` - ${msg}`;
const baseURL = await setting("primaryBaseURL");
const baseURL = await Settings.get("primaryBaseURL");
if (baseURL && monitorJSON) {
textMsg += ` >> ${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
}

View File

@@ -11,7 +11,7 @@ class AliyunSMS extends NotificationProvider {
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
if (heartbeatJSON != null) {
@@ -44,7 +44,7 @@ class AliyunSMS extends NotificationProvider {
* Send the SMS notification
* @param {BeanModel} notification Notification details
* @param {string} msgbody Message template
* @returns {boolean} True if successful else false
* @returns {Promise<boolean>} True if successful else false
*/
async sendSms(notification, msgbody) {
let params = {

View File

@@ -2,26 +2,29 @@ const NotificationProvider = require("./notification-provider");
const childProcessAsync = require("promisify-child-process");
class Apprise extends NotificationProvider {
name = "apprise";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const args = [ "-vv", "-b", msg, notification.appriseURL ];
if (notification.title) {
args.push("-t");
args.push(notification.title);
}
const s = await childProcessAsync.spawn("apprise", args);
const s = await childProcessAsync.spawn("apprise", args, {
encoding: "utf8",
});
const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
if (output) {
if (! output.includes("ERROR")) {
return "Sent Successfully";
return okMsg;
}
throw new Error(output);

View File

@@ -46,29 +46,29 @@ class Bark extends NotificationProvider {
}
/**
* Add additional parameter for Bark v1 endpoints
* Add additional parameter for Bark v1 endpoints.
* Leads to better on device styles (iOS 15 optimized)
* @param {BeanModel} notification Notification to send
* @param {string} postUrl URL to append parameters to
* @returns {string} Additional URL parameters
*/
appendAdditionalParameters(notification, postUrl) {
additionalParameters(notification) {
// set icon to uptime kuma icon, 11kb should be fine
postUrl += "?icon=" + barkNotificationAvatar;
let params = "?icon=" + barkNotificationAvatar;
// grouping all our notifications
if (notification.barkGroup != null) {
postUrl += "&group=" + notification.barkGroup;
params += "&group=" + notification.barkGroup;
} else {
// default name
postUrl += "&group=" + "UptimeKuma";
params += "&group=" + "UptimeKuma";
}
// picked a sound, this should follow system's mute status when arrival
if (notification.barkSound != null) {
postUrl += "&sound=" + notification.barkSound;
params += "&sound=" + notification.barkSound;
} else {
// default sound
postUrl += "&sound=" + "telegraph";
params += "&sound=" + "telegraph";
}
return postUrl;
return params;
}
/**
@@ -92,7 +92,7 @@ class Bark extends NotificationProvider {
* @param {string} title Message title
* @param {string} subtitle Message
* @param {string} endpoint Endpoint to send request to
* @returns {string} Success message
* @returns {Promise<string>} Success message
*/
async postNotification(notification, title, subtitle, endpoint) {
let result;
@@ -100,9 +100,8 @@ class Bark extends NotificationProvider {
// url encode title and subtitle
title = encodeURIComponent(title);
subtitle = encodeURIComponent(subtitle);
let postUrl = endpoint + "/" + title + "/" + subtitle;
postUrl = this.appendAdditionalParameters(notification, postUrl);
result = await axios.get(postUrl);
const params = this.additionalParameters(notification);
result = await axios.get(`${endpoint}/${title}/${subtitle}${params}`);
} else {
result = await axios.post(`${endpoint}/push`, {
title,

View File

@@ -0,0 +1,31 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP } = require("../../src/util");
class Bitrix24 extends NotificationProvider {
name = "Bitrix24";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
try {
const params = {
user_id: notification.bitrix24UserID,
message: "[B]Uptime Kuma[/B]",
"ATTACH[COLOR]": (heartbeatJSON ?? {})["status"] === UP ? "#b73419" : "#67b518",
"ATTACH[BLOCKS][0][MESSAGE]": msg
};
await axios.get(`${notification.bitrix24WebhookURL}/im.notify.system.add.json`, { params });
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Bitrix24;

View File

@@ -0,0 +1,23 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class CallMeBot extends NotificationProvider {
name = "CallMeBot";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
try {
const url = new URL(notification.callMeBotEndpoint);
url.searchParams.set("text", msg);
await axios.get(url.toString());
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = CallMeBot;

View File

@@ -0,0 +1,39 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Cellsynt extends NotificationProvider {
name = "Cellsynt";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const data = {
// docs at https://www.cellsynt.com/en/sms/api-integration
params: {
"username": notification.cellsyntLogin,
"password": notification.cellsyntPassword,
"destination": notification.cellsyntDestination,
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"originatortype": notification.cellsyntOriginatortype,
"originator": notification.cellsyntOriginator,
"allowconcat": notification.cellsyntAllowLongSMS ? 6 : 1
}
};
try {
const resp = await axios.post("https://se-1.cellsynt.net/sms.php", null, data);
if (resp.data == null ) {
throw new Error("Could not connect to Cellsynt, please try again.");
} else if (resp.data.includes("Error:")) {
resp.data = resp.data.replaceAll("Error:", "");
throw new Error(resp.data);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Cellsynt;

View File

@@ -2,14 +2,15 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class ClickSendSMS extends NotificationProvider {
name = "clicksendsms";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const url = "https://rest.clicksend.com/v3/sms/send";
try {
let config = {
headers: {
@@ -28,7 +29,7 @@ class ClickSendSMS extends NotificationProvider {
}
]
};
let resp = await axios.post("https://rest.clicksend.com/v3/sms/send", data, config);
let resp = await axios.post(url, data, config);
if (resp.data.data.messages[0].status !== "SUCCESS") {
let error = "Something gone wrong. Api returned " + resp.data.data.messages[0].status + ".";
this.throwGeneralAxiosError(error);

View File

@@ -10,7 +10,7 @@ class DingDing extends NotificationProvider {
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
if (heartbeatJSON != null) {
@@ -19,9 +19,12 @@ class DingDing extends NotificationProvider {
markdown: {
title: `[${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]}`,
text: `## [${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]} \n> ${heartbeatJSON["msg"]}\n> Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
},
"at": {
"isAtAll": notification.mentioning === "everyone"
}
};
if (this.sendToDingDing(notification, params)) {
if (await this.sendToDingDing(notification, params)) {
return okMsg;
}
} else {
@@ -31,7 +34,7 @@ class DingDing extends NotificationProvider {
content: msg
}
};
if (this.sendToDingDing(notification, params)) {
if (await this.sendToDingDing(notification, params)) {
return okMsg;
}
}
@@ -44,7 +47,7 @@ class DingDing extends NotificationProvider {
* Send message to DingDing
* @param {BeanModel} notification Notification to send
* @param {object} params Parameters of message
* @returns {boolean} True if successful else false
* @returns {Promise<boolean>} True if successful else false
*/
async sendToDingDing(notification, params) {
let timestamp = Date.now();
@@ -62,7 +65,7 @@ class DingDing extends NotificationProvider {
if (result.data.errmsg === "ok") {
return true;
}
return false;
throw new Error(result.data.errmsg);
}
/**

View File

@@ -3,17 +3,20 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Discord extends NotificationProvider {
name = "discord";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
const discordDisplayName = notification.discordUsername || "Uptime Kuma";
const webhookUrl = new URL(notification.discordWebhookUrl);
if (notification.discordChannelType === "postToThread") {
webhookUrl.searchParams.append("thread_id", notification.threadId);
}
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
@@ -21,30 +24,15 @@ class Discord extends NotificationProvider {
username: discordDisplayName,
content: msg,
};
await axios.post(notification.discordWebhookUrl, discordtestdata);
if (notification.discordChannelType === "createNewForumPost") {
discordtestdata.thread_name = notification.postName;
}
await axios.post(webhookUrl.toString(), discordtestdata);
return okMsg;
}
let address;
switch (monitorJSON["type"]) {
case "ping":
address = monitorJSON["hostname"];
break;
case "port":
case "dns":
case "gamedig":
case "steam":
address = monitorJSON["hostname"];
if (monitorJSON["port"]) {
address += ":" + monitorJSON["port"];
}
break;
default:
address = monitorJSON["url"];
break;
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON["status"] === DOWN) {
let discorddowndata = {
@@ -60,7 +48,7 @@ class Discord extends NotificationProvider {
},
{
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
value: this.extractAdress(monitorJSON),
},
{
name: `Time (${heartbeatJSON["timezone"]})`,
@@ -73,12 +61,14 @@ class Discord extends NotificationProvider {
],
}],
};
if (notification.discordChannelType === "createNewForumPost") {
discorddowndata.thread_name = notification.postName;
}
if (notification.discordPrefixMessage) {
discorddowndata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discorddowndata);
await axios.post(webhookUrl.toString(), discorddowndata);
return okMsg;
} else if (heartbeatJSON["status"] === UP) {
@@ -95,7 +85,7 @@ class Discord extends NotificationProvider {
},
{
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
value: this.extractAdress(monitorJSON),
},
{
name: `Time (${heartbeatJSON["timezone"]})`,
@@ -109,11 +99,15 @@ class Discord extends NotificationProvider {
}],
};
if (notification.discordChannelType === "createNewForumPost") {
discordupdata.thread_name = notification.postName;
}
if (notification.discordPrefixMessage) {
discordupdata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discordupdata);
await axios.post(webhookUrl.toString(), discordupdata);
return okMsg;
}
} catch (error) {

View File

@@ -9,8 +9,7 @@ class Feishu extends NotificationProvider {
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let feishuWebHookUrl = notification.feishuWebHookUrl;
const okMsg = "Sent Successfully.";
try {
if (heartbeatJSON == null) {
@@ -20,59 +19,67 @@ class Feishu extends NotificationProvider {
text: msg,
},
};
await axios.post(feishuWebHookUrl, testdata);
await axios.post(notification.feishuWebHookUrl, testdata);
return okMsg;
}
if (heartbeatJSON["status"] === DOWN) {
let downdata = {
msg_type: "post",
content: {
post: {
zh_cn: {
title: "UptimeKuma Alert: [Down] " + monitorJSON["name"],
content: [
[
{
tag: "text",
text:
"[Down] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
},
],
],
},
msg_type: "interactive",
card: {
config: {
update_multi: false,
wide_screen_mode: true,
},
},
header: {
title: {
tag: "plain_text",
content: "UptimeKuma Alert: [Down] " + monitorJSON["name"],
},
template: "red",
},
elements: [
{
tag: "div",
text: {
tag: "lark_md",
content: getContent(heartbeatJSON),
},
}
]
}
};
await axios.post(feishuWebHookUrl, downdata);
await axios.post(notification.feishuWebHookUrl, downdata);
return okMsg;
}
if (heartbeatJSON["status"] === UP) {
let updata = {
msg_type: "post",
content: {
post: {
zh_cn: {
title: "UptimeKuma Alert: [Up] " + monitorJSON["name"],
content: [
[
{
tag: "text",
text:
"[Up] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
},
],
],
},
msg_type: "interactive",
card: {
config: {
update_multi: false,
wide_screen_mode: true,
},
},
header: {
title: {
tag: "plain_text",
content: "UptimeKuma Alert: [UP] " + monitorJSON["name"],
},
template: "green",
},
elements: [
{
tag: "div",
text: {
tag: "lark_md",
content: getContent(heartbeatJSON),
},
},
]
}
};
await axios.post(feishuWebHookUrl, updata);
await axios.post(notification.feishuWebHookUrl, updata);
return okMsg;
}
} catch (error) {
@@ -81,4 +88,17 @@ class Feishu extends NotificationProvider {
}
}
/**
* Get content
* @param {?object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {string} Return Successful Message
*/
function getContent(heartbeatJSON) {
return [
"**Message**: " + heartbeatJSON["msg"],
"**Ping**: " + (heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms"),
`**Time (${heartbeatJSON["timezone"]})**: ${heartbeatJSON["localDateTime"]}`
].join("\n");
}
module.exports = Feishu;

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
const { Settings } = require("../settings");
const successMessage = "Sent Successfully.";
class FlashDuty extends NotificationProvider {
@@ -62,6 +62,15 @@ class FlashDuty extends NotificationProvider {
* @returns {string} Success message
*/
async postNotification(notification, title, body, monitorInfo, eventStatus) {
let labels = {
resource: this.genMonitorUrl(monitorInfo),
check: monitorInfo.name,
};
if (monitorInfo.tags && monitorInfo.tags.length > 0) {
for (let tag of monitorInfo.tags) {
labels[tag.name] = tag.value;
}
}
const options = {
method: "POST",
url: "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey,
@@ -71,13 +80,11 @@ class FlashDuty extends NotificationProvider {
title,
event_status: eventStatus || "Info",
alert_key: String(monitorInfo.id) || Math.random().toString(36).substring(7),
labels: monitorInfo?.tags?.reduce((acc, item) => ({ ...acc,
[item.name]: item.value
}), { resource: this.genMonitorUrl(monitorInfo) }),
labels,
}
};
const baseURL = await setting("primaryBaseURL");
const baseURL = await Settings.get("primaryBaseURL");
if (baseURL && monitorInfo) {
options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);

View File

@@ -2,14 +2,14 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class FreeMobile extends NotificationProvider {
name = "FreeMobile";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
await axios.post(`https://smsapi.free-mobile.fr/sendmsg?msg=${encodeURIComponent(msg.replace("🔴", "⛔️"))}`, {
"user": notification.freemobileUser,

View File

@@ -3,21 +3,20 @@ const axios = require("axios");
const { UP } = require("../../src/util");
class GoAlert extends NotificationProvider {
name = "GoAlert";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
let closeAction = "close";
let data = {
summary: msg,
};
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
data["action"] = closeAction;
data["action"] = "close";
}
let headers = {
"Content-Type": "multipart/form-data",
@@ -27,7 +26,6 @@ class GoAlert extends NotificationProvider {
};
await axios.post(`${notification.goAlertBaseURL}/api/v2/generic/incoming?token=${notification.goAlertToken}`, data, config);
return okMsg;
} catch (error) {
let msg = (error.response.data) ? error.response.data : "Error without response";
throw new Error(msg);

View File

@@ -1,41 +1,85 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { setting } = require("../util-server");
const { getMonitorRelativeURL } = require("../../src/util");
const { DOWN, UP } = require("../../src/util");
const { getMonitorRelativeURL, UP } = require("../../src/util");
const { Settings } = require("../settings");
class GoogleChat extends NotificationProvider {
name = "GoogleChat";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
// Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic
let textMsg = "";
if (heartbeatJSON && heartbeatJSON.status === UP) {
textMsg = "✅ Application is back online\n";
} else if (heartbeatJSON && heartbeatJSON.status === DOWN) {
textMsg = "🔴 Application went down\n";
let chatHeader = {
title: "Uptime Kuma Alert",
};
if (monitorJSON && heartbeatJSON) {
chatHeader["title"] =
heartbeatJSON["status"] === UP
? `${monitorJSON["name"]} is back online`
: `🔴 ${monitorJSON["name"]} went down`;
}
if (monitorJSON && monitorJSON.name) {
textMsg += `*${monitorJSON.name}*\n`;
// always show msg
let sectionWidgets = [
{
textParagraph: {
text: `<b>Message:</b>\n${msg}`,
},
},
];
// add time if available
if (heartbeatJSON) {
sectionWidgets.push({
textParagraph: {
text: `<b>Time (${heartbeatJSON["timezone"]}):</b>\n${heartbeatJSON["localDateTime"]}`,
},
});
}
textMsg += `${msg}`;
const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) {
textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
// add button for monitor link if available
const baseURL = await Settings.get("primaryBaseURL");
if (baseURL) {
const urlPath = monitorJSON ? getMonitorRelativeURL(monitorJSON.id) : "/";
sectionWidgets.push({
buttonList: {
buttons: [
{
text: "Visit Uptime Kuma",
onClick: {
openLink: {
url: baseURL + urlPath,
},
},
},
],
},
});
}
const data = {
"text": textMsg,
let chatSections = [
{
widgets: sectionWidgets,
},
];
// construct json data
let data = {
cardsV2: [
{
card: {
header: chatHeader,
sections: chatSections,
},
},
],
};
await axios.post(notification.googleChatWebhookURL, data);

View File

@@ -2,14 +2,13 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Gorush extends NotificationProvider {
name = "gorush";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
let platformMapping = {
"ios": 1,

View File

@@ -2,14 +2,14 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Gotify extends NotificationProvider {
name = "gotify";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) {
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);

View File

@@ -3,19 +3,18 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class GrafanaOncall extends NotificationProvider {
name = "GrafanaOncall";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
if (!notification.GrafanaOncallURL) {
throw new Error("GrafanaOncallURL cannot be empty");
}
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON === null) {
let grafanaupdata = {
@@ -23,10 +22,7 @@ class GrafanaOncall extends NotificationProvider {
message: msg,
state: "alerting",
};
await axios.post(
notification.GrafanaOncallURL,
grafanaupdata
);
await axios.post(notification.GrafanaOncallURL, grafanaupdata);
return okMsg;
} else if (heartbeatJSON["status"] === DOWN) {
let grafanadowndata = {
@@ -34,10 +30,7 @@ class GrafanaOncall extends NotificationProvider {
message: heartbeatJSON["msg"],
state: "alerting",
};
await axios.post(
notification.GrafanaOncallURL,
grafanadowndata
);
await axios.post(notification.GrafanaOncallURL, grafanadowndata);
return okMsg;
} else if (heartbeatJSON["status"] === UP) {
let grafanaupdata = {
@@ -45,10 +38,7 @@ class GrafanaOncall extends NotificationProvider {
message: heartbeatJSON["msg"],
state: "ok",
};
await axios.post(
notification.GrafanaOncallURL,
grafanaupdata
);
await axios.post(notification.GrafanaOncallURL, grafanaupdata);
return okMsg;
}
} catch (error) {

View File

@@ -0,0 +1,33 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class GtxMessaging extends NotificationProvider {
name = "gtxmessaging";
/**
* @inheritDoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
// The UP/DOWN symbols will be replaced with `???` by gtx-messaging
const text = msg.replaceAll("🔴 ", "").replaceAll("✅ ", "");
try {
const data = new URLSearchParams();
data.append("from", notification.gtxMessagingFrom.trim());
data.append("to", notification.gtxMessagingTo.trim());
data.append("text", text);
const url = `https://rest.gtx-messaging.net/smsc/sendsms/${notification.gtxMessagingApiKey}/json`;
await axios.post(url, data);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = GtxMessaging;

View File

@@ -0,0 +1,52 @@
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class HeiiOnCall extends NotificationProvider {
name = "HeiiOnCall";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const payload = heartbeatJSON || {};
const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorJSON) {
payload["url"] = baseURL + getMonitorRelativeURL(monitorJSON.id);
}
const config = {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + notification.heiiOnCallApiKey,
},
};
const heiiUrl = `https://heiioncall.com/triggers/${notification.heiiOnCallTriggerId}/`;
// docs https://heiioncall.com/docs#manual-triggers
try {
if (!heartbeatJSON) {
// Testing or general notification like certificate expiry
payload["msg"] = msg;
await axios.post(heiiUrl + "alert", payload, config);
return okMsg;
}
if (heartbeatJSON.status === DOWN) {
await axios.post(heiiUrl + "alert", payload, config);
return okMsg;
}
if (heartbeatJSON.status === UP) {
await axios.post(heiiUrl + "resolve", payload, config);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = HeiiOnCall;

View File

@@ -9,7 +9,9 @@ class HomeAssistant extends NotificationProvider {
/**
* @inheritdoc
*/
async send(notification, message, monitor = null, heartbeat = null) {
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
const notificationService = notification?.notificationService || defaultNotificationService;
try {
@@ -17,10 +19,12 @@ class HomeAssistant extends NotificationProvider {
`${notification.homeAssistantUrl.trim().replace(/\/*$/, "")}/api/services/notify/${notificationService}`,
{
title: "Uptime Kuma",
message,
message: msg,
...(notificationService !== "persistent_notification" && { data: {
name: monitor?.name,
status: heartbeat?.status,
name: monitorJSON?.name,
status: heartbeatJSON?.status,
channel: "Uptime Kuma",
icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
} }),
},
{
@@ -31,7 +35,7 @@ class HomeAssistant extends NotificationProvider {
}
);
return "Sent Successfully.";
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}

View File

@@ -0,0 +1,42 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Keep extends NotificationProvider {
name = "Keep";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
msg,
};
let config = {
headers: {
"x-api-key": notification.webhookAPIKey,
"content-type": "application/json",
},
};
let url = notification.webhookURL;
if (url.endsWith("/")) {
url = url.slice(0, -1);
}
let webhookURL = url + "/alerts/event/uptimekuma";
await axios.post(webhookURL, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Keep;

View File

@@ -2,15 +2,15 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Kook extends NotificationProvider {
name = "Kook";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let url = "https://www.kookapp.cn/api/v3/message/create";
const okMsg = "Sent Successfully.";
const url = "https://www.kookapp.cn/api/v3/message/create";
let data = {
target_id: notification.kookGuildID,
content: msg,

View File

@@ -3,16 +3,16 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Line extends NotificationProvider {
name = "line";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const url = "https://api.line.me/v2/bot/message/push";
try {
let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
let config = {
headers: {
"Content-Type": "application/json",
@@ -29,7 +29,7 @@ class Line extends NotificationProvider {
}
]
};
await axios.post(lineAPIUrl, testMessage, config);
await axios.post(url, testMessage, config);
} else if (heartbeatJSON["status"] === DOWN) {
let downMessage = {
"to": notification.lineUserID,
@@ -43,7 +43,7 @@ class Line extends NotificationProvider {
}
]
};
await axios.post(lineAPIUrl, downMessage, config);
await axios.post(url, downMessage, config);
} else if (heartbeatJSON["status"] === UP) {
let upMessage = {
"to": notification.lineUserID,
@@ -57,7 +57,7 @@ class Line extends NotificationProvider {
}
]
};
await axios.post(lineAPIUrl, upMessage, config);
await axios.post(url, upMessage, config);
}
return okMsg;
} catch (error) {

View File

@@ -4,16 +4,16 @@ const qs = require("qs");
const { DOWN, UP } = require("../../src/util");
class LineNotify extends NotificationProvider {
name = "LineNotify";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const url = "https://notify-api.line.me/api/notify";
try {
let lineAPIUrl = "https://notify-api.line.me/api/notify";
let config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
@@ -24,7 +24,7 @@ class LineNotify extends NotificationProvider {
let testMessage = {
"message": msg,
};
await axios.post(lineAPIUrl, qs.stringify(testMessage), config);
await axios.post(url, qs.stringify(testMessage), config);
} else if (heartbeatJSON["status"] === DOWN) {
let downMessage = {
"message": "\n[🔴 Down]\n" +
@@ -32,7 +32,7 @@ class LineNotify extends NotificationProvider {
heartbeatJSON["msg"] + "\n" +
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lineAPIUrl, qs.stringify(downMessage), config);
await axios.post(url, qs.stringify(downMessage), config);
} else if (heartbeatJSON["status"] === UP) {
let upMessage = {
"message": "\n[✅ Up]\n" +
@@ -40,7 +40,7 @@ class LineNotify extends NotificationProvider {
heartbeatJSON["msg"] + "\n" +
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lineAPIUrl, qs.stringify(upMessage), config);
await axios.post(url, qs.stringify(upMessage), config);
}
return okMsg;
} catch (error) {

View File

@@ -3,28 +3,23 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class LunaSea extends NotificationProvider {
name = "lunasea";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let lunaseaurl = "";
if (notification.lunaseaTarget === "user") {
lunaseaurl = "https://notify.lunasea.app/v1/custom/user/" + notification.lunaseaUserID;
} else {
lunaseaurl = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice;
}
const okMsg = "Sent Successfully.";
const url = "https://notify.lunasea.app/v1";
try {
const target = this.getTarget(notification);
if (heartbeatJSON == null) {
let testdata = {
"title": "Uptime Kuma Alert",
"body": msg,
};
await axios.post(lunaseaurl, testdata);
await axios.post(`${url}/custom/${target}`, testdata);
return okMsg;
}
@@ -35,7 +30,7 @@ class LunaSea extends NotificationProvider {
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lunaseaurl, downdata);
await axios.post(`${url}/custom/${target}`, downdata);
return okMsg;
}
@@ -46,13 +41,25 @@ class LunaSea extends NotificationProvider {
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lunaseaurl, updata);
await axios.post(`${url}/custom/${target}`, updata);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
/**
* Generates the lunasea target to send the notification to
* @param {BeanModel} notification Notification details
* @returns {string} The target to send the notification to
*/
getTarget(notification) {
if (notification.lunaseaTarget === "user") {
return "user/" + notification.lunaseaUserID;
}
return "device/" + notification.lunaseaDevice;
}
}

View File

@@ -10,7 +10,7 @@ class Matrix extends NotificationProvider {
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const size = 20;
const randomString = encodeURIComponent(

View File

@@ -3,14 +3,14 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Mattermost extends NotificationProvider {
name = "mattermost";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
// If heartbeatJSON is null, assume non monitoring notification (Certificate warning) or testing.
@@ -98,10 +98,7 @@ class Mattermost extends NotificationProvider {
},
],
};
await axios.post(
notification.mattermostWebhookUrl,
mattermostdata
);
await axios.post(notification.mattermostWebhookUrl, mattermostdata);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);

View File

@@ -1,4 +1,3 @@
const { log } = require("../../src/util");
const NotificationProvider = require("./notification-provider");
const {
relayInit,
@@ -12,16 +11,7 @@ const {
// polyfills for node versions
const semver = require("semver");
const nodeVersion = process.version;
if (semver.lt(nodeVersion, "16.0.0")) {
log.warn("monitor", "Node <= 16 is unsupported for nostr, sorry :(");
} else if (semver.lt(nodeVersion, "18.0.0")) {
// polyfills for node 16
global.crypto = require("crypto");
global.WebSocket = require("isomorphic-ws");
if (typeof crypto !== "undefined" && !crypto.subtle && crypto.webcrypto) {
crypto.subtle = crypto.webcrypto.subtle;
}
} else if (semver.lt(nodeVersion, "20.0.0")) {
if (semver.lt(nodeVersion, "20.0.0")) {
// polyfills for node 18
global.crypto = require("crypto");
global.WebSocket = require("isomorphic-ws");
@@ -107,7 +97,7 @@ class Nostr extends NotificationProvider {
/**
* Get public keys for recipients
* @param {string} recipients Newline delimited list of recipients
* @returns {nip19.DecodeResult[]} Public keys
* @returns {Promise<nip19.DecodeResult[]>} Public keys
*/
async getPublicKeys(recipients) {
const recipientsList = recipients.split("\n");

View File

@@ -19,6 +19,36 @@ class NotificationProvider {
throw new Error("Have to override Notification.send(...)");
}
/**
* Extracts the address from a monitor JSON object based on its type.
* @param {?object} monitorJSON Monitor details (For Up/Down only)
* @returns {string} The extracted address based on the monitor type.
*/
extractAdress(monitorJSON) {
if (!monitorJSON) {
return "";
}
switch (monitorJSON["type"]) {
case "push":
return "Heartbeat";
case "ping":
return monitorJSON["hostname"];
case "port":
case "dns":
case "gamedig":
case "steam":
if (monitorJSON["port"]) {
return monitorJSON["hostname"] + ":" + monitorJSON["port"];
}
return monitorJSON["hostname"];
default:
if (![ "https://", "http://", "" ].includes(monitorJSON["url"])) {
return monitorJSON["url"];
}
return "";
}
}
/**
* Throws an error
* @param {any} error The error to throw

View File

@@ -3,14 +3,14 @@ const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Ntfy extends NotificationProvider {
name = "ntfy";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
let headers = {};
if (notification.ntfyAuthenticationMethod === "usernamePassword") {
@@ -31,7 +31,7 @@ class Ntfy extends NotificationProvider {
"priority": notification.ntfyPriority,
"tags": [ "test_tube" ],
};
await axios.post(`${notification.ntfyserverurl}`, ntfyTestData, { headers: headers });
await axios.post(notification.ntfyserverurl, ntfyTestData, { headers: headers });
return okMsg;
}
let tags = [];
@@ -54,20 +54,23 @@ class Ntfy extends NotificationProvider {
"priority": priority,
"title": monitorJSON.name + " " + status + " [Uptime-Kuma]",
"tags": tags,
"actions": [
};
if (monitorJSON.url && monitorJSON.url !== "https://") {
data.actions = [
{
"action": "view",
"label": "Open " + monitorJSON.name,
"url": monitorJSON.url,
}
]
};
},
];
}
if (notification.ntfyIcon) {
data.icon = notification.ntfyIcon;
}
await axios.post(`${notification.ntfyserverurl}`, data, { headers: headers });
await axios.post(notification.ntfyserverurl, data, { headers: headers });
return okMsg;

View File

@@ -2,14 +2,15 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Octopush extends NotificationProvider {
name = "octopush";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const urlV2 = "https://api.octopush.com/v1/public/sms-campaign/send";
const urlV1 = "https://www.octopush-dm.com/api/sms/json";
try {
// Default - V2
@@ -33,7 +34,7 @@ class Octopush extends NotificationProvider {
"purpose": "alert",
"sender": notification.octopushSenderName
};
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config);
await axios.post(urlV2, data, config);
} else if (notification.octopushVersion === "1") {
let data = {
"user_login": notification.octopushDMLogin,
@@ -55,7 +56,7 @@ class Octopush extends NotificationProvider {
// V1 API returns 200 even on error so we must check
// response data
let response = await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config);
let response = await axios.post(urlV1, {}, config);
if ("error_code" in response.data) {
if (response.data.error_code !== "000") {
this.throwGeneralAxiosError(`Octopush error ${JSON.stringify(response.data)}`);

View File

@@ -2,23 +2,23 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class OneBot extends NotificationProvider {
name = "OneBot";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
try {
let httpAddr = notification.httpAddr;
if (!httpAddr.startsWith("http")) {
httpAddr = "http://" + httpAddr;
let url = notification.httpAddr;
if (!url.startsWith("http")) {
url = "http://" + url;
}
if (!httpAddr.endsWith("/")) {
httpAddr += "/";
if (!url.endsWith("/")) {
url += "/";
}
let onebotAPIUrl = httpAddr + "send_msg";
url += "send_msg";
let config = {
headers: {
"Content-Type": "application/json",
@@ -37,7 +37,7 @@ class OneBot extends NotificationProvider {
data["message_type"] = "private";
data["user_id"] = notification.recieverId;
}
await axios.post(onebotAPIUrl, data, config);
await axios.post(url, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);

View File

@@ -0,0 +1,47 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Onesender extends NotificationProvider {
name = "Onesender";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const okMsg = "Sent Successfully.";
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
msg,
to: notification.onesenderReceiver,
type: "text",
recipient_type: "individual",
text: {
body: msg
}
};
if (notification.onesenderTypeReceiver === "private") {
data.to = notification.onesenderReceiver + "@s.whatsapp.net";
} else {
data.recipient_type = "group";
data.to = notification.onesenderReceiver + "@g.us";
}
let config = {
headers: {
"Authorization": "Bearer " + notification.onesenderToken,
}
};
await axios.post(notification.onesenderURL, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Onesender;

View File

@@ -4,10 +4,9 @@ const { UP, DOWN } = require("../../src/util");
const opsgenieAlertsUrlEU = "https://api.eu.opsgenie.com/v2/alerts";
const opsgenieAlertsUrlUS = "https://api.opsgenie.com/v2/alerts";
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
class Opsgenie extends NotificationProvider {
name = "Opsgenie";
/**

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
const { Settings } = require("../settings");
let successMessage = "Sent Successfully.";
class PagerDuty extends NotificationProvider {
@@ -95,7 +95,7 @@ class PagerDuty extends NotificationProvider {
}
};
const baseURL = await setting("primaryBaseURL");
const baseURL = await Settings.get("primaryBaseURL");
if (baseURL && monitorInfo) {
options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
const { Settings } = require("../settings");
let successMessage = "Sent Successfully.";
class PagerTree extends NotificationProvider {
@@ -74,7 +74,7 @@ class PagerTree extends NotificationProvider {
}
};
const baseURL = await setting("primaryBaseURL");
const baseURL = await Settings.get("primaryBaseURL");
if (baseURL && monitorJSON) {
options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorJSON.id);

View File

@@ -2,14 +2,14 @@ const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class PromoSMS extends NotificationProvider {
name = "promosms";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const okMsg = "Sent Successfully.";
const url = "https://promosms.com/api/rest/v3_2/sms";
if (notification.promosmsAllowLongSMS === undefined) {
notification.promosmsAllowLongSMS = false;
@@ -36,7 +36,7 @@ class PromoSMS extends NotificationProvider {
"sender": notification.promosmsSenderName
};
let resp = await axios.post("https://promosms.com/api/rest/v3_2/sms", data, config);
let resp = await axios.post(url, data, config);
if (resp.data.response.status !== 0) {
let error = "Something gone wrong. Api returned " + resp.data.response.status + ".";

Some files were not shown because too many files have changed in this diff Show More