Compare commits

..

917 Commits
1.5.2 ... 1.9.1

Author SHA1 Message Date
Louis Lam
c6fc385289 update to 1.9.1 2021-10-19 00:20:27 +08:00
Louis Lam
c645658161 Merge remote-tracking branch 'origin/master' 2021-10-19 00:19:41 +08:00
Louis Lam
182597944d fix #721 2021-10-19 00:19:26 +08:00
Louis Lam
8eaa8116c3 update email 2021-10-18 23:55:00 +08:00
Louis Lam
3512faad14 move kubernetes folder to the k8s-unofficial branch 2021-10-18 23:38:29 +08:00
Louis Lam
f11417e854 [upload artifacts] no idea why suddenly not working via env var, hardcode the VERSION instead 2021-10-18 20:43:17 +08:00
Louis Lam
5f36d2acda fix upload artifacts 2021-10-18 20:02:50 +08:00
Louis Lam
cc36ff5210 update to 1.9.0 2021-10-18 19:46:15 +08:00
Louis Lam
c363d3374e fix "build-docker-nightly-alpine" wrong path 2021-10-18 19:45:50 +08:00
Louis Lam
65a8cb5307 Merge pull request #738 from NixNotCastey/promosms
PromoSMS - Fixed values for sms type
2021-10-18 18:28:58 +08:00
Lukas
f74b2662c5 Fixed values for sms type 2021-10-18 12:02:54 +02:00
Louis Lam
6e18f39eb4 [steam] code cleanup 2021-10-18 17:15:28 +08:00
Louis Lam
68d44dd9b3 [steam] do not request if there is no steam api key 2021-10-18 17:11:41 +08:00
Louis Lam
20d59e5a13 fix and move the steam api key to settings page 2021-10-18 17:02:05 +08:00
Louis Lam
ae31eb6ba9 Merge branch 'master' into Revyn112_master
# Conflicts:
#	server/model/monitor.js
#	src/languages/en.js
#	src/pages/EditMonitor.vue
2021-10-18 15:50:35 +08:00
Louis Lam
df5efcc71c Merge pull request #730 from sargonas/sargonas-patch-1
Update README.md
2021-10-17 23:25:19 +08:00
J. Eckert
4cd66b20b1 Update README.md
additional spelling correction
2021-10-17 08:08:44 -07:00
J. Eckert
1276102c18 Update README.md
minor grammatical edits for slight improvements. (Honestly though, your grammar is not that bad at all for not being a native speaker!)
2021-10-16 23:07:25 -07:00
Louis Lam
6944b35ea7 Merge pull request #667 from zsxeee/i18n
Missing i18n and zh-CN translation
2021-10-16 22:47:58 +08:00
Louis Lam
88757ebbbe Merge pull request #722 from dpatrongomez/master
Add spanish translation for monitor history and records
2021-10-16 22:46:29 +08:00
Daniel Patrón Gómez
0a73b84ae6 Add records translations and fix pause translation 2021-10-16 12:45:41 +02:00
Daniel Patrón Gómez
15f36f96c3 Add spanish translation for monitor history 2021-10-16 12:15:39 +02:00
Louis Lam
edcaf93446 Merge pull request #721 from dpatrongomez/master
Add Status Translation
2021-10-16 17:41:46 +08:00
Louis Lam
dec175d55f Merge pull request #719 from bertyhell/bugfix/edit-monitor
fix(edit-monitor): no need to escape placeholder {} if not translated
2021-10-16 17:38:00 +08:00
Louis Lam
9a60f69f66 Merge pull request #720 from bertyhell/bugfix/fix-error-on-first-hearthbeat
fix(monitor): safely get status of previous beat if first beat
2021-10-16 17:34:44 +08:00
Daniel Patrón Gómez
53a008ae2b Add Status Translation 2021-10-16 11:32:15 +02:00
Bert Verhelst
1d63dd9ddd fix(monitor): safely get status of previous beat if first beat 2021-10-16 11:28:03 +02:00
Bert Verhelst
61627545a5 fix(edit-monitor): no need to escape placeholder {} if not translated 2021-10-16 11:26:32 +02:00
Louis Lam
176fa6b60d merge package-lock.json 2021-10-16 16:32:28 +08:00
Louis Lam
cb43ecb46e Merge branch 'master' into background-jobs
# Conflicts:
#	package-lock.json
#	package.json
#	src/languages/en.js
2021-10-16 15:06:59 +08:00
Louis Lam
2e24312f67 [test] jest please 2021-10-16 14:54:45 +08:00
Louis Lam
6ff3cb275e Merge pull request #642 from andreasbrett/patch-2
Harden 2FA/TOTP implementation according to rfc6238 (part 3)
2021-10-16 14:30:25 +08:00
Louis Lam
9d364b28b1 Merge pull request #715 from januridp/master
Fix(Language): Bahasa Indonesia (Indonesian)
2021-10-16 13:02:57 +08:00
Louis Lam
bc3e3f9118 [test] update 2021-10-16 13:02:04 +08:00
Louis Lam
0f3ab7b1d8 [test] increase the timeout for reset-password 2021-10-16 12:56:33 +08:00
januridp
d94fbede32 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:39:32 +07:00
januridp
76e619c066 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:36:07 +07:00
januridp
4e4f94ab98 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:29:51 +07:00
januridp
ed3a558397 Fix(Language): Bahasa Indonesia (Indonesian) 2021-10-16 07:15:01 +07:00
Louis Lam
a419aa527f Merge remote-tracking branch 'origin/master' 2021-10-16 01:33:56 +08:00
Louis Lam
4d26825cbe [test] reset-password 2021-10-16 01:33:44 +08:00
Louis Lam
7276f34d90 fix reset-password 2021-10-16 00:57:26 +08:00
Louis Lam
e1eeb44e7f Update SECURITY.md 2021-10-15 19:44:29 +08:00
Louis Lam
f4b8da0a5c Merge branch 'feature/add-support-for-method-body-and-headers' 2021-10-15 19:02:49 +08:00
Louis Lam
4178983df3 Merge remote-tracking branch 'origin/master' 2021-10-15 19:01:04 +08:00
Louis Lam
7ac0ab2e34 [http options] beautify the json format when clicked the save button 2021-10-15 18:57:27 +08:00
Louis Lam
cd211a6be7 [http options] fine tune 2021-10-15 18:36:40 +08:00
Louis Lam
4e71ab7406 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-15 16:07:05 +08:00
Louis Lam
76c68071f1 Merge pull request #709 from iooner/patch-1
Update fr-FR.js
2021-10-15 15:16:22 +08:00
iooner
8242a1586d Update fr-FR.js 2021-10-14 22:23:01 +02:00
Louis Lam
c593a962c2 Merge pull request #627 from NixNotCastey/smtp-subject
Add support for custom subject in emails
2021-10-15 00:54:31 +08:00
Louis Lam
c9b4d2ae2a Merge pull request #698 from erktime/master
Add monitor name context to Slack fallback text.
2021-10-14 23:31:48 +08:00
Louis Lam
37105d720b Merge pull request #706 from firattemel/master
Update tr-TR.js
2021-10-14 22:40:45 +08:00
Louis Lam
3b74b727f2 [Push Type] fix missing important flag and missing up notification 2021-10-14 22:32:15 +08:00
firattemel
2f0119bc3f Update tr-TR.js 2021-10-14 17:29:13 +03:00
Louis Lam
a7d2a34dae fix ping bug 2021-10-14 18:48:40 +08:00
Louis Lam
60acb91fc8 Merge pull request #687 from xjoker/master
Add new notification `Aliyun Sms` and `DingDing`
2021-10-14 17:02:23 +08:00
Louis Lam
f51156f18e run eslint for #687 2021-10-14 16:24:03 +08:00
Louis Lam
8338881927 [SMTP] change {{HOSTNAME}} to {{HOSTNAME_OR_URL}}, support for http montior type, some UI improvements 2021-10-14 16:07:25 +08:00
Louis Lam
674b387c95 Merge branch 'master' into smtp-subject 2021-10-14 14:59:54 +08:00
Louis Lam
5ff9a64e5e [Push Type] Fix missing duration calculation (#685) 2021-10-14 14:42:34 +08:00
Louis Lam
4bee57ea7f Merge remote-tracking branch 'giacomo892/patch-1'
# Conflicts:
#	server/ping-lite.js
2021-10-14 14:10:51 +08:00
Louis Lam
f75c9e4f0c add UPTIME_KUMA_HOST, UPTIME_KUMA_PORT and special handling for FreeBSD 2021-10-14 14:09:16 +08:00
xJoker
8ab4788f80 Update src/components/notifications/index.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 09:33:36 +08:00
xJoker
4e4ab0577e Update src/components/notifications/index.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 09:33:31 +08:00
xJoker
6e04ec436e Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:45 +08:00
xJoker
2d471a5e84 Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:33 +08:00
xJoker
cae194f58f Update server/notification-providers/dingding.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-14 07:34:24 +08:00
Aaron Erkenswick
655ccc86b9 Add monitor name context to Slack fallback text.
The text block of a slack notification payload is used for mobile
devices and plain text previews. This change allows slack users to see
the name of the failing service without having to open up Slack to read
the entire message.
2021-10-13 11:47:23 -07:00
Louis Lam
e2dbacb383 Fix encoding problem of ping result for non-English Windows 2021-10-14 00:22:49 +08:00
Lukas
89b34b5748 Use double curly brackets and sanity check for customSubject 2021-10-13 18:05:18 +02:00
Louis Lam
9b05e86c25 set newLine to LF for ts compiler 2021-10-13 22:31:36 +08:00
Louis Lam
2ff7c4de5d [test] genSecret 2021-10-13 22:16:46 +08:00
Louis Lam
178e5cd2c0 Merge pull request #689 from hrtkpf/master
fix translations (de-DE and en)
2021-10-13 17:47:20 +08:00
hrtkpf
84507268ad fix translations (de-DE and en) 2021-10-13 11:13:51 +02:00
wuwenjing
843992c410 Add DingDing notification 2021-10-13 16:13:46 +08:00
zsxeee
33f773fcd0 Move param out of the translation file 2021-10-13 15:36:07 +08:00
zsxeee
26841a64f0 Update zh-CN.js 2021-10-13 15:10:59 +08:00
wuwenjing
57a76e6129 remove alicloud/pop-core keep simple 2021-10-13 14:41:59 +08:00
giacomo892
3fe3450533 Prioritize port passed from args
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-13 08:29:55 +02:00
Lukas
330cd6e058 Minor rehabilitanty impedyment
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-13 07:32:09 +02:00
wuwenjing
a2f2253221 Add aliyun sms notification 2021-10-13 11:55:01 +08:00
Louis Lam
1e5ce92917 Merge pull request #678 from wellart/master
add indonesian language
2021-10-13 11:23:21 +08:00
Louis Lam
0d7c2960b0 Merge pull request #683 from Saibamen/fix_markdown
Fix some of markdownlint warnings
2021-10-13 11:01:37 +08:00
Louis Lam
82343de972 Merge pull request #682 from Saibamen/patch-1
Fix build and tests
2021-10-13 10:54:30 +08:00
Adam Stachowicz
521d57c483 Fix some of markdownlint warnings 2021-10-13 02:01:34 +02:00
Adam Stachowicz
281671b938 Fix build
Add `cross-env` deleted in 11c3c636e0
2021-10-13 01:10:17 +02:00
Lukas
30d8aadf12 Slightly refactor 2021-10-12 23:24:34 +02:00
KangAlleW
2939bd4138 fix locale 2021-10-13 03:12:26 +07:00
KangAlleW
dcf15c3eb7 edit i18n.js 2021-10-13 02:58:09 +07:00
KangAlleW
7c9ed98408 rename id.js to id-ID.js 2021-10-13 02:57:36 +07:00
KangAlleW
4b9f0a3fe6 add indonesian language and edit settings.vue 2021-10-13 02:53:46 +07:00
KangAlleW
5b67fec084 add indonesian language 2021-10-13 02:46:11 +07:00
Louis Lam
407581ee07 move jest config files to config dir 2021-10-13 02:53:59 +08:00
Louis Lam
11c3c636e0 move dockerfile, docker-compose.yml to docker folder 2021-10-13 02:32:02 +08:00
Louis Lam
911d4ea37b move vite.config.js to config folder 2021-10-13 02:27:25 +08:00
Louis Lam
4039c6549e Merge remote-tracking branch 'origin/master' 2021-10-13 02:16:03 +08:00
giacomo892
d733ec018e Prioritize host arg
Otherwise launching the program with the --host argument does nothing
2021-10-12 19:37:58 +02:00
Nelson Chan
03b07730d3 Fix: Increase default kept period 2021-10-12 23:28:21 +08:00
Louis Lam
dbc87d8ab3 Merge pull request #670 from dhfhfk/master
Update ko-KR.js
2021-10-12 22:27:27 +08:00
dhfhfk
05b691d4c9 Fix typo 2021-10-12 20:39:15 +09:00
Louis Lam
029d6412da Merge pull request #669 from pemassi/patch-2
Update ko-KR.js
2021-10-12 18:27:11 +08:00
Louis Lam
9f12f95cce Merge pull request #666 from Rohlik/patch-1
Fix length
2021-10-12 17:32:20 +08:00
Kyungyoon Kim
c1112a32df Update ko-KR.js 2021-10-12 18:19:44 +09:00
Kyungyoon Kim
efc78acfeb Update ko-KR.js 2021-10-12 18:17:13 +09:00
zsxeee
9e01959d15 Update zh-CN.js 2021-10-12 16:30:39 +08:00
zsxeee
3fe91c52cb Fix i18n
Missing webhook json description
Ajust Telegram context-based sentence, (also changed translated language files)
Missing primary base url label
Wrong PromoSMS i18n
Missing Octopush legacy hint
Missing Matrix i18n
Missing push url i18n
2021-10-12 16:29:18 +08:00
Tomas Rohrer
5269dcec60 Fix length
In fact, it is 10 min demo :)
2021-10-12 10:21:03 +02:00
zsxeee
a2cc7d1db9 Avoid directory not found error 2021-10-12 15:10:32 +08:00
Louis Lam
18c5a16783 Update README.md 2021-10-12 11:15:02 +08:00
Andreas Brett
2538bd04ce notp verification defaults 2021-10-11 20:18:40 +02:00
Louis Lam
b7528b9a4e Merge pull request #657 from thomasleveil/patch-2
[i18n] minor fixes to french translations
2021-10-12 02:17:53 +08:00
Thomas LÉVEIL
9c058054b9 [i18n] minor update to french translations 2021-10-11 19:55:02 +02:00
Louis Lam
c6683e2a9b Merge pull request #648 from atlochowski/patch-1
Update pl.js
2021-10-12 00:04:33 +08:00
Louis Lam
b558708be2 Merge pull request #650 from xjoker/master
Add Feishu as notification provider
2021-10-12 00:02:29 +08:00
Louis Lam
fd0dd2d284 Merge pull request #652 from GhostSlayer/patch-1
Create PM2 Config file
2021-10-11 23:50:37 +08:00
xJoker
1bc77a06e5 Update server/notification-providers/feishu.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-11 20:38:32 +08:00
xJoker
69c623ac2b Update server/notification-providers/feishu.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-11 20:38:19 +08:00
Atlochowski
69ffee55dd Update pl.js 2021-10-11 14:33:00 +02:00
Slayer
d769c4426c Create PM2 Config file
I don't know if this is a good idea, but users that prefer to use PM2 instead, they can run `pm2 start` instead of `pm2 start server/server.js --name uptime-kuma` with this configuration
2021-10-11 15:22:52 +03:00
Atlochowski
ebf0671fef Update pl.js 2021-10-11 13:45:25 +02:00
Louis Lam
97af09fd50 Update README.md 2021-10-11 19:41:56 +08:00
Louis Lam
5b19e3f025 Update README.md 2021-10-11 19:40:51 +08:00
wuwenjing
ce2df137e6 change text to using variable msg 2021-10-11 17:53:13 +08:00
wuwenjing
6d9b71c054 Add Feishu notification 2021-10-11 17:20:09 +08:00
Atlochowski
a433de74e6 Update pl.js
small fix
2021-10-11 09:24:45 +02:00
LouisLam
8e3f43d60b Merge remote-tracking branch 'origin/master' 2021-10-11 13:28:42 +08:00
Louis Lam
2a1fd93444 Merge pull request #643 from andreasbrett/patch-3
translation fixes (german)
2021-10-11 12:55:25 +08:00
Andreas Brett
e223e826a3 linting 2021-10-11 01:02:54 +02:00
Andreas Brett
503d1f0a91 translation fixes 2021-10-10 23:20:56 +02:00
Andreas Brett
b5b391c73b avoid default values for token verification
override default values: window=1, window size=30 (see https://github.com/louislam/uptime-kuma/issues/640)
2021-10-10 22:13:18 +02:00
Louis Lam
ad0cde6554 Merge pull request #633 from dhfhfk/master
Update ko-KR
2021-10-11 01:04:32 +08:00
Louis Lam
25d18f0da3 Merge pull request #636 from DanielRTRD/add-norwegian-lang
Add Norwegian Language
2021-10-11 01:03:55 +08:00
RisedSky
e9445bb2e3 Merge pull request #1 from RisedSky/RisedSky-patch-FR-Lang
Update fr-FR.js (again)
2021-10-10 19:03:22 +02:00
RisedSky
ecc25ba596 Update fr-FR.js
Fixed wrong string
Also, "Primary Base URL" is missing from english file
2021-10-10 19:02:35 +02:00
LouisLam
e5286b0973 eslint for ko-KR.js 2021-10-11 00:52:46 +08:00
LouisLam
4e94cb9aad fix upload dist path 2021-10-11 00:51:18 +08:00
Daniel S. Billing
037fdd73a3 Norsk 2021-10-10 18:50:18 +02:00
Daniel S. Billing
bb9a936658 Translated some of the notifications services 2021-10-10 18:49:03 +02:00
Bert Verhelst
5445c2a2ff fix(monitor): revert unintentional change to comment 2021-10-10 18:41:29 +02:00
Bert Verhelst
dc08510e72 Merge remote-tracking branch 'origin/master' into feature/add-support-for-method-body-and-headers 2021-10-10 18:40:53 +02:00
Daniel S. Billing
62805014df Update Settings.vue 2021-10-10 18:38:19 +02:00
Daniel S. Billing
c79e80442a Update i18n.js 2021-10-10 18:38:15 +02:00
Daniel S. Billing
f0ff96afd9 Create nb-NO.js 2021-10-10 18:31:17 +02:00
Louis Lam
8083368a81 Merge pull request #634 from RisedSky/patch-1
Update fr-FR.js
2021-10-10 23:58:09 +08:00
RisedSky
afb75e07d5 Update fr-FR.js
Fixed string (Device => Appareil in French)
2021-10-10 17:37:05 +02:00
RisedSky
efd3822930 Update fr-FR.js
All the lines corresponds to the English version (sorted correctly)
Plus, updated all the translated strings
2021-10-10 17:33:02 +02:00
dhfhfk
2adac64c83 Update ko-KR.js 2021-10-11 00:02:37 +09:00
Louis
cee225bcb2 no idea why npm prune --production suddenly not working, switch to npm ci --production 2021-10-10 17:55:31 +08:00
Louis
8958c21736 Merge remote-tracking branch 'origin/master' 2021-10-10 17:41:54 +08:00
Louis
2286f78f57 update to 1.8.0 2021-10-10 16:37:53 +08:00
Louis
d9eab90a69 Merge branch 'no-need-build'
# Conflicts:
#	.dockerignore
#	.gitignore
2021-10-10 16:34:13 +08:00
Louis Lam
4ba2025451 minor 2021-10-10 14:40:19 +08:00
LouisLam
272d4bde45 find promossms language key typo 2021-10-10 13:19:10 +08:00
LouisLam
0550ceb6d4 Merge remote-tracking branch 'origin/master' 2021-10-10 13:09:52 +08:00
LouisLam
82131f4dd2 merge conflict 2021-10-10 13:09:30 +08:00
Louis Lam
0c88e4b2f6 Merge pull request #629 from NixNotCastey/pl-update
Polish translation update
2021-10-10 12:45:26 +08:00
Louis Lam
5c6230da58 Merge pull request #630 from xinac721/master
Update Simplified Chinese Language(更新简体中文语言)
2021-10-10 12:43:17 +08:00
Louis Lam
d6753b8833 Update SECURITY.md 2021-10-10 12:20:29 +08:00
Louis Lam
93a021d027 Update SECURITY.md 2021-10-10 12:20:15 +08:00
新逸Cary
54f864a1bb Update Simplified Chinese Language(更新简体中文语言) 2021-10-10 10:04:07 +08:00
Lukas
d6f7be9112 Translated all missing texts and updated few of previously translated. 2021-10-10 00:38:19 +02:00
Bert Verhelst
5137c80c07 fix(monitor): handle empty headers 2021-10-09 21:51:24 +02:00
Lukas
792f3c7c5c Add support for values of Name, Hostname and Status 2021-10-09 21:48:28 +02:00
Bert Verhelst
8a739af5ad Merge remote-tracking branch 'origin/master' into feature/add-support-for-method-body-and-headers 2021-10-09 21:44:22 +02:00
Lukas
edb75808d8 Merge branch 'louislam:master' into smtp-subject 2021-10-09 20:37:12 +02:00
LouisLam
56ae6f6117 fix demoMode export 2021-10-10 02:36:20 +08:00
Lukas
5e3ea3293c Very basic email subject customization 2021-10-09 20:32:45 +02:00
LouisLam
5c89562650 not allow lower than 20s for demo mode 2021-10-10 02:23:27 +08:00
Louis Lam
1f7f20526f Merge pull request #620 from MrEddX/bulgarian
Update bg-BG.js
2021-10-10 01:12:38 +08:00
Louis Lam
0f5b437015 Merge pull request #622 from robinschneider/patch-1
Fixed spelling for german language support
2021-10-10 00:25:04 +08:00
Nelson Chan
ac80631bcd Fix: Run clear data at specific time 2021-10-10 00:16:29 +08:00
Nelson Chan
8caf47988c Fix: Allow setting settings type 2021-10-10 00:16:13 +08:00
Robin Schneider
6fe014fa5e Fixed spelling for german language support
Fixed spelling for german language support, hacktoberfest-accepted label woul be appreciated.
2021-10-09 18:05:52 +02:00
Nelson Chan
6cf2eb036d Fix: Improve settings layout and wording 2021-10-09 23:51:05 +08:00
MrEddX
9d7def93a5 Update bg-BG.js 2021-10-09 18:44:12 +03:00
Nelson Chan
dca5a59dbc Feat: Implement data clearing logic & frontend 2021-10-09 23:33:47 +08:00
Nelson Chan
656a4d6270 WIP: Enable background jobs
WIP: Remove better-sqlite3
2021-10-09 21:46:59 +08:00
Louis Lam
6dd0e082b4 Merge pull request #614 from pemassi/patch-1
More translate to Korean
2021-10-09 19:43:49 +08:00
Kyungyoon Kim
2e95e2016d Translate to Korean 2021-10-09 05:25:49 -06:00
Louis Lam
4169127143 Update FUNDING.yml 2021-10-09 19:17:01 +08:00
LouisLam
cac0a46bac fix error if tls info object is in old format 2021-10-09 19:08:38 +08:00
Bert Verhelst
d71d27220b fix(edit-monitor): store headers as JSON 2021-10-09 12:42:32 +02:00
Bert Verhelst
fba4f86552 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-09 12:35:08 +02:00
LouisLam
e023ddf1c2 Merge remote-tracking branch 'origin/master' 2021-10-09 17:45:20 +08:00
LouisLam
23a2d33f8c [backup] restore pushToken 2021-10-09 17:45:05 +08:00
Bert Verhelst
b8093e909b fix(edit-monitor): fix minification of translations containing { } 2021-10-09 11:38:12 +02:00
Bert Verhelst
c3c273f9df fix(edit-monitor): fix regex to allow a single header 2021-10-09 11:20:33 +02:00
Bert Verhelst
daab2a05f5 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-09 11:13:16 +02:00
LouisLam
a15e9077fc [status page] clear cache if it is an important beat 2021-10-09 17:04:51 +08:00
Louis Lam
8431a25a3a Update README.md 2021-10-09 16:54:26 +08:00
Louis Lam
e8cc7ff771 Merge pull request #609 from dpatrongomez/patch-1
Update es-ES.js
2021-10-09 14:02:06 +08:00
新逸Cary
5d617012a3 Merge pull request #5 from louislam/master
update
2021-10-09 09:28:53 +08:00
Daniel Patrón Gómez
d7eac1a413 Update es-ES.js 2021-10-08 23:48:37 +02:00
Louis Lam
c589bd836d [test] change to npm install for pull requests 2021-10-08 22:39:35 +08:00
LouisLam
5ce09953e2 use Segoe UI font for Windows among all languages 2021-10-08 20:15:54 +08:00
LouisLam
fc8d1e78b6 [push type] hide upside down mode, apply primary base url 2021-10-08 20:03:52 +08:00
LouisLam
3f26327f95 Merge remote-tracking branch 'origin/master' 2021-10-08 18:29:01 +08:00
Louis Lam
efb3f2b19c Merge pull request #605 from NixNotCastey/promosms
Add PromoSMS as notification provider
2021-10-08 18:19:06 +08:00
Lukas
db791c880a Don't use then with await. 2021-10-08 11:49:12 +02:00
新逸Cary
cdda182311 Merge pull request #4 from louislam/master
update
2021-10-08 15:51:06 +08:00
LouisLam
a1c2a1bc52 [test] auto test for node lts only 2021-10-08 15:34:19 +08:00
Louis Lam
432b156fce Merge pull request #595 from kvpt/status-page-default-locale
Use browser language as default language
2021-10-08 15:33:38 +08:00
LouisLam
01812cc446 [test] add test for i18n currentLocale 2021-10-08 15:11:50 +08:00
Lukas
dfd63386ba Make PromoSMS actually working
Make PromoSMS actually working and inform on success only when API return 0
2021-10-08 09:11:13 +02:00
LouisLam
11abc1f1e0 [test] add test for i18n currentLocale 2021-10-08 13:35:04 +08:00
LouisLam
288e87bb3d Merge branch 'master' into status-page-default-locale 2021-10-08 12:12:29 +08:00
Louis Lam
79ee0e1ef4 Update ask-for-help.md 2021-10-08 11:39:31 +08:00
LouisLam
8ae79ab9bf improve #560 2021-10-08 10:51:03 +08:00
Lukas
12b5489eb5 PromoSMS as Notification Provider
Add PromoSMS (Polish SMS Gateway) as new notification provider
2021-10-07 21:56:32 +02:00
LouisLam
ddad2dcb4a Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/languages/en.js
2021-10-08 01:40:34 +08:00
LouisLam
e96121f69a fix merging mistake 2021-10-08 01:39:14 +08:00
LouisLam
5b4af550fb Merge branch 'master' into DeeJayPee_master 2021-10-08 01:27:06 +08:00
Louis Lam
dd183e2ec2 Merge pull request #567 from Empty2k12/feature/matrix-notifications
Matrix Notifications
2021-10-08 01:21:52 +08:00
LouisLam
0fcb310b97 Merge remote-tracking branch 'Empty2k12/feature/matrix-notifications' into feature/matrix-notifications
# Conflicts:
#	src/languages/en.js
2021-10-08 01:13:09 +08:00
LouisLam
3a0143ac46 [matrix] use encodeURIComponent to handle the url encode 2021-10-08 01:11:33 +08:00
LouisLam
2ce5c28ed4 Merge branch 'master' into feature/matrix-notifications
# Conflicts:
#	src/languages/en.js
2021-10-08 00:59:39 +08:00
Bert Verhelst
ec4b7e4064 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-07 18:22:59 +02:00
Kevin Petit
5b758a4e98 Better locale default for status page. 2021-10-07 18:07:24 +02:00
LouisLam
7907c07034 Merge remote-tracking branch 'origin/master' 2021-10-07 23:07:16 +08:00
LouisLam
adfe640f42 show fewer beat on mobile 2021-10-07 22:53:13 +08:00
Louis Lam
469f7a3e32 Update README.md 2021-10-07 22:33:39 +08:00
LouisLam
9f1e7b0a88 Revert "fix(heartbeat-bar): cleanup css styling and minor syntax issues"
This reverts commit 3d6c8b7f
2021-10-07 21:47:11 +08:00
LouisLam
cdf81a36d3 fix broken animation caused by #521 2021-10-07 21:42:36 +08:00
LouisLam
bf4ac0cf17 fix dockerfile issue on arm 2021-10-07 21:24:10 +08:00
LouisLam
c0846124c2 update vite to 1.6.4, since it fixed the issue 2021-10-07 21:23:28 +08:00
LouisLam
3d30ed3d3b update security policy 2021-10-07 20:16:15 +08:00
LouisLam
deec15c09e [test] better job name 2021-10-07 20:14:24 +08:00
LouisLam
3423cb5d8e [test] try to auto test Windows and MacOS 2021-10-07 20:05:12 +08:00
LouisLam
20af179a82 [test] try to auto test Windows and MacOS 2021-10-07 20:01:33 +08:00
LouisLam
3c60800eab [test] github action please ok🙏🏻🙏🏻🙏🏻 2021-10-07 18:47:44 +08:00
LouisLam
34586d7b8f [test] github action please ok 2021-10-07 18:10:35 +08:00
LouisLam
2c19aef4dc minor 2021-10-07 18:06:43 +08:00
LouisLam
67a623be18 e2e testing, it's hard 2021-10-07 17:48:13 +08:00
LouisLam
e5f6d7f047 slack and rocket.chat use the primary base url
env var to show time logger
2021-10-07 17:39:58 +08:00
LouisLam
b69550f5b9 Improve the test 2021-10-07 17:01:17 +08:00
LouisLam
d08a71ab49 Set primary base url in settings page 2021-10-07 16:30:16 +08:00
LouisLam
ed67803af8 improve minor style 2021-10-07 16:10:21 +08:00
Louis Lam
a6c839709c Merge pull request #589 from MrEddX/bulgarian
Updated Bulgarian language
2021-10-07 15:15:54 +08:00
Louis Lam
5eb3c6b194 Merge branch 'master' into bulgarian 2021-10-07 15:12:53 +08:00
LouisLam
8233f3b875 try to standardize the language name list 2021-10-07 15:06:16 +08:00
Bert Verhelst
8be4bf0e16 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-07 08:56:29 +02:00
LouisLam
cccf393ee5 update zh-HK.js 2021-10-07 14:55:32 +08:00
MrEddX
9f5bf37a96 Update Settings.vue 2021-10-07 09:37:13 +03:00
LouisLam
18e4702375 ignore .env 2021-10-07 14:34:30 +08:00
MrEddX
1eb3f63a82 Update bg-BG.js 2021-10-07 09:27:05 +03:00
Louis Lam
8c63536eb8 Merge pull request #451 from zsxeee/notification_form_i18n
Notification form i18n
2021-10-07 14:13:08 +08:00
LouisLam
3e1788983e Merge remote-tracking branch 'origin/master' 2021-10-07 14:10:15 +08:00
LouisLam
a8badb027d update modded node-sqlite3 to 6.0.0 2021-10-07 14:09:50 +08:00
MrEddX
0c6b434d79 Moved Bulgarian to the Cyrillic family languages 2021-10-07 08:00:53 +03:00
Louis Lam
b5bd92ce78 Merge pull request #578 from jtagcat/et5
l10n: update et
2021-10-07 11:31:02 +08:00
Louis Lam
3f80cf5e54 Merge pull request #581 from chakflying/patch-2
Fix: Allow underscore in hostname
2021-10-07 11:30:33 +08:00
Bert Verhelst
162ef04c41 Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-06 21:56:28 +02:00
Nelson Chan
a87595a849 Fix: Allow underscore in hostname 2021-10-07 03:29:42 +08:00
jtagcat
7626e1f2e4 l10n: update et 2021-10-06 21:02:34 +03:00
zsxeee
7f1edb49bc Fix i18n
Upgrade vue-i18n to 9.1.9.
Fix wrong tag name.
2021-10-07 00:04:13 +08:00
Gero Gerke
d184733af9 update text 2021-10-06 13:44:36 +02:00
Gero Gerke
704d63b49f Merge branch 'master' into feature/matrix-notifications 2021-10-06 13:36:28 +02:00
zsxeee
7002a778f0 Rollback vue-i18n version to 9.1.7 2021-10-06 19:12:23 +08:00
zsxeee
54d2fbcc02 Fix i18n
Prevent use esm-bundler build vue-i18n
Escape keyword:  '@'
2021-10-06 18:21:31 +08:00
Gero Gerke
fbd4d54812 Update src/components/notifications/Matrix.vue
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-06 10:55:21 +02:00
LouisLam
c8706b9aa1 Merge branch 'master' into notification_form_i18n
# Conflicts:
#	src/components/notifications/SMTP.vue
#	src/languages/en.js
2021-10-06 16:48:14 +08:00
Louis Lam
54d7830813 Update CONTRIBUTING.md 2021-10-06 15:36:45 +08:00
LouisLam
fef26f3d5e Merge remote-tracking branch 'origin/master' 2021-10-06 15:35:39 +08:00
LouisLam
cb263f2a08 [test] wait for #language 2021-10-06 15:34:57 +08:00
Louis Lam
52102d72a0 Merge pull request #572 from Brainpitcher/patch-1
Update ru-RU.js
2021-10-06 15:28:29 +08:00
LouisLam
2b00e59c7a [test] update 2021-10-06 15:25:00 +08:00
Louis Lam
8ea7a693a1 Merge pull request #499 from Saibamen/fix_pl_i18n
Fix Polish language, add missing `Status Page` i18n
2021-10-06 13:29:02 +08:00
LouisLam
4ded0c073a [test] fix timeout issue 2021-10-06 13:26:43 +08:00
Brainpitcher
7a8b6a03e0 Update ru-RU.js
"Cert Exp.": "Сертификат истекает" now it sounds in russian
2021-10-06 09:49:44 +05:00
Gero Gerke
6bebc623f9 UI polish 2021-10-05 21:59:58 +02:00
Gero Gerke
34b86352f2 remove double spaces 2021-10-05 21:40:59 +02:00
Gero Gerke
99e8a33118 escape room characters 2021-10-05 21:36:01 +02:00
Gero Gerke
d7cc585101 Update server/notification-providers/matrix.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-05 20:42:44 +02:00
Gero Gerke
8c357a04bf Update src/components/notifications/index.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-05 20:42:36 +02:00
DeeJayPee
044c78aa2d Typo \o/ 2021-10-05 20:16:16 +02:00
DeeJayPee
49eaa1a166 Typo fix 2021-10-05 20:13:24 +02:00
DeeJayPee
215cc07907 Rename versions 2021-10-05 20:12:36 +02:00
LouisLam
22227be408 update version in wiki too 2021-10-06 02:06:59 +08:00
Gero Gerke
5decfb9fad Matrix Notifications 2021-10-05 20:03:56 +02:00
DeeJayPee
f9d7b99367 Add legacy version back on refactored master branch 2021-10-05 20:01:07 +02:00
DeeJayPee
359fe52c2e Merge branch 'louislam-master' 2021-10-05 19:59:20 +02:00
DeeJayPee
bc4db6c692 Merge branch 'master' of https://github.com/louislam/uptime-kuma into louislam-master 2021-10-05 19:57:27 +02:00
DeeJayPee
f14a798b2c Fix indentation + typo 2021-10-05 19:43:04 +02:00
Bert Verhelst
a0ffa42b42 fix(translations): add translations for method body and headers to dutch 2021-10-05 18:21:31 +02:00
Bert Verhelst
550825927c Merge branch 'master' into feature/add-support-for-method-body-and-headers 2021-10-05 18:19:07 +02:00
LouisLam
c1501742f5 test github secret 2021-10-05 21:15:30 +08:00
LouisLam
7c98fe603e test github secret 2021-10-05 21:11:11 +08:00
LouisLam
b7ae49c644 update github action 2021-10-05 20:42:15 +08:00
LouisLam
edad2caf8e return the correct exit code from jest 2021-10-05 20:40:40 +08:00
LouisLam
2bf6a04f81 fix preparing test 2021-10-05 20:37:32 +08:00
Louis Lam
f0670dde20 Update auto-test.yml 2021-10-05 20:33:47 +08:00
Louis Lam
73068763c0 Create auto-test.yml 2021-10-05 20:32:48 +08:00
LouisLam
73bf1216d1 [wip] more test 2021-10-05 20:27:43 +08:00
LouisLam
98436f91b5 Merge remote-tracking branch 'origin/master' 2021-10-05 19:14:41 +08:00
LouisLam
49720c709c improve the test with a single command only "npm test" 2021-10-05 19:13:57 +08:00
Louis Lam
842d359ad3 Update CONTRIBUTING.md 2021-10-05 18:17:54 +08:00
LouisLam
e71f5bf314 update dependencies 2021-10-05 17:53:17 +08:00
LouisLam
79e0c9e1f1 setup unit test for setup 2021-10-05 17:39:44 +08:00
LouisLam
e6ff957d9d Merge remote-tracking branch 'origin/master' 2021-10-05 16:12:44 +08:00
Louis Lam
865b721b79 Merge pull request #519 from chakflying/improve-certInfo
Feat: Improve Certificate Info Display
2021-10-05 16:09:08 +08:00
LouisLam
b5d987863d Merge remote-tracking branch 'origin/master' 2021-10-05 16:04:50 +08:00
Louis Lam
911690bea8 Merge pull request #521 from bertyhell/bugfix/heartbeat-bar-cleanup
fix(heartbeat-bar): cleanup css styling and minor syntax issues
2021-10-05 16:04:33 +08:00
LouisLam
d25603e629 update jest test 2021-10-05 16:03:35 +08:00
LouisLam
259bcf9426 [SMTP] "To Email" is not required if CC/BCC is set. (#461) 2021-10-05 15:57:13 +08:00
Bert Verhelst
afeb424dc0 fix(edit-monitor): add translations to en.js 2021-10-05 09:20:24 +02:00
Bert Verhelst
6b44116245 Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers 2021-10-05 08:54:40 +02:00
Louis Lam
aa478af286 Merge pull request #557 from Saibamen/patch-1
Fix typo in `CONTRIBUTING.md`
2021-10-05 12:54:29 +08:00
Adam Stachowicz
5f8d0faacd Update CONTRIBUTING.md 2021-10-05 02:44:50 +02:00
Adam Stachowicz
707e05c330 Fix after merge 2021-10-04 23:39:56 +02:00
Adam Stachowicz
4da63c5fb8 Revert silentTranslationWarn change 2021-10-04 23:33:52 +02:00
Adam Stachowicz
8ae64843fc run update-language-files 2021-10-04 23:32:16 +02:00
Adam Stachowicz
23e64b8efd Merge branch 'master' into fix_pl_i18n 2021-10-04 23:29:21 +02:00
Louis Lam
8c55a8bf98 Update CONTRIBUTING.md 2021-10-04 23:31:36 +08:00
Louis Lam
387a8919f9 Merge pull request #542 from csabibela/master
Hungarian translation
2021-10-04 21:14:12 +08:00
Louis Lam
a1edc23b1d Merge pull request #540 from jtagcat/et4
i10n: update estonian
2021-10-04 21:13:14 +08:00
Bert Verhelst
7ee89fab5c fix(edit-monitor): Make json capitalised
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-10-04 11:29:43 +02:00
Csábi Béla
980342546e Merge branch 'hu' 2021-10-03 23:38:13 +02:00
Csábi Béla
6b60dc9630 Initial Hungarian translation 2021-10-03 23:15:34 +02:00
Csábi Béla
8d22b43f24 Revert "Initial Hungarian translation"
This reverts commit dad58341c6.
2021-10-03 23:11:19 +02:00
Csábi Béla
dad58341c6 Initial Hungarian translation 2021-10-03 23:09:17 +02:00
jtagcat
275902be38 update estonian translation 2021-10-03 20:35:41 +03:00
jtagcat
38213585f3 update style for et translation
kuigi pingviini stiilijuhend
https://viki.pingviin.org/Stiilijuhend_tarkvara_t%C3%B5lkimiseks
on otseses konfliktis (ok:seiskamine;nok:seiska),
tundub nupudel nok ikkagi paremini

nupud on ikkagi käsk, mitte tegevus

muutus ühtlustab ka nuppude keele
2021-10-03 20:35:30 +03:00
LouisLam
37d1e50ff1 fix data path for test 2021-10-03 18:18:47 +08:00
LouisLam
a2a4c70cf5 setup jest-puppeteer 2021-10-03 18:16:55 +08:00
Louis Lam
446fc1af0b Update CONTRIBUTING.md 2021-10-03 16:04:16 +08:00
LouisLam
51acd107e3 Merge branch 'master' into notification_form_i18n 2021-10-03 15:28:33 +08:00
LouisLam
d3517e76c1 change to npm ci 2021-10-03 15:27:15 +08:00
Bert Verhelst
3f0b85e5a8 feat(http-requests): add support for methods, body and headers for http 2021-10-02 16:48:27 +02:00
LouisLam
2625cbe0d2 add script for downloading the dist files from github 2021-10-02 14:43:31 +08:00
LouisLam
c93f42794f upload prebuilt dist to github release 2021-10-02 01:48:15 +08:00
Nelson Chan
668fd58af3 Fix: Slightly improve validity styling 2021-10-01 22:43:09 +08:00
Nelson Chan
b7568e9caa Fix: Update Certificate Icon 2021-10-01 22:29:22 +08:00
Bert Verhelst
1c2adf8723 fix(monitor-list): increase padding to keep same monitor item height 2021-10-01 15:43:20 +02:00
Bert Verhelst
96129921e9 Merge remote-tracking branch 'origin/master' into bugfix/heartbeat-bar-cleanup 2021-10-01 15:40:48 +02:00
Louis Lam
2b1fe815f9 Merge pull request #522 from bertyhell/bugfix/improve-setup-styles
fix(setup): increase left padding input fields + avoid clipping
2021-10-01 21:38:18 +08:00
Louis Lam
fcf017d5c7 Merge pull request #523 from bertyhell/bugfix/hide-dashboard-and-settings-when-not-logged-in
fix(layout): hide dashboard and settings buttons when not logged in
2021-10-01 21:19:33 +08:00
Bert Verhelst
843830a38a fix(layout): hide dashboard and settings buttons when not logged in 2021-10-01 15:12:37 +02:00
Bert Verhelst
ee830621dd fix(login): fix the same padding issues for the login screen 2021-10-01 15:07:05 +02:00
Bert Verhelst
c2a560e2ed fix(setup): increase left padding input fields + avoid clipping 2021-10-01 14:57:31 +02:00
Nelson Chan
13bdfefa9d Feat: Improve Certificaet Info Display 2021-10-01 18:56:28 +08:00
Bert Verhelst
3d6c8b7f05 fix(heartbeat-bar): cleanup css styling and minor syntax issues 2021-10-01 12:49:49 +02:00
LouisLam
2aaed66b38 [docker: debian] reduce the image size dramatically! Compressed size from 240 MB~ to 100 MB~ 2021-10-01 18:25:21 +08:00
LouisLam
7b4c70860c split the base images in order to prevent recompile the base part 2021-10-01 17:28:00 +08:00
LouisLam
6513c3e75f allow update minor version only, to prevent the breaking change in the future like vite 2.6 2021-10-01 16:48:23 +08:00
LouisLam
7fa1cb83af [push type] add ping parameter 2021-10-01 16:43:11 +08:00
Louis Lam
9d079eeec0 Merge pull request #487 from cmandesign/feature/fa-lang-rtl
Feature/fa lang rtl
2021-10-01 16:28:20 +08:00
LouisLam
21dd5ad3dd Merge remote-tracking branch 'origin/master' 2021-10-01 15:48:04 +08:00
LouisLam
3394e1f148 fix undefined callback 2021-10-01 15:47:51 +08:00
Louis Lam
ee22406301 Update README.md 2021-10-01 02:38:41 +08:00
LouisLam
8d5eaaf8a7 minor 2021-10-01 00:26:27 +08:00
LouisLam
b246c8e0f2 Fix 2fa for iOS Google authenticator (#486) 2021-10-01 00:23:18 +08:00
LouisLam
1ed4ac9494 add Push-based monitoring (#279) 2021-10-01 00:09:43 +08:00
zsxeee
0f2059cde0 Use named slot translation when has multi-slot 2021-09-30 19:48:24 +08:00
zsxeee
138ddf5608 Move attribute tag to start of tag 2021-09-30 19:22:17 +08:00
zsxeee
dcd68213b1 Merge remote-tracking branch 'upstream/master' into notification_form_i18n
# Conflicts:
#	src/languages/en.js
2021-09-30 19:16:14 +08:00
Louis Lam
9e95d568c2 Merge pull request #501 from Saibamen/incident_use_local_timezone
[status-page] Display created and updated time in local timezone. Fixes #491
2021-09-30 18:13:55 +08:00
Louis Lam
fa3da819f8 Merge pull request #512 from chakflying/reduce-bundle-size
Build: Reduce client bundle size with more async components
2021-09-30 18:04:27 +08:00
Louis Lam
9e1f1f006b Merge pull request #511 from chakflying/fix-multiselect-css
Chore: Move multiselect css to its own file
2021-09-30 17:56:58 +08:00
Louis Lam
55cb497301 Merge pull request #427 from zsxeee/notification_component
chore(NotificationDialog): Convert notification form to separate component
2021-09-30 17:53:36 +08:00
LouisLam
8d8d5987e7 update to 1.7.3 2021-09-30 14:44:37 +08:00
LouisLam
1fa90bffaa freeze vite version to 2.5.* (2.6.* is broken when build the project) 2021-09-30 14:43:29 +08:00
LouisLam
67f221d3c7 update to 1.7.2 2021-09-30 13:04:49 +08:00
Nelson Chan
05f28fecb2 Build: Use async component for status and settings 2021-09-30 00:16:15 +08:00
Nelson Chan
ba4a4aaf1c Chore: Move multiselect css to own file 2021-09-30 00:08:37 +08:00
LouisLam
6eceb4c744 Merge remote-tracking branch 'origin/master' 2021-09-29 17:20:58 +08:00
LouisLam
3e4154dfb5 Fix retry interval affected bug 2021-09-29 17:20:35 +08:00
Louis Lam
fbc8828ddc Merge pull request #464 from chakflying/fix-delete-monitor
Fix: clear important beats after deleteMonitor
2021-09-29 15:39:57 +08:00
Soroosh
f2c7308c96 Fixed Change Language Direction In Setting Page 2021-09-29 10:44:34 +03:30
LouisLam
3677aa639f update to 1.7.1 2021-09-29 14:26:30 +08:00
LouisLam
aaddfa1786 update package-lock.json 2021-09-29 13:30:28 +08:00
LouisLam
6d65d248f4 Merge branch 'bulgarian'
# Conflicts:
#	src/languages/da-DK.js
#	src/languages/fr-FR.js
#	src/languages/it-IT.js
#	src/languages/pl.js
#	src/languages/ru-RU.js
#	src/languages/zh-CN.js
2021-09-29 13:28:43 +08:00
LouisLam
87a4748b40 check back to root user for Debian, until problem has been found (#488) 2021-09-29 12:40:28 +08:00
Louis Lam
182bdf13a7 Merge pull request #500 from Saibamen/remove_timezone_console_log
Remove "Skip Timezone" console log from frontend
2021-09-29 12:16:51 +08:00
MrEddX
0f66e5cfc5 Added Bulgarian Language 2021-09-28 23:21:59 +03:00
Louis Lam
fe5ae46013 Merge pull request #480 from gaby/init-support
Add dumb-init to avoid zombie processes
2021-09-28 21:58:26 +08:00
Denis Freund
efbadd0737 only allow ip address for hostname when monitor type is steam 2021-09-28 13:38:46 +02:00
Adam Stachowicz
f9d633e02b Display created and updated time in local timezone. Fixes #491 2021-09-28 08:07:42 +02:00
Adam Stachowicz
756f317f82 Remove "Skip Timezone" console log from frontend 2021-09-28 07:29:32 +02:00
Adam Stachowicz
fa9d26416c silentTranslationWarn if not development 2021-09-28 07:02:19 +02:00
Adam Stachowicz
58aa83331e Fix Polish language, add missing Status Page i18n 2021-09-28 06:53:23 +02:00
Louis Lam
cc9fe26584 Merge pull request #498 from kry008/patch-2
Update pl.js
2021-09-28 11:59:25 +08:00
kry008
99818aa370 Update pl.js
Aby było że to serwer sprawdza, a nie użytkownik ma.
Dokończenie tłumaczenia
2021-09-27 21:58:18 +02:00
Louis Lam
682265fe9c Merge pull request #481 from DX37/translation-ru
fix some ru-RU lines and add new ones
2021-09-28 00:13:37 +08:00
Louis Lam
4406e51ab6 Merge pull request #496 from bertyhell/bugfix/remove-zero-width-spaces
fix(nl-NL): remove ZWSP from translation
2021-09-28 00:13:16 +08:00
Louis Lam
c3d5be5a5e Merge pull request #493 from iomataani/master
updated translation
2021-09-28 00:08:12 +08:00
Louis Lam
dfe12c99c1 Merge pull request #490 from Minvinea/master
Update fr-FR.js
2021-09-28 00:07:58 +08:00
Louis Lam
bb69851160 Merge pull request #485 from Lrss/patch-1
Update Danish translation
2021-09-28 00:07:44 +08:00
Louis Lam
6c9323351d Merge pull request #484 from xinac721/master
Update Simplified Chinese Language(更新简体中文语言)
2021-09-28 00:06:57 +08:00
Bert Verhelst
8e9fa20e57 fix(nl-NL): remove ZWSP from translation 2021-09-27 17:51:48 +02:00
Ioma Taani
da7c29f4b9 updated translation 2021-09-27 13:23:53 +02:00
Denis Freund
b67b4d5afd add steam gameserver for monitoring 2021-09-27 11:17:57 +02:00
Soroosh
9f06d54688 Remove font import and update font-family for lang fa 2021-09-27 09:37:43 +03:30
Soroosh
1448de7b19 Update some translations 2021-09-27 00:32:51 +03:30
Minvinea
e545d48583 Update fr-FR.js
Update line 112 to 181
2021-09-26 21:39:38 +02:00
Soroosh
47749ca58d Replace some hardcoded with translations 2021-09-26 19:56:43 +03:30
Soroosh
04b7a4a423 Merge Conflict Resolved
Farsi Lang Updated
Some hardcoded words has been replaced with translations
2021-09-26 19:56:25 +03:30
Soroosh
56d8f585fd Add postcss-rtlcss and postcss-scss 2021-09-26 18:54:51 +03:30
Soroosh
07c9d78829 Update Farsi translations 2021-09-26 18:52:53 +03:30
Soroosh
15c4a8fb02 Add RTL direction support to styles using postcss & rtlcss 2021-09-26 18:52:38 +03:30
Soroosh
f41e95921f Enable localization for pagination 2021-09-26 18:50:12 +03:30
Soroosh
647184e5d1 Update Title to use translation files 2021-09-26 18:49:39 +03:30
Soroosh
e60426bdcd Add Localization CSS & Persian Font 2021-09-26 18:49:03 +03:30
Lars Sørensen
8d84d8f891 Update da-DK.js with new strings 2021-09-26 15:28:46 +02:00
新逸Cary
74acc2cea7 Update Simplified Chinese Language(更新简体中文语言) 2021-09-26 14:31:16 +08:00
新逸Cary
ba4a4089eb Merge pull request #3 from louislam/master
update-21.09.26
2021-09-26 13:17:48 +08:00
Soroosh
251d42f1a6 Add localeDirection method to i18n.js
Add dir to html tag based on localeDirection
Add Farsi to the languages
2021-09-26 01:09:00 +03:30
DX37
122631c91b fix some ru-RU lines and add new ones 2021-09-26 01:44:00 +07:00
Juan Calderon-Perez
1e12f25c4b Add support for using init to start service 2021-09-25 12:26:30 -04:00
LouisLam
9e10296290 Merge remote-tracking branch 'origin/master' 2021-09-25 22:44:47 +08:00
LouisLam
87e213085f add /status, alias of /status-page (#471) 2021-09-25 22:44:29 +08:00
Louis Lam
d9038f1da2 Merge pull request #473 from Ponkhy/german-language
Added german translation for the status page
2021-09-24 23:26:30 +08:00
Louis Lam
3fb2d0ce68 Update README.md 2021-09-24 22:38:54 +08:00
Ponkhy
50a33e3b45 Added german translation for the status page 2021-09-24 15:56:35 +02:00
zsxeee
f24abac7fc Merge branch 'master' into notification_component 2021-09-24 20:33:29 +08:00
LouisLam
ce9a97a107 update to 1.7.0 2021-09-24 19:17:29 +08:00
LouisLam
0afa3a2c21 Merge branch '1.6.X'
# Conflicts:
#	server/database.js
#	server/server.js
2021-09-24 19:12:57 +08:00
LouisLam
51512b6f5f update dependencies 2021-09-24 19:00:06 +08:00
Louis Lam
3d79e841c9 Merge pull request #466 from chakflying/public-dashboard
[status-page] hide select if no monitors to add
2021-09-24 18:33:00 +08:00
Nelson Chan
d2af42e579 Fix: Add placeholder if no monitors to add 2021-09-24 16:51:53 +08:00
LouisLam
d6e3bd2282 Merge remote-tracking branch 'origin/master' 2021-09-24 16:01:18 +08:00
LouisLam
70fbe9a2d9 fix alpine nightly build 2021-09-24 16:00:08 +08:00
Louis Lam
c669e7eaba Merge pull request #469 from chakflying/patch-1
Fix: Fix importing tag if tag doesn't exist
2021-09-24 15:41:13 +08:00
LouisLam
9a9fd41f62 Merge branch 'master' into patch-1 2021-09-24 15:33:13 +08:00
LouisLam
50b868e751 fix #465 2021-09-24 15:26:48 +08:00
LouisLam
a856780066 fix the active link problem 2021-09-24 15:00:52 +08:00
LouisLam
266b03fbf7 language key from "Status Page Nothing" to "statusPageNothing" 2021-09-24 14:42:54 +08:00
Nelson Chan
662c97dcde Fix: Fix importing tag if tag doesn't exist 2021-09-24 14:34:53 +08:00
Louis Lam
1ebf752f1a Merge pull request #415 from Ponkhy/import-export
Added import/export compatibility for version 1.7
2021-09-24 14:00:28 +08:00
Mawoka
69bb6ef290 Removed empty selector if no monitor available 2021-09-24 00:19:09 +08:00
Ponkhy
720ea850e1 Added space between Dashboard and settings button 2021-09-23 17:33:23 +02:00
Ponkhy
7fb55b8875 Fixed issues 2021-09-23 17:31:01 +02:00
Ponkhy
4786514e9f Merge branch 'louislam:master' into import-export 2021-09-23 17:24:53 +02:00
Nelson Chan
32c9dfbb31 Fix: clear important beats after deleteMonitor 2021-09-23 23:21:08 +08:00
Ponkhy
d3d4363031 Used compare-version instead of replace 2021-09-23 17:15:11 +02:00
Louis Lam
6f352a6e3c [guide] update how to start dev server 2021-09-23 22:48:19 +08:00
Louis Lam
3fb06a8ba4 Merge pull request #463 from CosmoAbdon/ptBR-Translation
add: BR Portuguese Language and new words
2021-09-23 21:04:39 +08:00
Louis Lam
b3a5a5b0ba Merge pull request #462 from Saibamen/development_server
`start-server-dev` command. Fixes #460
2021-09-23 21:04:23 +08:00
Cosmo Abdon
a4cad3db65 add: BR Portuguese Language and new words 2021-09-23 09:42:33 -03:00
Adam Stachowicz
5fa9b33c79 Install cross-env as dev dependency for now 2021-09-23 12:49:15 +02:00
Adam Stachowicz
f6a984b671 start-server-dev. Fixes #460 2021-09-23 12:45:30 +02:00
LouisLam
23a63213aa Merge branch 'master' into import-export
# Conflicts:
#	server/server.js
2021-09-23 17:20:13 +08:00
Louis Lam
a9bb8ae6a1 Merge pull request #124 from chakflying/public-dashboard
Implement Public Dashboard
2021-09-23 16:54:07 +08:00
LouisLam
27d4c3c194 [status page] improve mobile layout 2021-09-23 16:31:45 +08:00
LouisLam
439f45d91e [status page] improve the entry 2021-09-23 13:57:24 +08:00
Louis Lam
66598a81cc Merge pull request #452 from chakflying/patch-8
Fix: Fix TCP Ping hostname Regex
2021-09-23 12:49:56 +08:00
Nelson Chan
45a6f364e1 Chore: Fix backslash in IP regex 2021-09-22 23:05:46 +08:00
Nelson Chan
95391be2ab Revert Regex update 2021-09-22 22:36:27 +08:00
zsxeee
624f632a7a Apprise status translation key 2021-09-22 22:15:50 +08:00
LouisLam
5f533b9091 [status page] fix wrong foreign key 2021-09-22 18:50:20 +08:00
LouisLam
29920f6b60 [status page] use normal link to status-page, due to data reset problem 2021-09-22 18:05:40 +08:00
zsxeee
6e9d12638c Avoid space ending in translation key 2021-09-22 16:20:59 +08:00
zsxeee
6e55c44773 Chore 2021-09-22 16:13:23 +08:00
LouisLam
ad1bb93730 [status page] fix logo url 2021-09-22 15:31:15 +08:00
LouisLam
0a5a6e6a4b [status page] fix monitor order 2021-09-22 15:23:58 +08:00
LouisLam
fe0fc63843 [status page] send uptime 2021-09-22 15:10:08 +08:00
Nelson Chan
5b2e1f6086 Fix: Allow number ending TLD 2021-09-22 05:29:56 +08:00
LouisLam
8c7ee94769 add modified apicache library with disabling client cache 2021-09-22 00:58:22 +08:00
LouisLam
0834770509 [status page] also apply the title to <title> 2021-09-21 23:59:46 +08:00
LouisLam
a71814c80b add util.ts comments 2021-09-21 21:28:45 +08:00
LouisLam
17073fd786 Merge branch 'master' into public-dashboard 2021-09-21 21:25:16 +08:00
LouisLam
15c00d9158 upload logo and expose ./data/upload to url 2021-09-21 21:22:35 +08:00
Nelson Chan
d2b34f9285 Chore: make eslint happy 2021-09-21 18:53:45 +08:00
Nelson Chan
a96a515087 Fix: Fix hostname with dash 2021-09-21 18:44:32 +08:00
Nelson Chan
2eab919ae0 Fix: Fix typo 2021-09-21 18:41:19 +08:00
Nelson Chan
c09b67b94d Fix: Fix TCP Ping hostname Regex 2021-09-21 18:27:40 +08:00
zsxeee
601204ae77 Default friendly name i18n and auto increase 2021-09-21 17:25:54 +08:00
LouisLam
61c737c53c fix alert text color in dark mode 2021-09-21 17:24:46 +08:00
Louis Lam
2820118eba Merge pull request #446 from Saibamen/util
Add comments from `util.ts` to `util.js`
2021-09-21 17:23:12 +08:00
LouisLam
469e8f6fd6 Merge branch 'master' into public-dashboard
# Conflicts:
#	package-lock.json
2021-09-21 17:05:13 +08:00
LouisLam
780cb0a145 install-legacy-peer-deps to install-legacy 2021-09-21 17:03:37 +08:00
zsxeee
8c941b1d56 Add i18n for notification form 2021-09-21 13:02:41 +08:00
LouisLam
1c8b3ce451 no need to build sqlite and add nightly for alpine 2021-09-21 10:47:55 +08:00
Adam Stachowicz
e8ef63e0a3 removeComments to false 2021-09-21 00:48:26 +02:00
LouisLam
4591adc05e second attempt: prebuilt node-sqlite3 and update SQLite to 3.36 2021-09-21 01:15:20 +08:00
LouisLam
5f6aa32844 fix store/fetch status page config 2021-09-20 20:44:07 +08:00
LouisLam
a8e170f6a8 Merge branch 'master' into public-dashboard
# Conflicts:
#	server/server.js
2021-09-20 18:48:44 +08:00
LouisLam
6aaf984f29 fix retryInterval = 0 if it is an old monitor. (#380 https://github.com/louislam/uptime-kuma/pull/380#issuecomment-922618356) 2021-09-20 18:23:53 +08:00
LouisLam
76a39f1388 update to 1.6.3 2021-09-20 16:56:03 +08:00
LouisLam
34c0fa59a8 fix reset-password (#448)
(cherry picked from commit b0e9c5bcb4)
2021-09-20 16:33:22 +08:00
LouisLam
0664217a09 Merge remote-tracking branch 'origin/master' 2021-09-20 16:29:43 +08:00
LouisLam
b0e9c5bcb4 fix reset-password (#448) 2021-09-20 16:29:18 +08:00
LouisLam
0b572df3d0 [status page] store config 2021-09-20 16:22:18 +08:00
zsxeee
ad6fcc2f2e Merge branch 'master' into notification_component 2021-09-20 13:16:24 +08:00
Adam Stachowicz
bcc02c1680 Add formatting and comments from util.ts to util.js 2021-09-19 23:09:52 +02:00
Louis Lam
795d5f586f Merge pull request #444 from Saibamen/avg-ping_avg-response
Avg. Ping and Avg. Response translation keys
2021-09-20 02:23:50 +08:00
LouisLam
7ee98d989c [status page] implement rest api for heartbeat 2021-09-19 23:24:51 +08:00
Adam Stachowicz
7dc1e84e44 Avg. Ping and Avg. Response translation keys 2021-09-19 15:15:12 +02:00
LouisLam
6350c43cc3 fix newline? 2021-09-19 19:20:27 +08:00
LouisLam
fd95d41d9f [status page] many update and save group list 2021-09-19 19:04:51 +08:00
zsxeee
ffbc25722d Move default setting to child component 2021-09-19 18:05:22 +08:00
Louis Lam
7665bae927 Merge pull request #441 from iomataani/master
Italian translation updated
2021-09-19 16:32:08 +08:00
Ioma Taani
09e38269c6 Merge branch 'louislam:master' into master 2021-09-19 09:45:42 +02:00
LouisLam
6681f49a58 Merge branch 'master' into public-dashboard
# Conflicts:
#	.dockerignore
2021-09-19 15:16:13 +08:00
Louis Lam
3fc2ba3d76 Merge pull request #436 from nbvcxz/running_non-root_user
Docker entrypoint to run the application as non-root user
2021-09-19 14:33:20 +08:00
Louis Lam
78e12ab899 Merge pull request #439 from gaby/dockerignore
Add missing entries to dockerignore
2021-09-19 12:58:55 +08:00
Juan Calderon-Perez
2b8c049e7b Add missing entries to dockerignore 2021-09-18 13:53:24 -04:00
LouisLam
f0ac3c82d2 add some comments 2021-09-19 00:51:05 +08:00
Louis Lam
803029a9e4 Merge pull request #429 from Ponkhy/main
Added regex to hostname input
2021-09-19 00:38:40 +08:00
Louis Lam
c3f1cebeab Merge pull request #435 from DX37/translation-ru
fresh translations to ru-RU.js and Settings.vue
2021-09-19 00:37:37 +08:00
Louis Lam
5d9814559c Merge pull request #424 from Saibamen/fix_PL
Fix polish translation and translate new keys
2021-09-19 00:37:23 +08:00
DX37
9ef45a9c7e fresh translations to ru-RU.js and Settings.vue 2021-09-18 22:25:06 +07:00
Michal Ciania
7f78cc8d0f Substitute default values only once 2021-09-18 11:33:25 +02:00
Ioma Taani
ca84044809 Merge branch 'louislam:master' into master 2021-09-17 23:05:56 +02:00
Michal Ciania
9eaa4ab846 Docker entrypoint for running the application as non-root user 2021-09-17 22:57:27 +02:00
Marcos Sigueros Fernández
c3122a9807 Update es-ES.js (#430)
Simple but annoying language fix.
2021-09-17 22:20:45 +08:00
Ponkhy
f6498caa9a Merge branch 'main' of https://github.com/Ponkhy/uptime-kuma into main 2021-09-17 16:02:28 +02:00
Ponkhy
3a0bc80016 Added regex to hostname input 2021-09-17 16:02:20 +02:00
zsxeee
2fb3c40307 Variable name and key binding 2021-09-17 20:40:57 +08:00
zsxeee
66e40d9fcb Edit comment and remove unused variable 2021-09-17 20:20:44 +08:00
Ioma Taani
cbbc503f8e Merge branch 'louislam:master' into master 2021-09-17 11:31:21 +02:00
zsxeee
de8b61ef2b Remove unused imports 2021-09-17 17:16:52 +08:00
zsxeee
534ac4b720 Move Apprise check to child component 2021-09-17 16:54:50 +08:00
zsxeee
e9735d239b Convert notification form to separate component 2021-09-17 16:07:03 +08:00
Louis
5be51abd8f Merge remote-tracking branch 'origin/master' 2021-09-17 15:19:36 +08:00
Louis
6ec219908b test nodejs 16 2021-09-17 15:19:19 +08:00
Louis
a6fdd272a6 [status page] minor 2021-09-17 14:42:19 +08:00
Ponkhy
1b5e723f60 Added descriptions to uploadBackup function 2021-09-17 03:25:18 +02:00
Ponkhy
4bdada36a9 Removed if includes version 2021-09-16 20:18:31 +02:00
Adam Stachowicz
250c2bdd6d Missing this 2021-09-16 20:17:25 +02:00
Adam Stachowicz
8e49d84050 Fix polish translation and translate new keys 2021-09-16 20:13:57 +02:00
Louis Lam
112d72da47 update discussion 2021-09-17 00:48:04 +08:00
LouisLam
9b8f01cfc6 since eslint can auto fix semicolon, standardize all end with semicolon 2021-09-16 22:57:34 +08:00
LouisLam
579e07ded4 Merge branch 'master' into public-dashboard 2021-09-16 22:52:05 +08:00
kry008
a04878fa84 Finishing the translation of PL (#422) 2021-09-16 22:49:24 +08:00
LouisLam
2955abb5d9 [status page] create incident 2021-09-16 22:48:28 +08:00
LouisLam
8230cfe13f use "next" tag to keep vue3 up-to-update 2021-09-16 20:07:26 +08:00
Ponkhy
8b463e70df Apply suggestions from @Saibamen
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-16 12:29:33 +02:00
LouisLam
392f8275b3 Merge branch 'master' into public-dashboard
# Conflicts:
#	server/database.js
2021-09-16 16:53:54 +08:00
Ioma Taani
783ec97004 updated translations 2021-09-16 09:10:13 +02:00
Siim Ots
4f4fe39c9b Simple "Add to Homescreen" manifest (PWA) (#411)
* simple "add to homescreen" manifest

* remove ?source=pwa

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>

* run TinyPNG

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
Co-authored-by: LouisLam <louislam@users.noreply.github.com>
2021-09-16 14:42:56 +08:00
LouisLam
611d214a32 [status page] checkpoint 2021-09-16 14:37:57 +08:00
Louis Lam
7a0cebf5bb Merge pull request #414 from Saibamen/lang_fixes
Fix languages
2021-09-16 14:33:05 +08:00
Ponkhy
54aa68ec58 Added import/export compatibility for version 1.7 2021-09-15 22:07:28 +02:00
Adam Stachowicz
217637aa1f Add de-DE translation 2021-09-15 21:19:16 +02:00
Adam Stachowicz
ab22961538 More info about update-language-files command 2021-09-15 21:16:22 +02:00
Adam Stachowicz
8ba8de07ae Missing this 2021-09-15 20:59:29 +02:00
Adam Stachowicz
a34b2623c8 Remove double spaces in console.log output with multiple parameters 2021-09-15 20:57:46 +02:00
Adam Stachowicz
79920b5f2c Fix languages after 80322cbfe7
Fix ESLint deprecated warning. Update `README.md` for  languages. Update `update-language-files` script
2021-09-15 20:52:39 +02:00
Louis Lam
72783fd94c Merge pull request #380 from No0Vad/retry-heartbeat-interval
Added support for a retry interval to monitors
2021-09-16 00:21:53 +08:00
LouisLam
80322cbfe7 Merge branch 'import-export'
# Conflicts:
#	src/languages/it-IT.js
#	src/languages/nl-NL.js
2021-09-16 00:18:07 +08:00
LouisLam
7e0272077b cleanup demo mode code 2021-09-16 00:13:28 +08:00
LouisLam
b2ff6b6098 update ignore files and delete demo db 2021-09-16 00:12:39 +08:00
LouisLam
512ff09cca set entry page 2021-09-15 20:40:26 +08:00
LouisLam
e8f4fabcd0 [status page] crop and resize logo 2021-09-15 18:28:48 +08:00
Ponkhy
f679c1cbaf Export button align left 2021-09-15 12:01:30 +02:00
LouisLam
2ab06f87b8 Merge branch 'master' into public-dashboard 2021-09-15 16:46:05 +08:00
Louis Lam
76db55b657 Merge pull request #395 from WillianRod/feat/add-microsoft-teams-notification
Add microsoft teams notification provider
2021-09-15 16:38:57 +08:00
LouisLam
1693873f4a [Teams] change handleTestNotification to GeneralNotification 2021-09-15 16:38:28 +08:00
Louis Lam
53bfdb7dad Merge pull request #409 from iomataani/master
updated
2021-09-15 14:38:17 +08:00
LouisLam
db05b506f3 [status page] checkpoint 2021-09-15 14:34:30 +08:00
Ioma Taani
0752f810f1 updated 2021-09-15 08:33:30 +02:00
Louis Lam
200d233607 Merge pull request #407 from SametKUM/master
Turkish Language added
2021-09-15 11:08:25 +08:00
samet.kum
7274913686 Turkish Language added 2021-09-15 02:55:04 +03:00
No0Vad
1300448bed Adjustments to the retry interval
The monitor logic for when to use "retryInterval" is updated. Also removed some texts when they are no longer needed.
2021-09-15 00:59:06 +02:00
Louis Lam
3f67326fce Merge pull request #404 from Ponkhy/main
Added search key
2021-09-15 01:20:51 +08:00
Louis Lam
8b48f9bd22 Merge pull request #405 from zidingz/patch-1
Update SECURITY.md
2021-09-15 01:19:29 +08:00
Louis Lam
76348cae5d Update SECURITY.md 2021-09-15 01:17:33 +08:00
Louis Lam
0b32adadb8 Update SECURITY.md 2021-09-15 01:12:59 +08:00
Ziding Zhang
3425a27a0e Update SECURITY.md
I'd like to report a security issue and felt it most prudent not to disclose it publicly, as per your Security policy instructions.

If not a hassle, might you kindly add an email, or other private contact method? GitHub [recommends](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.

Thank you for your consideration!
2021-09-14 17:54:51 +01:00
Ponkhy
fff5fd8e9e Added search key 2021-09-14 17:48:29 +02:00
LouisLam
1d6670ed9a Merge branch 'master' into public-dashboard 2021-09-14 23:29:22 +08:00
LouisLam
795411a11c Merge remote-tracking branch 'origin/master' 2021-09-14 23:28:52 +08:00
LouisLam
3234aec5b3 NODE_ENV if not set, change to production 2021-09-14 23:28:38 +08:00
LouisLam
afe91078c4 [status page] checkpoint 2021-09-14 23:27:11 +08:00
Louis Lam
2009f7097d Merge pull request #403 from Ponkhy/main
Added german translation for tags manager
2021-09-14 23:21:40 +08:00
Ponkhy
c14b71a4a7 Added german translation for tags manager 2021-09-14 17:05:17 +02:00
LouisLam
35fe9690e8 update to 1.6.2 2021-09-14 19:34:54 +08:00
LouisLam
ad2062713c fix pi4 on docker 2021-09-14 19:04:56 +08:00
Louis Lam
379656335d Merge pull request #402 from xoniq/lang-nl
Update new / updated translations to nl_NL
2021-09-14 15:49:26 +08:00
LouisLam
fc9b4617ca link interval and retryInterval 2021-09-14 15:48:25 +08:00
Jelle Posthuma
40d301457a Update new / updated translations to nl_NL 2021-09-14 09:22:50 +02:00
LouisLam
9902c181bc Merge branch 'master' into public-dashboard
# Conflicts:
#	src/components/MonitorList.vue
#	src/icon.js
2021-09-14 15:05:04 +08:00
Louis Lam
069c811af8 Merge pull request #278 from chakflying/tags
Monitor: Tags with metadata
2021-09-14 14:57:53 +08:00
LouisLam
f9311e4e7f [status page] 2021-09-14 14:55:45 +08:00
LouisLam
34abff4724 manual merge master 2021-09-14 14:19:23 +08:00
LouisLam
d7a230ac15 Merge branch 'master' into public-dashboard
# Conflicts:
#	server/server.js
#	src/main.js
2021-09-14 14:16:24 +08:00
LouisLam
9da2a01a74 [status page] checkpoint 2021-09-14 14:12:27 +08:00
Nelson Chan
74da02da2c Fix: Prevent enter in modal from submitting form 2021-09-14 12:25:31 +08:00
LouisLam
97360dab26 add /.well-known/change-password 2021-09-14 12:10:25 +08:00
Louis Lam
56dad006b5 Merge pull request #401 from NeuralMiner/patch-1
Allow SMTP that doesnt require authentication.
2021-09-14 10:05:43 +08:00
NeuralMiner
47092258f8 Allow SMTP that doesnt require authentication. 2021-09-13 16:36:37 -06:00
Willian Rodrigues Barbosa
a0838543b9 Merge branch 'feat/add-microsoft-teams-notification' of https://github.com/WillianRod/uptime-kuma into feat/add-microsoft-teams-notification 2021-09-13 14:03:14 -03:00
Willian Rodrigues Barbosa
ccb8736b3d fix: send msg if heartbeat message is not set 2021-09-13 14:02:52 -03:00
LouisLam
f351b423c5 link to the english version of microsoft link 2021-09-13 20:25:41 +08:00
LouisLam
ab6b97d77a update dependencies, fix axios security problem 2021-09-13 20:14:50 +08:00
LouisLam
0e16e88fa8 Merge remote-tracking branch 'origin/master' 2021-09-13 20:06:24 +08:00
LouisLam
c746923018 merge italian language file 2021-09-13 20:05:48 +08:00
LouisLam
df8754827d Merge branch 'italian-language-update'
# Conflicts:
#	src/languages/it-IT.js
2021-09-13 20:04:46 +08:00
Louis Lam
2c02dad1f9 Merge pull request #393 from Revyn112/master
add possibility to have a prefixMessage in discord notification before the embed
2021-09-13 20:02:55 +08:00
LouisLam
600ec6d171 Merge branch 'master' into i18n_router_own_files 2021-09-13 19:58:14 +08:00
LouisLam
728636de06 Merge branch 'feature/timezones-package' 2021-09-13 19:56:57 +08:00
LouisLam
8be7fd5c45 Merge remote-tracking branch 'bertyhell/feature/timezones-package' 2021-09-13 19:56:29 +08:00
LouisLam
59ff9fa293 update missing timezone console msg 2021-09-13 19:53:29 +08:00
LouisLam
a654e409df Merge branch 'master' into feature/timezones-package 2021-09-13 19:48:42 +08:00
LouisLam
76f1f34a0a fix #397, incorrect encode function for export file 2021-09-13 19:25:12 +08:00
LouisLam
35aca46b68 [status page] checkpoint 2021-09-13 19:21:39 +08:00
DeeJayPee
29d0db805d Add legacy octopush (Octopush-DM from 2011 to 2020 accounts) version 2021-09-13 10:25:44 +02:00
Paride Barison
6a925c7ed4 sistemato l'ordine 2021-09-13 09:19:07 +02:00
Paride Barison
8b9b86b478 updated 2021-09-13 09:10:42 +02:00
Nelson Chan
7c4b5f189b Fix: Fix hiding form when reusing draft tags 2021-09-13 14:12:17 +08:00
Nelson Chan
a6f9762c4d Feat: Split add tag to modal
Fix: Fix wrong css class

Fix: More misc. styling fixes
2021-09-13 14:12:17 +08:00
Nelson Chan
a0e4e96160 Fix: Fix tag removal, reuse and validation 2021-09-13 14:12:17 +08:00
Nelson Chan
fcbeed55bf Fix: Allow removing and re-adding the same tag
like, why would you even do this?
2021-09-13 14:12:17 +08:00
Nelson Chan
9b5abf9bb1 Fix: reset editMonitor after save 2021-09-13 14:12:17 +08:00
Nelson Chan
73f4b8e177 Fix: Improve various tags validation logic 2021-09-13 14:12:17 +08:00
Nelson Chan
e6669fbb9e Fix: Improve searchbar padding on mobile 2021-09-13 14:12:17 +08:00
Nelson Chan
e66a2d362d Feat: Add monitorList search box 2021-09-13 14:12:17 +08:00
Nelson Chan
c2148fc0f1 Fix: Slightly reduce tag opacity on light theme 2021-09-13 14:12:16 +08:00
Nelson Chan
6e3a904aaa WIP: Add tags functionality
WIP: add color column, show tags

WIP: Improve TagsManager styling & workflow

WIP: Improve styling & validation, use translation

WIP: Complete TagsManager functionality

WIP: Add tags display in monitorList & Details

Fix: update tags list after edit

Fix: slightly improve tags styling

Fix: Improve mobile UI

Fix: Fix tags not showing on create monitor

Fix: bring existingTags inside tagsManager

Fix: remove unused tags prop

Fix: Fix formatting, bump db version
2021-09-13 14:12:11 +08:00
Louis Lam
50175b733c Merge pull request #396 from Ponkhy/2fa
Hide 2FA section if disabled auth
2021-09-13 09:16:09 +08:00
No0Vad
2617e1f4d8 Update database.js 2021-09-13 00:25:18 +02:00
No0Vad
91ee39ec60 Merge branch 'master' into retry-heartbeat-interval 2021-09-13 00:19:51 +02:00
Ponkhy
4711aeb355 Merge branch 'master' into import-export 2021-09-12 23:21:33 +02:00
Ponkhy
db9e97d859 Merge pull request #2 from Saibamen/import-export
Separate import and export. Fix ESLint
2021-09-12 23:20:16 +02:00
Adam Stachowicz
7199bb5ead Remove unused i18n key 2021-09-12 23:11:07 +02:00
Ponkhy
bb87972543 Hide 2FA section if disabled auth 2021-09-12 21:58:52 +02:00
Adam Stachowicz
ea723c7595 Separate import and export. Fix ESLint 2021-09-12 21:35:37 +02:00
LouisLam
e205adfd7b [status page] developing 2021-09-13 02:26:45 +08:00
Willian Rodrigues Barbosa
063d64eec8 feat: add microsoft teams notification provider 2021-09-12 14:46:59 -03:00
Adam Stachowicz
5abf2e7597 Missing this 2021-09-12 19:44:33 +02:00
Adam Stachowicz
7c83bed273 Remove double spaces in Dockerfiles 2021-09-12 19:43:14 +02:00
Adam Stachowicz
1cc788da68 Missing this 2021-09-12 19:39:28 +02:00
Adam Stachowicz
54ffef8b7a Own files for i18n and router + fix some of markdown warnings 2021-09-12 19:23:51 +02:00
Revyn112
44aa837a9d Update NotificationDialog.vue 2021-09-12 19:17:09 +02:00
LouisLam
f47f7758f9 Merge branch 'master' into public-dashboard
# Conflicts:
#	server/database.js
#	server/server.js
#	server/util-server.js
2021-09-13 01:09:01 +08:00
Bert Verhelst
ddcd3c2a19 Merge remote-tracking branch 'origin/master' into feature/timezones-package 2021-09-12 19:03:05 +02:00
LouisLam
7df9698e5d eslint: comma-dangle for language files 2021-09-13 00:58:45 +08:00
Bert Verhelst
471333ad03 fix(util-frontend): revert typescript conversion 2021-09-12 18:56:39 +02:00
Ponkhy
d313966d80 Merge branch 'master' into import-export 2021-09-12 18:46:11 +02:00
LouisLam
8205f90f3d update language files 2021-09-13 00:45:43 +08:00
LouisLam
02247c4174 Merge remote-tracking branch 'origin/master' 2021-09-13 00:38:17 +08:00
Denis Freund
8352d9abbe add posibility to have a prefixMessage before the embed 2021-09-12 17:54:12 +02:00
Louis Lam
2d7767c905 Merge pull request #392 from bertyhell/bugfix/button-margin-settings
fix(settings): add some button bottom margin for narrow screens
2021-09-12 23:49:21 +08:00
Louis Lam
c52b9b63d5 Merge pull request #389 from Ponkhy/default-notification
Wording correction for notification modal
2021-09-12 23:49:09 +08:00
Louis Lam
e55193270d Merge pull request #377 from Lrss/master
Fix: Inconsistent white-space for the "Avg."-Ping/Response title.
2021-09-12 23:48:29 +08:00
Louis Lam
9d39071a91 Merge pull request #363 from Ponkhy/2fa
Added simple TOTP Two Factor Authentication
2021-09-12 23:47:42 +08:00
No0Vad
389d247463 Update server/database.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-12 17:05:23 +02:00
Bert Verhelst
a170694a83 fix(util-frontend): improve formatting timezoneList
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-12 15:21:40 +02:00
Bert Verhelst
5969e913b5 fix(util-frontend): improve formatting
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-12 15:19:27 +02:00
Louis Lam
0d280f5edb Merge pull request #376 from chakflying/patch-7
Fix: Fix events table width on mobile
2021-09-12 21:12:56 +08:00
Bert Verhelst
660b969178 fix(settings): switch custom styles to bootstrap class for bottom margin 2021-09-12 14:52:47 +02:00
Bert Verhelst
c26622aa39 fix(settings): add some button bottom margin for narrow screens 2021-09-12 13:40:07 +02:00
Bert Verhelst
a7674755ba Move timezones to npm package and convert util-frontend.js to typescript 2021-09-12 13:27:41 +02:00
Louis Lam
6d7cb44acc Merge pull request #387 from jtagcat/et2
i10n: update Estonian
2021-09-12 14:03:02 +08:00
LouisLam
400fc13cf2 add npm install script for npm7 2021-09-12 12:39:22 +08:00
Ponkhy
db14768229 Wording correction for notification modal 2021-09-12 01:44:05 +02:00
No0Vad
3d27561e5a Update en.js
Fixed spelling error
2021-09-12 00:24:33 +02:00
jtagcat
b80c88d9a0 i10n: update Estonian 2021-09-12 00:51:03 +03:00
Ponkhy
862672731f Minor typo fix 2021-09-11 22:06:26 +02:00
Ponkhy
7fee4a7ea7 Added import options 2021-09-11 21:53:17 +02:00
LouisLam
c4f78d776e [2fa] "UptimeKuma" to "Uptime Kuma" 2021-09-12 02:25:51 +08:00
No0Vad
f8f9f59464 Added support for a retry interval to monitors
If a check fails and retries are used you can now specify a specific value for that. So you can check faster if the site goes back up again.
2021-09-11 18:54:55 +02:00
LouisLam
934685637a [Status Page] WIP: Checkpoint 2021-09-11 23:43:07 +08:00
Ponkhy
295ccba44b Adjusted for new db patch management 2021-09-11 16:37:33 +02:00
Ponkhy
8cd5bad44c Merge branch 'master' into 2fa 2021-09-11 16:32:11 +02:00
Ponkhy
d003abcd60 Update src/languages/en.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-11 14:34:23 +02:00
Ponkhy
f6d1a82989 Update server/server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-11 14:34:19 +02:00
Ponkhy
651b525d06 Update server/server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-11 14:34:12 +02:00
Lars Sørensen
66769e1c79 Fixed inconsistent white-space for the "Avg."-Ping/Response title. 2021-09-11 13:52:41 +02:00
LouisLam
3e25f0e9d9 [Status Page] WIP: Checkpoint 2021-09-11 19:40:03 +08:00
Lars Sørensen
39c7b5e748 Added new Strings to Danish translation 2021-09-11 13:37:53 +02:00
Lars Sørensen
b709857e19 Better wording for Danish language 2021-09-11 13:20:40 +02:00
Nelson Chan
9e10f7d35f Fix: Fix events table width on mobile 2021-09-11 18:56:27 +08:00
Louis Lam
6caae725f9 Merge pull request #375 from Saibamen/format_backup_json
Format JSON file before download
2021-09-11 16:57:33 +08:00
Adam Stachowicz
c387fb264e Format JSON file before download 2021-09-11 10:50:56 +02:00
LouisLam
4b0a8087a2 do not connect to socket io for status page 2021-09-11 16:22:30 +08:00
LouisLam
08c7a37052 Merge branch 'master' into public-dashboard 2021-09-11 15:09:08 +08:00
LouisLam
0969be5981 Revert "WIP: Add login page, nav bar buttons"
This reverts commit b9a6088f50.
2021-09-11 15:08:56 +08:00
LouisLam
2ed8f12dc0 Merge remote-tracking branch 'origin/master' 2021-09-11 14:46:04 +08:00
LouisLam
2da77d8448 fix ipv6 connection problem 2021-09-11 14:37:05 +08:00
Louis Lam
f8322de5da Update bug_report.md 2021-09-11 13:44:06 +08:00
Louis Lam
e21a18a593 Update ask-for-help.md 2021-09-11 13:41:22 +08:00
Ponkhy
14b7688b70 Added NOT NULL to twofa_status 2021-09-11 00:13:05 +02:00
LouisLam
d953ba7c60 update to 1.6.1 2021-09-11 03:12:25 +08:00
LouisLam
ef1604675b change to node:14-bullseye-slim, and reduce the docker image size 2021-09-11 02:50:29 +08:00
Louis Lam
83fc844e8e Update deployment.yml 2021-09-10 22:32:11 +08:00
Louis Lam
22a7acbaf6 Update README.md 2021-09-10 22:03:28 +08:00
LouisLam
201bba63d7 add a healthcheck comment 2021-09-10 21:17:20 +08:00
Ioma Taani
0d6888c586 Merge branch 'louislam:master' into italian-language-update 2021-09-10 13:36:43 +02:00
Ioma Taani
a06fc14213 Merge branch 'louislam:master' into italian-language-update 2021-09-10 12:08:29 +02:00
LouisLam
31b4a5c33e build-docker, debian first 2021-09-10 18:08:23 +08:00
Paride Barison
951ece8a65 updated 2021-09-10 12:07:54 +02:00
LouisLam
a49df29a87 update to 1.6.0 2021-09-10 17:56:33 +08:00
LouisLam
08de0090dc add a better approach for patching db, change setting.value from varchar to TEXT, restore Database.close() to 1.2.0 2021-09-10 17:23:45 +08:00
Louis Lam
97df200d6c Merge pull request #365 from Ponkhy/master
Minor translation fixes
2021-09-10 11:46:20 +08:00
Louis Lam
d5b17a3778 Merge pull request #367 from xinac721/master
update Simplified Chinese language(更新简体中文语言)
2021-09-10 11:45:57 +08:00
新逸Cary
2f0d994c83 update Simplified Chinese language(更新简体中文语言) 2021-09-10 09:26:01 +08:00
新逸Cary
e683033f4e Merge pull request #2 from louislam/master
update
2021-09-10 08:52:38 +08:00
Ponkhy
c3c576bd13 Added "Show URI" translation 2021-09-10 01:33:26 +02:00
Ponkhy
c62732fa9c Minor translation fixes 2021-09-10 01:24:21 +02:00
Ponkhy
d823493fad Added maxlengh to token verify input 2021-09-09 22:08:34 +02:00
Ponkhy
463c385cf1 Only show modal footer if uri was generated 2021-09-09 21:38:39 +02:00
Ponkhy
59cccf8c50 Fixed typo 2021-09-09 21:12:29 +02:00
Ponkhy
403202d4d4 Added simple TOTP Two Factor Authentication 2021-09-09 21:10:31 +02:00
Louis Lam
2583dbe0e0 Update README.md 2021-09-09 22:58:31 +08:00
LouisLam
ca479673e7 avoid Chrome duplicate id warning 2021-09-09 21:28:27 +08:00
LouisLam
573c7faddd switch on the notification, if it is added in EditMonitor.vue 2021-09-09 21:24:29 +08:00
LouisLam
7a4432de1e better switch button color in dark mode 2021-09-09 20:45:58 +08:00
LouisLam
94a6c1a344 update language files 2021-09-09 20:38:19 +08:00
Louis Lam
ee9e48fe53 Merge pull request #361 from iomataani/italian-translation-update
Italian translation update
2021-09-09 20:26:07 +08:00
LouisLam
a4aee49b03 Merge remote-tracking branch 'origin/master' 2021-09-09 20:22:44 +08:00
LouisLam
e330875c80 fix restore fail if no isDefault 2021-09-09 20:22:32 +08:00
Paride Barison
44eba70b78 altri errorini sistemati 2021-09-09 14:19:15 +02:00
Paride Barison
c6dab944d1 altra correzione 2021-09-09 14:12:30 +02:00
Ioma Taani
0f4bc5850b Merge branch 'louislam:master' into italian-translation-update 2021-09-09 14:05:00 +02:00
Louis Lam
f37190a73d Update README.md 2021-09-09 18:02:58 +08:00
Louis Lam
29f96fae93 Update README.md 2021-09-09 18:01:15 +08:00
Louis Lam
331ae5ec20 Merge pull request #306 from Ponkhy/import-export
Added import and export function
2021-09-09 16:15:48 +08:00
LouisLam
8ee34c7904 also handle the first beat problem in uptime 2021-09-09 15:55:34 +08:00
LouisLam
4f07c2ea9a uptime calculation fully sum in sql 2021-09-09 15:46:28 +08:00
LouisLam
0b1d0d22ff add indexes, improve queries 2021-09-09 12:43:18 +08:00
LouisLam
24facc79d7 sendHeartbeatList use raw data 2021-09-09 11:57:23 +08:00
LouisLam
9f9c1007d7 increase sqlite cache size from 2MB to 12MB 2021-09-09 11:56:42 +08:00
LouisLam
70a6f0313c update .dockerignore 2021-09-09 11:41:43 +08:00
LouisLam
1d05df6ec9 [docker healthcheck] use retries instead, longer start-period causes starting problem in Docker Swarm mode 2021-09-09 11:41:28 +08:00
Ioma Taani
b54bbc302d migliorata la traduzione 2021-09-08 21:34:48 +02:00
LouisLam
dd283423ab improve smtp with cc, bbc and ignore tls 2021-09-09 01:13:09 +08:00
LouisLam
6006038689 fix monitor.stop() in some cases 2021-09-08 20:00:16 +08:00
LouisLam
a7b50c3630 Revert "add user-monitor-list"
This reverts commit 230a9bfaf9.
2021-09-08 19:54:38 +08:00
LouisLam
0ddbac5109 Revert "move userMonitorList out of server.js"
This reverts commit a7cf14c663.
2021-09-08 19:54:38 +08:00
LouisLam
0f440596c8 Revert "cache last heartbeat list in memory"
This reverts commit 87678ea92d.
2021-09-08 19:54:37 +08:00
LouisLam
5a0fcebd6e update link style 2021-09-08 18:59:30 +08:00
LouisLam
87678ea92d cache last heartbeat list in memory 2021-09-08 18:58:02 +08:00
LouisLam
0bf0cfa104 Merge remote-tracking branch 'origin/master' 2021-09-08 18:56:43 +08:00
LouisLam
a7cf14c663 move userMonitorList out of server.js 2021-09-08 16:27:37 +08:00
Louis Lam
325ce72a71 Merge pull request #352 from iomataani/italian-language
Italian language
2021-09-08 16:12:44 +08:00
Louis Lam
9d00bd9029 Merge pull request #357 from Saibamen/nav_from_dashHome
Route to monitor on `DashboardHome`
2021-09-08 16:11:39 +08:00
Adam Stachowicz
f6a168282e Route to monitor on DashboardHome 2021-09-08 09:55:08 +02:00
Ponkhy
acf6f1883b Fixed missing spaces in backup file 2021-09-08 09:02:24 +02:00
Paride Barison
f44f951e40 Tradotte le ultime cose come richiesto 2021-09-08 08:41:19 +02:00
Ioma Taani
dea9b05d49 Merge branch 'louislam:master' into italian-language 2021-09-08 08:30:10 +02:00
LouisLam
230a9bfaf9 add user-monitor-list 2021-09-08 14:16:30 +08:00
Louis Lam
d761d54d0e Merge pull request #340 from Ponkhy/default-notification
Added the option for default notifications
2021-09-08 12:49:14 +08:00
LouisLam
e37621853e Merge branch 'estonian'
# Conflicts:
#	src/pages/Settings.vue
2021-09-08 11:00:19 +08:00
LouisLam
f2cec60481 minor 2021-09-08 10:48:03 +08:00
LouisLam
2212cf9c08 fix confirmDisableAuth no msg if not tranlated 2021-09-08 10:28:40 +08:00
LouisLam
f7562e00c1 improve notification component, no double template 2021-09-08 10:25:03 +08:00
Ponkhy
678e248966 Added indicator while changing database records 2021-09-08 01:10:19 +02:00
Ponkhy
c1aebef005 Fixed SQL error due notification setup 2021-09-08 01:03:24 +02:00
Ponkhy
1ef4562905 Paused monitors stay paused after import 2021-09-07 23:32:25 +02:00
Ponkhy
69a7c4dab4 Improved importBackup handling 2021-09-07 23:14:44 +02:00
jtagcat
2c3880a326 Added Estonian.
- "Avg.": using ≈, as it's commonly enough known.
   There is no known shorthand for 'average' that I know of.

- Leaved some strings empty, as repeating the same words is useless, just noise.
2021-09-07 23:31:48 +03:00
Ponkhy
f4e885982d Added export/import description for de-DE 2021-09-07 21:10:47 +02:00
Ioma Taani
dcf6dd8b32 Ultimi aggiustamenti 2021-09-07 19:16:29 +02:00
Ioma Taani
093cfec8dd Typo. 2021-09-07 19:13:04 +02:00
Ioma Taani
f13dcb3c5b Update it-IT.js
Completed.
2021-09-07 19:07:50 +02:00
LouisLam
1a5ffd4755 add description for export/import 2021-09-08 00:46:48 +08:00
Ioma Taani
a2f9e9e9c4 Creata lingua italiana 2021-09-07 18:25:24 +02:00
LouisLam
62712f5cc4 Merge branch 'master' into import-export 2021-09-08 00:18:43 +08:00
LouisLam
f0f1847ad8 update language files 2021-09-07 23:53:20 +08:00
LouisLam
0aeaf87f5b Merge branch 'master' into default-notification
# Conflicts:
#	server/notification.js
#	src/components/NotificationDialog.vue
2021-09-07 23:40:42 +08:00
LouisLam
5ca0dd628d remove debug msg 2021-09-07 23:36:55 +08:00
LouisLam
4a106431f3 convert Telegram into a vue components 2021-09-07 23:32:05 +08:00
LouisLam
d164b6ccce prevent Chrome ask for saving password for notification settings (change to one-time-code to solve it) 2021-09-07 23:06:49 +08:00
LouisLam
da74391c3e convert notifications into modules 2021-09-07 22:42:46 +08:00
Louis Lam
850563442a Merge pull request #349 from Ponkhy/redirect-after-setup
Auto login after setup
2021-09-07 21:16:54 +08:00
Ponkhy
242e494cb5 Merge branch 'master' into import-export 2021-09-07 14:12:53 +02:00
Ponkhy
4faa409027 Merge branch 'master' into default-notification 2021-09-07 14:07:50 +02:00
Louis Lam
f842fb409e Update README.md 2021-09-07 17:44:51 +08:00
Louis Lam
72dd894856 Update README.md 2021-09-07 17:44:39 +08:00
LouisLam
0b0417e064 Merge remote-tracking branch 'origin/master' 2021-09-07 17:40:36 +08:00
LouisLam
fe35d95441 merge language files 2021-09-07 17:38:04 +08:00
LouisLam
da131a5156 Merge branch 'master' into clear-monitor-data
# Conflicts:
#	src/languages/da-DK.js
#	src/languages/en.js
#	src/languages/es-ES.js
#	src/languages/fr-FR.js
#	src/languages/ja.js
#	src/languages/ko-KR.js
#	src/languages/nl-NL.js
#	src/languages/ru-RU.js
#	src/languages/sr-latn.js
#	src/languages/sr.js
#	src/languages/sv-SE.js
#	src/languages/zh-CN.js
#	src/languages/zh-HK.js
2021-09-07 17:36:37 +08:00
Louis Lam
926c15ea40 Update healthcheck.js 2021-09-07 17:30:50 +08:00
LouisLam
87e874406a Merge branch 'Ponkhy_master_hidden' 2021-09-07 17:09:24 +08:00
LouisLam
0e288ea92d disable chrome save password dialog for autocompelete = off only 2021-09-07 17:06:33 +08:00
LouisLam
8a4a87716f disable chrome save password dialog 2021-09-07 16:46:42 +08:00
Ponkhy
d01fa9bcfc Auto login after setup 2021-09-07 10:25:25 +02:00
LouisLam
ec8c1cad31 update healthcheck.js for changing ssl cert / hostname / port 2021-09-07 16:06:28 +08:00
LouisLam
fb0fa2a843 add sqlite cli to docker 2021-09-07 15:36:29 +08:00
LouisLam
f5c6d422d5 switch debian to be the main docker base 2021-09-07 15:31:32 +08:00
Louis Lam
ea4240455f Merge pull request #347 from gaby/refactor-docker
Debian Dockerfile refactoring and improvements
2021-09-07 15:21:19 +08:00
Juan Calderon-Perez
ce30ee74e9 Revert changes breaking ARM releases 2021-09-07 01:26:42 -04:00
Juan Calderon-Perez
66b5f157eb Update Dockerfile order to improve layer caching. 2021-09-07 00:13:07 -04:00
Juan Calderon-Perez
5fe8a0917a Refactor Debian Dockerfile 2021-09-06 22:20:12 -04:00
Ponkhy
848296b77a Changed input for sensitive data to HiddenInput 2021-09-07 02:30:53 +02:00
Ponkhy
edfaacbb5f Added component HiddenInput.vue 2021-09-07 02:24:02 +02:00
Ponkhy
bb8385d690 Merge branch 'default-notification' of https://github.com/Ponkhy/uptime-kuma into default-notification 2021-09-06 09:46:36 +02:00
Ponkhy
b95404b6e0 Fixed suggestions 2021-09-06 09:45:52 +02:00
Louis Lam
cc351b2883 Merge pull request #339 from Minvinea/master
Update few words and file name
2021-09-06 13:38:26 +08:00
Louis Lam
3c06f249ca Merge pull request #338 from Saibamen/pl_fixes
[LANG] `pl` fixes
2021-09-06 12:44:56 +08:00
Minvinea
31648dc5e8 Update #2 2021-09-05 23:41:57 +02:00
Minvinea
1197cfa11e Update file name 2021-09-05 23:37:26 +02:00
Ponkhy
fd8c95d64e Merge branch 'master' into default-notification 2021-09-05 23:32:31 +02:00
Ponkhy
58240aceef Added the option for default notifications 2021-09-05 23:23:06 +02:00
Adam Stachowicz
e8b814733d Fix typo 2021-09-05 23:14:56 +02:00
Minvinea
dcc7799108 Update few words and name of file
https://prnt.sc/1rbosiy
2021-09-05 21:34:32 +02:00
Adam Stachowicz
424f20f8e1 Better upsideDownModeDescription 2021-09-05 20:59:04 +02:00
Adam Stachowicz
d4ff5d8b32 [LANG] pl fixes 2021-09-05 20:55:25 +02:00
LouisLam
899b33b3a9 add language missing keys 2021-09-06 01:31:05 +08:00
Ponkhy
1b8b33c4c3 Added set language to the Setup.vue 2021-09-05 15:40:35 +02:00
Louis Lam
d34ed00841 Merge pull request #333 from Misly16/master
Add Polish Locale
2021-09-05 12:03:07 +08:00
Louis Lam
f9c177b150 Merge pull request #315 from tgxn/patch-1
Notifications Content Improvements: SMTP, LunaSea, PushBullet
2021-09-05 11:52:50 +08:00
Misly
9952463350 Add Polish Locale 2021-09-04 20:38:53 +00:00
LouisLam
5837c353b7 change the default theme to auto from light 2021-09-05 02:47:31 +08:00
LouisLam
d5b32ffbb8 add language missing keys 2021-09-05 02:25:40 +08:00
LouisLam
61936ae5eb Merge remote-tracking branch 'Ponkhy/clear-monitor-data' into clear-monitor-data 2021-09-05 02:04:48 +08:00
LouisLam
299506ce45 reset the heartbeat list instead of reload the page after cleared events or heartbeats 2021-09-05 02:03:40 +08:00
LouisLam
ca8d4ab61b Merge branch 'master' into clear-monitor-data 2021-09-05 00:24:09 +08:00
LouisLam
ffbdf97478 improve heartbeat bar rendering in different dpi 2021-09-05 00:08:24 +08:00
LouisLam
cc25787878 improve heartbeat bar rendering in different dpi 2021-09-04 23:32:46 +08:00
Louis Lam
17453a8812 Merge pull request #332 from LeviSnoot/master
Update & Improve Swedish Translation
2021-09-04 23:16:01 +08:00
Levi
f1a151b4a1 Update & Improve Swedish Translation 2021-09-04 16:03:34 +02:00
Levi
d35b205fcc Update & Improve Swedish Translation 2021-09-04 16:01:41 +02:00
Levi
2a34e41d8c Improved Swedish translation 2021-09-04 15:59:49 +02:00
Domenic Horner
41d32bb9dd Undo parm changes 2021-09-04 20:08:18 +08:00
Ponkhy
0b9e410ea5 Added i18n key to en.js 2021-09-04 13:36:08 +02:00
Ponkhy
904ed326ca Merge branch 'louislam:master' into clear-monitor-data 2021-09-04 13:34:29 +02:00
LouisLam
778995a4fb add dockerfile-debian 2021-09-04 15:48:25 +08:00
LouisLam
ee60d74910 fix dockerfile path 2021-09-04 15:32:55 +08:00
LouisLam
6a603203cc update to 1.5.3 2021-09-04 15:07:32 +08:00
LouisLam
4b8e7fcffc update "build-docker" to build both platforms 2021-09-04 15:03:26 +08:00
LouisLam
7fd12f5485 rename nl_NL.js to nl-NL.js 2021-09-04 14:42:55 +08:00
LouisLam
0d87a6dfea update vue to 3.2.8 2021-09-04 14:41:45 +08:00
Domenic Horner
b0acda52f9 Add time to smtp body content 2021-09-04 11:27:18 +08:00
Domenic Horner
e9cd9be03a Use constants for UP/DOWN through notifications class 2021-09-04 11:09:34 +08:00
Domenic Horner
6ae279c7f3 Move title generation to notification class 2021-09-04 11:06:06 +08:00
Domenic Horner
9c32adfb55 Update pushbullet down body
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-04 18:26:44 +08:00
Domenic Horner
d346afd33b Update pushbullet up body
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-04 18:26:38 +08:00
Domenic Horner
3bf380c684 Update lunasea "up" body content
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-04 18:26:10 +08:00
Domenic Horner
dca5c59982 Update lunasea body content
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-04 18:24:43 +08:00
Ponkhy
8f9a973ede Merge branch 'master' into import-export 2021-09-04 01:26:48 +02:00
Ponkhy
b98ec0c3d4 Added i18n keys if necessary for some languages 2021-09-04 01:26:06 +02:00
Louis Lam
2082c3f68a Merge pull request #313 from xoniq/lang-nl
Add Dutch/Nederlands language (nl_NL)
2021-09-04 00:05:05 +08:00
Louis Lam
ea9f434553 Merge pull request #328 from Ponkhy/german-language
Small german language adjustments
2021-09-03 23:06:32 +08:00
Louis Lam
fa1216c198 Merge pull request #329 from dhfhfk/New-string
Add missing strings for Korean
2021-09-03 23:05:48 +08:00
dhfhfk
6d8aa20fc6 Add missing string for Korean
confirmDisableAuth, new string
2021-09-03 23:18:18 +09:00
Ponkhy
5430da955a Small german language adjustments 2021-09-03 15:54:14 +02:00
J Posthuma
e6e2b0ebf8 Update nl_NL.js 2021-09-03 13:42:14 +02:00
J Posthuma
445dc486be Merge branch 'master' into lang-nl 2021-09-03 13:41:10 +02:00
Louis Lam
d2151737c1 Update README.md 2021-09-03 17:09:11 +08:00
Louis Lam
78fc6de542 Merge pull request #325 from Saibamen/patch-1
Update `README.md` - Open Collective URL
2021-09-03 15:49:23 +08:00
Adam Stachowicz
c15e6631ae Update README.md
Open Collective URL
2021-09-03 09:16:54 +02:00
LouisLam
ebf362754c Merge branch 'master' into clear-monitor-data 2021-09-03 14:56:43 +08:00
Louis Lam
f84135ca67 Merge pull request #322 from chakflying/patch-6
Fix: Fix Notification "Test" btn styling
2021-09-03 14:34:39 +08:00
LouisLam
80eca886ce Merge branch 'add-serbian-latin'
# Conflicts:
#	src/main.js
#	src/pages/Settings.vue
2021-09-03 14:32:12 +08:00
Louis Lam
274a9050c0 Merge pull request #321 from dusansimic/add-serbian-cyrillic
Add Serbian Cyrillic locale
2021-09-03 14:00:43 +08:00
Louis Lam
1f4ad938b1 Merge pull request #323 from xinac721/master
Update Chinese Translation(更新简体中文语言翻译)
2021-09-03 13:55:30 +08:00
新逸Cary
312aedf391 Update Chinese Translation(更新简体中文语言翻译) 2021-09-03 13:33:26 +08:00
Louis Lam
4f28bfbde3 Merge pull request #309 from Saibamen/updateDependencies
Update dependencies
2021-09-03 12:56:55 +08:00
Louis Lam
ee76ab4ec2 Merge pull request #317 from AtaxyaNetwork/master
change translation FR
2021-09-03 12:56:11 +08:00
Nelson Chan
18616ee590 Fix: Fix Notification "Test" btn styling 2021-09-03 12:05:49 +08:00
Dušan Simić
19a4d570ec Fix word capitalization 2021-09-03 02:24:31 +02:00
Dušan Simić
79fda8f442 Add Serbian Latin locale 2021-09-03 02:20:01 +02:00
Dušan Simić
53a14cf4f5 Add Serbian Cyrilic locale 2021-09-03 02:06:26 +02:00
cecile
2241d8817f change translation FR 2021-09-02 23:41:47 +02:00
Louis Lam
3831dfe0b9 Update README.md 2021-09-03 02:55:36 +08:00
Louis Lam
fdde862549 Merge pull request #316 from aictur/feature/es-ES
Add spanish language and update Readme
2021-09-02 23:00:36 +08:00
LouisLam
e31be8caf5 demo mode 2021-09-02 22:52:20 +08:00
LouisLam
60f2f08cea add demo db 2021-09-02 22:39:04 +08:00
LouisLam
b1647a310e add demo db 2021-09-02 22:37:51 +08:00
Victor M. Vicente Cuevas
23f1a73fc8 Add language to src/main and update README 2021-09-02 16:31:45 +02:00
Victor M. Vicente Cuevas
d2bf2a551d Add spanish language and update Readme 2021-09-02 16:23:07 +02:00
Ponkhy
7d70c4d8cd Code optimizations 2021-09-02 16:13:31 +02:00
Domenic Horner
532ad3044c Add space to pushbullet and lunasea notifications
Start changes regarding standardization of notification messages
2021-09-02 21:55:25 +08:00
LouisLam
f23ecef636 add missing cert parameters 2021-09-02 21:16:04 +08:00
LouisLam
51cf2ff6f9 add missing cert parameters 2021-09-02 21:13:59 +08:00
LouisLam
b30b1d3a52 create data dir before copy 2021-09-02 21:11:20 +08:00
LouisLam
582e14098d create data dir before copy 2021-09-02 21:10:18 +08:00
LouisLam
6e3e2fc85c fix db path 2021-09-02 21:08:00 +08:00
LouisLam
b604807cfe create data dir if not exists 2021-09-02 20:42:55 +08:00
LouisLam
3ee13bddd1 dash style for args 2021-09-02 20:36:52 +08:00
LouisLam
c74986647e allow changing data dir 2021-09-02 20:27:18 +08:00
LouisLam
dac410c850 Merge remote-tracking branch 'origin/master' 2021-09-02 20:20:47 +08:00
LouisLam
b88b357b55 add support for https 2021-09-02 20:18:27 +08:00
Louis Lam
9a5cd5172b Merge pull request #314 from xinac721/master
Chinese Translation (from zh-CN.js)
2021-09-02 20:14:05 +08:00
新逸Cary
941788db49 Chinese Translation (from zh-CN.js) 2021-09-02 18:33:09 +08:00
Jelle Posthuma
99725aabe7 Add Dutch/Nederlands language (nl_NL) 2021-09-02 11:10:54 +02:00
Ponkhy
2dd392e609 Added uptime kuma version to backup file 2021-09-02 10:15:25 +02:00
Louis Lam
9d41da4aa2 Merge pull request #311 from xinac721/master
Chinese Translation (from zh-CN.js)
2021-09-02 15:59:06 +08:00
新逸Cary
a0f372e946 Chinese Translation (from zh-CN.js) 2021-09-02 15:34:46 +08:00
新逸Cary
877ad1438f Merge branch 'louislam:master' into master 2021-09-02 15:25:09 +08:00
LouisLam
9116654a33 update language files and run eslint on these 2021-09-02 15:21:46 +08:00
新逸Cary
dbc70bc5ed Merge pull request #1 from louislam/master
update
2021-09-02 14:33:34 +08:00
Louis Lam
a29b65e801 Merge pull request #308 from DX37/translation-ru
Add Russian translation
2021-09-02 12:56:12 +08:00
Louis Lam
bd9568ce5b Merge pull request #310 from Saibamen/more_i18n
Make `Resp. Time (ms)` and `N/A` i18n
2021-09-02 12:55:11 +08:00
Ponkhy
c13cc62d3d Update server/server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-09-01 21:32:33 +02:00
Adam Stachowicz
7a109689d9 Make Resp. Time (ms) and N/A i18n 2021-09-01 21:17:50 +02:00
Adam Stachowicz
e7929f461d Update dependencies 2021-09-01 20:53:12 +02:00
DX37
a2cf7f394e Add Russian translation 2021-09-02 01:37:01 +07:00
Louis Lam
e1f378ee6c Update README.md 2021-09-02 02:11:44 +08:00
LouisLam
eeb00a5511 fix data type 2021-09-02 01:56:02 +08:00
LouisLam
8e1e4b9204 override file-selector-button hover color 2021-09-02 01:51:51 +08:00
LouisLam
66f9ad5754 Merge branch 'master' into import-export 2021-09-02 01:35:42 +08:00
Louis Lam
129dc3324b Merge pull request #307 from dhfhfk/master
Added Korean Language
2021-09-02 01:17:54 +08:00
Louis Lam
268d2e2353 Merge branch 'master' into master 2021-09-02 01:17:48 +08:00
Louis Lam
7d2cf0ee57 Merge pull request #304 from LeviSnoot/master
Added Swedish Language
2021-09-02 01:17:10 +08:00
오로라
2d408732db Update main.js
Prevent conflict with #304 (Added Swedish Language)
2021-09-02 01:01:18 +09:00
dhfhfk
0cc5053f14 Update ko-KR.js 2021-09-02 00:35:50 +09:00
dhfhfk
1c4e5b79be Create ko-KR.js 2021-09-02 00:30:08 +09:00
dhfhfk
4aae402b36 Update main.js 2021-09-02 00:29:01 +09:00
Ponkhy
b604910bbb Merge branch 'master' into clear-monitor-data 2021-09-01 17:17:40 +02:00
Ponkhy
2f6c5963c5 Added import and export function 2021-09-01 17:09:32 +02:00
Levi
dce2ba8f9f Update sv-SE.js 2021-09-01 16:41:51 +02:00
Levi
ed5c75282c Update main.js 2021-09-01 16:38:05 +02:00
Levi
00ac560bd6 Create sv-SE.js 2021-09-01 16:30:08 +02:00
Louis Lam
8ea4dec5a0 Merge pull request #303 from Saibamen/patch-1
Update translation docs
2021-09-01 22:08:44 +08:00
Adam Stachowicz
3006d13aee Update translation docs 2021-09-01 15:37:56 +02:00
LouisLam
c7e6cb9f37 update language files 2021-09-01 20:10:54 +08:00
LouisLam
9b82bb248e Merge branch 'master' into kometchtech_patch-1
# Conflicts:
#	src/main.js
2021-09-01 19:46:44 +08:00
Louis Lam
e55cf65f28 Merge pull request #291 from Ponkhy/master
Added translation to Setup.vue
2021-09-01 19:45:20 +08:00
Louis Lam
f24002838c Merge branch 'master' into master 2021-09-01 19:45:06 +08:00
Louis Lam
b4fd4f7d90 Merge pull request #301 from Lrss/master
Added Danish language
2021-09-01 19:44:01 +08:00
kometchtech
3a3f1762ac Update ja.js
* Perform translation for added keys.
2021-09-01 19:04:35 +09:00
Lars Sørensen
6376d4eb92 Translated to Danish 2021-09-01 11:51:22 +02:00
LouisLam
e37cf9b1a7 add missing and add Japanese to the list 2021-09-01 17:32:41 +08:00
LouisLam
3b0191210b Merge branch 'master' into kometchtech_patch-1 2021-09-01 17:28:49 +08:00
LouisLam
923d325b44 add language file preparation script 2021-09-01 17:10:12 +08:00
LouisLam
ad38e61c26 add language file preparation script 2021-09-01 17:08:27 +08:00
LouisLam
22095c09b1 add translation guide 2021-09-01 15:49:35 +08:00
kometchtech
25db162721 Create ja.js
* Created a Japanese language file.
2021-09-01 13:23:44 +09:00
Ponkhy
7b92166d18 Added clear all db statistics function 2021-09-01 00:36:24 +02:00
Ponkhy
1341d220ed Merge branch 'louislam:master' into clear-monitor-data 2021-08-31 23:22:45 +02:00
Ponkhy
14b9afb400 Added translation to Setup.vue 2021-08-29 23:07:58 +02:00
Ponkhy
1952e34110 Added the possibility to clear monitor data 2021-08-29 18:47:01 +02:00
Nelson Chan
b9a6088f50 WIP: Add login page, nav bar buttons 2021-07-30 15:27:18 +08:00
202 changed files with 27370 additions and 5801 deletions

View File

@@ -2,8 +2,12 @@
/dist
/node_modules
/data
/out
/test
/kubernetes
/.do
**/.dockerignore
/private
**/.git
**/.gitignore
**/docker-compose*
@@ -15,11 +19,16 @@ README.md
.eslint*
.stylelint*
/.github
package-lock.json
yarn.lock
app.json
CODE_OF_CONDUCT.md
CONTRIBUTING.md
CNAME
install.sh
SECURITY.md
tsconfig.json
.env
/tmp
### .gitignore content (commented rules are duplicated)

View File

@@ -1,4 +1,5 @@
module.exports = {
root: true,
env: {
browser: true,
commonjs: true,
@@ -16,8 +17,10 @@ module.exports = {
requireConfigFile: false,
},
rules: {
"linebreak-style": ["error", "unix"],
"camelcase": ["warn", {
"properties": "never"
"properties": "never",
"ignoreImports": true
}],
// override/add rules settings here, such as:
// 'vue/no-unused-vars': 'error'
@@ -31,11 +34,12 @@ module.exports = {
},
],
quotes: ["warn", "double"],
//semi: ['off', 'never'],
semi: "warn",
"vue/html-indent": ["warn", 4], // default: 2
"vue/max-attributes-per-line": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/html-self-closing": "off",
"vue/attribute-hyphenation": "off", // This change noNL to "no-n-l" unexpectedly
"no-multi-spaces": ["error", {
ignoreEOLComments: true,
}],
@@ -81,4 +85,29 @@ module.exports = {
"one-var": ["error", "never"],
"max-statements-per-line": ["error", { "max": 1 }]
},
}
"overrides": [
{
"files": [ "src/languages/*.js", "src/icon.js" ],
"rules": {
"comma-dangle": ["error", "always-multiline"],
}
},
// Override for jest puppeteer
{
"files": [
"**/*.spec.js",
"**/*.spec.jsx"
],
env: {
jest: true,
},
globals: {
page: true,
browser: true,
context: true,
jestPuppeteer: true,
},
}
]
};

2
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: louislam # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
open_collective: uptime-kuma # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username

View File

@@ -9,9 +9,13 @@ assignees: ''
**Is it a duplicate question?**
Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q=
**Describe your problem**
Please describe what you are asking for
**Info**
Uptime Kuma Version:
Using Docker?: Yes/No
Docker Version:
Node.js Version (Without Docker only):
OS:
Browser:

View File

@@ -15,6 +15,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -23,12 +24,13 @@ Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Info**
- Uptime Kuma Version:
- Using Docker?: Yes/No
- OS:
- Browser:
Uptime Kuma Version:
Using Docker?: Yes/No
Docker Version:
Node.js Version (Without Docker only):
OS:
Browser:
**Screenshots**
If applicable, add screenshots to help explain your problem.
@@ -36,3 +38,5 @@ If applicable, add screenshots to help explain your problem.
**Error Log**
It is easier for us to find out the problem.
Docker: `docker logs <container id>`
PM2: `~/.pm2/logs/` (e.g. `/home/ubuntu/.pm2/logs`)

35
.github/workflows/auto-test.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Auto Test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
auto-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
node-version: [14.x, 16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm run install-legacy
- run: npm run build
- run: npm test
env:
HEADLESS_TEST: 1
JUST_FOR_TEST: ${{ secrets.JUST_FOR_TEST }}

7
.gitignore vendored
View File

@@ -7,4 +7,9 @@ dist-ssr
/data
!/data/.gitkeep
.vscode
.vscode
/private
/out
/tmp
.env

View File

@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
louis@uptimekuma.louislam.net.
uptime@kuma.pet.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within

View File

@@ -2,58 +2,77 @@
First of all, thank you everyone who made pull requests for Uptime Kuma, I never thought GitHub Community can be that nice! And also because of this, I also never thought other people actually read my code and edit my code. It is not structed and commented so well, lol. Sorry about that.
The project was created with vite.js (vue3). Then I created a sub-directory called "server" for server part. Both frontend and backend share the same package.json.
The project was created with vite.js (vue3). Then I created a sub-directory called "server" for server part. Both frontend and backend share the same package.json.
The frontend code build into "dist" directory. The server uses "dist" as root. This is how production is working.
The frontend code build into "dist" directory. The server (express.js) exposes the "dist" directory as root of the endpoint. This is how production is working.
# Can I create a pull request for Uptime Kuma?
## Key Technical Skills
Generally, if the pull request is working fine and it do not affect any existing logic, workflow and perfomance, I will merge to the master branch once it is tested.
- Node.js (You should know what are promise, async/await and arrow function etc.)
- Socket.io
- SCSS
- Vue.js
- Bootstrap
- SQLite
## Directories
- data (App data)
- dist (Frontend build)
- 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?
Generally, if the pull request is working fine and it do not affect any existing logic, workflow and perfomance, I will merge into the master branch once it is tested.
If you are not sure, feel free to create an empty pull request draft first.
## Pull Request Examples
### Pull Request Examples
### ✅ High - Medium Priority
#### ✅ High - Medium Priority
- Add a new notification
- Add a chart
- Fix a bug
- Translations
### *️⃣ Requires one more reviewer
#### *️⃣ Requires one more reviewer
I do not have such knowledge to test it.
- Add k8s supports
- Add k8s supports
### *️⃣ Low Priority
#### *️⃣ Low Priority
It changed my current workflow and require further studies.
- Change my release approach
### ❌ Won't Merge
#### ❌ Won't Merge
- Duplicated pull request
- Buggy
- Existing logic is completely modified or deleted
- A function that is completely out of scope
# Project Styles
## Project Styles
I personally do not like something need to learn so much and need to config so much before you can finally start the app.
For example, recently, because I am not a python expert, I spent a 2 hours to resolve all problems in order to install and use the Apprise cli. Apprise requires so many hidden requirements, I have to figure out myself how to solve the problems by Google search for my OS. That is painful. I do not want Uptime Kuma to be like this way, so:
I personally do not like something need to learn so much and need to config so much before you can finally start the app.
- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort to get it run
- Single container for Docker users, no very complex docker-composer file. Just map the volume and expose the port, then good to go
- All settings in frontend.
- Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go
- Settings should be configurable in the frontend. Env var is not encouraged.
- Easy to use
# Coding Styles
## Coding Styles
- Follow .editorconfig
- Follow eslint
- 4 spaces indentation
- Follow `.editorconfig`
- Follow ESLint
## Name convention
@@ -61,92 +80,101 @@ For example, recently, because I am not a python expert, I spent a 2 hours to re
- SQLite: underscore_type
- CSS/SCSS: dash-type
# Tools
## Tools
- Node.js >= 14
- Git
- IDE that supports .editorconfig and eslint (I am using Intellji Idea)
- A SQLite tool (I am using SQLite Expert Personal)
- IDE that supports ESLint and EditorConfig (I am using Intellji Idea)
- A SQLite tool (SQLite Expert Personal is suggested)
# Install dependencies
## Install dependencies
```bash
npm install --dev
npm ci
```
For npm@7, you need --legacy-peer-deps
## How to start the Backend Dev Server
```
npm install --legacy-peer-deps --dev
```
# Backend Dev
(2021-09-23 Update)
```bash
npm run start-server
# Or
node server/server.js
npm run start-server-dev
```
It binds to 0.0.0.0:3001 by default.
It binds to `0.0.0.0:3001` by default.
## Backend Details
### Backend Details
It is mainly a socket.io app + express.js.
express.js is just used for serving the frontend built files (index.html, .js and .css etc.)
express.js is just used for serving the frontend built files (index.html, .js and .css etc.)
# Frontend Dev
- model/ (Object model, auto mapping to the database table name)
- modules/ (Modified 3rd-party modules)
- notification-providers/ (indivdual notification logic)
- routers/ (Express Routers)
- scoket-handler (Socket.io Handlers)
- server.js (Server main logic)
Start frontend dev server. Hot-reload enabled in this way. It binds to 0.0.0.0:3000.
## How to start the Frontend Dev Server
```bash
npm run dev
```
1. Set the env var `NODE_ENV` to "development".
2. Start the frontend dev server by the following command.
PS: You can ignore those scss warnings, those warnings are from Bootstrap that I cannot fix.
```bash
npm run dev
```
You can use Vue Devtool Chrome extension for debugging.
It binds to `0.0.0.0:3000` by default.
After the frontend server started. It cannot connect to the websocket server even you have started the server. You need to tell the frontend that is a dev env by running this in DevTool console and refresh:
You can use Vue.js devtools Chrome extension for debugging.
```javascript
localStorage.dev = "dev";
```
So that the frontend will try to connect websocket server in 3001.
Alternately, you can specific NODE_ENV to "development".
## Build the frontend
### Build the frontend
```bash
npm run build
```
## Frontend Details
### Frontend Details
Uptime Kuma Frontend is a single page application (SPA). Most paths are handled by Vue Router.
The router in "src/main.js"
The router is in `src/router.js`
As you can see, most data in frontend is stored in root level, even though you changed the current router to any other pages.
The data and socket logic in "src/mixins/socket.js"
The data and socket logic are in `src/mixins/socket.js`.
# Database Migration
## Database Migration
1. create `patch{num}.sql` in `./db/`
1. update `latestVersion` in `./server/database.js`
1. Create `patch-{name}.sql` in `./db/`
2. Add your patch filename in the `patchList` list in `./server/database.js`
# Unit Test
## Unit Test
Yes, no unit test for now. I know it is very important, but at the same time my spare time is very limited. I want to implement my ideas first. I will go back to this in some points.
It is an end-to-end testing. It is using Jest and Puppeteer.
```bash
npm run build
npm test
```
By default, the Chromium window will be shown up during the test. Specifying `HEADLESS_TEST=1` for terminal environments.
## Update Dependencies
Install `ncu`
https://github.com/raineorshine/npm-check-updates
```bash
ncu -u -t patch
npm install
```
Since previously updating vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only.
Patch release = the third digit ([Semantic Versioning](https://semver.org/))
## Translations
Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages

View File

@@ -1,6 +1,6 @@
# Uptime Kuma
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a>
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Backers&color=brightgreen" /></a>
<div align="center" width="100%">
<img src="./public/icon.svg" width="128" alt="" />
@@ -8,14 +8,28 @@
It is a self-hosted monitoring tool like "Uptime Robot".
<img src="https://louislam.net/uptimekuma/1.jpg" width="512" alt="" />
<img src="https://uptime.kuma.pet/img/dark.jpg" width="700" alt="" />
## 🥔 Live Demo
Try it!
https://demo.uptime.kuma.pet
It is a temporary live demo, all data will be deleted after 10 minutes. The server is located at Tokyo, so if you live far from there it may affect your experience. I suggest that you should install and try it out for the best demo experience.
VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollective.com/uptime-kuma)! Thank you so much!
## ⭐ Features
* Monitoring uptime for HTTP(s) / TCP / Ping / DNS Record.
* Monitoring uptime for HTTP(s) / TCP / Ping / DNS Record / Push.
* Fancy, Reactive, Fast UI/UX.
* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [70+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/issues/284).
* 20 seconds interval.
* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [70+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications).
* 20 second intervals.
* [Multi Languages](https://github.com/louislam/uptime-kuma/tree/master/src/languages)
* Simple Status Page
* Ping Chart
* Certificate Info
## 🔧 How to Install
@@ -26,13 +40,16 @@ docker volume create uptime-kuma
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
```
Browse to http://localhost:3001 after started.
Browse to http://localhost:3001 after starting.
### 💪🏻 Without Docker
Required Tools: Node.js >= 14, git and pm2.
```bash
# Update your npm to the latest version
npm install npm -g
git clone https://github.com/louislam/uptime-kuma.git
cd uptime-kuma
npm run setup
@@ -41,11 +58,11 @@ npm run setup
node server/server.js
# (Recommended) Option 2. Run in background using PM2
# Install PM2 if you don't have: npm install pm2 -g
# Install PM2 if you don't have it: npm install pm2 -g
pm2 start server/server.js --name uptime-kuma
```
Browse to http://localhost:3001 after started.
Browse to http://localhost:3001 after starting.
### Advanced Installation
@@ -53,7 +70,6 @@ If you need more options or need to browse via a reserve proxy, please read:
https://github.com/louislam/uptime-kuma/wiki/%F0%9F%94%A7-How-to-Install
## 🆙 How to Update
Please read:
@@ -66,11 +82,19 @@ I will mark requests/issues to the next milestone.
https://github.com/louislam/uptime-kuma/milestones
Project Plan:
https://github.com/louislam/uptime-kuma/projects/1
## 🖼 More Screenshots
Dark Mode:
Light Mode:
<img src="https://user-images.githubusercontent.com/1336778/128710166-908f8d88-9256-43f3-9c49-bfc2c56011d2.png" width="400" alt="" />
<img src="https://uptime.kuma.pet/img/light.jpg" width="512" alt="" />
Status Page:
<img src="https://user-images.githubusercontent.com/1336778/134628766-a3fe0981-0926-4285-ab46-891a21c3e4cb.png" width="512" alt="" />
Settings Page:
@@ -91,9 +115,23 @@ Telegram Notification Sample:
If you love this project, please consider giving me a ⭐.
## 🗣️ Discussion
### Issues Page
You can discuss or ask for help in [Issues](https://github.com/louislam/uptime-kuma/issues).
### Subreddit
My Reddit account: louislamlam
You can mention me if you ask a question on Reddit.
https://www.reddit.com/r/UptimeKuma/
## Contribute
If you want to report a bug or request a new feature. Free feel to open a new issue.
If you want to report a bug or request a new feature. Free feel to open a [new issue](https://github.com/louislam/uptime-kuma/issues).
If you want to translate Uptime Kuma into your langauge, please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages
If you want to modify Uptime Kuma, this guideline may be useful for you: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md

View File

@@ -5,10 +5,27 @@
Use this section to tell people about which versions of your project are
currently being supported with security updates.
### Uptime Kuma Versions
| Version | Supported |
| ------- | ------------------ |
| 1.x.x | :white_check_mark: |
| 1.8.X | :white_check_mark: |
| <= 1.7.X | ❌ |
### Upgradable Docker Tags
| Tag | Supported |
| ------- | ------------------ |
| 1 | :white_check_mark: |
| 1-debian | :white_check_mark: |
| 1-alpine | :white_check_mark: |
| latest | :white_check_mark: |
| debian | :white_check_mark: |
| alpine | :white_check_mark: |
| All other tags | ❌ |
## Reporting a Vulnerability
https://github.com/louislam/uptime-kuma/issues
Please report security issues to uptime@kuma.pet.
Do not use the issue tracker or discuss it in the public as it will cause more damage.

11
babel.config.js Normal file
View File

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

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
module.exports = {
"launch": {
"headless": process.env.HEADLESS_TEST || false,
"userDataDir": "./data/test-chrome-profile",
}
};

11
config/jest.config.js Normal file
View File

@@ -0,0 +1,11 @@
module.exports = {
"verbose": true,
"preset": "jest-puppeteer",
"globals": {
"__DEV__": true
},
"testRegex": "./test/e2e.spec.js",
"rootDir": "..",
"testTimeout": 30000,
};

24
config/vite.config.js Normal file
View File

@@ -0,0 +1,24 @@
import legacy from "@vitejs/plugin-legacy";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
const postCssScss = require("postcss-scss");
const postcssRTLCSS = require("postcss-rtlcss");
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ["ie > 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"]
})
],
css: {
postcss: {
"parser": postCssScss,
"map": false,
"plugins": [postcssRTLCSS]
}
},
});

10
db/patch-2fa.sql Normal file
View File

@@ -0,0 +1,10 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE user
ADD twofa_secret VARCHAR(64);
ALTER TABLE user
ADD twofa_status BOOLEAN default 0 NOT NULL;
COMMIT;

View File

@@ -0,0 +1,7 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD retry_interval INTEGER default 0 not null;
COMMIT;

30
db/patch-group-table.sql Normal file
View File

@@ -0,0 +1,30 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
create table `group`
(
id INTEGER not null
constraint group_pk
primary key autoincrement,
name VARCHAR(255) not null,
created_date DATETIME default (DATETIME('now')) not null,
public BOOLEAN default 0 not null,
active BOOLEAN default 1 not null,
weight BOOLEAN NOT NULL DEFAULT 1000
);
CREATE TABLE [monitor_group]
(
[id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[monitor_id] INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE,
[group_id] INTEGER NOT NULL REFERENCES [group] ([id]) ON DELETE CASCADE ON UPDATE CASCADE,
weight BOOLEAN NOT NULL DEFAULT 1000
);
CREATE INDEX [fk]
ON [monitor_group] (
[monitor_id],
[group_id]);
COMMIT;

View File

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

View File

@@ -0,0 +1,10 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
-- For sendHeartbeatList
CREATE INDEX monitor_time_index ON heartbeat (monitor_id, time);
-- For sendImportantHeartbeatList
CREATE INDEX monitor_important_time_index ON heartbeat (monitor_id, important,time);
COMMIT;

View File

@@ -0,0 +1,18 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
create table incident
(
id INTEGER not null
constraint incident_pk
primary key autoincrement,
title VARCHAR(255) not null,
content TEXT not null,
style VARCHAR(30) default 'warning' not null,
created_date DATETIME default (DATETIME('now')) not null,
last_updated_date DATETIME,
pin BOOLEAN default 1 not null,
active BOOLEAN default 1 not null
);
COMMIT;

View File

@@ -0,0 +1,7 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD push_token VARCHAR(20) DEFAULT NULL;
COMMIT;

View File

@@ -0,0 +1,22 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
-- Generated by Intellij IDEA
create table setting_dg_tmp
(
id INTEGER
primary key autoincrement,
key VARCHAR(200) not null
unique,
value TEXT,
type VARCHAR(20)
);
insert into setting_dg_tmp(id, key, value, type) select id, key, value, type from setting;
drop table setting;
alter table setting_dg_tmp rename to setting;
COMMIT;

19
db/patch10.sql Normal file
View File

@@ -0,0 +1,19 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
CREATE TABLE tag (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
color VARCHAR(255) NOT NULL,
created_date DATETIME DEFAULT (DATETIME('now')) NOT NULL
);
CREATE TABLE monitor_tag (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
monitor_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
value TEXT,
CONSTRAINT FK_tag FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_monitor FOREIGN KEY (monitor_id) REFERENCES monitor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX monitor_tag_monitor_id_index ON monitor_tag (monitor_id);
CREATE INDEX monitor_tag_tag_id_index ON monitor_tag (tag_id);

7
db/patch9.sql Normal file
View File

@@ -0,0 +1,7 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE notification
ADD is_default BOOLEAN default 0 NOT NULL;
COMMIT;

View File

@@ -0,0 +1,8 @@
# DON'T UPDATE TO alpine3.13, 1.14, see #41.
FROM node:14-alpine3.12
WORKDIR /app
# Install apprise, iputils for non-root ping, setpriv
RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \
pip3 --no-cache-dir install apprise && \
rm -rf /root/.cache

View File

@@ -0,0 +1,12 @@
# DON'T UPDATE TO node:14-bullseye-slim, see #372.
# If the image changed, the second stage image should be changed too
FROM node:14-buster-slim
WORKDIR /app
# Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv
# Stupid python3 and python3-pip actually install a lot of useless things into Debian, specific --no-install-recommends to skip them, make the base even smaller than alpine!
RUN apt update && \
apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \
sqlite3 iputils-ping util-linux dumb-init && \
pip3 --no-cache-dir install apprise && \
rm -rf /var/lib/apt/lists/*

51
docker/dockerfile Normal file
View File

@@ -0,0 +1,51 @@
FROM louislam/uptime-kuma:base-debian AS build
WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . .
RUN npm ci && \
npm run build && \
npm ci --production && \
chmod +x /app/extra/entrypoint.sh
FROM louislam/uptime-kuma:base-debian AS release
WORKDIR /app
# Copy app files from build layer
COPY --from=build /app /app
EXPOSE 3001
VOLUME ["/app/data"]
HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD node extra/healthcheck.js
ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"]
CMD ["node", "server/server.js"]
FROM release AS nightly
RUN npm run mark-as-nightly
# Upload the artifact to Github
FROM louislam/uptime-kuma:base-debian AS upload-artifact
WORKDIR /
RUN apt update && \
apt --yes install curl file
ARG GITHUB_TOKEN
ARG TARGETARCH
ARG PLATFORM=debian
ARG VERSION=1.9.0
ARG FILE=$PLATFORM-$TARGETARCH-$VERSION.tar.gz
ARG DIST=dist.tar.gz
COPY --from=build /app /app
RUN chmod +x /app/extra/upload-github-release-asset.sh
# Full Build
# RUN tar -zcvf $FILE app
# RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=$FILE
# Dist only
RUN cd /app && tar -zcvf $DIST dist
RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=/app/$DIST

26
docker/dockerfile-alpine Normal file
View File

@@ -0,0 +1,26 @@
FROM louislam/uptime-kuma:base-alpine AS build
WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . .
RUN npm ci && \
npm run build && \
npm ci --production && \
chmod +x /app/extra/entrypoint.sh
FROM louislam/uptime-kuma:base-alpine AS release
WORKDIR /app
# Copy app files from build layer
COPY --from=build /app /app
EXPOSE 3001
VOLUME ["/app/data"]
HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD node extra/healthcheck.js
ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"]
CMD ["node", "server/server.js"]
FROM release AS nightly
RUN npm run mark-as-nightly

View File

@@ -1,26 +0,0 @@
# DON'T UPDATE TO alpine3.13, 1.14, see #41.
FROM node:14-alpine3.12 AS release
WORKDIR /app
# split the sqlite install here, so that it can caches the arm prebuilt
RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev git && \
ln -s /usr/bin/python3 /usr/bin/python && \
npm install mapbox/node-sqlite3#593c9d && \
apk del .build-deps && \
rm -f /usr/bin/python
# Install apprise
RUN apk add --no-cache python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib
RUN pip3 --no-cache-dir install apprise && \
rm -rf /root/.cache
COPY . .
RUN npm install --legacy-peer-deps && npm run build && npm prune
EXPOSE 3001
VOLUME ["/app/data"]
HEALTHCHECK --interval=60s --timeout=30s --start-period=300s CMD node extra/healthcheck.js
CMD ["node", "server/server.js"]
FROM release AS nightly
RUN npm run mark-as-nightly

6
ecosystem.config.js Normal file
View File

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

57
extra/download-dist.js Normal file
View File

@@ -0,0 +1,57 @@
console.log("Downloading dist");
const https = require("https");
const tar = require("tar");
const packageJSON = require("../package.json");
const fs = require("fs");
const version = packageJSON.version;
const filename = "dist.tar.gz";
const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`;
download(url);
function download(url) {
console.log(url);
https.get(url, (response) => {
if (response.statusCode === 200) {
console.log("Extracting dist...");
if (fs.existsSync("./dist")) {
if (fs.existsSync("./dist-backup")) {
fs.rmdirSync("./dist-backup", {
recursive: true
});
}
fs.renameSync("./dist", "./dist-backup");
}
const tarStream = tar.x({
cwd: "./",
});
tarStream.on("close", () => {
fs.rmdirSync("./dist-backup", {
recursive: true
});
console.log("Done");
});
tarStream.on("error", () => {
if (fs.existsSync("./dist-backup")) {
fs.renameSync("./dist-backup", "./dist");
}
console.log("Done");
});
response.pipe(tarStream);
} else if (response.statusCode === 302) {
download(response.headers.location);
} else {
console.log("dist not found");
}
});
}

21
extra/entrypoint.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env sh
# set -e Exit the script if an error happens
set -e
PUID=${PUID=0}
PGID=${PGID=0}
files_ownership () {
# -h Changes the ownership of an encountered symbolic link and not that of the file or directory pointed to by the symbolic link.
# -R Recursively descends the specified directories
# -c Like verbose but report only when a change is made
chown -hRc "$PUID":"$PGID" /app/data
}
echo "==> Performing startup jobs and maintenance tasks"
files_ownership
echo "==> Starting application with user $PUID group $PGID"
# --clear-groups Clear supplementary groups.
exec setpriv --reuid "$PUID" --regid "$PGID" --clear-groups "$@"

View File

@@ -1,19 +1,34 @@
let http = require("http");
/*
* This script should be run after a period of time (180s), because the server may need some time to prepare.
*/
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
let client;
if (process.env.SSL_KEY && process.env.SSL_CERT) {
client = require("https");
} else {
client = require("http");
}
let options = {
host: "localhost",
port: "3001",
timeout: 2000,
host: process.env.HOST || "127.0.0.1",
port: parseInt(process.env.PORT) || 3001,
timeout: 28 * 1000,
};
let request = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
if (res.statusCode == 200) {
let request = client.request(options, (res) => {
console.log(`Health Check OK [Res Code: ${res.statusCode}]`);
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on("error", function (err) {
console.log("ERROR");
console.error("Health Check ERROR");
process.exit(1);
});
request.end();

View File

@@ -6,54 +6,65 @@ const Database = require("../server/database");
const { R } = require("redbean-node");
const readline = require("readline");
const { initJWTSecret } = require("../server/util-server");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
(async () => {
const main = async () => {
Database.init(args);
await Database.connect();
try {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
rl.close();
break;
} else {
console.log("Passwords do not match, please try again.");
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
}
console.log("Password reset successfully.");
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
// Reset all sessions by reset jwt secret
await initJWTSecret();
break;
} else {
console.log("Passwords do not match, please try again.");
}
}
console.log("Password reset successfully.");
}
} catch (e) {
console.error("Error: " + e.message);
}
await Database.close();
rl.close();
console.log("Finished. You should restart the Uptime Kuma server.")
})();
console.log("Finished.");
};
function question(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
})
});
});
}
if (!process.env.TEST_BACKEND) {
main();
}
module.exports = {
main,
};

View File

@@ -0,0 +1,3 @@
package-lock.json
test.js
languages/

View File

@@ -0,0 +1,86 @@
// Need to use ES6 to read language files
import fs from "fs";
import path from "path";
import util from "util";
// https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js
/**
* Look ma, it's cp -R.
* @param {string} src The path to the thing to copy.
* @param {string} dest The path to the new copy.
*/
const copyRecursiveSync = function (src, dest) {
let exists = fs.existsSync(src);
let stats = exists && fs.statSync(src);
let isDirectory = exists && stats.isDirectory();
if (isDirectory) {
fs.mkdirSync(dest);
fs.readdirSync(src).forEach(function (childItemName) {
copyRecursiveSync(path.join(src, childItemName),
path.join(dest, childItemName));
});
} else {
fs.copyFileSync(src, dest);
}
};
console.log("Arguments:", process.argv);
const baseLangCode = process.argv[2] || "en";
console.log("Base Lang: " + baseLangCode);
if (fs.existsSync("./languages")) {
fs.rmdirSync("./languages", { recursive: true });
}
copyRecursiveSync("../../src/languages", "./languages");
const en = (await import("./languages/en.js")).default;
const baseLang = (await import(`./languages/${baseLangCode}.js`)).default;
const files = fs.readdirSync("./languages");
console.log("Files:", files);
for (const file of files) {
if (!file.endsWith(".js")) {
console.log("Skipping " + file);
continue;
}
console.log("Processing " + file);
const lang = await import("./languages/" + file);
let obj;
if (lang.default) {
obj = lang.default;
} else {
console.log("Empty file");
obj = {
languageName: "<Your Language name in your language (not in English)>"
};
}
// En first
for (const key in en) {
if (! obj[key]) {
obj[key] = en[key];
}
}
if (baseLang !== en) {
// Base second
for (const key in baseLang) {
if (! obj[key]) {
obj[key] = key;
}
}
}
const code = "export default " + util.inspect(obj, {
depth: null,
});
fs.writeFileSync(`../../src/languages/${file}`, code);
}
fs.rmdirSync("./languages", { recursive: true });
console.log("Done. Fixing formatting by ESLint...");

View File

@@ -0,0 +1,12 @@
{
"name": "update-language-files",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@@ -19,16 +19,22 @@ if (! newVersion) {
const exists = tagExists(newVersion);
if (! exists) {
// Process package.json
pkg.version = newVersion;
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion);
pkg.scripts["build-docker-alpine"] = pkg.scripts["build-docker-alpine"].replaceAll(oldVersion, newVersion);
pkg.scripts["build-docker-debian"] = pkg.scripts["build-docker-debian"].replaceAll(oldVersion, newVersion);
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
commit(newVersion);
tag(newVersion);
updateWiki(oldVersion, newVersion);
} else {
console.log("version exists")
console.log("version exists");
}
function commit(version) {
@@ -36,16 +42,16 @@ function commit(version) {
let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]);
let stdout = res.stdout.toString().trim();
console.log(stdout)
console.log(stdout);
if (stdout.includes("no changes added to commit")) {
throw new Error("commit error")
throw new Error("commit error");
}
}
function tag(version) {
let res = child_process.spawnSync("git", ["tag", version]);
console.log(res.stdout.toString().trim())
console.log(res.stdout.toString().trim());
}
function tagExists(version) {
@@ -57,3 +63,38 @@ function tagExists(version) {
return res.stdout.toString().trim() === version;
}
function updateWiki(oldVersion, newVersion) {
const wikiDir = "./tmp/wiki";
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
safeDelete(wikiDir);
child_process.spawnSync("git", ["clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir]);
let content = fs.readFileSync(howToUpdateFilename).toString();
content = content.replaceAll(`git checkout ${oldVersion}`, `git checkout ${newVersion}`);
fs.writeFileSync(howToUpdateFilename, content);
child_process.spawnSync("git", ["add", "-A"], {
cwd: wikiDir,
});
child_process.spawnSync("git", ["commit", "-m", `Update to ${newVersion} from ${oldVersion}`], {
cwd: wikiDir,
});
console.log("Pushing to Github");
child_process.spawnSync("git", ["push"], {
cwd: wikiDir,
});
safeDelete(wikiDir);
}
function safeDelete(dir) {
if (fs.existsSync(dir)) {
fs.rmdirSync(dir, {
recursive: true,
});
}
}

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# Author: Stefan Buck
# License: MIT
# https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447
#
#
# This script accepts the following parameters:
#
# * owner
# * repo
# * tag
# * filename
# * github_api_token
#
# Script to upload a release asset using the GitHub API v3.
#
# Example:
#
# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip
#
# Check dependencies.
set -e
xargs=$(which gxargs || which xargs)
# Validate settings.
[ "$TRACE" ] && set -x
CONFIG=$@
for line in $CONFIG; do
eval "$line"
done
# Define variables.
GH_API="https://api.github.com"
GH_REPO="$GH_API/repos/$owner/$repo"
GH_TAGS="$GH_REPO/releases/tags/$tag"
AUTH="Authorization: token $github_api_token"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
CURL_ARGS="-LJO#"
if [[ "$tag" == 'LATEST' ]]; then
GH_TAGS="$GH_REPO/releases/latest"
fi
# Validate token.
curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; }
# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
# Get ID of the asset based on given filename.
eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
# Upload asset
echo "Uploading asset... "
# Construct url
GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)"
curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="manifest" href="manifest.json" />
<meta name="theme-color" id="theme-color" content="" />
<meta name="description" content="Uptime Kuma monitoring tool" />
<title>Uptime Kuma</title>

View File

@@ -1,28 +0,0 @@
# Uptime-Kuma K8s Deployment
## How does it work?
Kustomize is a tool which builds a complete deployment file for all config elements.
You can edit the files in the ```uptime-kuma``` folder except the ```kustomization.yml``` until you know what you're doing.
If you want to choose another namespace you can edit the ```kustomization.yml``` in the ```kubernetes```-Folder and change the ```namespace: uptime-kuma``` to something you like.
It creates a certificate with the specified Issuer and creates the Ingress for the Uptime-Kuma ClusterIP-Service
## What do i have to edit?
You have to edit the ```ingressroute.yml``` to your needs.
This ingressroute.yml is for the [nginx-ingress-controller](https://kubernetes.github.io/ingress-nginx/) in combination with the [cert-manager](https://cert-manager.io/).
- host
- secrets and secret names
- (Cluster)Issuer (optional)
- the Version in the Deployment-File
- update:
- change to newer version and run the above commands, it will update the pods one after another
## How To use:
- install [kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/)
- Edit files mentioned above to your needs
- run ```kustomize build > apply.yml```
- run ```kubectl apply -f apply.yml```
Now you should see some k8s magic and Uptime-Kuma should be available at the specified address.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

14070
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "uptime-kuma",
"version": "1.5.2",
"version": "1.9.1",
"license": "MIT",
"repository": {
"type": "git",
@@ -10,21 +10,34 @@
"node": "14.*"
},
"scripts": {
"install-legacy": "npm install --legacy-peer-deps",
"update-legacy": "npm update --legacy-peer-deps",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore",
"lint": "npm run lint:js && npm run lint:style",
"dev": "vite --host",
"dev": "vite --host --config ./config/vite.config.js",
"start": "npm run start-server",
"start-server": "node server/server.js",
"start-demo-server": "set NODE_ENV=demo && node server/server.js",
"update": "",
"build": "vite build",
"vite-preview-dist": "vite preview --host",
"build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.5.2 --target release . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"build-docker-1.5.0-debian": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:1.5.0-debian --target release . --push",
"setup": "git checkout 1.5.2 && npm install --legacy-peer-deps && node node_modules/esbuild/install.js && npm run build && npm prune",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"build": "vite build --config ./config/vite.config.js",
"test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
"test-with-build": "npm run build && npm test",
"jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend && jest --config=./config/jest.config.js",
"jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js",
"jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js",
"tsc": "tsc",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
"build-docker": "npm run build-docker-debian && npm run build-docker-alpine",
"build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
"build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
"build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.9.1-alpine --target release . --push",
"build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.9.1 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.9.1-debian --target release . --push",
"build-docker-nightly": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.9.1 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"update-version": "node extra/update-version.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@@ -33,60 +46,83 @@
"test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .",
"test-install-script-ubuntu": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu.dockerfile .",
"test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
"simple-dns-server": "node extra/simple-dns-server.js"
"test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
"simple-dns-server": "node extra/simple-dns-server.js",
"update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
"update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/vue-fontawesome": "^3.0.0-4",
"@popperjs/core": "^2.9.3",
"args-parser": "^1.3.0",
"axios": "^0.21.1",
"bcryptjs": "^2.4.3",
"bootstrap": "^5.1.0",
"chart.js": "^3.5.1",
"chartjs-adapter-dayjs": "^1.0.0",
"command-exists": "^1.2.9",
"compare-versions": "^3.6.0",
"dayjs": "^1.10.6",
"express": "^4.17.1",
"express-basic-auth": "^1.2.0",
"form-data": "^4.0.0",
"http-graceful-shutdown": "^3.1.3",
"jsonwebtoken": "^8.5.1",
"nodemailer": "^6.6.3",
"password-hash": "^1.2.2",
"prom-client": "^13.2.0",
"prometheus-api-metrics": "^3.2.0",
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-4",
"@louislam/sqlite3": "~6.0.0",
"@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0",
"axios": "~0.21.4",
"bcryptjs": "~2.4.3",
"bootstrap": "~5.1.1",
"chardet": "^1.3.0",
"bree": "~6.3.1",
"chart.js": "~3.5.1",
"chartjs-adapter-dayjs": "~1.0.0",
"command-exists": "~1.2.9",
"compare-versions": "~3.6.0",
"dayjs": "~1.10.7",
"express": "~4.17.1",
"express-basic-auth": "~1.2.0",
"form-data": "~4.0.0",
"http-graceful-shutdown": "~3.1.4",
"iconv-lite": "^0.6.3",
"jsonwebtoken": "~8.5.1",
"nodemailer": "~6.6.5",
"notp": "~2.0.3",
"password-hash": "~1.2.2",
"postcss-rtlcss": "~3.4.1",
"postcss-scss": "~4.0.1",
"prom-client": "~13.2.0",
"prometheus-api-metrics": "~3.2.0",
"qrcode": "~1.4.4",
"redbean-node": "0.1.2",
"socket.io": "^4.1.3",
"socket.io-client": "^4.1.3",
"sqlite3": "github:mapbox/node-sqlite3#593c9d",
"tcp-ping": "^0.1.1",
"v-pagination-3": "^0.1.6",
"vue": "^3.2.2",
"vue-chart-3": "^0.5.7",
"vue-confirm-dialog": "^1.0.2",
"vue-i18n": "^9.1.7",
"vue-multiselect": "^3.0.0-alpha.2",
"vue-router": "^4.0.11",
"vue-toastification": "^2.0.0-rc.1"
"socket.io": "~4.2.0",
"socket.io-client": "~4.2.0",
"tar": "^6.1.11",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
"timezones-list": "~3.0.1",
"v-pagination-3": "~0.1.6",
"vue": "next",
"vue-chart-3": "~0.5.8",
"vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2",
"vue-qrcode": "~1.0.0",
"vue-router": "~4.0.11",
"vue-toastification": "~2.0.0-rc.1",
"vuedraggable": "~4.1.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.15.0",
"@types/bootstrap": "^5.1.1",
"@vitejs/plugin-legacy": "^1.5.1",
"@vitejs/plugin-vue": "^1.4.0",
"@vue/compiler-sfc": "^3.2.2",
"core-js": "^3.16.1",
"dns2": "^2.0.1",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^7.16.0",
"sass": "^1.37.5",
"stylelint": "^13.13.1",
"stylelint-config-standard": "^22.0.0",
"typescript": "^4.3.5",
"vite": "^2.4.4"
"@babel/eslint-parser": "~7.15.7",
"@babel/preset-env": "^7.15.8",
"@types/bootstrap": "~5.1.6",
"@vitejs/plugin-legacy": "~1.6.1",
"@vitejs/plugin-vue": "~1.9.2",
"@vue/compiler-sfc": "~3.2.19",
"babel-plugin-rewire": "~1.2.0",
"core-js": "~3.18.1",
"cross-env": "~7.0.3",
"dns2": "~2.0.1",
"eslint": "~7.32.0",
"eslint-plugin-vue": "~7.18.0",
"jest": "~27.2.4",
"jest-puppeteer": "~6.0.0",
"puppeteer": "~10.4.0",
"sass": "~1.42.1",
"stylelint": "~13.13.1",
"stylelint-config-standard": "~22.0.0",
"typescript": "~4.4.3",
"vite": "~2.6.4"
}
}

BIN
public/icon-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/icon-512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

19
public/manifest.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "Uptime Kuma",
"short_name": "Uptime Kuma",
"start_url": "/",
"background_color": "#fff",
"display": "standalone",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

View File

@@ -1,6 +1,5 @@
const { setSetting } = require("./util-server");
const axios = require("axios");
const { isDev } = require("../src/util");
exports.version = require("../package.json").version;
exports.latestVersion = null;
@@ -18,11 +17,10 @@ exports.startInterval = () => {
// For debug
if (process.env.TEST_CHECK_VERSION === "1") {
res.data.version = "1000.0.0"
res.data.version = "1000.0.0";
}
exports.latestVersion = res.data.version;
console.log("Latest Version: " + exports.latestVersion);
} catch (_) { }
};

100
server/client.js Normal file
View File

@@ -0,0 +1,100 @@
/*
* For Client Socket
*/
const { TimeLogger } = require("../src/util");
const { R } = require("redbean-node");
const { io } = require("./server");
const { setting } = require("./util-server");
const checkVersion = require("./check-version");
async function sendNotificationList(socket) {
const timeLogger = new TimeLogger();
let result = [];
let list = await R.find("notification", " user_id = ? ", [
socket.userID,
]);
for (let bean of list) {
result.push(bean.export());
}
io.to(socket.userID).emit("notificationList", result);
timeLogger.print("Send Notification List");
return list;
}
/**
* Send Heartbeat History list to socket
* @param toUser True = send to all browsers with the same user id, False = send to the current browser only
* @param overwrite Overwrite client-side's heartbeat list
*/
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
const timeLogger = new TimeLogger();
let list = await R.getAll(`
SELECT * FROM heartbeat
WHERE monitor_id = ?
ORDER BY time DESC
LIMIT 100
`, [
monitorID,
]);
let result = list.reverse();
if (toUser) {
io.to(socket.userID).emit("heartbeatList", monitorID, result, overwrite);
} else {
socket.emit("heartbeatList", monitorID, result, overwrite);
}
timeLogger.print(`[Monitor: ${monitorID}] sendHeartbeatList`);
}
/**
* Important Heart beat list (aka event list)
* @param socket
* @param monitorID
* @param toUser True = send to all browsers with the same user id, False = send to the current browser only
* @param overwrite Overwrite client-side's heartbeat list
*/
async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
const timeLogger = new TimeLogger();
let list = await R.find("heartbeat", `
monitor_id = ?
AND important = 1
ORDER BY time DESC
LIMIT 500
`, [
monitorID,
]);
timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`);
if (toUser) {
io.to(socket.userID).emit("importantHeartbeatList", monitorID, list, overwrite);
} else {
socket.emit("importantHeartbeatList", monitorID, list, overwrite);
}
}
async function sendInfo(socket) {
socket.emit("info", {
version: checkVersion.version,
latestVersion: checkVersion.latestVersion,
primaryBaseURL: await setting("primaryBaseURL")
});
}
module.exports = {
sendNotificationList,
sendImportantHeartbeatList,
sendHeartbeatList,
sendInfo
};

7
server/config.js Normal file
View File

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

View File

@@ -1,41 +1,123 @@
const fs = require("fs");
const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server");
const { debug, sleep } = require("../src/util");
const dayjs = require("dayjs");
const knex = require("knex");
/**
* Database & App Data Folder
*/
class Database {
static templatePath = "./db/kuma.db"
static path = "./data/kuma.db";
static latestVersion = 8;
static templatePath = "./db/kuma.db";
/**
* Data Dir (Default: ./data)
*/
static dataDir;
/**
* User Upload Dir (Default: ./data/upload)
*/
static uploadDir;
static path;
/**
* @type {boolean}
*/
static patched = false;
/**
* For Backup only
*/
static backupPath = null;
/**
* Add patch filename in key
* Values:
* true: Add it regardless of order
* false: Do nothing
* { parents: []}: Need parents before add it
*/
static patchList = {
"patch-setting-value-type.sql": true,
"patch-improve-performance.sql": true,
"patch-2fa.sql": true,
"patch-add-retry-interval-monitor.sql": true,
"patch-incident-table.sql": true,
"patch-group-table.sql": true,
"patch-monitor-push_token.sql": true,
"patch-http-monitor-method-body-and-headers.sql": true,
}
/**
* The final version should be 10 after merged tag feature
* @deprecated Use patchList for any new feature
*/
static latestVersion = 10;
static noReject = true;
static sqliteInstance = null;
static init(args) {
// Data Directory (must be end with "/")
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
Database.path = Database.dataDir + "kuma.db";
if (! fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true });
}
Database.uploadDir = Database.dataDir + "upload/";
if (! fs.existsSync(Database.uploadDir)) {
fs.mkdirSync(Database.uploadDir, { recursive: true });
}
console.log(`Data Dir: ${Database.dataDir}`);
}
static async connect() {
const acquireConnectionTimeout = 120 * 1000;
R.setup("sqlite", {
filename: Database.path,
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
Dialect.prototype._driver = () => require("@louislam/sqlite3");
const knexInstance = knex({
client: Dialect,
connection: {
filename: Database.path,
acquireConnectionTimeout: acquireConnectionTimeout,
},
useNullAsDefault: true,
acquireConnectionTimeout: acquireConnectionTimeout,
}, {
min: 1,
max: 1,
idleTimeoutMillis: 120 * 1000,
propagateCreateError: false,
acquireTimeoutMillis: acquireConnectionTimeout,
pool: {
min: 1,
max: 1,
idleTimeoutMillis: 120 * 1000,
propagateCreateError: false,
acquireTimeoutMillis: acquireConnectionTimeout,
}
});
R.setup(knexInstance);
if (process.env.SQL_LOG === "1") {
R.debug(true);
}
// Auto map the model to a bean object
R.freeze(true)
R.freeze(true);
await R.autoloadModels("./server/model");
await R.exec("PRAGMA foreign_keys = ON");
// Change to WAL
await R.exec("PRAGMA journal_mode = WAL");
await R.exec("PRAGMA cache_size = -12000");
console.log("SQLite config:");
console.log(await R.getAll("PRAGMA journal_mode"));
console.log(await R.getAll("PRAGMA cache_size"));
console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()"));
}
static async patch() {
@@ -53,21 +135,9 @@ class Database {
} else if (version > this.latestVersion) {
console.info("Warning: Database version is newer than expected");
} else {
console.info("Database patch is needed")
console.info("Database patch is needed");
console.info("Backup the db")
const backupPath = "./data/kuma.db.bak" + version;
fs.copyFileSync(Database.path, backupPath);
const shmPath = Database.path + "-shm";
if (fs.existsSync(shmPath)) {
fs.copyFileSync(shmPath, shmPath + ".bak" + version);
}
const walPath = Database.path + "-wal";
if (fs.existsSync(walPath)) {
fs.copyFileSync(walPath, walPath + ".bak" + version);
}
this.backup(version);
// Try catch anything here, if gone wrong, restore the backup
try {
@@ -78,18 +148,95 @@ class Database {
console.info(`Patched ${sqlFile}`);
await setSetting("database_version", i);
}
console.log("Database Patched Successfully");
} catch (ex) {
await Database.close();
console.error("Patch db failed!!! Restoring the backup")
fs.copyFileSync(backupPath, Database.path);
console.error(ex)
console.error("Start Uptime-Kuma failed due to patch db failed")
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues")
console.error(ex);
console.error("Start Uptime-Kuma failed due to patch db failed");
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
this.restore();
process.exit(1);
}
}
await this.patch2();
}
/**
* Call it from patch() only
* @returns {Promise<void>}
*/
static async patch2() {
console.log("Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) {
databasePatchedFiles = {};
}
debug("Patched files:");
debug(databasePatchedFiles);
try {
for (let sqlFilename in this.patchList) {
await this.patch2Recursion(sqlFilename, databasePatchedFiles);
}
if (this.patched) {
console.log("Database Patched Successfully");
}
} catch (ex) {
await Database.close();
console.error(ex);
console.error("Start Uptime-Kuma failed due to patch db failed");
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
this.restore();
process.exit(1);
}
await setSetting("databasePatchedFiles", databasePatchedFiles);
}
/**
* Used it patch2() only
* @param sqlFilename
* @param databasePatchedFiles
*/
static async patch2Recursion(sqlFilename, databasePatchedFiles) {
let value = this.patchList[sqlFilename];
if (! value) {
console.log(sqlFilename + " skip");
return;
}
// Check if patched
if (! databasePatchedFiles[sqlFilename]) {
console.log(sqlFilename + " is not patched");
if (value.parents) {
console.log(sqlFilename + " need parents");
for (let parentSQLFilename of value.parents) {
await this.patch2Recursion(parentSQLFilename, databasePatchedFiles);
}
}
this.backup(dayjs().format("YYYYMMDDHHmmss"));
console.log(sqlFilename + " is patching");
this.patched = true;
await this.importSQLFile("./db/" + sqlFilename);
databasePatchedFiles[sqlFilename] = true;
console.log(sqlFilename + " is patched successfully");
} else {
debug(sqlFilename + " is already patched, skip");
}
}
/**
@@ -106,12 +253,12 @@ class Database {
// Remove all comments (--)
let lines = text.split("\n");
lines = lines.filter((line) => {
return ! line.startsWith("--")
return ! line.startsWith("--");
});
// Split statements by semicolon
// Filter out empty line
text = lines.join("\n")
text = lines.join("\n");
let statements = text.split(";")
.map((statement) => {
@@ -119,7 +266,7 @@ class Database {
})
.filter((statement) => {
return statement !== "";
})
});
for (let statement of statements) {
await R.exec(statement);
@@ -135,10 +282,96 @@ class Database {
* @returns {Promise<void>}
*/
static async close() {
if (this.sqliteInstance) {
this.sqliteInstance.close();
const listener = (reason, p) => {
Database.noReject = false;
};
process.addListener("unhandledRejection", listener);
console.log("Closing DB");
while (true) {
Database.noReject = true;
await R.close();
await sleep(2000);
if (Database.noReject) {
break;
} else {
console.log("Waiting to close the db");
}
}
console.log("SQLite closed");
process.removeListener("unhandledRejection", listener);
}
/**
* One backup one time in this process.
* Reset this.backupPath if you want to backup again
* @param version
*/
static backup(version) {
if (! this.backupPath) {
console.info("Backup the db");
this.backupPath = this.dataDir + "kuma.db.bak" + version;
fs.copyFileSync(Database.path, this.backupPath);
const shmPath = Database.path + "-shm";
if (fs.existsSync(shmPath)) {
this.backupShmPath = shmPath + ".bak" + version;
fs.copyFileSync(shmPath, this.backupShmPath);
}
const walPath = Database.path + "-wal";
if (fs.existsSync(walPath)) {
this.backupWalPath = walPath + ".bak" + version;
fs.copyFileSync(walPath, this.backupWalPath);
}
}
}
/**
*
*/
static restore() {
if (this.backupPath) {
console.error("Patch db failed!!! Restoring the backup");
const shmPath = Database.path + "-shm";
const walPath = Database.path + "-wal";
// Delete patch failed db
try {
if (fs.existsSync(Database.path)) {
fs.unlinkSync(Database.path);
}
if (fs.existsSync(shmPath)) {
fs.unlinkSync(shmPath);
}
if (fs.existsSync(walPath)) {
fs.unlinkSync(walPath);
}
} catch (e) {
console.log("Restore failed, you may need to restore the backup manually");
process.exit(1);
}
// Restore backup
fs.copyFileSync(this.backupPath, Database.path);
if (this.backupShmPath) {
fs.copyFileSync(this.backupShmPath, shmPath);
}
if (this.backupWalPath) {
fs.copyFileSync(this.backupWalPath, walPath);
}
} else {
console.log("Nothing to restore");
}
console.log("Stopped database");
}
}

57
server/image-data-uri.js Normal file
View File

@@ -0,0 +1,57 @@
/*
From https://github.com/DiegoZoracKy/image-data-uri/blob/master/lib/image-data-uri.js
Modified with 0 dependencies
*/
let fs = require("fs");
let ImageDataURI = (() => {
function decode(dataURI) {
if (!/data:image\//.test(dataURI)) {
console.log("ImageDataURI :: Error :: It seems that it is not an Image Data URI. Couldn't match \"data:image/\"");
return null;
}
let regExMatches = dataURI.match("data:(image/.*);base64,(.*)");
return {
imageType: regExMatches[1],
dataBase64: regExMatches[2],
dataBuffer: new Buffer(regExMatches[2], "base64")
};
}
function encode(data, mediaType) {
if (!data || !mediaType) {
console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType ");
return null;
}
mediaType = (/\//.test(mediaType)) ? mediaType : "image/" + mediaType;
let dataBase64 = (Buffer.isBuffer(data)) ? data.toString("base64") : new Buffer(data).toString("base64");
let dataImgBase64 = "data:" + mediaType + ";base64," + dataBase64;
return dataImgBase64;
}
function outputFile(dataURI, filePath) {
filePath = filePath || "./";
return new Promise((resolve, reject) => {
let imageDecoded = decode(dataURI);
fs.writeFile(filePath, imageDecoded.dataBuffer, err => {
if (err) {
return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4));
}
resolve(filePath);
});
});
}
return {
decode: decode,
encode: encode,
outputFile: outputFile,
};
})();
module.exports = ImageDataURI;

31
server/jobs.js Normal file
View File

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

View File

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

View File

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

34
server/model/group.js Normal file
View File

@@ -0,0 +1,34 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
const { R } = require("redbean-node");
class Group extends BeanModel {
async toPublicJSON() {
let monitorBeanList = await this.getMonitorList();
let monitorList = [];
for (let bean of monitorBeanList) {
monitorList.push(await bean.toPublicJSON());
}
return {
id: this.id,
name: this.name,
weight: this.weight,
monitorList,
};
}
async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(`
SELECT monitor.* FROM monitor, monitor_group
WHERE monitor.id = monitor_group.monitor_id
AND group_id = ?
ORDER BY monitor_group.weight
`, [
this.id,
]));
}
}
module.exports = Group;

View File

@@ -1,8 +1,8 @@
const dayjs = require("dayjs");
const utc = require("dayjs/plugin/utc")
let timezone = require("dayjs/plugin/timezone")
dayjs.extend(utc)
dayjs.extend(timezone)
const utc = require("dayjs/plugin/utc");
let timezone = require("dayjs/plugin/timezone");
dayjs.extend(utc);
dayjs.extend(timezone);
const { BeanModel } = require("redbean-node/dist/bean-model");
/**
@@ -13,6 +13,15 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
*/
class Heartbeat extends BeanModel {
toPublicJSON() {
return {
status: this.status,
time: this.time,
msg: "", // Hide for public
ping: this.ping,
};
}
toJSON() {
return {
monitorID: this.monitor_id,

18
server/model/incident.js Normal file
View File

@@ -0,0 +1,18 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
class Incident extends BeanModel {
toPublicJSON() {
return {
id: this.id,
style: this.style,
title: this.title,
content: this.content,
pin: this.pin,
createdDate: this.createdDate,
lastUpdatedDate: this.lastUpdatedDate,
};
}
}
module.exports = Incident;

View File

@@ -1,17 +1,19 @@
const https = require("https");
const dayjs = require("dayjs");
const utc = require("dayjs/plugin/utc")
let timezone = require("dayjs/plugin/timezone")
dayjs.extend(utc)
dayjs.extend(timezone)
const utc = require("dayjs/plugin/utc");
let timezone = require("dayjs/plugin/timezone");
dayjs.extend(utc);
dayjs.extend(timezone);
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom } = require("../util-server");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting } = require("../util-server");
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification")
const { Notification } = require("../notification");
const { demoMode } = require("../config");
const version = require("../../package.json").version;
const apicache = require("../modules/apicache");
/**
* status:
@@ -20,22 +22,42 @@ const version = require("../../package.json").version;
* 2 = PENDING
*/
class Monitor extends BeanModel {
/**
* Return a object that ready to parse to JSON for public
* Only show necessary data to public
*/
async toPublicJSON() {
return {
id: this.id,
name: this.name,
};
}
/**
* Return a object that ready to parse to JSON
*/
async toJSON() {
let notificationIDList = {};
let list = await R.find("monitor_notification", " monitor_id = ? ", [
this.id,
])
]);
for (let bean of list) {
notificationIDList[bean.notification_id] = true;
}
const tags = await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ?", [this.id]);
return {
id: this.id,
name: this.name,
url: this.url,
method: this.method,
body: this.body,
headers: this.headers,
hostname: this.hostname,
port: this.port,
maxretries: this.maxretries,
@@ -43,6 +65,7 @@ class Monitor extends BeanModel {
active: this.active,
type: this.type,
interval: this.interval,
retryInterval: this.retryInterval,
keyword: this.keyword,
ignoreTls: this.getIgnoreTls(),
upsideDown: this.isUpsideDown(),
@@ -51,7 +74,9 @@ class Monitor extends BeanModel {
dns_resolve_type: this.dns_resolve_type,
dns_resolve_server: this.dns_resolve_server,
dns_last_result: this.dns_last_result,
pushToken: this.pushToken,
notificationIDList,
tags: tags,
};
}
@@ -60,7 +85,7 @@ class Monitor extends BeanModel {
* @returns {boolean}
*/
getIgnoreTls() {
return Boolean(this.ignoreTls)
return Boolean(this.ignoreTls);
}
/**
@@ -90,12 +115,12 @@ class Monitor extends BeanModel {
if (! previousBeat) {
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
this.id,
])
]);
}
const isFirstBeat = !previousBeat;
let bean = R.dispense("heartbeat")
let bean = R.dispense("heartbeat");
bean.monitor_id = this.id;
bean.time = R.isoDateTime(dayjs.utc());
bean.status = DOWN;
@@ -116,11 +141,15 @@ class Monitor extends BeanModel {
// Do not do any queries/high loading things before the "bean.ping"
let startTime = dayjs().valueOf();
let res = await axios.get(this.url, {
const options = {
url: this.url,
method: (this.method || "get").toLowerCase(),
...(this.body ? { data: JSON.parse(this.body) } : {}),
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version,
...(this.headers ? JSON.parse(this.headers) : {}),
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
@@ -130,8 +159,9 @@ class Monitor extends BeanModel {
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
});
bean.msg = `${res.status} - ${res.statusText}`
};
let res = await axios.request(options);
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
// Check certificate if https is used
@@ -141,12 +171,18 @@ class Monitor extends BeanModel {
tlsInfo = await this.updateTlsInfo(checkCertificate(res));
} catch (e) {
if (e.message !== "No TLS certificate in response") {
console.error(e.message)
console.error(e.message);
}
}
}
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms")
if (process.env.TIMELOGGER === "1") {
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) {
console.log(res.data);
}
if (this.type === "http") {
bean.status = UP;
@@ -156,26 +192,26 @@ class Monitor extends BeanModel {
// Convert to string for object/array
if (typeof data !== "string") {
data = JSON.stringify(data)
data = JSON.stringify(data);
}
if (data.includes(this.keyword)) {
bean.msg += ", keyword is found"
bean.msg += ", keyword is found";
bean.status = UP;
} else {
throw new Error(bean.msg + ", but keyword is not found")
throw new Error(bean.msg + ", but keyword is not found");
}
}
} else if (this.type === "port") {
bean.ping = await tcping(this.hostname, this.port);
bean.msg = ""
bean.msg = "";
bean.status = UP;
} else if (this.type === "ping") {
bean.ping = await ping(this.hostname);
bean.msg = ""
bean.msg = "";
bean.status = UP;
} else if (this.type === "dns") {
let startTime = dayjs().valueOf();
@@ -195,7 +231,7 @@ class Monitor extends BeanModel {
dnsRes.forEach(record => {
dnsMessage += `Hostname: ${record.exchange} - Priority: ${record.priority} | `;
});
dnsMessage = dnsMessage.slice(0, -2)
dnsMessage = dnsMessage.slice(0, -2);
} else if (this.dns_resolve_type == "NS") {
dnsMessage += "Servers: ";
dnsMessage += dnsRes.join(" | ");
@@ -205,7 +241,7 @@ class Monitor extends BeanModel {
dnsRes.forEach(record => {
dnsMessage += `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight} | `;
});
dnsMessage = dnsMessage.slice(0, -2)
dnsMessage = dnsMessage.slice(0, -2);
}
if (this.dnsLastResult !== dnsMessage) {
@@ -217,6 +253,68 @@ class Monitor extends BeanModel {
bean.msg = dnsMessage;
bean.status = UP;
} else if (this.type === "push") { // Type: Push
const time = R.isoDateTime(dayjs.utc().subtract(this.interval, "second"));
let heartbeatCount = await R.count("heartbeat", " monitor_id = ? AND time > ? ", [
this.id,
time
]);
debug("heartbeatCount" + heartbeatCount + " " + time);
if (heartbeatCount <= 0) {
throw new Error("No heartbeat in the time window");
} else {
// No need to insert successful heartbeat for push type, so end here
retries = 0;
this.heartbeatInterval = setTimeout(beat, this.interval * 1000);
return;
}
} else if (this.type === "steam") {
const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/";
const steamAPIKey = await setting("steamAPIKey");
const filter = `addr\\${this.hostname}:${this.port}`;
if (!steamAPIKey) {
throw new Error("Steam API Key not found");
}
let res = await axios.get(steamApiUrl, {
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version,
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: ! this.getIgnoreTls(),
}),
maxRedirects: this.maxredirects,
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
params: {
filter: filter,
key: steamAPIKey,
}
});
if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) {
bean.status = UP;
bean.msg = res.data.response.servers[0].name;
try {
bean.ping = await ping(this.hostname);
} catch (_) { }
} else {
throw new Error("Server not found on Steam");
}
} else {
bean.msg = "Unknown Monitor Type";
bean.status = PENDING;
}
if (this.isUpsideDown()) {
@@ -244,79 +342,65 @@ class Monitor extends BeanModel {
}
}
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
let isImportant = isFirstBeat ||
(previousBeat.status === UP && bean.status === DOWN) ||
(previousBeat.status === DOWN && bean.status === UP) ||
(previousBeat.status === PENDING && bean.status === DOWN);
let beatInterval = this.interval;
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
// Mark as important if status changed, ignore pending pings,
// Don't notify if disrupted changes to up
if (isImportant) {
bean.important = true;
// Send only if the first beat is DOWN
if (!isFirstBeat || bean.status === DOWN) {
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
this.id,
])
let text;
if (bean.status === UP) {
text = "✅ Up"
} else {
text = "🔴 Down"
}
let msg = `[${this.name}] [${text}] ${bean.msg}`;
for (let notification of notificationList) {
try {
await Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON())
} catch (e) {
console.error("Cannot send notification to " + notification.name);
console.log(e);
}
}
}
await Monitor.sendNotification(isFirstBeat, this, bean);
} else {
bean.important = false;
}
if (bean.status === UP) {
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`)
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
} else if (bean.status === PENDING) {
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Type: ${this.type}`)
if (this.retryInterval > 0) {
beatInterval = this.retryInterval;
}
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`);
} else {
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`)
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
}
io.to(this.user_id).emit("heartbeat", bean.toJSON());
Monitor.sendStats(io, this.id, this.user_id)
Monitor.sendStats(io, this.id, this.user_id);
await R.store(bean);
prometheus.update(bean, tlsInfo);
previousBeat = bean;
this.heartbeatInterval = setTimeout(beat, this.interval * 1000);
}
if (! this.isStop) {
beat();
if (demoMode) {
if (beatInterval < 20) {
console.log("beat interval too low, reset to 20s");
beatInterval = 20;
}
}
this.heartbeatInterval = setTimeout(beat, beatInterval * 1000);
}
};
// Delay Push Type
if (this.type === "push") {
setTimeout(() => {
beat();
}, this.interval * 1000);
} else {
beat();
}
}
stop() {
clearTimeout(this.heartbeatInterval);
this.isStop = true;
}
/**
@@ -402,66 +486,124 @@ class Monitor extends BeanModel {
* https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime
* @param duration : int Hours
*/
static async sendUptime(duration, io, monitorID, userID) {
static async calcUptime(duration, monitorID) {
const timeLogger = new TimeLogger();
let sec = duration * 3600;
const startTime = R.isoDateTime(dayjs.utc().subtract(duration, "hour"));
let heartbeatList = await R.getAll(`
SELECT duration, time, status
// Handle if heartbeat duration longer than the target duration
// e.g. If the last beat's duration is bigger that the 24hrs window, it will use the duration between the (beat time - window margin) (THEN case in SQL)
let result = await R.getRow(`
SELECT
-- SUM all duration, also trim off the beat out of time window
SUM(
CASE
WHEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 < duration
THEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400
ELSE duration
END
) AS total_duration,
-- SUM all uptime duration, also trim off the beat out of time window
SUM(
CASE
WHEN (status = 1)
THEN
CASE
WHEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 < duration
THEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400
ELSE duration
END
END
) AS uptime_duration
FROM heartbeat
WHERE time > DATETIME('now', ? || ' hours')
AND monitor_id = ? `, [
-duration,
WHERE time > ?
AND monitor_id = ?
`, [
startTime, startTime, startTime, startTime, startTime,
monitorID,
]);
timeLogger.print(`[Monitor: ${monitorID}][${duration}] sendUptime`);
let downtime = 0;
let total = 0;
let uptime;
let totalDuration = result.total_duration;
let uptimeDuration = result.uptime_duration;
let uptime = 0;
// Special handle for the first heartbeat only
if (heartbeatList.length === 1) {
if (heartbeatList[0].status === 1) {
uptime = 1;
} else {
if (totalDuration > 0) {
uptime = uptimeDuration / totalDuration;
if (uptime < 0) {
uptime = 0;
}
} else {
for (let row of heartbeatList) {
let value = parseInt(row.duration)
let time = row.time
// Handle new monitor with only one beat, because the beat's duration = 0
let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [ monitorID ]));
// Handle if heartbeat duration longer than the target duration
// e.g. Heartbeat duration = 28hrs, but target duration = 24hrs
if (value > sec) {
let trim = dayjs.utc().diff(dayjs(time), "second");
value = sec - trim;
if (value < 0) {
value = 0;
}
}
total += value;
if (row.status === 0 || row.status === 2) {
downtime += value;
}
}
uptime = (total - downtime) / total;
if (uptime < 0) {
uptime = 0;
if (status === UP) {
uptime = 1;
}
}
return uptime;
}
/**
* Send Uptime
* @param duration : int Hours
*/
static async sendUptime(duration, io, monitorID, userID) {
const uptime = await this.calcUptime(duration, monitorID);
io.to(userID).emit("uptime", monitorID, duration, uptime);
}
static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
let isImportant = isFirstBeat ||
(previousBeatStatus === UP && currentBeatStatus === DOWN) ||
(previousBeatStatus === DOWN && currentBeatStatus === UP) ||
(previousBeatStatus === PENDING && currentBeatStatus === DOWN);
return isImportant;
}
static async sendNotification(isFirstBeat, monitor, bean) {
if (!isFirstBeat || bean.status === DOWN) {
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
monitor.id,
]);
let text;
if (bean.status === UP) {
text = "✅ Up";
} else {
text = "🔴 Down";
}
let msg = `[${monitor.name}] [${text}] ${bean.msg}`;
for (let notification of notificationList) {
try {
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON());
} catch (e) {
console.error("Cannot send notification to " + notification.name);
console.log(e);
}
}
// Clear Status Page Cache
apicache.clear();
}
}
}
module.exports = Monitor;

13
server/model/tag.js Normal file
View File

@@ -0,0 +1,13 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
class Tag extends BeanModel {
toJSON() {
return {
id: this._id,
name: this._name,
color: this._color,
};
}
}
module.exports = Tag;

View File

@@ -0,0 +1,749 @@
let url = require("url");
let MemoryCache = require("./memory-cache");
let t = {
ms: 1,
second: 1000,
minute: 60000,
hour: 3600000,
day: 3600000 * 24,
week: 3600000 * 24 * 7,
month: 3600000 * 24 * 30,
};
let instances = [];
let matches = function (a) {
return function (b) {
return a === b;
};
};
let doesntMatch = function (a) {
return function (b) {
return !matches(a)(b);
};
};
let logDuration = function (d, prefix) {
let str = d > 1000 ? (d / 1000).toFixed(2) + "sec" : d + "ms";
return "\x1b[33m- " + (prefix ? prefix + " " : "") + str + "\x1b[0m";
};
function getSafeHeaders(res) {
return res.getHeaders ? res.getHeaders() : res._headers;
}
function ApiCache() {
let memCache = new MemoryCache();
let globalOptions = {
debug: false,
defaultDuration: 3600000,
enabled: true,
appendKey: [],
jsonp: false,
redisClient: false,
headerBlacklist: [],
statusCodes: {
include: [],
exclude: [],
},
events: {
expire: undefined,
},
headers: {
// 'cache-control': 'no-cache' // example of header overwrite
},
trackPerformance: false,
respectCacheControl: false,
};
let middlewareOptions = [];
let instance = this;
let index = null;
let timers = {};
let performanceArray = []; // for tracking cache hit rate
instances.push(this);
this.id = instances.length;
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);
}
function shouldCacheResponse(request, response, toggle) {
let opt = globalOptions;
let codes = opt.statusCodes;
if (!response) {
return false;
}
if (toggle && !toggle(request, response)) {
return false;
}
if (codes.exclude && codes.exclude.length && codes.exclude.indexOf(response.statusCode) !== -1) {
return false;
}
if (codes.include && codes.include.length && codes.include.indexOf(response.statusCode) === -1) {
return false;
}
return true;
}
function addIndexEntries(key, req) {
let groupName = req.apicacheGroup;
if (groupName) {
debug("group detected \"" + groupName + "\"");
let group = (index.groups[groupName] = index.groups[groupName] || []);
group.unshift(key);
}
index.all.unshift(key);
}
function filterBlacklistedHeaders(headers) {
return Object.keys(headers)
.filter(function (key) {
return globalOptions.headerBlacklist.indexOf(key) === -1;
})
.reduce(function (acc, header) {
acc[header] = headers[header];
return acc;
}, {});
}
function createCacheObject(status, headers, data, encoding) {
return {
status: status,
headers: filterBlacklistedHeaders(headers),
data: data,
encoding: encoding,
timestamp: new Date().getTime() / 1000, // seconds since epoch. This is used to properly decrement max-age headers in cached responses.
};
}
function cacheResponse(key, value, duration) {
let redis = globalOptions.redisClient;
let expireCallback = globalOptions.events.expire;
if (redis && redis.connected) {
try {
redis.hset(key, "response", JSON.stringify(value));
redis.hset(key, "duration", duration);
redis.expire(key, duration / 1000, expireCallback || function () {});
} catch (err) {
debug("[apicache] error in redis.hset()");
}
} else {
memCache.add(key, value, duration, expireCallback);
}
// add automatic cache clearing from duration, includes max limit on setTimeout
timers[key] = setTimeout(function () {
instance.clear(key, true);
}, Math.min(duration, 2147483647));
}
function accumulateContent(res, content) {
if (content) {
if (typeof content == "string") {
res._apicache.content = (res._apicache.content || "") + content;
} else if (Buffer.isBuffer(content)) {
let oldContent = res._apicache.content;
if (typeof oldContent === "string") {
oldContent = !Buffer.from ? new Buffer(oldContent) : Buffer.from(oldContent);
}
if (!oldContent) {
oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0);
}
res._apicache.content = Buffer.concat(
[oldContent, content],
oldContent.length + content.length
);
} else {
res._apicache.content = content;
}
}
}
function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) {
// monkeypatch res.end to create cache object
res._apicache = {
write: res.write,
writeHead: res.writeHead,
end: res.end,
cacheable: true,
content: undefined,
};
// append header overwrites if applicable
Object.keys(globalOptions.headers).forEach(function (name) {
res.setHeader(name, globalOptions.headers[name]);
});
res.writeHead = function () {
// add cache control headers
if (!globalOptions.headers["cache-control"]) {
if (shouldCacheResponse(req, res, toggle)) {
res.setHeader("cache-control", "max-age=" + (duration / 1000).toFixed(0));
} else {
res.setHeader("cache-control", "no-cache, no-store, must-revalidate");
}
}
res._apicache.headers = Object.assign({}, getSafeHeaders(res));
return res._apicache.writeHead.apply(this, arguments);
};
// patch res.write
res.write = function (content) {
accumulateContent(res, content);
return res._apicache.write.apply(this, arguments);
};
// patch res.end
res.end = function (content, encoding) {
if (shouldCacheResponse(req, res, toggle)) {
accumulateContent(res, content);
if (res._apicache.cacheable && res._apicache.content) {
addIndexEntries(key, req);
let headers = res._apicache.headers || getSafeHeaders(res);
let cacheObject = createCacheObject(
res.statusCode,
headers,
res._apicache.content,
encoding
);
cacheResponse(key, cacheObject, duration);
// 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);
}
}
return res._apicache.end.apply(this, arguments);
};
next();
}
function sendCachedResponse(request, response, cacheObject, toggle, next, duration) {
if (toggle && !toggle(request, response)) {
return next();
}
let headers = getSafeHeaders(response);
// Modified by @louislam, removed Cache-control, since I don't need client side cache!
// Original Source: https://github.com/kwhitley/apicache/blob/0d5686cc21fad353c6dddee646288c2fca3e4f50/src/apicache.js#L254
Object.assign(headers, filterBlacklistedHeaders(cacheObject.headers || {}));
// only embed apicache headers when not in production environment
if (process.env.NODE_ENV !== "production") {
Object.assign(headers, {
"apicache-store": globalOptions.redisClient ? "redis" : "memory",
"apicache-version": "1.6.2-modified",
});
}
// unstringify buffers
let data = cacheObject.data;
if (data && data.type === "Buffer") {
data =
typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
}
// test Etag against If-None-Match for 304
let cachedEtag = cacheObject.headers.etag;
let requestEtag = request.headers["if-none-match"];
if (requestEtag && cachedEtag === requestEtag) {
response.writeHead(304, headers);
return response.end();
}
response.writeHead(cacheObject.status || 200, headers);
return response.end(data, cacheObject.encoding);
}
function syncOptions() {
for (let i in middlewareOptions) {
Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions);
}
}
this.clear = function (target, isAutomatic) {
let group = index.groups[target];
let redis = globalOptions.redisClient;
if (group) {
debug("clearing group \"" + target + "\"");
group.forEach(function (key) {
debug("clearing cached entry for \"" + key + "\"");
clearTimeout(timers[key]);
delete timers[key];
if (!globalOptions.redisClient) {
memCache.delete(key);
} else {
try {
redis.del(key);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")");
}
}
index.all = index.all.filter(doesntMatch(key));
});
delete index.groups[target];
} else if (target) {
debug("clearing " + (isAutomatic ? "expired" : "cached") + " entry for \"" + target + "\"");
clearTimeout(timers[target]);
delete timers[target];
// clear actual cached entry
if (!redis) {
memCache.delete(target);
} else {
try {
redis.del(target);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + target + "\")");
}
}
// remove from global index
index.all = index.all.filter(doesntMatch(target));
// remove target from each group that it may exist in
Object.keys(index.groups).forEach(function (groupName) {
index.groups[groupName] = index.groups[groupName].filter(doesntMatch(target));
// delete group if now empty
if (!index.groups[groupName].length) {
delete index.groups[groupName];
}
});
} else {
debug("clearing entire index");
if (!redis) {
memCache.clear();
} else {
// clear redis keys one by one from internal index to prevent clearing non-apicache entries
index.all.forEach(function (key) {
clearTimeout(timers[key]);
delete timers[key];
try {
redis.del(key);
} catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")");
}
});
}
this.resetIndex();
}
return this.getIndex();
};
function parseDuration(duration, defaultDuration) {
if (typeof duration === "number") {
return duration;
}
if (typeof duration === "string") {
let split = duration.match(/^([\d\.,]+)\s?(\w+)$/);
if (split.length === 3) {
let len = parseFloat(split[1]);
let unit = split[2].replace(/s$/i, "").toLowerCase();
if (unit === "m") {
unit = "ms";
}
return (len || 1) * (t[unit] || 0);
}
}
return defaultDuration;
}
this.getDuration = function (duration) {
return parseDuration(duration, globalOptions.defaultDuration);
};
/**
* Return cache performance statistics (hit rate). Suitable for putting into a route:
* <code>
* app.get('/api/cache/performance', (req, res) => {
* res.json(apicache.getPerformance())
* })
* </code>
*/
this.getPerformance = function () {
return performanceArray.map(function (p) {
return p.report();
});
};
this.getIndex = function (group) {
if (group) {
return index.groups[group];
} else {
return index;
}
};
this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
let duration = instance.getDuration(strDuration);
let opt = {};
middlewareOptions.push({
options: opt,
});
let options = function (localOptions) {
if (localOptions) {
middlewareOptions.find(function (middleware) {
return middleware.options === opt;
}).localOptions = localOptions;
}
syncOptions();
return opt;
};
options(localOptions);
/**
* A Function for non tracking performance
*/
function NOOPCachePerformance() {
this.report = this.hit = this.miss = function () {}; // noop;
}
/**
* A function for tracking and reporting hit rate. These statistics are returned by the getPerformance() call above.
*/
function CachePerformance() {
/**
* Tracks the hit rate for the last 100 requests.
* If there have been fewer than 100 requests, the hit rate just considers the requests that have happened.
*/
this.hitsLast100 = new Uint8Array(100 / 4); // each hit is 2 bits
/**
* Tracks the hit rate for the last 1000 requests.
* If there have been fewer than 1000 requests, the hit rate just considers the requests that have happened.
*/
this.hitsLast1000 = new Uint8Array(1000 / 4); // each hit is 2 bits
/**
* Tracks the hit rate for the last 10000 requests.
* If there have been fewer than 10000 requests, the hit rate just considers the requests that have happened.
*/
this.hitsLast10000 = new Uint8Array(10000 / 4); // each hit is 2 bits
/**
* Tracks the hit rate for the last 100000 requests.
* If there have been fewer than 100000 requests, the hit rate just considers the requests that have happened.
*/
this.hitsLast100000 = new Uint8Array(100000 / 4); // each hit is 2 bits
/**
* The number of calls that have passed through the middleware since the server started.
*/
this.callCount = 0;
/**
* The total number of hits since the server started
*/
this.hitCount = 0;
/**
* The key from the last cache hit. This is useful in identifying which route these statistics apply to.
*/
this.lastCacheHit = null;
/**
* The key from the last cache miss. This is useful in identifying which route these statistics apply to.
*/
this.lastCacheMiss = null;
/**
* Return performance statistics
*/
this.report = function () {
return {
lastCacheHit: this.lastCacheHit,
lastCacheMiss: this.lastCacheMiss,
callCount: this.callCount,
hitCount: this.hitCount,
missCount: this.callCount - this.hitCount,
hitRate: this.callCount == 0 ? null : this.hitCount / this.callCount,
hitRateLast100: this.hitRate(this.hitsLast100),
hitRateLast1000: this.hitRate(this.hitsLast1000),
hitRateLast10000: this.hitRate(this.hitsLast10000),
hitRateLast100000: this.hitRate(this.hitsLast100000),
};
};
/**
* Computes a cache hit rate from an array of hits and misses.
* @param {Uint8Array} array An array representing hits and misses.
* @returns a number between 0 and 1, or null if the array has no hits or misses
*/
this.hitRate = function (array) {
let hits = 0;
let misses = 0;
for (let i = 0; i < array.length; i++) {
let n8 = array[i];
for (let j = 0; j < 4; j++) {
switch (n8 & 3) {
case 1:
hits++;
break;
case 2:
misses++;
break;
}
n8 >>= 2;
}
}
let total = hits + misses;
if (total == 0) {
return null;
}
return hits / total;
};
/**
* Record a hit or miss in the given array. It will be recorded at a position determined
* by the current value of the callCount variable.
* @param {Uint8Array} array An array representing hits and misses.
* @param {boolean} hit true for a hit, false for a miss
* Each element in the array is 8 bits, and encodes 4 hit/miss records.
* Each hit or miss is encoded as to bits as follows:
* 00 means no hit or miss has been recorded in these bits
* 01 encodes a hit
* 10 encodes a miss
*/
this.recordHitInArray = function (array, hit) {
let arrayIndex = ~~(this.callCount / 4) % array.length;
let bitOffset = (this.callCount % 4) * 2; // 2 bits per record, 4 records per uint8 array element
let clearMask = ~(3 << bitOffset);
let record = (hit ? 1 : 2) << bitOffset;
array[arrayIndex] = (array[arrayIndex] & clearMask) | record;
};
/**
* Records the hit or miss in the tracking arrays and increments the call count.
* @param {boolean} hit true records a hit, false records a miss
*/
this.recordHit = function (hit) {
this.recordHitInArray(this.hitsLast100, hit);
this.recordHitInArray(this.hitsLast1000, hit);
this.recordHitInArray(this.hitsLast10000, hit);
this.recordHitInArray(this.hitsLast100000, hit);
if (hit) {
this.hitCount++;
}
this.callCount++;
};
/**
* Records a hit event, setting lastCacheMiss to the given key
* @param {string} key The key that had the cache hit
*/
this.hit = function (key) {
this.recordHit(true);
this.lastCacheHit = key;
};
/**
* Records a miss event, setting lastCacheMiss to the given key
* @param {string} key The key that had the cache miss
*/
this.miss = function (key) {
this.recordHit(false);
this.lastCacheMiss = key;
};
}
let perf = globalOptions.trackPerformance ? new CachePerformance() : new NOOPCachePerformance();
performanceArray.push(perf);
let cache = function (req, res, next) {
function bypass() {
debug("bypass detected, skipping cache.");
return next();
}
// initial bypass chances
if (!opt.enabled) {
return bypass();
}
if (
req.headers["x-apicache-bypass"] ||
req.headers["x-apicache-force-fetch"] ||
(opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
) {
return bypass();
}
// REMOVED IN 0.11.1 TO CORRECT MIDDLEWARE TOGGLE EXECUTE ORDER
// if (typeof middlewareToggle === 'function') {
// if (!middlewareToggle(req, res)) return bypass()
// } else if (middlewareToggle !== undefined && !middlewareToggle) {
// return bypass()
// }
// embed timer
req.apicacheTimer = new Date();
// In Express 4.x the url is ambigious based on where a router is mounted. originalUrl will give the full Url
let key = req.originalUrl || req.url;
// Remove querystring from key if jsonp option is enabled
if (opt.jsonp) {
key = url.parse(key).pathname;
}
// add appendKey (either custom function or response path)
if (typeof opt.appendKey === "function") {
key += "$$appendKey=" + opt.appendKey(req, res);
} else if (opt.appendKey.length > 0) {
let appendKey = req;
for (let i = 0; i < opt.appendKey.length; i++) {
appendKey = appendKey[opt.appendKey[i]];
}
key += "$$appendKey=" + appendKey;
}
// attempt cache hit
let redis = opt.redisClient;
let cached = !redis ? memCache.getValue(key) : null;
// 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));
perf.hit(key);
return sendCachedResponse(req, res, cached, middlewareToggle, next, duration);
}
// send if cache hit from redis
if (redis && redis.connected) {
try {
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));
perf.hit(key);
return sendCachedResponse(
req,
res,
JSON.parse(obj.response),
middlewareToggle,
next,
duration
);
} else {
perf.miss(key);
return makeResponseCacheable(
req,
res,
next,
key,
duration,
strDuration,
middlewareToggle
);
}
});
} catch (err) {
// bypass redis on error
perf.miss(key);
return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle);
}
} else {
perf.miss(key);
return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle);
}
};
cache.options = options;
return cache;
};
this.options = function (options) {
if (options) {
Object.assign(globalOptions, options);
syncOptions();
if ("defaultDuration" in options) {
// Convert the default duration to a number in milliseconds (if needed)
globalOptions.defaultDuration = parseDuration(globalOptions.defaultDuration, 3600000);
}
if (globalOptions.trackPerformance) {
debug("WARNING: using trackPerformance flag can cause high memory usage!");
}
return this;
} else {
return globalOptions;
}
};
this.resetIndex = function () {
index = {
all: [],
groups: {},
};
};
this.newInstance = function (config) {
let instance = new ApiCache();
if (config) {
instance.options(config);
}
return instance;
};
this.clone = function () {
return this.newInstance(this.options());
};
// initialize index
this.resetIndex();
}
module.exports = new ApiCache();

View File

@@ -0,0 +1,14 @@
const apicache = require("./apicache");
apicache.options({
headerBlacklist: [
"cache-control"
],
headers: {
// Disable client side cache, only server side cache.
// BUG! Not working for the second request
"cache-control": "no-cache",
},
});
module.exports = apicache;

View File

@@ -0,0 +1,59 @@
function MemoryCache() {
this.cache = {};
this.size = 0;
}
MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
let old = this.cache[key];
let instance = this;
let entry = {
value: value,
expire: time + Date.now(),
timeout: setTimeout(function () {
instance.delete(key);
return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
}, time)
};
this.cache[key] = entry;
this.size = Object.keys(this.cache).length;
return entry;
};
MemoryCache.prototype.delete = function (key) {
let entry = this.cache[key];
if (entry) {
clearTimeout(entry.timeout);
}
delete this.cache[key];
this.size = Object.keys(this.cache).length;
return null;
};
MemoryCache.prototype.get = function (key) {
let entry = this.cache[key];
return entry;
};
MemoryCache.prototype.getValue = function (key) {
let entry = this.get(key);
return entry && entry.value;
};
MemoryCache.prototype.clear = function () {
Object.keys(this.cache).forEach(function (key) {
this.delete(key);
}, this);
return true;
};
module.exports = MemoryCache;

View File

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

View File

@@ -0,0 +1,26 @@
const NotificationProvider = require("./notification-provider");
const child_process = require("child_process");
class Apprise extends NotificationProvider {
name = "apprise";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL])
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
if (output) {
if (! output.includes("ERROR")) {
return "Sent Successfully";
}
throw new Error(output)
} else {
return "No output from apprise";
}
}
}
module.exports = Apprise;

View File

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

View File

@@ -0,0 +1,115 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Discord extends NotificationProvider {
name = "discord";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
const discordDisplayName = notification.discordUsername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let discordtestdata = {
username: discordDisplayName,
content: msg,
}
await axios.post(notification.discordWebhookUrl, discordtestdata)
return okMsg;
}
let url;
if (monitorJSON["type"] === "port") {
url = monitorJSON["hostname"];
if (monitorJSON["port"]) {
url += ":" + monitorJSON["port"];
}
} else {
url = monitorJSON["url"];
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON["status"] == DOWN) {
let discorddowndata = {
username: discordDisplayName,
embeds: [{
title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
color: 16711680,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Error",
value: heartbeatJSON["msg"],
},
],
}],
}
if (notification.discordPrefixMessage) {
discorddowndata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discorddowndata)
return okMsg;
} else if (heartbeatJSON["status"] == UP) {
let discordupdata = {
username: discordDisplayName,
embeds: [{
title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
color: 65280,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Ping",
value: heartbeatJSON["ping"] + "ms",
},
],
}],
}
if (notification.discordPrefixMessage) {
discordupdata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discordupdata)
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Discord;

View File

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

View File

@@ -0,0 +1,28 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Gotify extends NotificationProvider {
name = "gotify";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) {
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);
}
await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, {
"message": msg,
"priority": notification.gotifyPriority || 8,
"title": "Uptime-Kuma",
})
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Gotify;

View File

@@ -0,0 +1,60 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Line extends NotificationProvider {
name = "line";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notification.lineChannelAccessToken
}
};
if (heartbeatJSON == null) {
let testMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "Test Successful!"
}
]
}
await axios.post(lineAPIUrl, testMessage, config)
} else if (heartbeatJSON["status"] == DOWN) {
let downMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
}
]
}
await axios.post(lineAPIUrl, downMessage, config)
} else if (heartbeatJSON["status"] == UP) {
let upMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
}
]
}
await axios.post(lineAPIUrl, upMessage, config)
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Line;

View File

@@ -0,0 +1,48 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class LunaSea extends NotificationProvider {
name = "lunasea";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
try {
if (heartbeatJSON == null) {
let testdata = {
"title": "Uptime Kuma Alert",
"body": "Testing Successful.",
}
await axios.post(lunaseadevice, testdata)
return okMsg;
}
if (heartbeatJSON["status"] == DOWN) {
let downdata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, downdata)
return okMsg;
}
if (heartbeatJSON["status"] == UP) {
let updata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, updata)
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = LunaSea;

View File

@@ -0,0 +1,45 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const Crypto = require("crypto");
const { debug } = require("../../src/util");
class Matrix extends NotificationProvider {
name = "matrix";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const size = 20;
const randomString = encodeURIComponent(
Crypto
.randomBytes(size)
.toString("base64")
.slice(0, size)
);
debug("Random String: " + randomString);
const roomId = encodeURIComponent(notification.internalRoomId);
debug("Matrix Room ID: " + roomId);
try {
let config = {
headers: {
"Authorization": `Bearer ${notification.accessToken}`,
}
};
let data = {
"msgtype": "m.text",
"body": msg
};
await axios.put(`${notification.homeserverUrl}/_matrix/client/r0/rooms/${roomId}/send/m.room.message/${randomString}`, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Matrix;

View File

@@ -0,0 +1,123 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Mattermost extends NotificationProvider {
name = "mattermost";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let mattermostTestData = {
username: mattermostUserName,
text: msg,
}
await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
return okMsg;
}
const mattermostChannel = notification.mattermostchannel;
const mattermostIconEmoji = notification.mattermosticonemo;
const mattermostIconUrl = notification.mattermosticonurl;
if (heartbeatJSON["status"] == DOWN) {
let mattermostdowndata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",
channel: mattermostChannel,
icon_emoji: mattermostIconEmoji,
icon_url: mattermostIconUrl,
attachments: [
{
fallback:
"Your " +
monitorJSON["name"] +
" service went down.",
color: "#FF0000",
title:
"❌ " +
monitorJSON["name"] +
" service went down. ❌",
title_link: monitorJSON["url"],
fields: [
{
short: true,
title: "Service Name",
value: monitorJSON["name"],
},
{
short: true,
title: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
short: false,
title: "Error",
value: heartbeatJSON["msg"],
},
],
},
],
};
await axios.post(
notification.mattermostWebhookUrl,
mattermostdowndata
);
return okMsg;
} else if (heartbeatJSON["status"] == UP) {
let mattermostupdata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",
channel: mattermostChannel,
icon_emoji: mattermostIconEmoji,
icon_url: mattermostIconUrl,
attachments: [
{
fallback:
"Your " +
monitorJSON["name"] +
" service went up!",
color: "#32CD32",
title:
"✅ " +
monitorJSON["name"] +
" service went up! ✅",
title_link: monitorJSON["url"],
fields: [
{
short: true,
title: "Service Name",
value: monitorJSON["name"],
},
{
short: true,
title: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
short: false,
title: "Ping",
value: heartbeatJSON["ping"] + "ms",
},
],
},
],
};
await axios.post(
notification.mattermostWebhookUrl,
mattermostupdata
);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Mattermost;

View File

@@ -0,0 +1,36 @@
class NotificationProvider {
/**
* Notification Provider Name
* @type string
*/
name = undefined;
/**
* @param notification : BeanModel
* @param msg : string General Message
* @param monitorJSON : object Monitor details (For Up/Down only)
* @param heartbeatJSON : object Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Return Successful Message
* Throw Error with fail msg
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
throw new Error("Have to override Notification.send(...)");
}
throwGeneralAxiosError(error) {
let msg = "Error: " + error + " ";
if (error.response && error.response.data) {
if (typeof error.response.data === "string") {
msg += error.response.data;
} else {
msg += JSON.stringify(error.response.data)
}
}
throw new Error(msg)
}
}
module.exports = NotificationProvider;

View File

@@ -0,0 +1,64 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Octopush extends NotificationProvider {
name = "octopush";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
// Default - V2
if (notification.octopushVersion == 2 || !notification.octopushVersion) {
let config = {
headers: {
"api-key": notification.octopushAPIKey,
"api-login": notification.octopushLogin,
"cache-control": "no-cache"
}
};
let data = {
"recipients": [
{
"phone_number": notification.octopushPhoneNumber
}
],
//octopush not supporting non ascii char
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"type": notification.octopushSMSType,
"purpose": "alert",
"sender": notification.octopushSenderName
};
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
} else if (notification.octopushVersion == 1) {
let data = {
"user_login": notification.octopushDMLogin,
"api_key": notification.octopushDMAPIKey,
"sms_recipients": notification.octopushDMPhoneNumber,
"sms_sender": notification.octopushDMSenderName,
"sms_type": (notification.octopushDMSMSType == "sms_premium") ? "FR" : "XXX",
"transactional": "1",
//octopush not supporting non ascii char
"sms_text": msg.replace(/[^\x00-\x7F]/g, ""),
};
let config = {
headers: {
"cache-control": "no-cache"
},
params: data
};
await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config)
} else {
throw new Error("Unknown Octopush version!");
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Octopush;

View File

@@ -0,0 +1,41 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class PromoSMS extends NotificationProvider {
name = "promosms";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString('base64'),
"Accept": "text/json",
}
};
let data = {
"recipients": [ notification.promosmsPhoneNumber ],
//Lets remove non ascii char
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"type": Number(notification.promosmsSMSType),
"sender": notification.promosmsSenderName
};
let resp = await axios.post("https://promosms.com/api/rest/v3_2/sms", data, config);
if (resp.data.response.status !== 0) {
let error = "Something gone wrong. Api returned " + resp.data.response.status + ".";
this.throwGeneralAxiosError(error);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = PromoSMS;

View File

@@ -0,0 +1,50 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Pushbullet extends NotificationProvider {
name = "pushbullet";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let pushbulletUrl = "https://api.pushbullet.com/v2/pushes";
let config = {
headers: {
"Access-Token": notification.pushbulletAccessToken,
"Content-Type": "application/json"
}
};
if (heartbeatJSON == null) {
let testdata = {
"type": "note",
"title": "Uptime Kuma Alert",
"body": "Testing Successful.",
}
await axios.post(pushbulletUrl, testdata, config)
} else if (heartbeatJSON["status"] == DOWN) {
let downdata = {
"type": "note",
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(pushbulletUrl, downdata, config)
} else if (heartbeatJSON["status"] == UP) {
let updata = {
"type": "note",
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(pushbulletUrl, updata, config)
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Pushbullet;

View File

@@ -0,0 +1,49 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Pushover extends NotificationProvider {
name = "pushover";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let pushoverlink = "https://api.pushover.net/1/messages.json"
try {
if (heartbeatJSON == null) {
let data = {
"message": "<b>Uptime Kuma Pushover testing successful.</b>",
"user": notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1,
}
await axios.post(pushoverlink, data)
return okMsg;
}
let data = {
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg + "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"],
"user": notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1,
}
await axios.post(pushoverlink, data)
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Pushover;

View File

@@ -0,0 +1,30 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Pushy extends NotificationProvider {
name = "pushy";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {
"to": notification.pushyToken,
"data": {
"message": "Uptime-Kuma"
},
"notification": {
"body": msg,
"badge": 1,
"sound": "ping.aiff"
}
})
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Pushy;

View File

@@ -0,0 +1,66 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const Slack = require("./slack");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
class RocketChat extends NotificationProvider {
name = "rocket.chat";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON == null) {
let data = {
"text": msg,
"channel": notification.rocketchannel,
"username": notification.rocketusername,
"icon_emoji": notification.rocketiconemo,
};
await axios.post(notification.rocketwebhookURL, data);
return okMsg;
}
const time = heartbeatJSON["time"];
let data = {
"text": "Uptime Kuma Alert",
"channel": notification.rocketchannel,
"username": notification.rocketusername,
"icon_emoji": notification.rocketiconemo,
"attachments": [
{
"title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
"text": "*Message*\n" + msg,
}
]
};
// Color
if (heartbeatJSON.status === DOWN) {
data.attachments[0].color = "#ff0000";
} else {
data.attachments[0].color = "#32cd32";
}
if (notification.rocketbutton) {
await Slack.deprecateURL(notification.rocketbutton);
}
const baseURL = await setting("primaryBaseURL");
if (baseURL) {
data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id);
}
await axios.post(notification.rocketwebhookURL, data);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = RocketChat;

View File

@@ -0,0 +1,27 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Signal extends NotificationProvider {
name = "signal";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let data = {
"message": msg,
"number": notification.signalNumber,
"recipients": notification.signalRecipients.replace(/\s/g, "").split(","),
};
let config = {};
await axios.post(notification.signalURL, data, config)
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Signal;

View File

@@ -0,0 +1,99 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { setSettings, setting } = require("../util-server");
const { getMonitorRelativeURL } = require("../../src/util");
class Slack extends NotificationProvider {
name = "slack";
/**
* Deprecated property notification.slackbutton
* Set it as primary base url if this is not yet set.
*/
static async deprecateURL(url) {
let currentPrimaryBaseURL = await setting("primaryBaseURL");
if (!currentPrimaryBaseURL) {
console.log("Move the url to be the primary base URL");
await setSettings("general", {
primaryBaseURL: url,
});
} else {
console.log("Already there, no need to move the primary base URL");
}
}
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON == null) {
let data = {
"text": msg,
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
};
await axios.post(notification.slackwebhookURL, data);
return okMsg;
}
const time = heartbeatJSON["time"];
const textMsg = "Uptime Kuma Alert";
let data = {
"text": monitorJSON ? textMsg + `: ${monitorJSON.name}` : textMsg,
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
"blocks": [{
"type": "header",
"text": {
"type": "plain_text",
"text": "Uptime Kuma Alert",
},
},
{
"type": "section",
"fields": [{
"type": "mrkdwn",
"text": "*Message*\n" + msg,
},
{
"type": "mrkdwn",
"text": "*Time (UTC)*\n" + time,
}],
}],
};
if (notification.slackbutton) {
await Slack.deprecateURL(notification.slackbutton);
}
const baseURL = await setting("primaryBaseURL");
// Button
if (baseURL) {
data.blocks.push({
"type": "actions",
"elements": [{
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit Uptime Kuma",
},
"value": "Uptime-Kuma",
"url": baseURL + getMonitorRelativeURL(monitorJSON.id),
}],
});
}
await axios.post(notification.slackwebhookURL, data);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Slack;

View File

@@ -0,0 +1,99 @@
const nodemailer = require("nodemailer");
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
class SMTP extends NotificationProvider {
name = "smtp";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
const config = {
host: notification.smtpHost,
port: notification.smtpPort,
secure: notification.smtpSecure,
};
// Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904
if (notification.smtpUsername || notification.smtpPassword) {
config.auth = {
user: notification.smtpUsername,
pass: notification.smtpPassword,
};
}
// Lets start with default subject and empty string for custom one
let subject = msg;
// Change the subject if:
// - The msg ends with "Testing" or
// - Actual Up/Down Notification
if ((monitorJSON && heartbeatJSON) || msg.endsWith("Testing")) {
let customSubject = "";
// Our subject cannot end with whitespace it's often raise spam score
// Once I got "Cannot read property 'trim' of undefined", better be safe than sorry
if (notification.customSubject) {
customSubject = notification.customSubject.trim();
}
// If custom subject is not empty, change subject for notification
if (customSubject !== "") {
// Replace "MACROS" with corresponding variable
let replaceName = new RegExp("{{NAME}}", "g");
let replaceHostnameOrURL = new RegExp("{{HOSTNAME_OR_URL}}", "g");
let replaceStatus = new RegExp("{{STATUS}}", "g");
// Lets start with dummy values to simplify code
let monitorName = "Test";
let monitorHostnameOrURL = "testing.hostname";
let serviceStatus = "⚠️ Test";
if (monitorJSON !== null) {
monitorName = monitorJSON["name"];
if (monitorJSON["type"] === "http" || monitorJSON["type"] === "keyword") {
monitorHostnameOrURL = monitorJSON["url"];
} else {
monitorHostnameOrURL = monitorJSON["hostname"];
}
}
if (heartbeatJSON !== null) {
serviceStatus = (heartbeatJSON["status"] === DOWN) ? "🔴 Down" : "✅ Up";
}
// Break replace to one by line for better readability
customSubject = customSubject.replace(replaceStatus, serviceStatus);
customSubject = customSubject.replace(replaceName, monitorName);
customSubject = customSubject.replace(replaceHostnameOrURL, monitorHostnameOrURL);
subject = customSubject;
}
}
let transporter = nodemailer.createTransport(config);
let bodyTextContent = msg;
if (heartbeatJSON) {
bodyTextContent = `${msg}\nTime (UTC): ${heartbeatJSON["time"]}`;
}
// send mail with defined transport object
await transporter.sendMail({
from: notification.smtpFrom,
cc: notification.smtpCC,
bcc: notification.smtpBCC,
to: notification.smtpTo,
subject: subject,
text: bodyTextContent,
tls: {
rejectUnauthorized: notification.smtpIgnoreTLSError || false,
},
});
return "Sent Successfully.";
}
}
module.exports = SMTP;

View File

@@ -0,0 +1,124 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class Teams extends NotificationProvider {
name = "teams";
_statusMessageFactory = (status, monitorName) => {
if (status === DOWN) {
return `🔴 Application [${monitorName}] went down`;
} else if (status === UP) {
return `✅ Application [${monitorName}] is back online`;
}
return "Notification";
};
_getThemeColor = (status) => {
if (status === DOWN) {
return "ff0000";
}
if (status === UP) {
return "00e804";
}
return "008cff";
};
_notificationPayloadFactory = ({
status,
monitorMessage,
monitorName,
monitorUrl,
}) => {
const notificationMessage = this._statusMessageFactory(
status,
monitorName
);
const facts = [];
if (monitorName) {
facts.push({
name: "Monitor",
value: monitorName,
});
}
if (monitorUrl) {
facts.push({
name: "URL",
value: monitorUrl,
});
}
return {
"@context": "https://schema.org/extensions",
"@type": "MessageCard",
themeColor: this._getThemeColor(status),
summary: notificationMessage,
sections: [
{
activityImage:
"https://raw.githubusercontent.com/louislam/uptime-kuma/master/public/icon.png",
activityTitle: "**Uptime Kuma**",
},
{
activityTitle: notificationMessage,
},
{
activityTitle: "**Description**",
text: monitorMessage,
facts,
},
],
};
};
_sendNotification = async (webhookUrl, payload) => {
await axios.post(webhookUrl, payload);
};
_handleGeneralNotification = (webhookUrl, msg) => {
const payload = this._notificationPayloadFactory({
monitorMessage: msg
});
return this._sendNotification(webhookUrl, payload);
};
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
if (heartbeatJSON == null) {
await this._handleGeneralNotification(notification.webhookUrl, msg);
return okMsg;
}
let url;
if (monitorJSON["type"] === "port") {
url = monitorJSON["hostname"];
if (monitorJSON["port"]) {
url += ":" + monitorJSON["port"];
}
} else {
url = monitorJSON["url"];
}
const payload = this._notificationPayloadFactory({
monitorMessage: heartbeatJSON.msg,
monitorName: monitorJSON.name,
monitorUrl: url,
status: heartbeatJSON.status,
});
await this._sendNotification(notification.webhookUrl, payload);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Teams;

View File

@@ -0,0 +1,27 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Telegram extends NotificationProvider {
name = "telegram";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
params: {
chat_id: notification.telegramChatID,
text: msg,
},
})
return okMsg;
} catch (error) {
let msg = (error.response.data.description) ? error.response.data.description : "Error without description"
throw new Error(msg)
}
}
}
module.exports = Telegram;

View File

@@ -0,0 +1,44 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const FormData = require("form-data");
class Webhook extends NotificationProvider {
name = "webhook";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
msg,
};
let finalData;
let config = {};
if (notification.webhookContentType === "form-data") {
finalData = new FormData();
finalData.append("data", JSON.stringify(data));
config = {
headers: finalData.getHeaders(),
}
} else {
finalData = data;
}
await axios.post(notification.webhookURL, finalData, config)
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
}
}
}
module.exports = Webhook;

View File

@@ -1,600 +1,87 @@
const axios = require("axios");
const { R } = require("redbean-node");
const FormData = require("form-data");
const nodemailer = require("nodemailer");
const child_process = require("child_process");
const Apprise = require("./notification-providers/apprise");
const Discord = require("./notification-providers/discord");
const Gotify = require("./notification-providers/gotify");
const Line = require("./notification-providers/line");
const LunaSea = require("./notification-providers/lunasea");
const Mattermost = require("./notification-providers/mattermost");
const Matrix = require("./notification-providers/matrix");
const Octopush = require("./notification-providers/octopush");
const PromoSMS = require("./notification-providers/promosms");
const Pushbullet = require("./notification-providers/pushbullet");
const Pushover = require("./notification-providers/pushover");
const Pushy = require("./notification-providers/pushy");
const RocketChat = require("./notification-providers/rocket-chat");
const Signal = require("./notification-providers/signal");
const Slack = require("./notification-providers/slack");
const SMTP = require("./notification-providers/smtp");
const Teams = require("./notification-providers/teams");
const Telegram = require("./notification-providers/telegram");
const Webhook = require("./notification-providers/webhook");
const Feishu = require("./notification-providers/feishu");
const AliyunSms = require("./notification-providers/aliyun-sms");
const DingDing = require("./notification-providers/dingding");
class Notification {
providerList = {};
static init() {
console.log("Prepare Notification Providers");
this.providerList = {};
const list = [
new Apprise(),
new AliyunSms(),
new DingDing(),
new Discord(),
new Teams(),
new Gotify(),
new Line(),
new LunaSea(),
new Feishu(),
new Mattermost(),
new Matrix(),
new Octopush(),
new PromoSMS(),
new Pushbullet(),
new Pushover(),
new Pushy(),
new RocketChat(),
new Signal(),
new Slack(),
new SMTP(),
new Telegram(),
new Webhook(),
];
for (let item of list) {
if (! item.name) {
throw new Error("Notification provider without name");
}
if (this.providerList[item.name]) {
throw new Error("Duplicate notification provider name");
}
this.providerList[item.name] = item;
}
}
/**
*
* @param notification
* @param msg
* @param monitorJSON
* @param heartbeatJSON
* @param notification : BeanModel
* @param msg : string General Message
* @param monitorJSON : object Monitor details (For Up/Down only)
* @param heartbeatJSON : object Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Successful msg
* Throw Error with fail msg
*/
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. ";
if (notification.type === "telegram") {
try {
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
params: {
chat_id: notification.telegramChatID,
text: msg,
},
})
return okMsg;
} catch (error) {
let msg = (error.response.data.description) ? error.response.data.description : "Error without description"
throw new Error(msg)
}
} else if (notification.type === "gotify") {
try {
if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) {
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);
}
await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, {
"message": msg,
"priority": notification.gotifyPriority || 8,
"title": "Uptime-Kuma",
})
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "webhook") {
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
msg,
};
let finalData;
let config = {};
if (notification.webhookContentType === "form-data") {
finalData = new FormData();
finalData.append("data", JSON.stringify(data));
config = {
headers: finalData.getHeaders(),
}
} else {
finalData = data;
}
await axios.post(notification.webhookURL, finalData, config)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "smtp") {
return await Notification.smtp(notification, msg)
} else if (notification.type === "discord") {
try {
const discordDisplayName = notification.discordUsername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let discordtestdata = {
username: discordDisplayName,
content: msg,
}
await axios.post(notification.discordWebhookUrl, discordtestdata)
return okMsg;
}
let url;
if (monitorJSON["type"] === "port") {
url = monitorJSON["hostname"];
if (monitorJSON["port"]) {
url += ":" + monitorJSON["port"];
}
} else {
url = monitorJSON["url"];
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON["status"] == 0) {
let discorddowndata = {
username: discordDisplayName,
embeds: [{
title: "❌ Your service " + monitorJSON["name"] + " went down. ❌",
color: 16711680,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Error",
value: heartbeatJSON["msg"],
},
],
}],
}
await axios.post(notification.discordWebhookUrl, discorddowndata)
return okMsg;
} else if (heartbeatJSON["status"] == 1) {
let discordupdata = {
username: discordDisplayName,
embeds: [{
title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
color: 65280,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Ping",
value: heartbeatJSON["ping"] + "ms",
},
],
}],
}
await axios.post(notification.discordWebhookUrl, discordupdata)
return okMsg;
}
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "signal") {
try {
let data = {
"message": msg,
"number": notification.signalNumber,
"recipients": notification.signalRecipients.replace(/\s/g, "").split(","),
};
let config = {};
await axios.post(notification.signalURL, data, config)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "pushy") {
try {
await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {
"to": notification.pushyToken,
"data": {
"message": "Uptime-Kuma"
},
"notification": {
"body": msg,
"badge": 1,
"sound": "ping.aiff"
}
})
return true;
} catch (error) {
console.log(error)
return false;
}
} else if (notification.type === "octopush") {
try {
let config = {
headers: {
"api-key": notification.octopushAPIKey,
"api-login": notification.octopushLogin,
"cache-control": "no-cache"
}
};
let data = {
"recipients": [
{
"phone_number": notification.octopushPhoneNumber
}
],
//octopush not supporting non ascii char
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"type": notification.octopushSMSType,
"purpose": "alert",
"sender": notification.octopushSenderName
};
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
return true;
} catch (error) {
console.log(error)
return false;
}
} else if (notification.type === "slack") {
try {
if (heartbeatJSON == null) {
let data = {
"text": "Uptime Kuma Slack testing successful.",
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
}
await axios.post(notification.slackwebhookURL, data)
return okMsg;
}
const time = heartbeatJSON["time"];
let data = {
"text": "Uptime Kuma Alert",
"channel": notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
"blocks": [{
"type": "header",
"text": {
"type": "plain_text",
"text": "Uptime Kuma Alert",
},
},
{
"type": "section",
"fields": [{
"type": "mrkdwn",
"text": "*Message*\n" + msg,
},
{
"type": "mrkdwn",
"text": "*Time (UTC)*\n" + time,
}],
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit Uptime Kuma",
},
"value": "Uptime-Kuma",
"url": notification.slackbutton || "https://github.com/louislam/uptime-kuma",
},
],
}],
}
await axios.post(notification.slackwebhookURL, data)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "rocket.chat") {
try {
if (heartbeatJSON == null) {
let data = {
"text": "Uptime Kuma Rocket.chat testing successful.",
"channel": notification.rocketchannel,
"username": notification.rocketusername,
"icon_emoji": notification.rocketiconemo,
}
await axios.post(notification.rocketwebhookURL, data)
return okMsg;
}
const time = heartbeatJSON["time"];
let data = {
"text": "Uptime Kuma Alert",
"channel": notification.rocketchannel,
"username": notification.rocketusername,
"icon_emoji": notification.rocketiconemo,
"attachments": [
{
"title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
"title_link": notification.rocketbutton,
"text": "*Message*\n" + msg,
"color": "#32cd32"
}
]
}
await axios.post(notification.rocketwebhookURL, data)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "mattermost") {
try {
const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let mattermostTestData = {
username: mattermostUserName,
text: msg,
}
await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
return okMsg;
}
const mattermostChannel = notification.mattermostchannel;
const mattermostIconEmoji = notification.mattermosticonemo;
const mattermostIconUrl = notification.mattermosticonurl;
if (heartbeatJSON["status"] == 0) {
let mattermostdowndata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",
channel: mattermostChannel,
icon_emoji: mattermostIconEmoji,
icon_url: mattermostIconUrl,
attachments: [
{
fallback:
"Your " +
monitorJSON["name"] +
" service went down.",
color: "#FF0000",
title:
"❌ " +
monitorJSON["name"] +
" service went down. ❌",
title_link: monitorJSON["url"],
fields: [
{
short: true,
title: "Service Name",
value: monitorJSON["name"],
},
{
short: true,
title: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
short: false,
title: "Error",
value: heartbeatJSON["msg"],
},
],
},
],
};
await axios.post(
notification.mattermostWebhookUrl,
mattermostdowndata
);
return okMsg;
} else if (heartbeatJSON["status"] == 1) {
let mattermostupdata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",
channel: mattermostChannel,
icon_emoji: mattermostIconEmoji,
icon_url: mattermostIconUrl,
attachments: [
{
fallback:
"Your " +
monitorJSON["name"] +
" service went up!",
color: "#32CD32",
title:
"✅ " +
monitorJSON["name"] +
" service went up! ✅",
title_link: monitorJSON["url"],
fields: [
{
short: true,
title: "Service Name",
value: monitorJSON["name"],
},
{
short: true,
title: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
short: false,
title: "Ping",
value: heartbeatJSON["ping"] + "ms",
},
],
},
],
};
await axios.post(
notification.mattermostWebhookUrl,
mattermostupdata
);
return okMsg;
}
} catch (error) {
throwGeneralAxiosError(error);
}
} else if (notification.type === "pushover") {
let pushoverlink = "https://api.pushover.net/1/messages.json"
try {
if (heartbeatJSON == null) {
let data = {
"message": "<b>Uptime Kuma Pushover testing successful.</b>",
"user": notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1,
}
await axios.post(pushoverlink, data)
return okMsg;
}
let data = {
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg + "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"],
"user": notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1,
}
await axios.post(pushoverlink, data)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "apprise") {
return Notification.apprise(notification, msg)
} else if (notification.type === "lunasea") {
let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
try {
if (heartbeatJSON == null) {
let testdata = {
"title": "Uptime Kuma Alert",
"body": "Testing Successful.",
}
await axios.post(lunaseadevice, testdata)
return okMsg;
}
if (heartbeatJSON["status"] == 0) {
let downdata = {
"title": "UptimeKuma Alert:" + monitorJSON["name"],
"body": "[🔴 Down]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, downdata)
return okMsg;
}
if (heartbeatJSON["status"] == 1) {
let updata = {
"title": "UptimeKuma Alert:" + monitorJSON["name"],
"body": "[✅ Up]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, updata)
return okMsg;
}
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "pushbullet") {
try {
let pushbulletUrl = "https://api.pushbullet.com/v2/pushes";
let config = {
headers: {
"Access-Token": notification.pushbulletAccessToken,
"Content-Type": "application/json"
}
};
if (heartbeatJSON == null) {
let testdata = {
"type": "note",
"title": "Uptime Kuma Alert",
"body": "Testing Successful.",
}
await axios.post(pushbulletUrl, testdata, config)
} else if (heartbeatJSON["status"] == 0) {
let downdata = {
"type": "note",
"title": "UptimeKuma Alert:" + monitorJSON["name"],
"body": "[🔴 Down]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
}
await axios.post(pushbulletUrl, downdata, config)
} else if (heartbeatJSON["status"] == 1) {
let updata = {
"type": "note",
"title": "UptimeKuma Alert:" + monitorJSON["name"],
"body": "[✅ Up]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
}
await axios.post(pushbulletUrl, updata, config)
}
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "line") {
try {
let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notification.lineChannelAccessToken
}
};
if (heartbeatJSON == null) {
let testMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "Test Successful!"
}
]
}
await axios.post(lineAPIUrl, testMessage, config)
} else if (heartbeatJSON["status"] == 0) {
let downMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
}
]
}
await axios.post(lineAPIUrl, downMessage, config)
} else if (heartbeatJSON["status"] == 1) {
let upMessage = {
"to": notification.lineUserID,
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
}
]
}
await axios.post(lineAPIUrl, upMessage, config)
}
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
if (this.providerList[notification.type]) {
return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON);
} else {
throw new Error("Notification type is not supported")
throw new Error("Notification type is not supported");
}
}
@@ -617,8 +104,15 @@ class Notification {
bean.name = notification.name;
bean.user_id = userID;
bean.config = JSON.stringify(notification)
bean.config = JSON.stringify(notification);
bean.is_default = notification.isDefault || false;
await R.store(bean)
if (notification.applyExisting) {
await applyNotificationEveryMonitor(bean.id, userID);
}
return bean;
}
static async delete(notificationID, userID) {
@@ -634,52 +128,6 @@ class Notification {
await R.trash(bean)
}
static async smtp(notification, msg) {
const config = {
host: notification.smtpHost,
port: notification.smtpPort,
secure: notification.smtpSecure,
};
// Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904
if (notification.smtpUsername || notification.smtpPassword) {
config.auth = {
user: notification.smtpUsername,
pass: notification.smtpPassword,
};
}
let transporter = nodemailer.createTransport(config);
// send mail with defined transport object
await transporter.sendMail({
from: `"Uptime Kuma" <${notification.smtpFrom}>`,
to: notification.smtpTo,
subject: msg,
text: msg,
});
return "Sent Successfully.";
}
static async apprise(notification, msg) {
let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL])
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
if (output) {
if (! output.includes("ERROR")) {
return "Sent Successfully";
}
throw new Error(output)
} else {
return ""
}
}
static checkApprise() {
let commandExistsSync = require("command-exists").sync;
let exists = commandExistsSync("apprise");
@@ -688,18 +136,24 @@ class Notification {
}
function throwGeneralAxiosError(error) {
let msg = "Error: " + error + " ";
async function applyNotificationEveryMonitor(notificationID, userID) {
let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
userID
]);
if (error.response && error.response.data) {
if (typeof error.response.data === "string") {
msg += error.response.data;
} else {
msg += JSON.stringify(error.response.data)
for (let i = 0; i < monitors.length; i++) {
let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [
monitors[i].id,
notificationID,
])
if (! checkNotification) {
let relation = R.dispense("monitor_notification");
relation.monitor_id = monitors[i].id;
relation.notification_id = notificationID;
await R.store(relation)
}
}
throw new Error(msg)
}
module.exports = {

View File

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

View File

@@ -6,7 +6,7 @@ const commonLabels = [
"monitor_url",
"monitor_hostname",
"monitor_port",
]
];
const monitor_cert_days_remaining = new PrometheusClient.Gauge({
name: "monitor_cert_days_remaining",
@@ -41,45 +41,46 @@ class Prometheus {
monitor_url: monitor.url,
monitor_hostname: monitor.hostname,
monitor_port: monitor.port
}
};
}
update(heartbeat, tlsInfo) {
if (typeof tlsInfo !== "undefined") {
try {
let is_valid = 0
let is_valid = 0;
if (tlsInfo.valid == true) {
is_valid = 1
is_valid = 1;
} else {
is_valid = 0
is_valid = 0;
}
monitor_cert_is_valid.set(this.monitorLabelValues, is_valid)
monitor_cert_is_valid.set(this.monitorLabelValues, is_valid);
} catch (e) {
console.error(e)
console.error(e);
}
try {
monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.daysRemaining)
monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining);
} catch (e) {
console.error(e)
console.error(e);
}
}
try {
monitor_status.set(this.monitorLabelValues, heartbeat.status)
monitor_status.set(this.monitorLabelValues, heartbeat.status);
} catch (e) {
console.error(e)
console.error(e);
}
try {
if (typeof heartbeat.ping === "number") {
monitor_response_time.set(this.monitorLabelValues, heartbeat.ping)
monitor_response_time.set(this.monitorLabelValues, heartbeat.ping);
} else {
// Is it good?
monitor_response_time.set(this.monitorLabelValues, -1)
monitor_response_time.set(this.monitorLabelValues, -1);
}
} catch (e) {
console.error(e)
console.error(e);
}
}
@@ -87,4 +88,4 @@ class Prometheus {
module.exports = {
Prometheus
}
};

View File

@@ -0,0 +1,225 @@
let express = require("express");
const { allowDevAllOrigin, getSettings, setting } = require("../util-server");
const { R } = require("redbean-node");
const server = require("../server");
const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor");
const dayjs = require("dayjs");
const { UP, flipStatus, debug } = require("../../src/util");
let router = express.Router();
let cache = apicache.middleware;
let io = server.io;
router.get("/api/entry-page", async (_, response) => {
allowDevAllOrigin(response);
response.json(server.entryPage);
});
router.get("/api/push/:pushToken", async (request, response) => {
try {
let pushToken = request.params.pushToken;
let msg = request.query.msg || "OK";
let ping = request.query.ping || null;
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
pushToken
]);
if (! monitor) {
throw new Error("Monitor not found or not active.");
}
const previousHeartbeat = await R.getRow(`
SELECT status, time FROM heartbeat
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
`, [
monitor.id
]);
let status = UP;
if (monitor.isUpsideDown()) {
status = flipStatus(status);
}
let isFirstBeat = true;
let previousStatus = status;
let duration = 0;
let bean = R.dispense("heartbeat");
bean.time = R.isoDateTime(dayjs.utc());
if (previousHeartbeat) {
isFirstBeat = false;
previousStatus = previousHeartbeat.status;
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
}
debug("PreviousStatus: " + previousStatus);
debug("Current Status: " + status);
bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status);
bean.monitor_id = monitor.id;
bean.status = status;
bean.msg = msg;
bean.ping = ping;
bean.duration = duration;
await R.store(bean);
io.to(monitor.user_id).emit("heartbeat", bean.toJSON());
Monitor.sendStats(io, monitor.id, monitor.user_id);
response.json({
ok: true,
});
if (bean.important) {
await Monitor.sendNotification(isFirstBeat, monitor, bean);
}
} catch (e) {
response.json({
ok: false,
msg: e.message
});
}
});
// Status Page Config
router.get("/api/status-page/config", async (_request, response) => {
allowDevAllOrigin(response);
let config = await getSettings("statusPage");
if (! config.statusPageTheme) {
config.statusPageTheme = "light";
}
if (! config.statusPagePublished) {
config.statusPagePublished = true;
}
if (! config.title) {
config.title = "Uptime Kuma";
}
response.json(config);
});
// Status Page - Get the current Incident
// Can fetch only if published
router.get("/api/status-page/incident", async (_, response) => {
allowDevAllOrigin(response);
try {
await checkPublished();
let incident = await R.findOne("incident", " pin = 1 AND active = 1");
if (incident) {
incident = incident.toPublicJSON();
}
response.json({
ok: true,
incident,
});
} catch (error) {
send403(response, error.message);
}
});
// Status Page - Monitor List
// Can fetch only if published
router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, response) => {
allowDevAllOrigin(response);
try {
await checkPublished();
const publicGroupList = [];
let list = await R.find("group", " public = 1 ORDER BY weight ");
for (let groupBean of list) {
publicGroupList.push(await groupBean.toPublicJSON());
}
response.json(publicGroupList);
} catch (error) {
send403(response, error.message);
}
});
// Status Page Polling Data
// Can fetch only if published
router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, response) => {
allowDevAllOrigin(response);
try {
await checkPublished();
let heartbeatList = {};
let uptimeList = {};
let monitorIDList = await R.getCol(`
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
WHERE monitor_group.group_id = \`group\`.id
AND public = 1
`);
for (let monitorID of monitorIDList) {
let list = await R.getAll(`
SELECT * FROM heartbeat
WHERE monitor_id = ?
ORDER BY time DESC
LIMIT 50
`, [
monitorID,
]);
list = R.convertToBeans("heartbeat", list);
heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
const type = 24;
uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID);
}
response.json({
heartbeatList,
uptimeList
});
} catch (error) {
send403(response, error.message);
}
});
async function checkPublished() {
if (! await isPublished()) {
throw new Error("The status page is not published");
}
}
/**
* Default is published
* @returns {Promise<boolean>}
*/
async function isPublished() {
const value = await setting("statusPagePublished");
if (value === null) {
return true;
}
return value;
}
function send403(res, msg = "") {
res.status(403).json({
"status": "fail",
"msg": msg,
});
}
module.exports = router;

File diff suppressed because it is too large Load Diff

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