Compare commits

...

736 Commits

Author SHA1 Message Date
Louis Lam
dd09351c8e Update to 1.17.0-beta.1 2022-06-16 19:34:47 +08:00
Louis Lam
ffad990ca4 Merge pull request #1773 from christopherpickering/patch-3
[beta] updated translation for "basic" auth
2022-06-16 14:37:05 +08:00
Christopher Pickering
42848bcd2e updated translations 2022-06-15 09:00:47 -05:00
Louis Lam
a3b94aa532 Merge pull request #1550 from Computroniks/jsdoc-for-src
JSDoc for src/*/*
2022-06-15 19:29:51 +08:00
Louis Lam
fdbdf83a0d Fix data type of notification.isDefault and notification.active (#1765) 2022-06-15 19:11:52 +08:00
Louis Lam
81d5360520 Merge remote-tracking branch 'origin/master' 2022-06-15 19:03:22 +08:00
Louis Lam
8f1e193de3 [vite] Change legacy browse target to since 2015 2022-06-15 19:02:55 +08:00
Louis Lam
da91317760 Merge pull request #1772 from chakflying/fix/mobile-monitor-list
Fix: Fix monitor list layout on mobile
2022-06-15 15:50:04 +08:00
Louis Lam
bef0febede Merge pull request #1768 from christopherpickering/patch-1
[beta] prevent null workstation #'s from passing..
2022-06-15 15:33:03 +08:00
Louis Lam
7d63b700e1 Merge pull request #1767 from christopherpickering/patch-2
[beta] workstation field should be text, not password
2022-06-15 15:31:24 +08:00
Louis Lam
0223f86a2a Merge remote-tracking branch 'origin/master' 2022-06-15 15:18:29 +08:00
Louis Lam
c170b1edd0 Better optgroup text color 2022-06-15 15:18:14 +08:00
Louis Lam
5943514a92 Merge pull request #1771 from christopherpickering/master
[beta] added default value for sql server connection string
2022-06-15 14:08:48 +08:00
Nelson Chan
62acd2edb1 Fix: misc. layout fix on mobile 2022-06-14 22:43:44 +08:00
Christopher Pickering
483cbfb636 added default value for sql server connection string 2022-06-14 09:00:23 -05:00
Christopher Pickering
660005b143 cleaned up code 2022-06-14 08:49:36 -05:00
Christopher Pickering
98f3c126e5 passed lint 2022-06-14 07:58:35 -05:00
sur.la.route
6995a29980 workstation field should be text, not password 2022-06-14 07:45:04 -05:00
sur.la.route
cf2ca71dee prevent null workstation #'s from passing..
to axios-ntlm
2022-06-14 07:42:53 -05:00
Louis Lam
0bd1c42080 Merge pull request #1763 from chakflying/fix/cert-exp-setting-default
Fix: Fix missing certificate exp. notif. default
2022-06-14 17:27:45 +08:00
Louis Lam
9b21b86e70 Merge pull request #1762 from kaysond/master
Fix upside down push monitors
2022-06-14 17:25:32 +08:00
Nelson Chan
f723930d11 Fix: Unify design with Security page 2022-06-14 15:04:46 +08:00
Nelson Chan
e425e408a2 Fix: Fix missing default settings 2022-06-14 15:04:14 +08:00
Aram Akhavan
c690d1c3a1 fix timeout bypass for upside down push monitor 2022-06-13 22:05:58 -07:00
Louis Lam
8abbc9fd15 Update to 1.17.0-beta.0 2022-06-14 11:00:55 +08:00
Louis Lam
af7c905b44 Merge pull request #1759 from MrEddX/bulgarian
Bulgarian
2022-06-14 10:57:14 +08:00
Louis Lam
0e8f6d2f85 Merge pull request #1639 from christopherpickering/ntml-auth
Add NTML Auth Option for HTTP
2022-06-14 10:56:55 +08:00
Louis Lam
d16be6fb7d Fix and use null as authMethod None instead of empty string 2022-06-14 10:49:30 +08:00
Louis Lam
f25ca96308 Update package-lock.json 2022-06-14 10:37:15 +08:00
Louis Lam
6682839ec8 Merge remote-tracking branch 'origin/master' into ntml-auth
# Conflicts:
#	package-lock.json
#	package.json
#	server/database.js
#	server/model/monitor.js
#	server/server.js
#	server/util-server.js
2022-06-14 10:36:29 +08:00
Louis Lam
817f6db4fd Remove SQL Server code (which in another pr already) 2022-06-14 10:32:38 +08:00
Louis Lam
dc2302244f Merge pull request #1754 from SIMULATAN/patch-1
Remove template comment in SECURITY.md
2022-06-14 10:22:08 +08:00
MrEddX
7a27d3752a Merge branch 'louislam:master' into bulgarian 2022-06-13 22:36:00 +03:00
MrEddX
f0e8f34aeb Update bg-BG.js
Added new fields
2022-06-13 22:32:55 +03:00
Louis Lam
69273a6c41 Merge remote-tracking branch 'origin/master' 2022-06-13 21:16:03 +08:00
Louis Lam
6424fe77ab Change successful log from info to debug in order to avoid large log and less disk usage 2022-06-13 21:15:47 +08:00
Louis Lam
fa60672cce Merge pull request #1728 from tamasmagyar/test/simplify-discord-unit-tests
test: simplified discord unit tests
2022-06-13 21:02:12 +08:00
Louis Lam
6e43ef1dd3 Merge remote-tracking branch 'origin/master' into feat/cert-exp-settings
# Conflicts:
#	server/model/monitor.js
#	src/languages/en.js
2022-06-13 20:56:14 +08:00
Louis Lam
f4f2b8ddb8 Try to fix #1658 2022-06-13 19:24:05 +08:00
Louis Lam
436bc13aeb Merge pull request #1598 from gregdev/feature/axios-cached-dns-resolve
add axios cached dns resolve to monitor
2022-06-13 18:58:16 +08:00
Louis Lam
b72a279361 Update package-lock.json 2022-06-13 18:54:02 +08:00
Louis Lam
a28ef56553 Merge remote-tracking branch 'gregdev/feature/axios-cached-dns-resolve' into feature/axios-cached-dns-resolve
# Conflicts:
#	package-lock.json
#	package.json
2022-06-13 18:53:19 +08:00
Louis Lam
7f432bd916 Remove axios-cached-dns-resolve-test 2022-06-13 18:52:08 +08:00
Louis Lam
f570d41142 Merge remote-tracking branch 'origin/master' into feature/axios-cached-dns-resolve
# Conflicts:
#	package-lock.json
#	package.json
2022-06-13 18:50:43 +08:00
Louis Lam
d4485fe62f Make the monitor type list a bit clear 2022-06-13 18:14:47 +08:00
Louis Lam
e1681ce370 Merge pull request #1636 from christopherpickering/master
Add SQLServer Monitor
2022-06-13 18:03:56 +08:00
Jakob
69d6633e6d Remove template comment 2022-06-13 11:34:36 +02:00
Louis Lam
04e22f17a9 Merge remote-tracking branch 'origin/master' into christopherpickering_master
# Conflicts:
#	package-lock.json
#	src/languages/en.js
2022-06-11 20:59:58 +08:00
Louis Lam
11243a6ca1 Merge pull request #1222 from NETivism/issue-1201
Show some pure text body in notification when keyword not found
2022-06-09 19:33:10 +08:00
Louis Lam
87428231ad Merge pull request #1727 from chakflying/patch-2
Fix: Fix error when status page desc. is null
2022-06-07 16:45:28 +08:00
tamasmagyar
a68d945cdc simplified backend unit tests 2022-06-07 09:10:50 +02:00
Nelson Chan
2c0180f323 Fix: Fix error when status page desc. is null 2022-06-07 14:57:23 +08:00
Louis Lam
4fdaa1abb6 [Push API] Response 404 if error, fix #1721 2022-06-06 22:40:26 +08:00
Louis Lam
6ee7b3696a Merge pull request #1633 from domingospanta/bugfix/1451_blank_page_on_unkown_resource
Bugfix/1451 blank page on unkown resource
2022-06-06 21:54:49 +08:00
Louis Lam
cc258dce14 Merge pull request #1674 from philippdormann/feature/ntfy-support
feat: ntfy push support
2022-06-06 21:52:41 +08:00
Louis Lam
fb420fa1b1 Compress SVG when building dist 2022-06-05 23:49:48 +08:00
Louis Lam
a707b51053 Page Loading Speed Optimization (#1711)
* Update Vite.js to 2.9.9 and add Rollup Plugin Visualizer
* Prebuild gzip and brotli for assets

Original: ~1.2MB
Optimized: ~370KB
2022-06-05 23:43:25 +08:00
Matthew Nickson
a927f5cd15 Fixed typos + improved clarity and detail of some JSDoc
Apply suggestions from code review

Co-authored-by: Nelson Chan <chakflying@hotmail.com>
2022-06-02 16:40:56 +01:00
Matthew Nickson
0e28707307 Minor formatting for JSDoc comments
Added a number of minor formatting changes to JSDoc comments in /src
2022-06-02 15:15:21 +01:00
Matthew Nickson
c94dcf1533 Added JSDoc for src/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-06-02 14:32:38 +01:00
Matthew Nickson
b0476cfb5b Added JSDoc for src/pages/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-06-02 13:46:44 +01:00
Matthew Nickson
2170229031 Improve JSDoc for some components
Apply suggestions from code review

Co-authored-by: Nelson Chan <chakflying@hotmail.com>
2022-06-02 10:42:37 +01:00
Matthew Nickson
213aca4fc3 Added JSDoc for src/mixins/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-06-02 10:38:17 +01:00
Matthew Nickson
2b42c3c828 Added JSDoc for src/components/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-06-02 00:32:05 +01:00
Matthew Nickson
d939d03690 Added JSDoc for src/components/settings/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-06-01 23:44:10 +01:00
Matthew Nickson
07888e43f1 [empty commit] pull request for JSDoc src/* 2022-06-01 22:51:26 +01:00
Louis Lam
c6c1bb5b5c Merge pull request #1710 from AnnAngela/master
Update zh-CN translation
2022-06-01 16:05:23 +08:00
Louis Lam
3210264e28 Update PULL_REQUEST_TEMPLATE.md 2022-06-01 14:20:49 +08:00
Louis Lam
54e948c2ca Update CONTRIBUTING.md 2022-06-01 14:08:10 +08:00
Louis Lam
80094ec4e1 Merge pull request #1513 from louislam/status-page-inject-html
[Status Page] Render title, meta tag or favicon etc. in server side
2022-06-01 13:25:41 +08:00
Louis Lam
091158cfe7 [Status Page] Preload data 2022-06-01 13:05:12 +08:00
AnnAngela-work
abb6ce2366 Update zhCN translation 2022-06-01 11:28:10 +08:00
Louis Lam
e4ad8cbfc8 Remove unused variables 2022-05-31 23:06:43 +08:00
Louis Lam
a674caa520 [Status Page] Add og meta tags 2022-05-31 22:53:48 +08:00
Nelson Chan
179e3569b5 Chore: Add code comments 2022-05-31 16:24:39 +08:00
Nelson Chan
43527f2f40 Chore: Update remaining languages 2022-05-30 18:05:28 +08:00
Nelson Chan
26ff6f45a0 Feat: Use i18n pluralization 2022-05-30 17:53:32 +08:00
Louis Lam
c095767f4a [Status Page] SSR 2022-05-30 15:45:44 +08:00
Louis Lam
ffb7ba176c Merge remote-tracking branch 'origin/master' into status-page-inject-html 2022-05-30 14:00:39 +08:00
Louis Lam
857e88b27e Update to 1.16.1 2022-05-29 12:47:07 +08:00
Louis Lam
90fe25e8ad Merge pull request #1428 from kaysond/master
Synchronize push monitor heartbeats to api calls (fixes #1422)
2022-05-29 12:34:16 +08:00
Louis Lam
46a593534b Merge pull request #1695 from furkanipek/update-tr-lang
Update tr-TR.js
2022-05-29 12:13:52 +08:00
Louis Lam
7a4b54f4ee Merge pull request #1702 from dhfhfk/master
Update Ko-KR.js
2022-05-29 12:12:07 +08:00
Aram Akhavan
ea10d89f51 show correct down message for first tick 2022-05-28 19:57:45 -07:00
Louis Lam
7f46223d68 Fix another log.debug call 2022-05-28 23:22:44 +08:00
Louis Lam
df4ce811d9 Merge remote-tracking branch 'origin/master' into kaysond_master
# Conflicts:
#	server/model/monitor.js
2022-05-28 23:19:58 +08:00
Louis Lam
30858ab038 Fix rollback issue of 9fc5a33 and one issue of #1694 2022-05-28 23:08:14 +08:00
dhfhfk
e25d406fa5 Eslint 2022-05-28 17:12:40 +09:00
dhfhfk
10e16782b1 Update ko-KR.js 2022-05-28 17:10:40 +09:00
Furkan İpek
107a44885c Update tr-TR.js 2022-05-27 15:05:09 +03:00
Furkan İpek
ef9f66fad9 Update tr-TR.js 2022-05-27 15:02:40 +03:00
Furkan İpek
46dae99695 Update tr-TR.js 2022-05-27 12:51:41 +03:00
Furkan İpek
edd9bf3887 Update tr-TR.js 2022-05-27 12:39:07 +03:00
Aram Akhavan
ab4edf2092 Fix log.debug calls 2022-05-26 21:45:56 -07:00
Louis Lam
334cb57fed Update to 1.16.0 2022-05-26 19:32:52 +08:00
Nelson Chan
cfa5b551a5 Feat: Make the expiry days sorted 2022-05-26 12:17:24 +08:00
Nelson Chan
46ee149b70 Chore: Slightly improve design 2022-05-26 12:12:29 +08:00
Louis Lam
0a8c922abf Fix default value of pagerduty-integration-url 2022-05-25 23:34:47 +08:00
Louis Lam
058e5442af Merge pull request #1310 from MarcHagen/feature/pagerduty
Add support for PagerDuty notifications
2022-05-25 23:26:33 +08:00
Louis Lam
ea1725737f Merge pull request #1687 from burakurer/patch-3
Update tr-TR.js
2022-05-25 23:24:10 +08:00
burakurer
5566b038c8 Update tr-TR.js 2022-05-25 17:35:05 +03:00
Marc Hagen
5830f1e0b5 [feat] Adding PagerDuty notification 2022-05-25 14:10:31 +02:00
Louis Lam
35b8e89457 Merge pull request #1473 from Computroniks/#1059-specify-dns-resolver-port
Added #1059: Allow to specify Resolver Port for DNS Monitor
2022-05-25 14:59:14 +08:00
Louis Lam
d892b2c549 Merge pull request #1686 from DasCanard/master
Improved Discord Notifications with Push Monitors
2022-05-25 14:57:25 +08:00
DasCanard
f23baf9c22 Added Push Monitor to Discord Notifications 2022-05-24 23:14:27 +02:00
Philipp Dormann
54184350a4 fix: missing semicolons 2022-05-23 21:13:57 +02:00
Philipp Dormann
14dbe7c334 clean up + default ntfs.sh server url 2022-05-23 21:11:01 +02:00
Philipp Dormann
122e6a842b clean up ntfy topic input 2022-05-23 21:08:56 +02:00
Philipp Dormann
77ef22bdb4 set proper ntfy priorities 2022-05-23 21:08:11 +02:00
Philipp Dormann
59f983d506 fix: unused import "HiddenInput" 2022-05-23 20:57:10 +02:00
Philipp Dormann
71f031c14e add ntfy support
ref https://github.com/louislam/uptime-kuma/issues/1622
2022-05-23 10:55:03 +02:00
Louis Lam
6ae2a48584 Merge pull request #1662 from kiznick/master
Translate to Thai
2022-05-21 14:14:42 +08:00
Louis Lam
7373747906 Merge pull request #1659 from MrEddX/bulgarian
Bulgarian
2022-05-21 14:13:48 +08:00
Yoswaris Lawpaiboon
9d87f8d390 Update th-TH.js 2022-05-20 19:16:37 +07:00
Domingos F. Panta Jr
73b965c867 Merge branch 'master' into bugfix/1451_blank_page_on_unkown_resource 2022-05-19 19:35:58 +01:00
MrEddX
751e5ac477 Bulgarian Translation
Updated Translation
2022-05-19 20:32:01 +03:00
Yoswaris Lawpaiboon
93e5023ead Translate to Thai ! 2022-05-19 19:44:59 +07:00
Nelson Chan
b7ba6330db Feat: Add cert exp. settings 2022-05-19 16:49:34 +08:00
MrEddX
4c3aa20eb0 Update bg-BG.js
Added new fields
2022-05-18 22:18:58 +03:00
MrEddX
f779c6286a Merge branch 'louislam:master' into bulgarian 2022-05-18 22:14:18 +03:00
Louis Lam
9fc5a3329f Revert #1208, due to the break animation 2022-05-18 20:16:50 +08:00
Louis Lam
23c4ece2a5 Merge pull request #1655 from alexislefebvre/patch-1
Fix typo “cros”?
2022-05-17 23:12:07 +08:00
Alexis Lefebvre
175556f9fc s/cros/CORS 2022-05-16 20:30:16 +02:00
Louis Lam
398219f847 Update to 1.16.0-beta.0 2022-05-17 01:03:51 +08:00
Louis Lam
7a50f0e3f3 Merge pull request #1589 from AnnAngela/1.15.1_zh-CN
Improve translation work
2022-05-17 00:03:25 +08:00
AnnAngela
4178b003a2 fix value 2022-05-14 15:27:55 +08:00
Louis Lam
8ede6d888f Merge remote-tracking branch 'origin/master' into fix-1448-discord-service-url 2022-05-14 14:37:12 +08:00
Louis Lam
cec0521834 [Discord] Fix ping type should no port, update better naming 2022-05-14 14:36:40 +08:00
Louis Lam
73b603dd10 Merge pull request #1627 from karelkryda/wrong-uptime-for-push
Fixed incorrect uptime calculation for push monitors
2022-05-14 14:18:19 +08:00
Louis Lam
92a43e1f30 Make the sibling beats a bit smaller 2022-05-14 14:11:43 +08:00
Louis Lam
0cf395dfc3 Fix merge issue 2022-05-14 14:06:35 +08:00
Louis Lam
749ca6f4a8 Merge remote-tracking branch 'origin/master' into feature-improve-status-styling
# Conflicts:
#	src/components/HeartbeatBar.vue
#	src/components/PublicGroupList.vue
2022-05-14 14:05:28 +08:00
Christopher Pickering
ef73af391f added option for ntlm authorization 2022-05-13 12:58:23 -05:00
Christopher Pickering
44f6fca945 added finally to close connection pool 2022-05-13 09:34:31 -05:00
Christopher Pickering
23ce7c6623 started db update script 2022-05-13 09:06:41 -05:00
Christopher Pickering
c346ea7864 updated name on export 2022-05-13 08:57:06 -05:00
Christopher Pickering
f0ad32a252 merged 2022-05-13 08:41:31 -05:00
Christopher Pickering
5720017fb4 updated name on import 2022-05-13 08:40:46 -05:00
Christopher Pickering
b7dc8e3ef8 started ui update 2022-05-13 07:22:52 -05:00
sur.la.route
5bba19f866 updated format
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-05-12 19:54:12 -05:00
sur.la.route
e198f2f1ab updated format
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-05-12 19:54:02 -05:00
Christopher Pickering
f91e5b98f9 [empty commit] pull request for Add NTML Auth Option for HTTP 2022-05-12 13:23:41 -05:00
Christopher Pickering
87f933df4f added sqlserver monitor 2022-05-12 12:48:03 -05:00
Christopher Pickering
332b9ab248 [empty commit] pull request for Add SQLServer Monitor 2022-05-12 10:39:12 -05:00
Domingos Panta
7cc89979f0 Moving change from axios interceptor to specific request. 2022-05-12 15:52:21 +01:00
Domingos Panta
668e97c5a9 fix: lint errors. 2022-05-12 13:39:10 +01:00
MrEddX
90473e7924 Merge branch 'louislam:master' into bulgarian 2022-05-12 13:34:19 +03:00
Domingos Panta
fdd781b081 Added a link to the home page on the lists of actions.
s new link was necessary to prevent a loop when the user tries to access a unknown resource, like status/123, and was redirected to /page-not-found. In this case going back to the last page would be /status/123 which does not exists.
2022-05-11 17:58:52 +01:00
Domingos Panta
373bd9b962 Added response interceptor to axios response.
This interceptor checks for response code 404 and redicts the user if that is the case.
2022-05-11 17:56:31 +01:00
Louis Lam
66971deaf4 Merge remote-tracking branch 'origin/master' into fix-1448-discord-service-url 2022-05-11 00:51:42 +08:00
Louis Lam
59be9bb971 working 2022-05-11 00:51:11 +08:00
Louis Lam
8077744c60 Merge pull request #1604 from c-w/fix-apprise-zulip
Fix apprise integration for Zulip Streams
2022-05-11 00:47:50 +08:00
Jordan Bertasso
c5faf709b8 Merge branch 'master' into fix-1448-discord-service-url 2022-05-10 22:53:55 +10:00
Karel Krýda
7da9f139c1 Bug fix 2022-05-09 21:10:12 +02:00
Karel Krýda
7acbfd2474 eslint fixes too much 2022-05-09 21:05:10 +02:00
Karel Krýda
9f493bbec7 clone master branch 2022-05-09 21:02:29 +02:00
Louis Lam
5bf58cc6c4 Merge pull request #1595 from Saibamen/fix_eslint
Fix ESLint warnings and errors
2022-05-09 13:55:08 +08:00
Louis Lam
d344914ca0 Update docker-compose.yml 2022-05-09 13:47:19 +08:00
Louis Lam
201a25c659 Draft 2022-05-09 00:26:49 +08:00
Clemens Wolff
b680371746 Make apprise notification title configurable in UI 2022-05-07 11:00:57 -04:00
Louis Lam
e488e2dc0a Merge pull request #1119 from jensneuber/uptime-badges
Add badges
2022-05-07 13:44:20 +08:00
Louis Lam
4e3258579d Merge branch 'master' into uptime-badges
# Conflicts:
#	server/util-server.js
2022-05-07 13:26:47 +08:00
MrEddX
aa8ea6d398 Update bg-BG.js (#1617)
Translation Fixes
2022-05-07 13:01:06 +08:00
Aram Akhavan
cd3fbc80b4 Add first parameter back to logging in api router 2022-05-06 16:05:24 -07:00
Aram Akhavan
bb7d67f717 Apply suggestions from code review
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-05-06 09:58:05 -07:00
Louis Lam
8b0813ceff Fix #1596 hopefully 2022-05-06 14:52:09 +08:00
Louis Lam
91178ce6a5 Merge remote-tracking branch 'origin/master' 2022-05-06 14:41:46 +08:00
Louis Lam
429ad384d0 Fix hardcoded path for error.log and move errorLog() to UptimeKumaServer.errorLog() 2022-05-06 14:41:34 +08:00
MrEddX
24e52726b2 Merge branch 'louislam:master' into bulgarian 2022-05-05 23:03:11 +03:00
MrEddX
e0a0a5db4c Update bg-BG.js
Translation Fixes
2022-05-05 22:46:34 +03:00
Greg Smith
cbfecab850 switch to the more-up-to-date esm-wallaby
https://github.com/wallabyjs/esm
2022-05-04 15:45:18 +09:30
Louis Lam
25cc54bf72 Try to give more time for axios-cached-dns-resolve test 2022-05-04 13:24:18 +08:00
Louis Lam
3700b16c5b Copy and add axios-cached-dns-resolve test 2022-05-04 13:16:22 +08:00
Clemens Wolff
4b9dc2890d Convert let to const 2022-05-02 11:16:08 -04:00
Clemens Wolff
f9004bcbed Add optional title to apprise notification 2022-05-02 11:14:26 -04:00
Clemens Wolff
bc174c3325 Extract child process args into variable 2022-05-02 11:00:14 -04:00
Louis Lam
4c2753af46 Remove an unused variable 2022-05-02 13:36:35 +08:00
Louis Lam
c6ba5b621c Remove isPublished, checkPublished which had been removed in upstream. 2022-05-02 13:32:19 +08:00
Louis Lam
96536ae391 Rebuild package-lock.json 2022-05-02 12:36:12 +08:00
Louis Lam
ba46544772 Merge pull request #1603 from Saibamen/fix_task_name
Fix actions/setup-node task name
2022-05-02 12:35:21 +08:00
Adam Stachowicz
5c852db1cf Fix actions/setup-node task name
Addendum to 000cbeb0ce
2022-05-02 01:23:05 +02:00
Adam Stachowicz
069d3765f0 Revert change for StatusPage 2022-05-02 01:13:17 +02:00
Louis Lam
15820c6937 Update SQLite 2022-05-01 19:45:00 +08:00
Louis Lam
000cbeb0ce Lower check-linters node version to 14, add Node.js 18, set timeout 15mins for each job 2022-05-01 18:25:52 +08:00
Louis Lam
e118d59ac8 Merge branch 'master' into uptime-badges 2022-05-01 18:01:26 +08:00
Louis Lam
39aa0a7f07 Merge branch 'fix/chart-error' 2022-05-01 17:57:17 +08:00
Louis Lam
a12dffd1bc Fallback to eqeq for PingChart.vue 2022-05-01 17:56:42 +08:00
Louis Lam
410805052e Log this.chartPeriodHrs 2022-05-01 17:46:43 +08:00
Louis Lam
02a8147f22 Remove undefined variable forceShowContent 2022-05-01 17:31:58 +08:00
Louis Lam
d962ab7a1c Merge branch 'master' into uptime-badges
# Conflicts:
#	package-lock.json
#	server/routers/api-router.js
2022-05-01 17:03:11 +08:00
Louis Lam
63c8d24d6f As legacy-peer-deps is specified in .npmrc, install-legacy and update-legacy are not actually needed. 2022-05-01 12:32:06 +08:00
Louis Lam
254a6bfd36 [CI] Run check linters first 2022-05-01 12:23:28 +08:00
Louis Lam
29f3cbe8c6 Merge pull request #1594 from GOGOsu/patch-1
Fix aliyun-sms "SignatureDoesNotMatch" Error
2022-05-01 12:14:09 +08:00
Louis Lam
53b98ad3e4 Add more comment for aliyun-sms fix 2022-05-01 12:10:47 +08:00
Louis Lam
dbd7c087e0 Merge pull request #1597 from chakflying/fix/chart-error
Fix: Fix chart error on switch back to recent
2022-05-01 11:09:57 +08:00
Greg Smith
d0546afe71 fix esm require: no ugly warnings 2022-05-01 10:22:16 +09:30
Nelson Chan
272956025c Fix: Fix chart error on switch back to recent 2022-05-01 05:18:08 +08:00
Louis Lam
db50ba91cc Fix #1593 2022-04-30 21:44:03 +08:00
Louis Lam
42ea3fb412 Update server/util-server.js
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-30 21:36:07 +08:00
Louis Lam
9f8b3151d8 Update server/util-server.js
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-30 21:36:00 +08:00
GOGOsu
73e38a13d2 Update server/notification-providers/aliyun-sms.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-30 21:08:35 +08:00
Greg Smith
f4515ad8c5 add axios cached dns resolve to monitor 2022-04-30 21:40:47 +09:30
GOGOsu
369477b4b9 Update aliyun-sms.js 2022-04-30 10:45:38 +08:00
GOGOsu
2347a01f7c Update aliyun-sms.js
Add comments for the changed code.
2022-04-30 10:42:59 +08:00
Adam Stachowicz
c114c053d6 Fix ESLint warnings and errors 2022-04-30 03:51:14 +02:00
GOGOsu
ae2c49a729 Update aliyun-sms.js 2022-04-30 06:28:16 +08:00
GOGOsu
b9e72b9645 Update aliyun-sms.js
aliyun-sms.js: escape more characters than encodeURIComponent
see https://help.aliyun.com/document_detail/315526.html
字符A~Z、a~z、0~9以及字符-、_、.、~不编码。对其它ASCII码字符进行编码。
2022-04-30 05:56:10 +08:00
AnnAngela
5a069b278d Update src/components/notifications/PromoSMS.vue
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-29 21:42:54 +08:00
AnnAngela
65ea2e6aeb Update src/components/notifications/PromoSMS.vue
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-29 21:42:48 +08:00
AnnAngela-work
e82fc1df61 Match up en lang file 2022-04-29 20:31:36 +08:00
AnnAngela-work
7dd5f5ea0d Improve translation 2022-04-29 20:26:56 +08:00
AnnAngela-work
45da7c5431 Improve translation work 2022-04-29 20:17:15 +08:00
Louis Lam
26230a3d3a Update to 1.15.1 2022-04-29 13:52:14 +08:00
Louis Lam
82aa52b330 Update Apprise to 0.9.8.3 2022-04-29 13:49:32 +08:00
Louis Lam
fa7d15cf64 Merge remote-tracking branch 'origin/master' 2022-04-29 13:40:17 +08:00
Louis Lam
d7f16908d8 Update CONTRIBUTING.md 2022-04-29 13:39:30 +08:00
Louis Lam
bddd5de22b Update CONTRIBUTING.md 2022-04-29 11:35:03 +08:00
Louis Lam
6333231f1b Merge pull request #1108 from trogper/push-status-param
Add status parameter to push monitor
2022-04-29 00:12:44 +08:00
Louis Lam
60538036c6 [#1108] Change "true" to "up" 2022-04-28 23:44:08 +08:00
Louis Lam
0ba5d031d0 Merge branch 'master' into push-status-param
# Conflicts:
#	server/routers/api-router.js
2022-04-28 23:34:51 +08:00
Louis Lam
66e4c89897 Merge pull request #1560 from MrEddX/bulgarian
Update bg-BG.js
2022-04-28 23:23:09 +08:00
Louis Lam
d210548ae8 Merge pull request #1581 from NixNotCastey/update-pl
Update polish translation
2022-04-28 23:22:31 +08:00
Louis Lam
023db1450d Fix #1577 2022-04-28 23:12:16 +08:00
Louis Lam
824c16a07c Merge pull request #1578 from kadavr95/typos-fixes
Typos fixes
2022-04-28 17:27:22 +08:00
Łukasz Szczepański
09fdef9bdc Update polish translation 2022-04-28 11:14:02 +02:00
Dmitry Yaskovich
7078b06272 Fix typo in Russian localization 2022-04-27 21:22:42 +03:00
Dmitry Yaskovich
d3bd2976c5 Fix typo in CONTRIBUTING.md 2022-04-27 21:18:47 +03:00
Dmitry Yaskovich
db646aa40b Fix typo in README 2022-04-27 21:10:47 +03:00
Matthew Nickson
3c01e8732c Merge branch 'master' into #1059-specify-dns-resolver-port 2022-04-27 18:26:11 +01:00
Louis Lam
b50f1bb7e8 No more opening browser 2022-04-27 15:29:54 +08:00
Phuong Nguyen Minh
a3baa3c149 Update language vi.js (#1572)
Update vi-VN.js
2022-04-27 15:07:42 +08:00
Louis Lam
2adb142ae2 Merge pull request #1571 from MarkusDick/update-german-translations
lang: update german translations
2022-04-27 15:04:04 +08:00
Louis Lam
752415dae6 Merge pull request #1566 from Saibamen/fix_duplicated_PR_comments
Fix duplicated PR comments
2022-04-27 15:01:52 +08:00
Louis Lam
1687de163c Merge pull request #1565 from Saibamen/eslint
Fix some of ESLint warnings
2022-04-27 15:00:49 +08:00
Louis Lam
245b13d3c8 Merge pull request #1542 from Computroniks/jsdoc-for-tests
JSDoc for test/*
2022-04-27 14:48:46 +08:00
Louis Lam
d6c3fdb6fb Merge pull request #1499 from Computroniks/add-JSDoc-comments
Add JSDoc comments for server/*/*
2022-04-27 14:46:23 +08:00
MrEddX
372bf57e9f Update bg-BG.js
Translation fixes
2022-04-27 09:19:13 +03:00
Aram Akhavan
39df4eea92 Ssynchronize push monitor heartbeats to api calls
Includes a 1s buffer time to allow the push url to be called before the monitor is checked
2022-04-26 13:48:44 -07:00
MrEddX
03e6f0a6c8 Update bg-BG.js
Translation fixes.
2022-04-26 21:39:14 +03:00
Markus Dick
dcec53a755 lang: update german translations 2022-04-26 20:27:00 +02:00
MrEddX
ce17ed163e Merge branch 'louislam:master' into bulgarian 2022-04-26 21:26:39 +03:00
Adam Stachowicz
3019d5dd64 Shorter node matrix 2022-04-26 02:03:47 +02:00
Adam Stachowicz
dcdbb7be8b Use separate job for linters 2022-04-26 01:57:49 +02:00
Adam Stachowicz
b874ea8b28 Add missing lint-fix:style command 2022-04-26 01:37:20 +02:00
Adam Stachowicz
1e595eaa76 Update linters 2022-04-26 01:26:57 +02:00
Adam Stachowicz
5fbfacf5ce Fix ESlint warnings 2022-04-26 00:26:26 +02:00
Louis Lam
d39dc94496 Add more info 2022-04-26 02:27:37 +08:00
Louis Lam
94ada36dfa Update dev guideline 2022-04-26 02:20:13 +08:00
Louis Lam
4114f43b48 Start both dev servers in one command 2022-04-26 02:18:14 +08:00
MrEddX
4c8da89c36 Update bg-BG.js
Translation fixes
2022-04-25 07:26:40 +03:00
Dick Tang
db3ef3805b correct wordings for Certificate Expiry Notification (#1554) 2022-04-25 08:25:06 +08:00
Louis Lam
751924b335 Update to 1.15.0 2022-04-24 22:57:42 +08:00
Louis Lam
edec1024b5 Merge remote-tracking branch 'origin/master' 2022-04-24 22:56:52 +08:00
Louis Lam
5f9f29f527 Update SECURITY.md 2022-04-24 22:54:06 +08:00
Louis Lam
13a3dd91bb Merge remote-tracking branch 'origin/master' 2022-04-24 22:41:29 +08:00
Matthew Nickson
d1a3cd047a Merge branch 'master' into #1059-specify-dns-resolver-port 2022-04-24 01:06:45 +01:00
Louis Lam
d9c5a7812c Merge pull request #1544 from mathiskir/readme-improvements
readme improvements
2022-04-24 03:44:21 +08:00
Sneeex
484d4a20ab Small improvements 2022-04-23 19:29:34 +02:00
Louis Lam
3582e99770 Merge pull request #1543 from gaby/github-actions
Upgrade to actions/checkout@v3
2022-04-24 01:22:56 +08:00
Juan Calderon-Perez
2197b98444 Upgrade to actions/checkout@v3 2022-04-23 09:47:57 -04:00
Matthew Nickson
b641c8a878 Add JSDoc to test/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-22 23:24:14 +01:00
Matthew Nickson
9130b3762c [empty commit] pull request for JSDoc test/* 2022-04-22 23:05:18 +01:00
Matthew Nickson
587faecf87 Made value nullable in apicache JSDoc
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-22 19:42:42 +01:00
Matthew Nickson
46da5e51be Fix JSDoc grammar
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-22 19:10:13 +01:00
Matthew Nickson
1eecdec2d9 Update JSDoc for better clarity
Co-authored-by: Nelson Chan <chakflying@hotmail.com>
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-22 18:42:47 +01:00
Sneeex
e6a1719ab4 Change notification count (#1536) 2022-04-22 15:59:34 +08:00
Louis Lam
7d5e7a577d Merge pull request #1535 from Computroniks/add-jsdoc-to-contributing
Added JSDoc requirement in contributing docs
2022-04-22 14:04:00 +08:00
Jens Neuber
64a33d7455 Update server/util-server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-22 07:54:13 +02:00
Matthew Nickson
09e61d9d63 Changed Array<T> to type[]
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 20:02:18 +01:00
Matthew Nickson
9996ba1636 Add JSDoc to server/modules/apicache/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 19:55:01 +01:00
Matthew Nickson
c2f6c5b42e Added JSDoc docs for mqttAsync
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 18:53:07 +01:00
Matthew Nickson
0083485d4c Updated server/model/* JSDoc to match new methods
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 18:30:04 +01:00
Louis Lam
630bb03d9c Update package-lock.json during release process 2022-04-22 00:47:04 +08:00
Matthew Nickson
4ddbf71920 Fixed trailing spaces in database.js
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 17:15:39 +01:00
Matthew Nickson
068b920553 Add JSDoc to server/socket-handlers/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 17:10:23 +01:00
Louis Lam
f1c83bb838 Merge remote-tracking branch 'origin/master' 2022-04-22 00:06:59 +08:00
Louis Lam
303a226ab7 Update eslint rule 2022-04-22 00:06:46 +08:00
Matthew Nickson
3d04befc1f Add JSDoc to server/routers/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 16:43:24 +01:00
Matthew Nickson
d3f0bdb440 Added JSDoc requirement in contributing docs
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-21 16:30:53 +01:00
Matthew Nickson
6d22ebedca Merge branch 'master' into add-JSDoc-comments 2022-04-21 13:01:22 +01:00
Louis Lam
e56ac7b03b Merge pull request #1530 from MrEddX/bulgarian
Bulgarian
2022-04-21 14:37:20 +08:00
Louis Lam
aafcbaf098 Update README.md 2022-04-21 10:22:18 +08:00
MrEddX
4d4d04adbd Update bg-BG.js
Some translation fixes
2022-04-20 22:01:57 +03:00
Matthew Nickson
03b2d8d521 Add JSDoc to server/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-20 19:56:40 +01:00
MrEddX
f8c9472ea2 Update bg-BG.js
- Updated and fixed
2022-04-20 21:54:57 +03:00
Louis Lam
4e28ad4ac2 Merge pull request #1524 from chakflying/fix/disable-auth-icon
Fix: Handle disabled auth in user dropdown
2022-04-20 23:50:33 +08:00
Louis Lam
06f326e49e Merge pull request #1527 from SiderealArt/patch-1
Update Traditional Chinese (Taiwan) translation
2022-04-20 22:43:41 +08:00
Louis Lam
07c0801ad5 Make logout button reactive, improve dropdown menu css 2022-04-20 22:39:07 +08:00
SiderealArt
8cefc96c78 Update Traditional Chinese (Taiwan) translation 2022-04-20 18:11:26 +08:00
Louis Lam
b326a69838 Merge pull request #1523 from koen20/patch-1
Update dutch translations
2022-04-20 17:24:36 +08:00
Jens Neuber
e103ac8335 Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-04-20 10:10:14 +02:00
Nelson Chan
a391576285 Chore: Add translation 2022-04-20 15:09:31 +08:00
Nelson Chan
e0966e55c8 Fix: Handle disabled auth in user dropdown 2022-04-20 15:01:13 +08:00
Koen Habets
59d9891105 Update nl-NL.js 2022-04-19 21:17:33 +02:00
Louis Lam
f8f19d8dc5 Merge remote-tracking branch 'origin/master' 2022-04-20 02:57:52 +08:00
Louis Lam
a3d79a93e9 Update Apprise to 0.9.8 2022-04-20 02:56:33 +08:00
Louis Lam
bdc23a3f57 Merge pull request #1519 from AnnAngela/1.15.0_zh-cn
Update en&zh-CN lang file with 1 new i18n entry
2022-04-19 23:48:07 +08:00
Louis Lam
7c13b1b6cb Update to 1.15.0-beta.1 2022-04-19 20:07:18 +08:00
Louis Lam
10f6a3c4f5 Merge pull request #1229 from Computroniks/#1209-Logout-button-in-navbar
Add #1209: logout button in navbar
2022-04-19 19:59:52 +08:00
Louis Lam
cd7c2beca6 Update CSS for dropdown menu 2022-04-19 19:46:21 +08:00
Louis Lam
8ee99760ec Minor lint 2022-04-19 19:41:52 +08:00
Louis Lam
cb55e23718 Add $root.username 2022-04-19 19:40:28 +08:00
Louis Lam
200fdfb808 Merge code manually since some code moved to another file 2022-04-19 16:46:45 +08:00
Louis Lam
29d2d95c71 Merge branch '1.14.X'
# Conflicts:
#	package.json
#	server/server.js
2022-04-19 16:43:13 +08:00
Louis Lam
b782b25e17 Update to 1.14.1 2022-04-19 16:01:08 +08:00
Louis Lam
919393cac9 Partially change the server core into a class, remove all require("./server") #1520 2022-04-19 15:38:59 +08:00
AnnAngela-work
18925293fb Update en&zh-CN lang file with 1 new i18n entry 2022-04-19 10:48:20 +08:00
Louis Lam
17d4003e5c Add dropdown menu 2022-04-19 00:39:49 +08:00
Louis Lam
cefb5bb60a Merge branch 'master' into #1209-Logout-button-in-navbar 2022-04-18 20:39:24 +08:00
Louis Lam
9bf3b3a0f4 Update README.md 2022-04-18 19:15:50 +08:00
Louis Lam
e2c45f93bf Merge pull request #1509 from chakflying/feat/mqtt-optional-message
Feat: Allow MQTT successMessage to be optional
2022-04-18 19:06:39 +08:00
Louis Lam
addf75daa7 Fix MQTT password do not save 2022-04-18 19:05:14 +08:00
Louis Lam
359a490ae3 Fix #1510 2022-04-18 15:21:58 +08:00
Nelson Chan
cd38dd3f68 Feat: Allow MQTT successMessage to be optional 2022-04-18 13:04:55 +08:00
Louis Lam
f712fe85e5 Update node-sqlite, sqlite from 3.36 to 3.38 2022-04-18 12:44:32 +08:00
Louis Lam
c28b90feb4 Update to 1.15.0-beta.0 2022-04-17 20:07:32 +08:00
Louis Lam
ceba096f3e Merge pull request #875 from tarun7singh/master
Added MQTT Monitor type
2022-04-17 20:05:41 +08:00
Louis Lam
2a248ad73f Change mqtt_topic from VARCHAR to TEXT 2022-04-17 19:56:47 +08:00
Louis Lam
5fa62a888c Merge branch 'master' into mqtt2
# Conflicts:
#	server/database.js
#	server/util-server.js
2022-04-17 19:46:33 +08:00
Louis Lam
e6a8a84278 Include only nessacary data in webhook 2022-04-17 19:30:58 +08:00
Louis Lam
47c72192e1 [eslint] Enable yoda and eqeqeq 2022-04-17 15:43:03 +08:00
Louis Lam
d71c086447 Standardize array bracket spacing 2022-04-17 15:27:35 +08:00
Louis Lam
46e1a628a7 Update package-lock.json 2022-04-17 15:04:59 +08:00
Louis Lam
cd3dfd3146 Merge pull request #1083 from patrickhafner/customstatuspage
Custom status page
2022-04-17 15:04:02 +08:00
Louis Lam
572f2b9838 eslint 2022-04-17 14:57:31 +08:00
Louis Lam
8eb83394f7 Refine UI/UX for custom css / footer text. Add switch for show/hide powered by 2022-04-17 14:53:13 +08:00
Louis Lam
1bc01d1077 Merge branch 'master' into customstatuspage
# Conflicts:
#	src/languages/de-DE.js
2022-04-17 13:21:41 +08:00
Matthew Nickson
45f44b183d Add JSDoc to server/model/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-16 21:11:45 +01:00
Matthew Nickson
5a209c74e1 Add JSDoc to server/notification-providers/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-16 20:24:53 +01:00
Louis Lam
4e6ddc8880 Merge pull request #1497 from MrEddX/bulgarian
Bulgarian
2022-04-17 01:40:43 +08:00
Louis Lam
07c474db0b Merge remote-tracking branch 'origin/master' 2022-04-17 01:40:05 +08:00
Louis Lam
8d8c38b1a8 Allow unused vars in args and fix more eslint issues 2022-04-17 01:39:49 +08:00
Louis Lam
03bcf5c766 Add a simple mqtt server for testing 2022-04-17 01:07:54 +08:00
Louis Lam
136fdf3768 MQTT password field to password type 2022-04-17 01:07:36 +08:00
Louis Lam
e34420368b Remove try-catch and fix username/password/port not working for mqtt 2022-04-17 01:06:47 +08:00
Matthew Nickson
60c63cc18e Add JSDoc to server/jobs/*
Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-16 11:10:51 +01:00
Louis Lam
566133e350 Domain Name Expiry Notification for https monitor only 2022-04-16 15:01:53 +08:00
Louis Lam
30e113755e Add HIDE_LOG and catch error if cannot subscribe topic 2022-04-16 14:50:48 +08:00
Louis Lam
083e8355b7 Change debug to log.debug 2022-04-16 13:37:17 +08:00
Louis Lam
64a0e1aa9b Update package-lock.json 2022-04-16 13:31:40 +08:00
Louis Lam
b1c7915bc1 Merge branch 'master' into mqtt2
# Conflicts:
#	package-lock.json
#	package.json
#	server/database.js
#	server/model/monitor.js
#	server/server.js
#	src/pages/EditMonitor.vue
2022-04-16 13:28:39 +08:00
Matthew Nickson
6fb66728e6 [empty commit] pull request for Add JSDoc comments 2022-04-15 20:08:54 +01:00
Matthew Nickson
a680331dd7 Fixes issue with ::1 port 5300 requests
Now the address is wrapped in `[]` in order to prevent ::1 port 5300
being interpreted as ::1:5300. Wrapping the IPv4 address in `[]` does
seem to have any effect on correct domain name resolution. In order to
prevent issues if a user inputs an address with brackets, they are
removed from the string if present before being re-added when it is
passed to `setServers`. I have also removed the JSDoc comment as this
will be added in a seperate PR

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-15 19:59:32 +01:00
Louis Lam
b7aebceaab Update bg-BG.js 2022-04-15 19:49:16 +08:00
MrEddX
0302fdbc96 Update bg-BG.js
Status pages - fix
2022-04-15 13:10:14 +03:00
MrEddX
84a50f058f Update bg-BG.js 2022-04-15 13:06:22 +03:00
MrEddX
9ec652639b Merge branch 'louislam:master' into bulgarian 2022-04-15 13:03:34 +03:00
Louis Lam
0c40e32d75 Merge pull request #1007 from chakflying/feat/save-chart-period
Feat: Save and restore chart period
2022-04-15 12:34:53 +08:00
Jordan Bertasso
288ed1e3ca Merge branch 'master' into fix-1448-discord-service-url 2022-04-15 14:13:44 +10:00
Nelson Chan
6f99d7577b Fix: Limit saved period & clear when set to recent 2022-04-15 03:24:58 +08:00
Nelson Chan
1417b6eacf Feat: Save and restore chart period 2022-04-15 02:56:21 +08:00
MrEddX
1baee42cf5 Merge branch 'louislam:master' into bulgarian 2022-04-14 10:55:04 +03:00
Louis Lam
fb0064082e Change Pushdeer to PushDeer 2022-04-14 14:34:30 +08:00
ngc7331
93c51504f9 fixes: formatting and security issues
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-14 14:34:30 +08:00
ngc7331
fb059f5e91 Add support for Pushdeer notifications 2022-04-14 14:34:30 +08:00
PhyxionNL
2e3414135f Update src/components/PingChart.vue
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-14 12:27:38 +08:00
PhyxionNL
e44699216e Fix readability of chart hover 2022-04-14 12:27:38 +08:00
DX37
fd8cba1dad nudge nodemailer strings; translate new ones 2022-04-14 11:10:24 +08:00
sovushik
03dd02fd38 Update ru-RU.js
Add RU for 1.14 version
2022-04-14 11:04:46 +08:00
Louis Lam
d0b5f147e2 Fix spelling and merge mistake 2022-04-14 10:58:28 +08:00
Louis Lam
ddf8a7a692 Fix camelCase 2022-04-14 10:58:28 +08:00
ColdThunder11
bd9df09f87 Apply suggestions from code review, fix style
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-14 10:58:28 +08:00
ColdThunder11
4656ab3d57 Add OneBot notification service 2022-04-14 10:58:28 +08:00
Louis Lam
0a5db0cecb Fix #1478 2022-04-14 10:12:31 +08:00
MrEddX
60b44c2cdd Update bg-BG.js
Fixed translation.
2022-04-14 09:46:43 +08:00
Matthew Nickson
8c8eeaf627 Merge branch 'master' into #1059-specify-dns-resolver-port 2022-04-13 21:24:04 +01:00
Matthew Nickson
b893d50e45 Implement specify Port for DNS Monitor #1059
This commit should fully implement #1059. When the user selects the DNS
monitor option, a new input box has been added below the resolver
address allowing the user to implement the port to access the resolver
on. This uses the same `monitor.port` as the TCP monitor but a monitor
has been added to prefill the port value to the default of `53` if the
value in this field has not already been set. This is then cleared if
the user selects a different monitor type and has not changed the port
value. A translation has also been added explaining what this field
does in order to reduce any confusion. JSDoc documentation has also been
added to the `dnsResolve` function in `util-server.js`.

Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-13 21:02:19 +01:00
MrEddX
16b61dba27 Merge branch 'louislam:master' into bulgarian 2022-04-13 21:59:53 +03:00
Louis Lam
2b0c184a88 Update package-lock 2022-04-14 01:21:43 +08:00
Louis Lam
2642e70fc8 Fix auto test failed due to autocrlf 2022-04-14 01:20:54 +08:00
Louis Lam
8d0446dc38 Add lint as part of test 2022-04-14 01:20:54 +08:00
Louis Lam
3436e26ed4 Fix styleline, fix css format issues globally 2022-04-14 01:20:54 +08:00
Louis Lam
649f3106e1 Enforce semicolon, fix format globally 2022-04-14 01:20:54 +08:00
Louis Lam
6f72ca481f Merge pull request #1479 from AnnAngela/1.14.0_translation
New translation for zh-CN
2022-04-14 00:05:07 +08:00
Louis Lam
670ea415b2 Merge pull request #1488 from c0derMo/update-german-translation
updating german translation
2022-04-14 00:03:53 +08:00
Louis Lam
17dcf6d3a2 Merge pull request #910 from andreasbrett/logging
introduce consistent logging
2022-04-13 23:47:08 +08:00
Louis Lam
e9ce1433cd Change log_info to log.info by making it into an object 2022-04-13 23:33:37 +08:00
c0derMo
f5d006add8 updating german translation 2022-04-13 14:45:41 +00:00
Louis Lam
4df147786d Merge pull request #1483 from Alexandre-Gliganic/master
Add automatic restart for docker-compose.yml
2022-04-12 22:39:29 +08:00
Alexandre Gliganic
27861f0d14 Add automatic restart docker-compose.yml 2022-04-12 16:09:01 +02:00
AnnAngela-work
5aa747a301 New translation 2022-04-12 20:41:25 +08:00
Louis Lam
81de2eedfb Fix template can contain one tag only, disable vue/require-component-is eslint rule 2022-04-12 20:23:11 +08:00
Louis Lam
4a6d7207ef Merge branch 'master' into customstatuspage
# Conflicts:
#	src/languages/de-DE.js
#	src/languages/en.js
#	src/pages/StatusPage.vue
2022-04-12 19:39:42 +08:00
Louis Lam
4053b9db1f Merge remote-tracking branch 'origin/master' 2022-04-12 17:46:24 +08:00
Louis Lam
772d009f43 Merge branch 'master' into fluencydoc_master
# Conflicts:
#	extra/update-version.js
#	server/client.js
#	server/server.js
2022-04-12 17:44:04 +08:00
MrEddX
ae54d9c011 Merge branch 'louislam:master' into bulgarian 2022-04-12 12:37:52 +03:00
Louis Lam
5ca606fe99 Merge pull request #1141 from marcules/issue/1138
Issue/1138
2022-04-12 17:32:52 +08:00
Louis Lam
6179f6c982 Merge branch 'master' into issue/1138
# Conflicts:
#	server/server.js
2022-04-12 17:15:33 +08:00
Louis Lam
94770cf865 Resolve log message null reference 2022-04-12 16:57:22 +08:00
Louis Lam
9ec29c1bc4 Add back debug() for safe, but it is marked as deprecated 2022-04-12 16:37:05 +08:00
Louis Lam
279e2eb3f6 Merge branch 'master' into logging
# Conflicts:
#	server/database.js
#	server/jobs.js
#	server/model/monitor.js
#	server/routers/api-router.js
#	server/server.js
#	server/socket-handlers/status-page-socket-handler.js
#	server/util-server.js
2022-04-12 16:32:14 +08:00
Louis Lam
1ba92d803e Update to 1.14.0 2022-04-12 14:17:13 +08:00
Louis Lam
45ca3085b2 Update CONTRIBUTING.md 2022-04-12 13:53:52 +08:00
jordanbertasso
0765f05090 Update Discord tests 2022-04-12 09:52:16 +10:00
jordanbertasso
2638d68c97 Cover dns and steam types in Discord notifs 2022-04-12 09:52:07 +10:00
Matthew Nickson
e38742a2d0 [empty commit] pull request for #1059 2022-04-11 19:58:31 +01:00
Louis Lam
a0d1ae2cce Better alignment of monitor list item 2022-04-11 18:02:18 +08:00
jordanbertasso
1b1e0f6dd9 Add Discord tests 2022-04-10 22:02:30 +10:00
jordanbertasso
0961c6d9b3 Check for ping and port type in discord notifs 2022-04-10 21:45:07 +10:00
jordanbertasso
ce7d8c38c5 [empty commit] pull request for issue #1448 2022-04-10 21:43:52 +10:00
Louis Lam
f030487f7d Fix theme color that do not apply to status page with a custom domain 2022-04-10 13:46:00 +08:00
Louis Lam
316e65d35a Update to 1.14.0-beta.2 2022-04-10 00:46:34 +08:00
Louis Lam
df5ba02f3f Merge pull request #1415 from louislam/status-page-domain
[Status Page] Map domain names to status pages
2022-04-10 00:42:31 +08:00
Louis Lam
c9fa183712 Manage domain names 2022-04-10 00:25:27 +08:00
Louis Lam
0b9b5102ec Minor 2022-04-09 17:23:22 +08:00
Louis Lam
c399984b7f Improve status page sidebar 2022-04-09 17:03:10 +08:00
Louis Lam
0afa0be5c2 Merge branch 'master' into status-page-domain
# Conflicts:
#	server/database.js
2022-04-09 16:07:09 +08:00
Louis Lam
6a30dbd71a Fix Mattermost when channel is empty #1468 2022-04-09 15:44:50 +08:00
Louis Lam
a2d9474e85 Copy some keys from zh-TW to zh-HK 2022-04-09 14:51:26 +08:00
Louis Lam
8479e772cd Merge pull request #1463 from JohnnyChiang/update-zh-TW-translation
Update zh-TW translation
2022-04-09 14:44:44 +08:00
Louis Lam
2e50ef0e8f Merge pull request #1450 from AnnAngela/1.14.0-zh_cn
1.14.0 translation improvement
2022-04-09 14:40:38 +08:00
Louis Lam
4fb2c69dd1 Merge pull request #1461 from louislam/proxy-improvement
Proxy Improvements
2022-04-09 13:54:28 +08:00
Louis Lam
c08910a65c Update README.md 2022-04-08 19:45:39 +08:00
Louis Lam
943c904256 Update docker-compose.yml 2022-04-08 19:43:51 +08:00
JohnnyChiang
25b5edea7f Update zh-TW translation 2022-04-08 01:47:01 +08:00
Louis Lam
7bbaeffd3e Fix reset-password (issue caused by 5027fcd320) 2022-04-08 00:56:56 +08:00
Louis Lam
008dc27f52 Reload proxy settings for monitors in the monitorList 2022-04-07 23:03:45 +08:00
Louis Lam
5027fcd320 Export server using an object class 2022-04-07 23:02:57 +08:00
Louis Lam
d5e68f8453 Export monitor list 2022-04-07 22:53:32 +08:00
Louis Lam
fcb577097b [Proxy] Change to radio button 2022-04-07 15:26:00 +08:00
Louis Lam
082c2dd32d Remove restartMonitors() and move proxy socket events to a socket handler file 2022-04-07 14:45:37 +08:00
Louis Lam
e89356b283 Show proxy option for http monitor only 2022-04-07 14:37:33 +08:00
Louis Lam
6014b9534f Merge remote-tracking branch 'origin/master' 2022-04-06 23:20:40 +08:00
Louis Lam
8b45a95cc3 Merge branch '1.13.X'
# Conflicts:
#	package.json
2022-04-06 23:20:22 +08:00
Louis Lam
02becfd113 Update to 1.13.2 2022-04-06 22:54:03 +08:00
Louis Lam
8ad992eac8 Fix setup issue when using npm 8.6.0 2022-04-06 22:50:21 +08:00
Louis Lam
c4e74c9943 Render <StatusPage> if domain matched 2022-04-06 22:43:22 +08:00
Louis Lam
fee88b32e3 Set PRAGMA synchronous = FULL 2022-04-06 20:48:13 +08:00
Louis Lam
ffc5bca51d Update required tools' links 2022-04-06 13:18:12 +08:00
AnnAngela
511b9dd425 Update fs-rmSync.js 2022-04-06 10:31:01 +08:00
AnnAngela
e9dd64b6f0 Update comments of fs-rmSync.js 2022-04-06 10:20:57 +08:00
Louis Lam
355aec46dc Merge branch 'master' into status-page-domain 2022-04-05 22:59:39 +08:00
Louis Lam
c9deea9fdf Merge pull request #1456 from Arubinu/alerta
Fix "API key parameter 'undefined' is invalid"
2022-04-05 22:51:33 +08:00
Louis Lam
70311f7a5a Add an option to enable/disable the domain name expiry notification #1364 2022-04-05 21:27:50 +08:00
Louis Lam
4b99160b1f Fix "Check Update" is not checked by default 2022-04-05 19:43:23 +08:00
Louis Lam
48d679234a Stop bree and cloudflared while the server shutting down 2022-04-05 19:41:29 +08:00
Louis Lam
d8b32d652f Update .dockerignore 2022-04-05 16:44:55 +08:00
Alvin Pergens
d3d1656625 Fix "API key parameter 'undefined' is invalid" 2022-04-05 08:47:35 +02:00
AnnAngela-work
8e78e62eee Make translation better 2022-04-04 17:24:22 +08:00
AnnAngela
706d6cee07 Update some components to use i18n function, update en & zh-CN translation 2022-04-04 11:33:02 +08:00
AnnAngela-work
43eed45bae first part of zh-CN.js translation 2022-04-03 22:08:24 +08:00
AnnAngela-work
19b7e2ba5e Using grep to search $t("foo")-like pattern to fill up the missing part of en i18n file 2022-04-03 22:08:03 +08:00
Louis Lam
99042e6991 Update to 1.14.0-beta.1 2022-04-03 22:06:39 +08:00
Louis Lam
f54084c888 Update beta release process 2022-04-03 22:06:36 +08:00
Louis Lam
87d3853b8e Merge pull request #1348 from AnnAngela/master
Detect if `fs.rmSync` is available to avoid the runtime deprecation warning
2022-04-02 18:21:12 +08:00
Louis Lam
4738581c66 Update dependencies 2022-04-02 11:34:00 +08:00
Louis Lam
3218a0eee8 Merge remote-tracking branch 'origin/master' 2022-04-02 11:26:24 +08:00
Louis Lam
87ee3c20bd Update proxy password field 2022-04-02 11:25:27 +08:00
Louis Lam
38e6e846bf Refresh sponsor list 2022-04-01 23:22:15 +08:00
Louis Lam
92ab2b12d0 Refresh sponsor list 2022-04-01 17:37:04 +08:00
Jimmy Huang
a4be651118 Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-01 15:26:50 +08:00
Louis Lam
04e3394d02 Merge branch 'master' into feature/request-with-http-proxy
# Conflicts:
#	package-lock.json
#	package.json
#	server/database.js
#	src/languages/en.js
#	src/mixins/socket.js
2022-04-01 14:57:35 +08:00
Louis Lam
f6cd2f60ca Merge pull request #1442 from BCsabaEngine/master
fix: update hu lang
2022-04-01 14:46:56 +08:00
Balázs Csaba
53cea7f8d3 fix: update hu lang 2022-03-31 22:25:22 +02:00
Louis Lam
aef7719426 Merge pull request #1435 from LoaderB0T/favico
feat: Favicon is updated based on satus page logo
2022-03-31 21:32:13 +08:00
Louis Lam
514b9fb68a Add remark 2022-03-31 21:30:07 +08:00
Louis Lam
da32a1aa19 Add uk-UA to the languageList 2022-03-31 16:24:28 +08:00
Louis Lam
7a69f9f56f Merge pull request #1438 from Deni7/master
Add ukrainian translation
2022-03-31 16:22:29 +08:00
Louis Lam
c50c20faa4 Minor fix for uk-UA 2022-03-31 16:19:04 +08:00
Louis Lam
cb6eeaef34 Bring connection error bar to the top 2022-03-31 16:15:34 +08:00
Louis Lam
6674005e8b Fix storing cloudflared token while start cloudflared 2022-03-31 15:58:39 +08:00
Denis Stepanov
ee3d7d8b42 Add ukrainian translation 2022-03-30 23:21:09 +03:00
Denis Stepanov
a277cfe9e8 Add ukrainian translation 2022-03-30 23:16:04 +03:00
Denis Stepanov
95b0df0270 Add ukrainian translation 2022-03-30 23:15:28 +03:00
Louis Lam
f02e9c44ec Update to 1.14.0-beta.0 2022-03-30 23:37:23 +08:00
Louis Lam
bb2b5cd6ac Merge pull request #1427 from louislam/cloudflared
Built-in ease-to-use reverse proxy with Cloudflare Tunnel
2022-03-30 20:15:48 +08:00
Louis Lam
b72a2d350f Set cloudflared token from env var or arg 2022-03-30 20:08:26 +08:00
Louis Lam
71be030733 Add package-lock.json and minor words 2022-03-30 18:52:10 +08:00
Janik Schumacher
73b338bba6 feat: Favicon is updated based on satus page logo 2022-03-30 12:09:38 +02:00
Louis Lam
82ea896bbc Improve the workflow of cloudflared 2022-03-30 11:59:49 +08:00
Louis Lam
f1f4b3b377 Add reverse proxy setting page for controlling cloudflared 2022-03-30 01:49:45 +08:00
Louis Lam
a6b52b7ba6 Merge branch 'master' into cloudflared 2022-03-29 17:42:55 +08:00
Louis Lam
b8dea3a823 Merge remote-tracking branch 'origin/master' 2022-03-29 17:39:12 +08:00
Louis Lam
0da6e6b1fb Some improvements 2022-03-29 17:38:48 +08:00
Louis Lam
44fb2a88f2 Add cloudflared socket handler 2022-03-29 14:48:02 +08:00
Louis Lam
623b06e33c Merge pull request #1426 from DX37/translation-ru
update russian translation
2022-03-29 14:14:21 +08:00
Louis Lam
7d3cbff794 [Cloudflared] Install into base docker 2022-03-29 02:24:10 +08:00
DX37
61d0a0abce update russian translation 2022-03-28 21:16:13 +07:00
AnnAngela
7fd5b61bab Inproperly conflict resolving 2022-03-27 21:12:51 +08:00
AnnAngela
96289fe014 Update index.js 2022-03-27 20:56:42 +08:00
AnnAngela
381605aca1 Update update-version.js 2022-03-27 20:55:28 +08:00
AnnAngela
742c6bcaa3 Merge branch 'master' into master 2022-03-27 20:54:24 +08:00
Matthew Nickson
88604845e6 Merge branch 'master' into #1209-Logout-button-in-navbar 2022-03-27 13:48:50 +01:00
Louis Lam
be88351eb3 Merge pull request #1136 from chakflying/fix/prometheus-on-delete
Fix: Remove prometheus metrics on delete [Test needed]
2022-03-27 11:05:50 +08:00
Louis Lam
34a0b54b93 Merge pull request #1418 from sovushik/patch-11
Update ru-RU.js
2022-03-26 14:11:32 +08:00
sovushik
e11ea7b061 Update ru-RU.js
Add new string for 1.13.1
2022-03-26 10:46:07 +05:00
Louis Lam
12237dec6e Merge remote-tracking branch 'origin/master' 2022-03-26 02:09:25 +08:00
Louis Lam
f6272155af Show page not found for invalid routes 2022-03-26 02:09:12 +08:00
Louis Lam
630b441a2d Merge pull request #1414 from ivanbratovic/croatian-language
Update croatian (hr-HR) translation file
2022-03-25 19:00:06 +08:00
Louis Lam
1ecd2e45d0 [Status Page] Plan to support domain names for status pages 2022-03-25 18:59:06 +08:00
Ivan Bratović
5922771909 Update croatian (hr-HR) translation file
Signed-off-by: Ivan Bratović <ivanbratovic4@gmail.com>
2022-03-25 11:05:51 +01:00
MrEddX
a7e1a78ea9 Update bg-BG.js
Fixed translation.
2022-03-25 07:31:26 +02:00
Louis Lam
623d03dc6f Fix release process 2022-03-25 00:03:25 +08:00
Louis Lam
f52e527850 Update to 1.13.1 2022-03-24 23:47:03 +08:00
Louis Lam
28d72fcd08 Fix #1409, slug cannot be empty 2022-03-24 23:43:07 +08:00
Louis Lam
6c7a0ff7d3 Fix release script 2022-03-24 22:44:22 +08:00
Louis Lam
2abdf2efad Update to 1.13.0 2022-03-24 22:21:19 +08:00
Louis Lam
71af08189e Clear useless code 2022-03-24 18:03:31 +08:00
Louis Lam
d32ba7cadd Fix #1318, basic auth is completely disabled if the auth is disabled 2022-03-24 18:02:34 +08:00
Louis Lam
775d1696fa Fix pushover device not working #1114 2022-03-24 12:14:17 +08:00
Louis Lam
7fb16d2f9a Limit the pm2 log size 2022-03-23 11:17:23 +08:00
Louis Lam
40991fbc28 Show reverse proxy guide along with websocket error 2022-03-22 23:46:13 +08:00
Louis Lam
bf20f9d290 Merge remote-tracking branch 'origin/master' 2022-03-22 21:37:18 +08:00
Louis Lam
5fa14161c4 Minor css 2022-03-22 21:37:04 +08:00
Louis Lam
5a2a59250d Merge pull request #1405 from sovushik/patch-9
Update ru-RU.js
2022-03-22 17:17:37 +08:00
Louis Lam
fcee93cbea Merge pull request #1404 from sovushik/patch-8
Update ru-RU.js
2022-03-22 17:16:55 +08:00
Louis Lam
668dffc2c5 Simplify final release 2022-03-22 16:45:07 +08:00
sovushik
210eebe144 Update ru-RU.js
Add some fix for 1.13
2022-03-22 13:00:16 +05:00
sovushik
4b04a9c214 Update ru-RU.js
Add new string for version 1.13
2022-03-22 12:58:38 +05:00
Louis Lam
909618a29a Update to 1.13.0-beta.2 2022-03-22 14:30:54 +08:00
Louis Lam
4a4ffc96dd Fix setup 2022-03-22 12:00:13 +08:00
Louis Lam
3713692bdd Merge remote-tracking branch 'origin/master' 2022-03-22 11:31:01 +08:00
Louis Lam
76f991ecd8 Update beta process 2022-03-22 11:30:45 +08:00
Louis Lam
84dcd81f21 Merge pull request #1400 from MrEddX/bulgarian
Update bg-BG.js
2022-03-22 10:50:59 +08:00
MrEddX
f65d0654a6 Update bg-BG.js
Added new field.
2022-03-21 21:12:15 +02:00
Louis Lam
b0bda9f9d2 Fix beta release script 2022-03-22 01:00:35 +08:00
Louis Lam
ad2130b7b5 [Status Page] Fix monitors are deleted unexpectedly #1399 2022-03-22 00:06:29 +08:00
Louis Lam
4545eec3fe Better sticky monitor list 2022-03-21 23:53:55 +08:00
Louis Lam
3adda48f3a Load the status page list earlier 2022-03-21 15:28:59 +08:00
Louis Lam
cafa61e3af Add beta tag 2022-03-21 14:32:55 +08:00
Louis Lam
58ee071fae Release process for beta 2022-03-21 14:31:29 +08:00
Louis Lam
9173838e1b Merge remote-tracking branch 'origin/master' 2022-03-21 11:50:27 +08:00
Louis Lam
833d9381ff Merge pull request #1393 from burakurer/patch-1
Update tr-TR.js
2022-03-21 11:50:17 +08:00
burakurer
73d904952d Update tr-TR.js
spelling correction "Sağlık Dırımları" => "Sağlık Durumları"
2022-03-20 17:03:32 +03:00
Louis Lam
4e95e9ea51 Add process for beta release 2022-03-20 11:08:33 +08:00
Louis Lam
c22cc4d794 Merge pull request #1385 from jtagcat/patch-1
Update et-EE.js
2022-03-20 10:56:24 +08:00
Louis Lam
8cbdefdc0d Merge pull request #1390 from pemassi/patch-3
Update ko-KR.js
2022-03-20 10:55:51 +08:00
Kyungyoon Kim
2f5beefa37 Update ko-KR.js 2022-03-19 03:19:02 -06:00
jtagcat
dae5ff690a Update et-EE.js 2022-03-18 13:56:45 +00:00
Louis Lam
fb9a206542 [Status Page] Fix - show no status page 2022-03-18 21:47:14 +08:00
jtagcat
dc3da45dd6 Update et-EE.js 2022-03-18 11:27:33 +00:00
Louis Lam
82049a2387 Merge pull request #863 from louislam/restructure-status-page
Restructure status page core implementation
2022-03-18 18:07:15 +08:00
Louis Lam
d7a839aa52 [Status Page] Fix reset entry page 2022-03-18 17:57:08 +08:00
Louis Lam
aef0a66205 [Status Page] Simplify show tags logic 2022-03-18 17:56:46 +08:00
Louis Lam
37be7df9b0 [Status Page] Delete status page 2022-03-18 15:19:52 +08:00
Louis Lam
243fab5f26 Rollback vite to 2.6.x (Not sure, but sometimes vue routes are no longer response during dev randomly) 2022-03-18 15:02:49 +08:00
Louis Lam
8d981c8f0b [Status Page] Fix migration and unpin incident 2022-03-18 14:14:22 +08:00
Louis Lam
220e46bc83 [Status Page] Fix theme bug 2022-03-18 12:57:37 +08:00
Louis Lam
59cdacc052 [Status Page] Enable Edit Mode only if the token is presented 2022-03-18 12:39:48 +08:00
Louis Lam
00738edbe7 [Status Page] Add ?edit 2022-03-18 00:00:56 +08:00
Louis Lam
27bfae67af [Status Page] Add a new status page 2022-03-17 23:38:43 +08:00
Louis Lam
719a136d1e [Status Page] Improved entry page 2022-03-17 22:44:47 +08:00
Louis Lam
502c7f87e7 [Status Page] Listing: Better loading effect 2022-03-17 19:07:05 +08:00
Louis Lam
78a732409b [Status Page] Fix translations 2022-03-17 18:56:59 +08:00
Louis Lam
c0c6419980 [Status Page] align icon and title using flexbox 2022-03-17 17:07:23 +08:00
Louis Lam
5474368263 Update vite to 2.8.6 2022-03-17 16:56:25 +08:00
Louis Lam
e87cdf4d09 [Status Page] wip, upload logo and status page listing 2022-03-17 16:42:26 +08:00
Louis Lam
bb1c951a96 Update node.js from 14 to 16 2022-03-17 13:04:43 +08:00
Louis Lam
1033ca5cf4 [Status Page] wip, combine api, add status_page_id into group and incident tables 2022-03-16 15:38:10 +08:00
Louis Lam
18ec42b060 [Status Page] wip 2022-03-16 14:14:47 +08:00
Louis Lam
7c7dbf68c1 [Status Page] wip, sidebar for editor 2022-03-15 12:00:29 +08:00
Louis Lam
3e96504813 Update denpendencies 2022-03-13 17:14:57 +08:00
Louis Lam
d765b1c57a Merge branch 'master' into restructure-status-page
# Conflicts:
#	src/pages/StatusPage.vue
2022-03-12 15:50:42 +08:00
Louis Lam
5f778b9763 Merge pull request #835 from willianrod/feat/add-favicon-badges
Add badges to favicon
2022-03-12 15:39:18 +08:00
Louis Lam
c68f7944e3 [Favicon] minor 2022-03-12 15:31:01 +08:00
Louis Lam
a9efdabcec [Favicon] Prevent error when no heartbeat 2022-03-12 15:30:02 +08:00
Louis Lam
b9dfcd1291 [Favicon] Code refactoring 2022-03-12 15:10:45 +08:00
Louis Lam
04d93c2747 Merge branch 'master' into willianrod_feat/add-favicon-badges
# Conflicts:
#	package-lock.json
#	src/mixins/socket.js
2022-03-12 11:17:32 +08:00
Louis Lam
c65d771fad Merge pull request #1358 from BCsabaEngine/master
fix: .hu lang
2022-03-12 10:38:37 +08:00
Louis Lam
3f8a396090 Merge pull request #1362 from MrEddX/bulgarian
Bulgarian
2022-03-12 10:37:46 +08:00
Louis Lam
9681957adf Merge pull request #1366 from deanilvincent/update-version-of-check-password-strength
check-password-strength new version update 2.0.5
2022-03-12 10:25:06 +08:00
Mark Vicente
95a2c967c6 check-password-strength new version 2.0.5 that support additional symbols/special characters referenced from owasp list. 2022-03-11 23:48:35 +08:00
Louis Lam
50d6e888c2 [new status page] wip 2022-03-10 21:34:30 +08:00
Louis Lam
ae14ad5a84 Add a word "Status Pages" 2022-03-09 22:14:07 +08:00
MrEddX
edd9202de9 Update bg-BG.js
Translation fix.
2022-03-08 22:14:07 +02:00
MrEddX
a97d2a5498 Update bg-BG.js
Added new fields.
2022-03-08 22:10:17 +02:00
Louis Lam
72ce28a541 Migrate status page table 2022-03-08 14:33:35 +08:00
Louis Lam
1e2a8453c6 Merge branch 'master' into restructure-status-page 2022-03-08 14:21:04 +08:00
Louis Lam
1fa4a16663 Check beta release 2022-03-07 16:24:24 +08:00
Louis Lam
6a57c443fd Set telegram as the default notification type 2022-03-07 15:52:17 +08:00
Uğur Erkan
8078d0618d Add socks proxy support to proxy feature
- Socks proxy support implemented.
- Monitor proxy agent create flow refactored
  and moved under proxy class.

Thanks for suggestion @thomasleveil
2022-03-06 19:34:51 +03:00
Uğur Erkan
9e27acb511 Add socks proxy agent 2022-03-06 19:34:51 +03:00
Uğur Erkan
78d76512ba Add http and https proxy feature
Added new proxy feature based on http and https proxy agents.
Proxy feature works like notifications, there is many proxy
could be related one proxy entry.

Supported features
- Proxies can activate and disable in bulk
- Proxies auto enabled by default for new monitors
- Proxies could be applied in bulk to current monitors
- Both authenticated and anonymous proxies supported
- Export and import support for proxies
2022-03-06 19:34:49 +03:00
Uğur Erkan
2cc7a990ff Add http and https agents 2022-03-06 19:29:28 +03:00
Balázs Csaba
157f0de61a fix: .hu lang 2022-03-05 21:24:50 +01:00
Louis Lam
88c3d952d3 Improve settings page's UI/UX on mobile 2022-03-04 23:20:42 +08:00
Louis Lam
e3a0eaf6af Sort notification types in case-insensitive 2022-03-04 21:48:35 +08:00
Louis Lam
8bbf55777e Merge pull request #1205 from arjunkomath/master
Add notification provider - Push
2022-03-04 21:39:59 +08:00
Louis Lam
c0e0698c21 Merge pull request #1225 from Computroniks/fix-checkbox-css
Fixed dark mode checkbox
2022-03-04 14:35:15 +08:00
Louis Lam
14d8095f12 Merge pull request #1228 from Arubinu/alerta
Alerta Notification Service
2022-03-04 14:19:53 +08:00
Louis Lam
fa490d0bf1 [Alerta] Handle general message 2022-03-04 14:13:44 +08:00
Louis Lam
c52c8a4206 Merge branch 'master' into alerta
# Conflicts:
#	server/notification.js
#	src/components/notifications/index.js
#	src/languages/en.js
2022-03-04 14:10:37 +08:00
Louis Lam
9789d8cde8 Merge branch 'master' into alerta 2022-03-04 14:09:01 +08:00
Louis Lam
ccb3d85a48 Merge pull request #1157 from zackelia/master
Implement gorush notifications
2022-03-03 22:03:09 +08:00
Louis Lam
333505b039 Merge remote-tracking branch 'origin/master' 2022-03-03 20:49:13 +08:00
Louis Lam
602da565eb Sort notification types 2022-03-03 20:49:00 +08:00
Louis Lam
b62d94184a Merge branch 'master' into restructure-status-page 2022-03-03 17:09:15 +08:00
Louis Lam
e0175d0010 Delete stale-bot.yml, no idea why it deleted some feature request 2022-03-03 10:21:34 +08:00
Louis Lam
3246055696 Merge pull request #1350 from deluxghost/patch-1
Update zh-CN.js
2022-03-02 23:20:47 +08:00
deluxghost
b3a690f3b1 Update zh-CN.js 2022-03-02 23:12:30 +08:00
Louis Lam
7bc8c447cd Merge branch 'MikMuellerDev_master'
# Conflicts:
#	src/languages/de-DE.js
2022-03-02 17:17:15 +08:00
Louis Lam
69ff6831ab Merge pull request #1311 from deluxghost/update-zh-cn
Update zh-CN translations
2022-03-02 16:58:54 +08:00
AnnAngela
88a798704b Update fs-rmSync.js 2022-03-02 16:10:14 +08:00
AnnAngela-work
783173fd1f Add a helper function 2022-03-02 15:48:08 +08:00
DX
0dba06e48b Update zh-CN translations 2022-03-02 15:34:11 +08:00
AnnAngela-work
281fe365c0 Mark the version as 1.11.4 in package-lock.json 2022-03-02 15:23:08 +08:00
Louis Lam
8e7c0a6163 Update pull request rules 2022-03-02 14:25:37 +08:00
Louis Lam
0671e4ea2b Merge remote-tracking branch 'AnnAngela/master'
# Conflicts:
#	src/languages/zh-CN.js
2022-03-02 13:43:34 +08:00
Louis Lam
cd8eaef903 Merge pull request #1187 from pfandie/translations-de
Updates some DE translations
2022-03-02 13:39:19 +08:00
Louis Lam
51f5c009e3 Merge remote-tracking branch 'PrikolMen/patch-1'
# Conflicts:
#	src/languages/ru-RU.js
2022-03-02 13:37:21 +08:00
Louis Lam
3bf62c9ceb Merge branch 'patch-7-ru'
# Conflicts:
#	src/languages/ru-RU.js
2022-03-02 13:33:37 +08:00
Louis Lam
7b11539cff Merge branch 'patch-62'
# Conflicts:
#	src/languages/ru-RU.js
2022-03-02 13:31:43 +08:00
PrikolMen:-b
b4a3d68356 More correct Russian translation
I tried to fix most of the shortcomings of the Russian translation...
2022-02-28 14:55:23 +04:00
Louis Lam
b31af8a15c update to 1.12.1 2022-02-26 17:05:13 +08:00
Louis Lam
60f67ccb35 Revert commit: a6fd626f 2022-02-26 16:57:13 +08:00
Louis Lam
81a9807a0a Update release procedures 2022-02-26 16:03:43 +08:00
Louis Lam
3681934d05 Update Apprise to 0.9.7 2022-02-26 15:57:26 +08:00
Louis Lam
d5d63474d8 update to 1.12.0 2022-02-26 15:41:32 +08:00
Louis Lam
a6fd626fb8 Locked Russian language, ask Putin to stop the war and unlock it 2022-02-26 14:56:57 +08:00
Louis Lam
3a5b413af4 Update axios to 0.26.0 due to vulnerability 2022-02-26 14:36:38 +08:00
Louis Lam
595cd93220 Check invalid interval 2022-02-24 15:11:17 +08:00
Louis Lam
e12c1511db Merge pull request #1330 from BCsabaEngine/master
fix: hu lang
2022-02-23 22:36:05 +08:00
Balázs Csaba
f3112c0b85 fix: hu lang 2022-02-23 09:35:56 +01:00
Louis Lam
af07850ddf Merge pull request #1287 from sovushik/patch-5
Update ru-RU.js
2022-02-21 15:12:27 +08:00
Louis Lam
211b44269c Do not close feature-request 2022-02-21 11:48:03 +08:00
Louis Lam
7638b73645 Fix #1300 2022-02-15 23:30:07 +08:00
Mik Mueller
a997f8e4f9 Update de-DE.js 2022-02-15 12:58:02 +01:00
Mik Mueller
09dbb143ea Merge branch 'louislam:master' into master 2022-02-15 12:51:21 +01:00
Hans Mayer
f19e983818 Merge remote-tracking branch 'origin/master' into translations-de 2022-02-14 14:39:21 +01:00
Louis Lam
d0ed99a310 Merge pull request #1298 from ananthkamath/master
Fix mattermost couldn't find channel issue
2022-02-13 23:56:12 +08:00
Ananth Kamath
258d93be72 Fix mattermost couldn't find channel issue 2022-02-13 21:17:02 +05:30
Jens Neuber
454c1687cf Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-02-13 11:12:22 +01:00
Louis Lam
986ddd92ff Merge pull request #1198 from Buchtic/master
CSY translation
2022-02-13 14:11:00 +08:00
AnnAngela
c75c6c5640 Update zh-CN.js
Update DingDing and AliyunSms setting dialogs for better translations and document links
2022-02-11 22:50:46 +08:00
AnnAngela
5aed36b470 Update zh-CN
----
关于 smtpDkimKeySelector:
Google Workspace 的帮助里用词为
[前缀选择器](https://annangela.page.link/smtpDkimKeySelector)
2022-02-10 22:45:24 +08:00
sovushik
76b9fb967f Update ru-RU.js
Add new string
2022-02-09 21:37:45 +05:00
sovushik
b58120d258 Update ru-RU.js
Correct some words on Russian
2022-02-09 21:26:47 +05:00
sovushik
79f99ce215 Update ru-RU.js
Add new string
2022-02-09 21:19:00 +05:00
Jimmy Huang
244a7b3671 Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-02-07 18:46:16 +08:00
Mik Mueller
7d8b72c6c0 Merge branch 'louislam:master' into master 2022-02-06 10:57:10 +01:00
Hans Mayer
40cc885eb8 resolve conflict after update state 2022-02-05 11:48:48 +01:00
Mik Mueller
f1007ad42f Update de-DE.js 2022-02-02 22:59:13 +01:00
Mik Mueller
dd28ecaa2d Update de-DE.js 2022-02-02 22:57:02 +01:00
Mik Mueller
ffa585376d Merge branch 'louislam:master' into master 2022-02-02 22:54:23 +01:00
Mik Mueller
11c2e86bfe Update src/languages/de-DE.js
Co-authored-by: Alf <62615304+Alf-Melmac@users.noreply.github.com>
2022-02-01 08:13:30 +01:00
Mik Mueller
1bbf17f3da Update src/languages/de-DE.js
Co-authored-by: Alf <62615304+Alf-Melmac@users.noreply.github.com>
2022-02-01 08:13:23 +01:00
Mik Mueller
39f8b30b36 Update src/languages/de-DE.js
Co-authored-by: Alf <62615304+Alf-Melmac@users.noreply.github.com>
2022-02-01 08:13:13 +01:00
Mik Mueller
ffb2c2996b Update src/languages/de-DE.js
Co-authored-by: Alf <62615304+Alf-Melmac@users.noreply.github.com>
2022-02-01 08:12:42 +01:00
Mik Mueller
b13b20bd95 improve certain German words and phrases, improve grammer in README.md 2022-01-30 00:17:25 +01:00
Alvin Pergens
8febff9282 fix comments 2022-01-28 15:35:33 +01:00
Alvin Pergens
90f2497548 change data for Alerta 2022-01-28 15:14:34 +01:00
Jens Neuber
28be32fc68 Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-01-28 08:36:05 +01:00
Alvin Pergens
cefe43800f add alerta service 2022-01-27 20:54:04 +01:00
Computroniks
97a5b400db Added log out button to nav bar
Implements Logout button in navbar #1209

Signed-off-by: Computroniks <mnickson@sidingsmedia.com>
2022-01-27 19:45:31 +00:00
Computroniks
ca89f84b9a Added sign-out-alt icon
Signed-off-by: Computroniks <mnickson@sidingsmedia.com>
2022-01-27 18:57:14 +00:00
Computroniks
eaf370637e Fixed dark mode checkbox
The border colour of the checkbox has been changed to make it more
visible to the user when the dark mode is in use.

Signed-off-by: Computroniks <mnickson@sidingsmedia.com>
2022-01-27 17:40:03 +00:00
Jimmy Huang
ee90d2713f refs issue-1201 in upstream.
Add 100 characters from response body to bean.msg after keyword not match.
2022-01-25 17:39:19 +08:00
Raphael Bernhart
dd3992063e Apply suggestions from code review
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-22 13:59:36 +01:00
Raphael Bernhart
0313acd4c5 🐛 Fix bug where a condition was wrong-false 2022-01-21 17:22:30 +01:00
Raphael Bernhart
cd19b9fc49 💫 Improve hearbeat animation 2022-01-21 17:13:38 +01:00
Raphael Bernhart
c57b2c4d28 💄 Fix spacing to get pixel perfectness 2022-01-21 17:13:24 +01:00
Raphael Bernhart
3dda5938f2 💄 Add condition to div tag for styling reasons 2022-01-21 15:39:49 +01:00
Arjun Komath
23796723dd Address code review
Add missing comma

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-21 20:42:08 +11:00
Arjun Komath
51b7a2badb remove log 2022-01-21 07:43:14 +00:00
Arjun Komath
74c584f544 Add Push by Techulus 2022-01-21 07:42:03 +00:00
Tarun Singh
0345719e53 added cleartimeout in case client is already ended 2022-01-20 13:20:54 -05:00
Tarun Singh
22256dfcd2 added timeout for removing the dead loop state 2022-01-20 13:04:59 -05:00
Buchtič
4713820da7 first csy translation 2022-01-18 14:44:11 +01:00
Buchtič
a99e87c02c cs-CZ 2022-01-18 08:50:11 +01:00
Buchtič
3f8ca82434 cs-CZ translation 2022-01-18 08:48:39 +01:00
Buchtič
60f1eb7b45 new cs-CZ.js 2022-01-17 18:42:32 +01:00
Hans Mayer
3e87eb596f change wording, according to PR suggestions 2022-01-15 12:25:17 +01:00
Hans Mayer
c679613f7e Updates some DE translations, fix typo in resolverserverDescription, removes some duplicates in languages 2022-01-14 19:06:21 +01:00
Louis Lam
227bbdea2f [MQTT] Try to improve error handling 2022-01-13 12:42:34 +08:00
Louis Lam
6272514820 [MQTT] Use existing fields instead of creating new ones (Server) 2022-01-13 11:53:08 +08:00
Louis Lam
1c8407a433 [MQTT] Use existing fields instead of creating new ones (UI) 2022-01-13 11:36:55 +08:00
Louis Lam
32ec4beda0 Merge branch 'master' into mqtt 2022-01-13 11:24:45 +08:00
Louis Lam
9462646ad3 Fix vulnerabilities 2022-01-13 11:22:58 +08:00
Louis Lam
482b3f9233 Update server/util-server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-13 11:20:32 +08:00
Louis Lam
6014ed1156 Fix conflict 2022-01-13 11:19:26 +08:00
Louis Lam
bfee63452d Merge branch 'master' into mqtt 2022-01-13 11:16:58 +08:00
Louis Lam
076d6bdbb6 Merge branch 'master' into mqtt
# Conflicts:
#	package-lock.json
#	server/database.js
2022-01-13 11:09:16 +08:00
Zack Elia
ea43422ccf Implement gorush notifications 2022-01-09 12:05:11 -05:00
Marc Harnos
0bbe157099 change parsing priority for all passed arguments
update all passed args in server.js to prioritize command line, then use
env.UPTIME_KUMA_ environment variables, then use the generic environment
variable versions env.HOST, env.PORT, env.SSL_KEY, env.SSL_CERT and fall
back to default values where applicable
2022-01-08 18:32:42 +01:00
Marc Harnos
0053a29d10 add validation to port value parsing
only port configurations that are valid (not isNaN) after parseInt
are considered to be used in port variable
2022-01-08 18:27:39 +01:00
Marc Harnos
2c8d5d28e9 simplify host fallback logic
move decision logic for freeBSD HOST environment var into temp var
2022-01-08 18:25:12 +01:00
Nelson Chan
1bbd744d02 Chore: Improve syntax 2022-01-07 14:29:42 +08:00
Nelson Chan
2e0e35a1ee Fix: Fix typo 2022-01-07 12:34:01 +08:00
Nelson Chan
1e92487f30 Chore: Remove onDelete as unused 2022-01-07 12:28:08 +08:00
Nelson Chan
edd2534a1b Fix: Clear metrics also on stop and edit 2022-01-07 12:26:26 +08:00
Nelson Chan
f6ef390c76 Fix: Remove Prom. metrics on delete monitor 2022-01-07 12:04:57 +08:00
Jens Neuber
f00ec4dfef PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:29 +01:00
Jens Neuber
43f8fc701c PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:23 +01:00
Jens Neuber
499042504f PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:07 +01:00
Jens Neuber
faf6719e7c PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:25:56 +01:00
Jens Neuber
a9d264ccfc PR feedback: remove spaces in comments
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:25:42 +01:00
Jens Neuber
df8f93f0c2 clean uptime percentage display 2022-01-05 11:48:25 +01:00
Jens Neuber
28c0e16a0c PR feedback 2022-01-04 16:01:40 +01:00
Jens Neuber
6acc9546a0 PR feedback + remove redundant code + add a test 2022-01-04 16:00:21 +01:00
Jens Neuber
f455e3a454 add shields.io 'style' parameter 2022-01-04 13:43:13 +01:00
Jens Neuber
7abbf421d0 PR feedback 2022-01-04 12:23:16 +01:00
Jens Neuber
3625915a85 add ping, status badge 2022-01-04 12:21:53 +01:00
Jens Neuber
d74404e106 minor fixes 2022-01-03 16:23:23 +01:00
Jens Neuber
1c5bce8afa a little documentation 2022-01-03 16:04:37 +01:00
Jens Neuber
8b5997691e Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-01-03 15:49:00 +01:00
Jens Neuber
35360e2069 add badges 2022-01-03 15:48:52 +01:00
Louis Lam
72a59ce7a4 add status page table 2021-12-27 18:54:48 +08:00
Jakub Blažej
3d002b3ce9 add status boolean parameter to push monitor 2021-12-25 20:25:21 +01:00
Patrick Hafner
3d6c52fbea Merge branch 'master' into customstatuspage 2021-12-25 04:12:05 +01:00
Patrick Hafner
9ee591417d Footer HTML support, updated german translation 2021-12-25 04:09:41 +01:00
Tarun Singh
4118de6d53 fix protocol not defined bug 2021-12-23 19:39:47 -05:00
Patrick Hafner
3a12e209da Edit: editMode check before toggle 2021-12-21 03:55:25 +01:00
Patrick Hafner
2c2a824f97 Add: en & de-DE language 2021-12-21 03:31:09 +01:00
Patrick Hafner
931ca6a3ef Add: customize status page (css and poweredby) 2021-12-21 03:27:05 +01:00
Tarun Singh
d3c90df8a8 fixed edit monitor fields empty issues 2021-12-18 16:35:18 -05:00
Andreas Brett
38f8a8ac2f Merge branch 'louislam:master' into logging 2021-12-10 17:21:55 +01:00
Louis Lam
807519d07d Merge branch 'master' into restructure-status-page 2021-12-09 21:46:35 +08:00
Andreas Brett
e684712a77 Merge branch 'louislam:master' into logging 2021-12-07 18:21:56 +01:00
Tarun Singh
5afc6a41e3 removed https requirement for url 2021-12-06 11:28:23 -05:00
Andreas Brett
dcc7856b5d Merge branch 'louislam:master' into logging 2021-12-04 09:59:10 +01:00
Andreas Brett
c9b0a81cdc Update MonitorHistory.vue 2021-11-29 20:39:57 +01:00
Andreas Brett
2f97f44086 Update MonitorHistory.vue 2021-11-29 20:37:44 +01:00
Andreas Brett
a13bdaac84 Merge branch 'master' into logging 2021-11-29 20:32:42 +01:00
Fluency
e3745da986 Merge branch 'master' into master 2021-11-23 10:26:45 -08:00
Tarun Singh
35da8c78f4 added connection timeout and refactored code 2021-11-22 03:21:53 -05:00
Fluency
7179c6cc4c Fix punctuation in extra/update-version.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-11-19 10:30:31 -08:00
Andreas Brett
ed96757b24 Merge branch 'louislam:master' into logging 2021-11-19 08:56:25 +01:00
Tarun Singh
3306f4a8e0 removed extra logging 2021-11-18 14:03:23 -05:00
Fluency
a2de9e4e36 Fixed export-function signature being scrambled. 2021-11-17 16:02:31 -08:00
Tarun Singh
3f5133d1ba Added authentication logic 2021-11-16 20:44:10 -05:00
Andreas Brett
6f2dcc6dd7 using provided tsc config 2021-11-15 18:07:18 +01:00
Andreas Brett
57bed4d672 Merge branch 'louislam:master' into logging 2021-11-15 18:04:45 +01:00
Andreas Brett
df36a4bb3c console.info for level "info" 2021-11-15 18:02:14 +01:00
Andreas Brett
e5913c5abc separate log functions 2021-11-15 17:52:28 +01:00
Andreas Brett
d21f7971b5 missed settings 2021-11-11 12:56:53 +01:00
Andreas Brett
bdcdf47e52 introduce consistent logging 2021-11-11 12:31:28 +01:00
Calum Bird
f55350bebc Generated documentation :) 2021-11-09 21:24:31 -08:00
Tarun Singh
3721d11259 changed table column names for more specifity 2021-11-09 18:53:00 -05:00
Tarun Singh
149015556b server url changes 2021-11-04 22:25:29 -04:00
Tarun Singh
2bcbeba384 update review suggestions
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-11-04 22:23:02 -04:00
Tarun Singh
d5d07da4ee update review suggestions
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-11-04 22:22:53 -04:00
Tarun Singh
2d802585ff Update review suggestions
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-11-04 22:21:54 -04:00
Tarun Singh
6828e8ef6d Merge branch 'master' of https://github.com/tarun7singh/uptime-kuma 2021-11-03 21:47:44 -04:00
Tarun Singh
670754b697 added MQTT monitor type 2021-11-03 21:46:43 -04:00
Louis Lam
6d1baa329a draft for restructure status page 2021-11-02 12:26:22 +08:00
Willian Rodrigues Barbosa
036218f711 Add badges to favicon 2021-10-29 22:25:32 -03:00
223 changed files with 24769 additions and 6340 deletions

View File

@@ -28,6 +28,8 @@ SECURITY.md
tsconfig.json
.env
/tmp
/babel.config.js
/ecosystem.config.js
### .gitignore content (commented rules are duplicated)
@@ -42,4 +44,6 @@ dist-ssr
#!/data/.gitkeep
#.vscode
### End of .gitignore content

View File

@@ -1,4 +1,9 @@
module.exports = {
ignorePatterns: [
"test/*",
"server/modules/apicache/*",
"src/util.js"
],
root: true,
env: {
browser: true,
@@ -17,39 +22,48 @@ module.exports = {
requireConfigFile: false,
},
rules: {
"linebreak-style": ["error", "unix"],
"camelcase": ["warn", {
"yoda": "error",
eqeqeq: [ "warn", "smart" ],
"linebreak-style": [ "error", "unix" ],
"camelcase": [ "warn", {
"properties": "never",
"ignoreImports": true
}],
// override/add rules settings here, such as:
// 'vue/no-unused-vars': 'error'
"no-unused-vars": "warn",
"no-unused-vars": [ "warn", {
"args": "none"
}],
indent: [
"error",
4,
{
ignoredNodes: ["TemplateLiteral"],
ignoredNodes: [ "TemplateLiteral" ],
SwitchCase: 1,
},
],
quotes: ["warn", "double"],
semi: "warn",
"vue/html-indent": ["warn", 4], // default: 2
quotes: [ "error", "double" ],
semi: "error",
"vue/html-indent": [ "error", 4 ], // default: 2
"vue/max-attributes-per-line": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/html-self-closing": "off",
"vue/require-component-is": "off", // not allow is="style" https://github.com/vuejs/eslint-plugin-vue/issues/462#issuecomment-430234675
"vue/attribute-hyphenation": "off", // This change noNL to "no-n-l" unexpectedly
"no-multi-spaces": ["error", {
"vue/multi-word-component-names": "off",
"no-multi-spaces": [ "error", {
ignoreEOLComments: true,
}],
"space-before-function-paren": ["error", {
"array-bracket-spacing": [ "warn", "always", {
"singleValue": true,
"objectsInArrays": false,
"arraysInArrays": false
}],
"space-before-function-paren": [ "error", {
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}],
"curly": "error",
"object-curly-spacing": ["error", "always"],
"object-curly-spacing": [ "error", "always" ],
"object-curly-newline": "off",
"object-property-newline": "error",
"comma-spacing": "error",
@@ -59,37 +73,37 @@ module.exports = {
"keyword-spacing": "warn",
"space-infix-ops": "warn",
"arrow-spacing": "warn",
"no-trailing-spaces": "warn",
"no-constant-condition": ["error", {
"no-trailing-spaces": "error",
"no-constant-condition": [ "error", {
"checkLoops": false,
}],
"space-before-blocks": "warn",
//'no-console': 'warn',
"no-extra-boolean-cast": "off",
"no-multiple-empty-lines": ["warn", {
"no-multiple-empty-lines": [ "warn", {
"max": 1,
"maxBOF": 0,
}],
"lines-between-class-members": ["warn", "always", {
"lines-between-class-members": [ "warn", "always", {
exceptAfterSingleLine: true,
}],
"no-unneeded-ternary": "error",
"array-bracket-newline": ["error", "consistent"],
"eol-last": ["error", "always"],
"array-bracket-newline": [ "error", "consistent" ],
"eol-last": [ "error", "always" ],
//'prefer-template': 'error',
"comma-dangle": ["warn", "only-multiline"],
"no-empty": ["error", {
"comma-dangle": [ "warn", "only-multiline" ],
"no-empty": [ "error", {
"allowEmptyCatch": true
}],
"no-control-regex": "off",
"one-var": ["error", "never"],
"max-statements-per-line": ["error", { "max": 1 }]
"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"],
"comma-dangle": [ "error", "always-multiline" ],
}
},

View File

@@ -1,3 +1,5 @@
👉 Delete this line if you have read and agree our pull request rules and guidelines: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma
# Description
Fixes #(issue)
@@ -20,6 +22,7 @@ Please delete any options that are not relevant.
- [ ] I ran ESLint and other linters for modified files
- [ ] I have performed a self-review of my own code and tested it
- [ ] I have commented my code, particularly in hard-to-understand areas
(including JSDoc for methods)
- [ ] My changes generate no new warnings
- [ ] My code needed automated testing. I have added them (this is optional task)

View File

@@ -11,25 +11,42 @@ on:
jobs:
auto-test:
needs: [ check-linters ]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
node-version: [14.x, 16.x, 17.x]
node: [ 14, 16, 17, 18 ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- run: git config --global core.autocrlf false # Mainly for Windows
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm run install-legacy
- run: npm install
- run: npm run build
- run: npm test
env:
HEADLESS_TEST: 1
JUST_FOR_TEST: ${{ secrets.JUST_FOR_TEST }}
check-linters:
runs-on: ubuntu-latest
steps:
- run: git config --global core.autocrlf false # Mainly for Windows
- uses: actions/checkout@v3
- name: Use Node.js 14
uses: actions/setup-node@v3
with:
node-version: 14
cache: 'npm'
- run: npm install
- run: npm run lint

View File

@@ -1,22 +0,0 @@
name: 'Automatically close stale issues and PRs'
on:
schedule:
- cron: '0 0 * * *'
#Run once a day at midnight
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'We are clearing up our old Pull Requests and yours has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.'
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
days-before-stale: 180
days-before-close: 7
exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,'
exempt-pr-labels: 'awaiting-approval,work-in-progress,enhancement,'
exempt-issue-assignees: 'louislam'
exempt-pr-assignees: 'louislam'

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

View File

@@ -1,9 +1,13 @@
{
"extends": "stylelint-config-standard",
"customSyntax": "postcss-html",
"rules": {
"indentation": 4,
"no-descending-specificity": null,
"selector-list-comma-newline-after": null,
"declaration-empty-line-before": null
"declaration-empty-line-before": null,
"alpha-value-notation": "number",
"color-function-notation": "legacy",
"shorthand-property-no-redundant-values": null
}
}

2
CNAME
View File

@@ -1 +1 @@
git.kuma.pet
git.kuma.pet

View File

@@ -27,12 +27,34 @@ The frontend code build into "dist" directory. The server (express.js) exposes t
## Can I create a pull request for Uptime Kuma?
Generally, if the pull request is working fine, and it does not affect any existing logic, workflow and performance, I will merge into the master branch once it is tested.
Yes, you can. However, since I don't want to waste your time, be sure to **create empty draft pull request, so we can discuss first** if it is a large pull request or you don't know it will be merged or not.
Also, please don't rush or ask for ETA, because I have to understand the pull request, make sure it is no breaking changes and stick to my vision of this project, especially for large pull requests.
I will mark your pull request in the [milestones](https://github.com/louislam/uptime-kuma/milestones), if I am plan to review and merge it.
✅ Accept:
- Bug/Security fix
- Translations
- Adding notification providers
⚠️ Discussion First
- Large pull requests
- New features
❌ Won't Merge
- Do not pass auto test
- Any breaking changes
- Duplicated pull request
- Buggy
- Existing logic is completely modified or deleted for no reason
- A function that is completely out of scope
If you are not sure whether I will accept your pull request, feel free to create an empty pull request draft first.
### Recommended Pull Request Guideline
Before deep into coding, discussion first is preferred. Creating an empty pull request for discussion would be recommended.
1. Fork the project
1. Clone your fork repo to local
1. Create a new branch
@@ -42,50 +64,7 @@ If you are not sure whether I will accept your pull request, feel free to create
1. Create a pull request: https://github.com/louislam/uptime-kuma/compare
1. Write a proper description
1. Click "Change to draft"
### Pull Request Examples
Here are some example situations in the past.
#### ✅ High - Medium Priority
Easy to review, no breaking change and not touching the existing code
- Add a new notification
- Add a chart
- Fix a bug
- Translations
- Add a independent new feature
#### *️⃣ Requires one more reviewer
I do not have such knowledge to test it.
- Add k8s supports
#### ⚠ Low Priority - Harsh Mode
Some pull requests are required to modify the core. To be honest, I do not want anyone to try to do that, because it would spend a lot of your time. I will review your pull request harshly. Also, you may need to write a lot of unit tests to ensure that there is no breaking change.
- Touch large parts of code of any very important features
- Touch monitoring logic
- Drop a table or drop a column for any reason
- Touch the entry point of Docker or Node.js
- Modify auth
#### *️⃣ Low Priority
It changed my current workflow and require further studies.
- Change my release approach
#### ❌ Won't Merge
- Any breaking changes
- Duplicated pull request
- Buggy
- Existing logic is completely modified or deleted
- A function that is completely out of scope
1. Discussion
## Project Styles
@@ -93,14 +72,16 @@ I personally do not like something need to learn so much and need to config so m
- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort to get it run
- Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go
- Settings should be configurable in the frontend. Env var is not encouraged.
- Settings should be configurable in the frontend. Environment variable is not encouraged, unless it is related to startup such as `DATA_DIR`.
- Easy to use
- The web UI styling should be consistent and nice.
## Coding Styles
- 4 spaces indentation
- Follow `.editorconfig`
- Follow ESLint
- Methods and functions should be documented with JSDoc
## Name convention
@@ -111,9 +92,10 @@ I personally do not like something need to learn so much and need to config so m
## Tools
- Node.js >= 14
- NPM >= 8.5
- Git
- IDE that supports ESLint and EditorConfig (I am using IntelliJ IDEA)
- A SQLite tool (SQLite Expert Personal is suggested)
- A SQLite GUI tool (SQLite Expert Personal is suggested)
## Install dependencies
@@ -121,39 +103,45 @@ I personally do not like something need to learn so much and need to config so m
npm ci
```
## How to start the Backend Dev Server
## Dev Server
(2021-09-23 Update)
(2022-04-26 Update)
We can start the frontend dev server and the backend dev server in one command.
Port `3000` and port `3001` will be used.
```bash
npm run start-server-dev
npm run dev
```
## Backend Server
It binds to `0.0.0.0:3001` by default.
### 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 used for:
- entry point such as redirecting to a status page or the dashboard
- serving the frontend built files (index.html, .js and .css etc.)
- serving internal APIs of status page
### Structure in /server/
- model/ (Object model, auto mapping to the database table name)
- modules/ (Modified 3rd-party modules)
- notification-providers/ (individual notification logic)
- routers/ (Express Routers)
- socket-handler (Socket.io Handlers)
- server.js (Server main logic)
- server.js (Server entry point and main logic)
## How to start the Frontend Dev Server
## Frontend Dev Server
1. Set the env var `NODE_ENV` to "development".
2. Start the frontend dev server by the following command.
It binds to `0.0.0.0:3000` by default. Frontend dev server is used for development only.
```bash
npm run dev
```
It binds to `0.0.0.0:3000` by default.
For production, it is not used. It will be compiled to `dist` directory instead.
You can use Vue.js devtools Chrome extension for debugging.
@@ -221,14 +209,13 @@ https://github.com/louislam/uptime-kuma/issues?q=sort%3Aupdated-desc
### Release Procedures
1. Draft a release note
1. Make sure the repo is cleared
1. `npm run update-version 1.X.X`
1. `npm run build`
1. `npm run build-docker`
1. `git push`
1. Publish the release note as 1.X.X
1. `npm run upload-artifacts`
1. SSH to demo site server and update to 1.X.X
2. Make sure the repo is cleared
3. `npm run release-final with env vars: `VERSION` and `GITHUB_TOKEN`
4. Wait until the `Press any key to continue`
5. `git push`
6. Publish the release note as 1.X.X
7. Press any key to continue
8. SSH to demo site server and update to 1.X.X
Checking:
@@ -236,6 +223,15 @@ Checking:
- Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 / armv7)
- Try clean installation with Node.js
### Release Beta Procedures
1. Draft a release note, check "This is a pre-release"
2. Make sure the repo is cleared
3. `npm run release-beta` with env vars: `VERSION` and `GITHUB_TOKEN`
4. Wait until the `Press any key to continue`
5. Publish the release note as 1.X.X-beta.X
6. Press any key to continue
### Release Wiki
#### Setup Repo

View File

@@ -25,19 +25,21 @@ VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollec
* Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / Ping / DNS Record / Push / Steam Game Server.
* Fancy, Reactive, Fast UI/UX.
* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [70+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications).
* Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [90+ 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
* Multiple Status Pages
* Map Status Page to Domain
* Ping Chart
* Certificate Info
* Proxy Support
* 2FA available
## 🔧 How to Install
### 🐳 Docker
```bash
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
```
@@ -47,7 +49,10 @@ Browse to http://localhost:3001 after starting.
### 💪🏻 Non-Docker
Required Tools: Node.js >= 14, git and pm2.
Required Tools:
- [Node.js](https://nodejs.org/en/download/) >= 14
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) - For run in background
```bash
# Update your npm to the latest version
@@ -61,12 +66,26 @@ npm run setup
node server/server.js
# (Recommended) Option 2. Run in background using PM2
# Install PM2 if you don't have it: npm install pm2 -g
pm2 start server/server.js --name uptime-kuma
```
# Install PM2 if you don't have it:
npm install pm2 -g && pm2 install pm2-logrotate
# Start Server
pm2 start server/server.js --name uptime-kuma
```
Browse to http://localhost:3001 after starting.
More useful PM2 Commands
```bash
# If you want to see the current console output
pm2 monit
# If you want to add it to startup
pm2 save && pm2 startup
```
### Advanced Installation
If you need more options or need to browse via a reverse proxy, please read:
@@ -93,7 +112,7 @@ https://github.com/louislam/uptime-kuma/projects/1
Thank you so much! (GitHub Sponsors will be updated manually. OpenCollective sponsors will be updated automatically, the list will be cached by GitHub though. It may need some time to be updated)
<img src="https://uptime.kuma.pet/sponsors?v=3" alt />
<img src="https://uptime.kuma.pet/sponsors?v=6" alt />
## 🖼 More Screenshots
@@ -115,7 +134,7 @@ Telegram Notification Sample:
## Motivation
* I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one. One of the close ones is statping. Unfortunately, it is not stable and unmaintained.
* I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one. One of the close ones is statping. Unfortunately, it is not stable and no longer maintained.
* Want to build a fancy UI.
* Learn Vue 3 and vite.js.
* Show the power of Bootstrap 5.
@@ -138,10 +157,17 @@ 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](https://github.com/louislam/uptime-kuma/issues).
### Beta Version
Check out the latest beta release here: https://github.com/louislam/uptime-kuma/releases
### Bug Reports / Feature Requests
If you want to report a bug or request a new feature, feel free to open a [new issue](https://github.com/louislam/uptime-kuma/issues).
### Translations
If you want to translate Uptime Kuma into your language, please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages
If you want to modify Uptime Kuma, this guideline may be useful for you: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md
Feel free to correct my grammar in this README, source code, or wiki, as my mother language is not English and my grammar is not that great.
English proofreading is needed too because my grammar is not that great, sadly. Feel free to correct my grammar in this README, source code, or wiki.
### Pull Requests
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

@@ -8,15 +8,9 @@ Do not use the issue tracker or discuss it in the public as it will cause more d
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
### Uptime Kuma Versions
| Version | Supported |
| ------- | ------------------ |
| 1.9.X | :white_check_mark: |
| <= 1.8.X | ❌ |
You should use or upgrade to the latest version of Uptime Kuma. All `1.X.X` versions are upgradable to the lastest version.
### Upgradable Docker Tags
@@ -24,8 +18,8 @@ currently being supported with security updates.
| ------- | ------------------ |
| 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: |
| 1-alpine | ⚠️ Deprecated |
| alpine | ⚠️ Deprecated |
| All other tags | ❌ |

View File

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

View File

@@ -1,24 +1,47 @@
import legacy from "@vitejs/plugin-legacy";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import visualizer from "rollup-plugin-visualizer";
import viteCompression from "vite-plugin-compression";
const postCssScss = require("postcss-scss");
const postcssRTLCSS = require("postcss-rtlcss");
const viteCompressionFilter = /\.(js|mjs|json|css|html|svg)$/i;
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ["ie > 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"]
})
targets: [ "since 2015" ],
}),
visualizer({
filename: "tmp/dist-stats.html"
}),
viteCompression({
algorithm: "gzip",
filter: viteCompressionFilter,
}),
viteCompression({
algorithm: "brotliCompress",
filter: viteCompressionFilter,
}),
],
css: {
postcss: {
"parser": postCssScss,
"map": false,
"plugins": [postcssRTLCSS]
"plugins": [ postcssRTLCSS ]
}
},
build: {
rollupOptions: {
output: {
manualChunks(id, { getModuleInfo, getModuleIds }) {
}
}
},
}
});

View File

@@ -0,0 +1,18 @@
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD auth_method VARCHAR(250);
ALTER TABLE monitor
ADD auth_domain TEXT;
ALTER TABLE monitor
ADD auth_workstation TEXT;
COMMIT;
BEGIN TRANSACTION;
UPDATE monitor
SET auth_method = 'basic'
WHERE basic_auth_user is not null;
COMMIT;

View File

@@ -0,0 +1,10 @@
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD database_connection_string VARCHAR(2000);
ALTER TABLE monitor
ADD database_query TEXT;
COMMIT

View File

@@ -0,0 +1,16 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD mqtt_topic TEXT;
ALTER TABLE monitor
ADD mqtt_success_message VARCHAR(255);
ALTER TABLE monitor
ADD mqtt_username VARCHAR(255);
ALTER TABLE monitor
ADD mqtt_password VARCHAR(255);
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 expiry_notification BOOLEAN default 1;
COMMIT;

23
db/patch-proxy.sql Normal file
View File

@@ -0,0 +1,23 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
CREATE TABLE proxy (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INT NOT NULL,
protocol VARCHAR(10) NOT NULL,
host VARCHAR(255) NOT NULL,
port SMALLINT NOT NULL,
auth BOOLEAN NOT NULL,
username VARCHAR(255) NULL,
password VARCHAR(255) NULL,
active BOOLEAN NOT NULL DEFAULT 1,
'default' BOOLEAN NOT NULL DEFAULT 0,
created_date DATETIME DEFAULT (DATETIME('now')) NOT NULL
);
ALTER TABLE monitor ADD COLUMN proxy_id INTEGER REFERENCES proxy(id);
CREATE INDEX proxy_id ON monitor (proxy_id);
CREATE INDEX proxy_user_id ON proxy (user_id);
COMMIT;

View File

@@ -0,0 +1,6 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
ALTER TABLE status_page ADD footer_text TEXT;
ALTER TABLE status_page ADD custom_css TEXT;
ALTER TABLE status_page ADD show_powered_by BOOLEAN NOT NULL DEFAULT 1;
COMMIT;

31
db/patch-status-page.sql Normal file
View File

@@ -0,0 +1,31 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
CREATE TABLE [status_page](
[id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[slug] VARCHAR(255) NOT NULL UNIQUE,
[title] VARCHAR(255) NOT NULL,
[description] TEXT,
[icon] VARCHAR(255) NOT NULL,
[theme] VARCHAR(30) NOT NULL,
[published] BOOLEAN NOT NULL DEFAULT 1,
[search_engine_index] BOOLEAN NOT NULL DEFAULT 1,
[show_tags] BOOLEAN NOT NULL DEFAULT 0,
[password] VARCHAR,
[created_date] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
[modified_date] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX [slug] ON [status_page]([slug]);
CREATE TABLE [status_page_cname](
[id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[status_page_id] INTEGER NOT NULL REFERENCES [status_page]([id]) ON DELETE CASCADE ON UPDATE CASCADE,
[domain] VARCHAR NOT NULL UNIQUE
);
ALTER TABLE incident ADD status_page_id INTEGER;
ALTER TABLE [group] ADD status_page_id INTEGER;
COMMIT;

View File

@@ -1,8 +1,8 @@
# DON'T UPDATE TO alpine3.13, 1.14, see #41.
FROM node:14-alpine3.12
FROM node:16-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==0.9.6 && \
pip3 --no-cache-dir install apprise==0.9.8.3 && \
rm -rf /root/.cache

View File

@@ -1,12 +1,28 @@
# 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
FROM node:16-buster-slim
ARG TARGETPLATFORM
WORKDIR /app
# Install Curl
# 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, specify --no-install-recommends to skip them, make the base even smaller than alpine!
RUN apt update && \
apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \
sqlite3 iputils-ping util-linux dumb-init && \
pip3 --no-cache-dir install apprise==0.9.6 && \
rm -rf /var/lib/apt/lists/*
pip3 --no-cache-dir install apprise==0.9.8.3 && \
rm -rf /var/lib/apt/lists/* && \
apt --yes autoremove
# Install cloudflared
# dpkg --add-architecture arm: cloudflared do not provide armhf, this is workaround. Read more: https://github.com/cloudflare/cloudflared/issues/583
COPY extra/download-cloudflared.js ./extra/download-cloudflared.js
RUN node ./extra/download-cloudflared.js $TARGETPLATFORM && \
dpkg --add-architecture arm && \
apt update && \
apt --yes --no-install-recommends install ./cloudflared.deb && \
rm -rf /var/lib/apt/lists/* && \
rm -f cloudflared.deb && \
apt --yes autoremove

View File

@@ -5,9 +5,10 @@ version: '3.3'
services:
uptime-kuma:
image: louislam/uptime-kuma
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./uptime-kuma:/app/data
- ./uptime-kuma-data:/app/data
ports:
- 3001:3001
- 3001:3001 # <Host Port>:<Container Port>
restart: always

View File

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

View File

@@ -0,0 +1,66 @@
const pkg = require("../../package.json");
const fs = require("fs");
const childProcess = require("child_process");
const util = require("../../src/util");
util.polyfill();
const version = process.env.VERSION;
console.log("Beta Version: " + version);
if (!version || !version.includes("-beta.")) {
console.error("invalid version, beta version only");
process.exit(1);
}
const exists = tagExists(version);
if (! exists) {
// Process package.json
pkg.version = version;
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
// Also update package-lock.json
childProcess.spawnSync("npm", [ "install" ]);
commit(version);
tag(version);
} else {
console.log("version tag exists, please delete the tag or use another tag");
process.exit(1);
}
function commit(version) {
let msg = "Update to " + version;
let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]);
let stdout = res.stdout.toString().trim();
console.log(stdout);
if (stdout.includes("no changes added to commit")) {
throw new Error("commit error");
}
res = childProcess.spawnSync("git", [ "push", "origin", "master" ]);
console.log(res.stdout.toString().trim());
}
function tag(version) {
let res = childProcess.spawnSync("git", [ "tag", version ]);
console.log(res.stdout.toString().trim());
res = childProcess.spawnSync("git", [ "push", "origin", version ]);
console.log(res.stdout.toString().trim());
}
function tagExists(version) {
if (! version) {
throw new Error("invalid version");
}
let res = childProcess.spawnSync("git", [ "tag", "-l", version ]);
return res.stdout.toString().trim() === version;
}

View File

@@ -29,7 +29,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
labels: ["invalid-format"]
labels: [ "invalid-format" ]
});
// Add the issue closing comment

View File

@@ -0,0 +1,44 @@
//
const http = require("https"); // or 'https' for https:// URLs
const fs = require("fs");
const platform = process.argv[2];
if (!platform) {
console.error("No platform??");
process.exit(1);
}
let arch = null;
if (platform === "linux/amd64") {
arch = "amd64";
} else if (platform === "linux/arm64") {
arch = "arm64";
} else if (platform === "linux/arm/v7") {
arch = "arm";
} else {
console.error("Invalid platform?? " + platform);
}
const file = fs.createWriteStream("cloudflared.deb");
get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb");
function get(url) {
http.get(url, function (res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
console.log("Redirect to " + res.headers.location);
get(res.headers.location);
} else if (res.statusCode >= 200 && res.statusCode < 300) {
res.pipe(file);
res.on("end", function () {
console.log("Downloaded");
});
} else {
console.error(res.statusCode);
process.exit(1);
}
});
}

View File

@@ -4,6 +4,7 @@ const tar = require("tar");
const packageJSON = require("../package.json");
const fs = require("fs");
const rmSync = require("./fs-rmSync.js");
const version = packageJSON.version;
const filename = "dist.tar.gz";
@@ -11,6 +12,12 @@ const filename = "dist.tar.gz";
const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`;
download(url);
/**
* Downloads the latest version of the dist from a GitHub release.
* @param {string} url The URL to download from.
*
* Generated by Trelent
*/
function download(url) {
console.log(url);
@@ -21,7 +28,7 @@ function download(url) {
if (fs.existsSync("./dist")) {
if (fs.existsSync("./dist-backup")) {
fs.rmdirSync("./dist-backup", {
rmSync("./dist-backup", {
recursive: true
});
}
@@ -35,7 +42,7 @@ function download(url) {
tarStream.on("close", () => {
if (fs.existsSync("./dist-backup")) {
fs.rmdirSync("./dist-backup", {
rmSync("./dist-backup", {
recursive: true
});
}

19
extra/env2arg.js Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env node
const childProcess = require("child_process");
let env = process.env;
let cmd = process.argv[2];
let args = process.argv.slice(3);
let replacedArgs = [];
for (let arg of args) {
for (let key in env) {
arg = arg.replaceAll(`$${key}`, env[key]);
}
replacedArgs.push(arg);
}
let child = childProcess.spawn(cmd, replacedArgs);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

23
extra/fs-rmSync.js Normal file
View File

@@ -0,0 +1,23 @@
const fs = require("fs");
/**
* Detect if `fs.rmSync` is available
* to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16,
* or the `recursive` property removing completely in the future Node.js version.
* See the link below.
*
* @todo Once we drop the support for Node.js v14 (or at least versions before v14.14.0), we can safely replace this function with `fs.rmSync`, since `fs.rmSync` was add in Node.js v14.14.0 and currently we supports all the Node.js v14 versions that include the versions before the v14.14.0, and this function have almost the same signature with `fs.rmSync`.
* @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation infomation of `fs.rmdirSync`
* @link https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options the document of `fs.rmSync`
* @param {fs.PathLike} path Valid types for path values in "fs".
* @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`.
*/
const rmSync = (path, options) => {
if (typeof fs.rmSync === "function") {
if (options.recursive) {
options.force = true;
}
return fs.rmSync(path, options);
}
return fs.rmdirSync(path, options);
};
module.exports = rmSync;

View File

@@ -189,7 +189,7 @@ if (type == "local") {
bash("check=$(pm2 --version)");
if (check == "") {
println("Installing PM2");
bash("npm install pm2 -g");
bash("npm install pm2 -g && pm2 install pm2-logrotate");
bash("pm2 startup");
}

View File

@@ -4,21 +4,21 @@ const util = require("../src/util");
util.polyfill();
const oldVersion = pkg.version
const newVersion = oldVersion + "-nightly"
const oldVersion = pkg.version;
const newVersion = oldVersion + "-nightly";
console.log("Old Version: " + oldVersion)
console.log("New Version: " + newVersion)
console.log("Old Version: " + oldVersion);
console.log("New Version: " + newVersion);
if (newVersion) {
// 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)
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n")
pkg.version = newVersion;
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion);
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
// Process README.md
if (fs.existsSync("README.md")) {
fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion))
fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion));
}
}

6
extra/press-any-key.js Normal file
View File

@@ -0,0 +1,6 @@
console.log("Git Push and Publish the release note on github, then press any key to continue");
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on("data", process.exit.bind(process, 0));

View File

@@ -1,11 +1,10 @@
console.log("== Uptime Kuma Reset Password Tool ==");
console.log("Loading the database");
const Database = require("../server/database");
const { R } = require("redbean-node");
const readline = require("readline");
const { initJWTSecret } = require("../server/util-server");
const User = require("../server/model/user");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
@@ -13,8 +12,9 @@ const rl = readline.createInterface({
});
const main = async () => {
console.log("Connecting the database");
Database.init(args);
await Database.connect();
await Database.connect(false, false, true);
try {
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
@@ -31,7 +31,7 @@ const main = async () => {
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await user.resetPassword(password);
await User.resetPassword(user.id, password);
// Reset all sessions by reset jwt secret
await initJWTSecret();

View File

@@ -26,7 +26,7 @@ server.on("request", (request, send, rinfo) => {
ttl: 300,
address: "1.2.3.4"
});
} if (question.type === Packet.TYPE.AAAA) {
} else if (question.type === Packet.TYPE.AAAA) {
response.answers.push({
name: question.name,
type: question.type,

View File

@@ -0,0 +1,50 @@
const { log } = require("../src/util");
const mqttUsername = "louis1";
const mqttPassword = "!@#$LLam";
class SimpleMqttServer {
aedes = require("aedes")();
server = require("net").createServer(this.aedes.handle);
constructor(port) {
this.port = port;
}
start() {
this.server.listen(this.port, () => {
console.log("server started and listening on port ", this.port);
});
}
}
let server1 = new SimpleMqttServer(10000);
server1.aedes.authenticate = function (client, username, password, callback) {
if (username && password) {
console.log(password.toString("utf-8"));
callback(null, username === mqttUsername && password.toString("utf-8") === mqttPassword);
} else {
callback(null, false);
}
};
server1.aedes.on("subscribe", (subscriptions, client) => {
console.log(subscriptions);
for (let s of subscriptions) {
if (s.topic === "test") {
server1.aedes.publish({
topic: "test",
payload: Buffer.from("ok"),
}, (error) => {
if (error) {
log.error("mqtt_server", error);
}
});
}
}
});
server1.start();

View File

@@ -3,6 +3,7 @@
import fs from "fs";
import path from "path";
import util from "util";
import rmSync from "../fs-rmSync.js";
// https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js
/**
@@ -30,7 +31,7 @@ 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 });
rmSync("./languages", { recursive: true });
}
copyRecursiveSync("../../src/languages", "./languages");
@@ -40,7 +41,7 @@ const files = fs.readdirSync("./languages");
console.log("Files:", files);
for (const file of files) {
if (!file.endsWith(".js")) {
if (! file.endsWith(".js")) {
console.log("Skipping " + file);
continue;
}
@@ -82,5 +83,5 @@ for (const file of files) {
fs.writeFileSync(`../../src/languages/${file}`, code);
}
fs.rmdirSync("./languages", { recursive: true });
rmSync("./languages", { recursive: true });
console.log("Done. Fixing formatting by ESLint...");

View File

@@ -1,14 +1,12 @@
const pkg = require("../package.json");
const fs = require("fs");
const child_process = require("child_process");
const childProcess = require("child_process");
const util = require("../src/util");
util.polyfill();
const oldVersion = pkg.version;
const newVersion = process.argv[2];
const newVersion = process.env.VERSION;
console.log("Old Version: " + oldVersion);
console.log("New Version: " + newVersion);
if (! newVersion) {
@@ -22,25 +20,31 @@ 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);
// Replace the version: https://regex101.com/r/hmj2Bc/1
pkg.scripts.setup = pkg.scripts.setup.replace(/(git checkout )([^\s]+)/, `$1${newVersion}`);
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
// Also update package-lock.json
childProcess.spawnSync("npm", [ "install" ]);
commit(newVersion);
tag(newVersion);
updateWiki(oldVersion, newVersion);
} else {
console.log("version exists");
}
/**
* Updates the version number in package.json and commits it to git.
* @param {string} version - The new version number
*
* Generated by Trelent
*/
function commit(version) {
let msg = "update to " + version;
let msg = "Update to " + version;
let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]);
let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]);
let stdout = res.stdout.toString().trim();
console.log(stdout);
@@ -50,51 +54,22 @@ function commit(version) {
}
function tag(version) {
let res = child_process.spawnSync("git", ["tag", version]);
let res = childProcess.spawnSync("git", [ "tag", version ]);
console.log(res.stdout.toString().trim());
}
/**
* Checks if a given version is already tagged in the git repository.
* @param {string} version - The version to check for.
*
* Generated by Trelent
*/
function tagExists(version) {
if (! version) {
throw new Error("invalid version");
}
let res = child_process.spawnSync("git", ["tag", "-l", version]);
let res = childProcess.spawnSync("git", [ "tag", "-l", 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,48 @@
const childProcess = require("child_process");
const fs = require("fs");
const newVersion = process.env.VERSION;
if (!newVersion) {
console.log("Missing version");
process.exit(1);
}
updateWiki(newVersion);
function updateWiki(newVersion) {
const wikiDir = "./tmp/wiki";
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
safeDelete(wikiDir);
childProcess.spawnSync("git", [ "clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir ]);
let content = fs.readFileSync(howToUpdateFilename).toString();
// Replace the version: https://regex101.com/r/hmj2Bc/1
content = content.replace(/(git checkout )([^\s]+)/, `$1${newVersion}`);
fs.writeFileSync(howToUpdateFilename, content);
childProcess.spawnSync("git", [ "add", "-A" ], {
cwd: wikiDir,
});
childProcess.spawnSync("git", [ "commit", "-m", `Update to ${newVersion}` ], {
cwd: wikiDir,
});
console.log("Pushing to Github");
childProcess.spawnSync("git", [ "push" ], {
cwd: wikiDir,
});
safeDelete(wikiDir);
}
function safeDelete(dir) {
if (fs.existsSync(dir)) {
fs.rmdirSync(dir, {
recursive: true,
});
}
}

View File

@@ -159,7 +159,7 @@ fi
check=$(pm2 --version)
if [ "$check" == "" ]; then
"echo" "-e" "Installing PM2"
npm install pm2 -g
npm install pm2 -g && pm2 install pm2-logrotate
pm2 startup
fi
mkdir -p $installPath

17005
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.11.4",
"version": "1.17.0-beta.1",
"license": "MIT",
"repository": {
"type": "git",
@@ -10,12 +10,15 @@
"node": "14.* || >=16.*"
},
"scripts": {
"install-legacy": "npm install --legacy-peer-deps",
"update-legacy": "npm update --legacy-peer-deps",
"install-legacy": "npm install",
"update-legacy": "npm update",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore",
"lint-fix:style": "stylelint \"**/*.{vue,css,scss}\" --fix --ignore-path .gitignore",
"lint": "npm run lint:js && npm run lint:style",
"dev": "vite --host --config ./config/vite.config.js",
"dev": "concurrently -k -r \"wait-on tcp:3000 && npm run start-server-dev \" \"npm run start-frontend-dev\"",
"start-frontend-dev": "cross-env NODE_ENV=development vite --host --config ./config/vite.config.js",
"start": "npm run start-server",
"start-server": "node server/server.js",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js",
@@ -30,15 +33,14 @@
"build-docker": "npm run build && 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.11.4-alpine --target release . --push",
"build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.4 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.4-debian --target release . --push",
"build-docker-alpine": "node ./extra/env2arg.js 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:$VERSION-alpine --target release . --push",
"build-docker-debian": "node ./extra/env2arg.js 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:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push",
"build-docker-nightly": "npm run build && 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 VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.11.4 && npm ci --production && npm run download-dist",
"setup": "git checkout 1.16.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",
"remove-2fa": "node extra/remove-2fa.js",
@@ -49,48 +51,69 @@
"test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
"test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
"simple-dns-server": "node extra/simple-dns-server.js",
"simple-mqtt-server": "node extra/simple-mqtt-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",
"ncu-patch": "ncu -u -t patch"
"ncu-patch": "npm-check-updates -u -t patch",
"release-final": "node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js",
"release-beta": "node extra/beta/update-version.js && npm run build && node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:beta . --target release --push && node ./extra/press-any-key.js && npm run upload-artifacts",
"git-remove-tag": "git tag -d",
"build-dist-and-restart": "npm run build && npm run start-server-dev"
},
"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-5",
"@louislam/sqlite3": "~6.0.1",
"@louislam/sqlite3": "~15.0.6",
"@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0",
"axios": "~0.21.4",
"axios": "~0.26.1",
"axios-cached-dns-resolve": "^3.0.6",
"axios-ntlm": "^1.3.0",
"badge-maker": "^3.3.1",
"bcryptjs": "~2.4.3",
"bootstrap": "5.1.3",
"bree": "~7.1.0",
"bree": "~7.1.5",
"chardet": "^1.3.0",
"chart.js": "~3.6.0",
"chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0",
"check-password-strength": "^2.0.3",
"check-password-strength": "^2.0.5",
"cheerio": "^1.0.0-rc.10",
"chroma-js": "^2.1.2",
"command-exists": "~1.2.9",
"compare-versions": "~3.6.0",
"dayjs": "~1.10.7",
"express": "~4.17.1",
"express-basic-auth": "~1.2.0",
"compression": "^1.7.4",
"dayjs": "^1.11.0",
"esm-wallaby": "^3.2.26",
"express": "~4.17.3",
"express-basic-auth": "~1.2.1",
"express-static-gzip": "^2.1.7",
"favico.js": "^0.3.10",
"form-data": "~4.0.0",
"http-graceful-shutdown": "~3.1.5",
"http-graceful-shutdown": "~3.1.7",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"iconv-lite": "^0.6.3",
"jsonwebtoken": "~8.5.1",
"jwt-decode": "^3.1.2",
"limiter": "^2.1.0",
"mqtt": "^4.2.8",
"mssql": "^8.1.0",
"node-cloudflared-tunnel": "~1.0.9",
"nodemailer": "~6.6.5",
"notp": "~2.0.3",
"password-hash": "~1.2.2",
"postcss-rtlcss": "~3.4.1",
"postcss-scss": "~4.0.2",
"postcss-scss": "~4.0.3",
"prismjs": "^1.27.0",
"prom-client": "~13.2.0",
"prometheus-api-metrics": "~3.2.0",
"prometheus-api-metrics": "~3.2.1",
"qrcode": "~1.5.0",
"redbean-node": "0.1.3",
"redbean-node": "0.1.4",
"socket.io": "~4.4.1",
"socket.io-client": "~4.4.1",
"socks-proxy-agent": "^6.1.1",
"tar": "^6.1.11",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
@@ -103,32 +126,42 @@
"vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2",
"vue-prism-editor": "^2.0.0-alpha.2",
"vue-qrcode": "~1.0.0",
"vue-router": "~4.0.12",
"vue-router": "~4.0.14",
"vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0"
},
"devDependencies": {
"@actions/github": "~5.0.0",
"@babel/eslint-parser": "~7.15.8",
"@actions/github": "~5.0.1",
"@babel/eslint-parser": "~7.17.0",
"@babel/preset-env": "^7.15.8",
"@types/bootstrap": "~5.1.6",
"@vitejs/plugin-legacy": "~1.6.3",
"@vitejs/plugin-vue": "~1.9.4",
"@vue/compiler-sfc": "~3.2.22",
"@types/bootstrap": "~5.1.9",
"@vitejs/plugin-legacy": "~1.8.2",
"@vitejs/plugin-vue": "~2.3.3",
"@vue/compiler-sfc": "~3.2.36",
"aedes": "^0.46.3",
"babel-plugin-rewire": "~1.2.0",
"concurrently": "^7.1.0",
"core-js": "~3.18.3",
"cross-env": "~7.0.3",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"eslint": "~7.32.0",
"eslint-plugin-vue": "~7.18.0",
"eslint": "~8.14.0",
"eslint-plugin-vue": "~8.7.1",
"jest": "~27.2.5",
"jest-puppeteer": "~6.0.0",
"jest-puppeteer": "~6.0.3",
"lru-cache": "^7.7.1",
"npm-check-updates": "^12.5.9",
"postcss-html": "^1.3.1",
"puppeteer": "~13.1.3",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "~1.42.1",
"stylelint": "~14.2.0",
"stylelint-config-standard": "~24.0.0",
"stylelint": "~14.7.1",
"stylelint-config-standard": "~25.0.0",
"typescript": "~4.4.4",
"vite": "~2.6.14"
"vite": "~2.9.9",
"vite-plugin-compression": "^0.5.1",
"wait-on": "^6.0.1"
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -1,8 +1,12 @@
const { checkLogin } = require("./util-server");
const { R } = require("redbean-node");
class TwoFA {
/**
* Disable 2FA for specified user
* @param {number} userID ID of user to disable
* @returns {Promise<void>}
*/
static async disable2FA(userID) {
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
userID,

View File

@@ -2,16 +2,19 @@ const basicAuth = require("express-basic-auth");
const passwordHash = require("./password-hash");
const { R } = require("redbean-node");
const { setting } = require("./util-server");
const { debug } = require("../src/util");
const { loginRateLimiter } = require("./rate-limiter");
/**
*
* @param username : string
* @param password : string
* @returns {Promise<Bean|null>}
* Login to web app
* @param {string} username
* @param {string} password
* @returns {Promise<(Bean|null)>}
*/
exports.login = async function (username, password) {
if (typeof username !== "string" || typeof password !== "string") {
return null;
}
let user = await R.findOne("user", " username = ? AND active = 1 ", [
username,
]);
@@ -30,32 +33,48 @@ exports.login = async function (username, password) {
return null;
};
function myAuthorizer(username, password, callback) {
setting("disableAuth").then((result) => {
if (result) {
callback(null, true);
} else {
// Login Rate Limit
loginRateLimiter.pass(null, 0).then((pass) => {
if (pass) {
exports.login(username, password).then((user) => {
callback(null, user != null);
/**
* Callback for myAuthorizer
* @callback myAuthorizerCB
* @param {any} err Any error encountered
* @param {boolean} authorized Is the client authorized?
*/
if (user == null) {
loginRateLimiter.removeTokens(1);
}
});
} else {
callback(null, false);
/**
* Custom authorizer for express-basic-auth
* @param {string} username
* @param {string} password
* @param {myAuthorizerCB} callback
*/
function myAuthorizer(username, password, callback) {
// Login Rate Limit
loginRateLimiter.pass(null, 0).then((pass) => {
if (pass) {
exports.login(username, password).then((user) => {
callback(null, user != null);
if (user == null) {
loginRateLimiter.removeTokens(1);
}
});
} else {
callback(null, false);
}
});
}
exports.basicAuth = basicAuth({
authorizer: myAuthorizer,
authorizeAsync: true,
challenge: true,
});
exports.basicAuth = async function (req, res, next) {
const middleware = basicAuth({
authorizer: myAuthorizer,
authorizeAsync: true,
challenge: true,
});
const disabledAuth = await setting("disableAuth");
if (!disabledAuth) {
middleware(req, res, next);
} else {
next();
}
};

View File

@@ -1,11 +1,13 @@
const { setSetting } = require("./util-server");
const { setSetting, setting } = require("./util-server");
const axios = require("axios");
const compareVersions = require("compare-versions");
exports.version = require("../package.json").version;
exports.latestVersion = null;
let interval;
/** Start 48 hour check interval */
exports.startInterval = () => {
let check = async () => {
try {
@@ -16,6 +18,19 @@ exports.startInterval = () => {
res.data.slow = "1000.0.0";
}
if (await setting("checkUpdate") === false) {
return;
}
let checkBeta = await setting("checkBeta");
if (checkBeta && res.data.beta) {
if (compareVersions.compare(res.data.beta, res.data.beta, ">")) {
exports.latestVersion = res.data.beta;
return;
}
}
if (res.data.slow) {
exports.latestVersion = res.data.slow;
}
@@ -28,6 +43,11 @@ exports.startInterval = () => {
interval = setInterval(check, 3600 * 1000 * 48);
};
/**
* Enable the check update feature
* @param {boolean} value Should the check update feature be enabled?
* @returns {Promise<void>}
*/
exports.enableCheckUpdate = async (value) => {
await setSetting("checkUpdate", value);

View File

@@ -3,10 +3,16 @@
*/
const { TimeLogger } = require("../src/util");
const { R } = require("redbean-node");
const { io } = require("./server");
const { UptimeKumaServer } = require("./uptime-kuma-server");
const io = UptimeKumaServer.getInstance().io;
const { setting } = require("./util-server");
const checkVersion = require("./check-version");
/**
* Send list of notification providers to client
* @param {Socket} socket Socket.io socket instance
* @returns {Promise<Bean[]>}
*/
async function sendNotificationList(socket) {
const timeLogger = new TimeLogger();
@@ -16,7 +22,10 @@ async function sendNotificationList(socket) {
]);
for (let bean of list) {
result.push(bean.export());
let notificationObject = bean.export();
notificationObject.isDefault = (notificationObject.isDefault === 1);
notificationObject.active = (notificationObject.active === 1);
result.push(notificationObject);
}
io.to(socket.userID).emit("notificationList", result);
@@ -28,8 +37,11 @@ async function sendNotificationList(socket) {
/**
* 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
* @param {Socket} socket Socket.io instance
* @param {number} monitorID ID of monitor to send heartbeat history
* @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list
* @returns {Promise<void>}
*/
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
const timeLogger = new TimeLogger();
@@ -55,11 +67,12 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite =
}
/**
* 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
* Important Heart beat list (aka event list)
* @param {Socket} socket Socket.io instance
* @param {number} monitorID ID of monitor to send heartbeat history
* @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only
* @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list
* @returns {Promise<void>}
*/
async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
const timeLogger = new TimeLogger();
@@ -83,6 +96,27 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
}
/**
* Emit proxy list to client
* @param {Socket} socket Socket.io socket instance
* @return {Promise<Bean[]>}
*/
async function sendProxyList(socket) {
const timeLogger = new TimeLogger();
const list = await R.find("proxy", " user_id = ? ", [ socket.userID ]);
io.to(socket.userID).emit("proxyList", list.map(bean => bean.export()));
timeLogger.print("Send Proxy List");
return list;
}
/**
* Emits the version information to the client.
* @param {Socket} socket Socket.io socket instance
* @returns {Promise<void>}
*/
async function sendInfo(socket) {
socket.emit("info", {
version: checkVersion.version,
@@ -95,6 +129,6 @@ module.exports = {
sendNotificationList,
sendImportantHeartbeatList,
sendHeartbeatList,
sendInfo
sendProxyList,
sendInfo,
};

View File

@@ -1,7 +1,20 @@
const args = require("args-parser")(process.argv);
const demoMode = args["demo"] || false;
const badgeConstants = {
naColor: "#999",
defaultUpColor: "#66c20a",
defaultDownColor: "#c2290a",
defaultPingColor: "blue", // as defined by badge-maker / shields.io
defaultStyle: "flat",
defaultPingValueSuffix: "ms",
defaultPingLabelSuffix: "h",
defaultUptimeValueSuffix: "%",
defaultUptimeLabelSuffix: "h",
};
module.exports = {
args,
demoMode
demoMode,
badgeConstants,
};

View File

@@ -1,7 +1,7 @@
const fs = require("fs");
const { R } = require("redbean-node");
const { setSetting, setting } = require("./util-server");
const { debug, sleep } = require("../src/util");
const { log, sleep } = require("../src/util");
const dayjs = require("dayjs");
const knex = require("knex");
@@ -53,7 +53,14 @@ class Database {
"patch-2fa-invalidate-used-token.sql": true,
"patch-notification_sent_history.sql": true,
"patch-monitor-basic-auth.sql": true,
}
"patch-status-page.sql": true,
"patch-proxy.sql": true,
"patch-monitor-expiry-notification.sql": true,
"patch-status-page-footer-css.sql": true,
"patch-added-mqtt-monitor.sql": true,
"patch-add-sqlserver-monitor.sql": true,
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
};
/**
* The final version should be 10 after merged tag feature
@@ -63,6 +70,10 @@ class Database {
static noReject = true;
/**
* Initialize the database
* @param {Object} args Arguments to initialize DB with
*/
static init(args) {
// Data Directory (must be end with "/")
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
@@ -77,10 +88,19 @@ class Database {
fs.mkdirSync(Database.uploadDir, { recursive: true });
}
console.log(`Data Dir: ${Database.dataDir}`);
log.info("db", `Data Dir: ${Database.dataDir}`);
}
static async connect(testMode = false) {
/**
* Connect to the database
* @param {boolean} [testMode=false] Should the connection be
* started in test mode?
* @param {boolean} [autoloadModels=true] Should models be
* automatically loaded?
* @param {boolean} [noLog=false] Should logs not be output?
* @returns {Promise<void>}
*/
static async connect(testMode = false, autoloadModels = true, noLog = false) {
const acquireConnectionTimeout = 120 * 1000;
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
@@ -110,7 +130,10 @@ class Database {
// Auto map the model to a bean object
R.freeze(true);
await R.autoloadModels("./server/model");
if (autoloadModels) {
await R.autoloadModels("./server/model");
}
await R.exec("PRAGMA foreign_keys = ON");
if (testMode) {
@@ -123,12 +146,20 @@ class Database {
await R.exec("PRAGMA cache_size = -12000");
await R.exec("PRAGMA auto_vacuum = FULL");
console.log("SQLite config:");
console.log(await R.getAll("PRAGMA journal_mode"));
console.log(await R.getAll("PRAGMA cache_size"));
console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()"));
// This ensures that an operating system crash or power failure will not corrupt the database.
// FULL synchronous is very safe, but it is also slower.
// Read more: https://sqlite.org/pragma.html#pragma_synchronous
await R.exec("PRAGMA synchronous = FULL");
if (!noLog) {
log.info("db", "SQLite config:");
log.info("db", await R.getAll("PRAGMA journal_mode"));
log.info("db", await R.getAll("PRAGMA cache_size"));
log.info("db", "SQLite Version: " + await R.getCell("SELECT sqlite_version()"));
}
}
/** Patch the database */
static async patch() {
let version = parseInt(await setting("database_version"));
@@ -136,15 +167,15 @@ class Database {
version = 0;
}
console.info("Your database version: " + version);
console.info("Latest database version: " + this.latestVersion);
log.info("db", "Your database version: " + version);
log.info("db", "Latest database version: " + this.latestVersion);
if (version === this.latestVersion) {
console.info("Database patch not needed");
log.info("db", "Database patch not needed");
} else if (version > this.latestVersion) {
console.info("Warning: Database version is newer than expected");
log.info("db", "Warning: Database version is newer than expected");
} else {
console.info("Database patch is needed");
log.info("db", "Database patch is needed");
this.backup(version);
@@ -152,17 +183,17 @@ class Database {
try {
for (let i = version + 1; i <= this.latestVersion; i++) {
const sqlFile = `./db/patch${i}.sql`;
console.info(`Patching ${sqlFile}`);
log.info("db", `Patching ${sqlFile}`);
await Database.importSQLFile(sqlFile);
console.info(`Patched ${sqlFile}`);
log.info("db", `Patched ${sqlFile}`);
await setSetting("database_version", i);
}
} catch (ex) {
await Database.close();
console.error(ex);
console.error("Start Uptime-Kuma failed due to issue patching the database");
console.error("Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
log.error("db", ex);
log.error("db", "Start Uptime-Kuma failed due to issue patching the database");
log.error("db", "Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
this.restore();
process.exit(1);
@@ -170,22 +201,25 @@ class Database {
}
await this.patch2();
await this.migrateNewStatusPage();
}
/**
* Patch DB using new process
* Call it from patch() only
* @private
* @returns {Promise<void>}
*/
static async patch2() {
console.log("Database Patch 2.0 Process");
log.info("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) {
databasePatchedFiles = {};
}
debug("Patched files:");
debug(databasePatchedFiles);
log.debug("db", "Patched files:");
log.debug("db", databasePatchedFiles);
try {
for (let sqlFilename in this.patchList) {
@@ -193,15 +227,15 @@ class Database {
}
if (this.patched) {
console.log("Database Patched Successfully");
log.info("db", "Database Patched Successfully");
}
} catch (ex) {
await Database.close();
console.error(ex);
console.error("Start Uptime-Kuma failed due to issue patching the database");
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
log.error("db", ex);
log.error("db", "Start Uptime-Kuma failed due to issue patching the database");
log.error("db", "Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues");
this.restore();
@@ -212,24 +246,95 @@ class Database {
}
/**
* Migrate status page value in setting to "status_page" table
* @returns {Promise<void>}
*/
static async migrateNewStatusPage() {
// Fix 1.13.0 empty slug bug
await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''");
let title = await setting("title");
if (title) {
console.log("Migrating Status Page");
let statusPageCheck = await R.findOne("status_page", " slug = 'default' ");
if (statusPageCheck !== null) {
console.log("Migrating Status Page - Skip, default slug record is already existing");
return;
}
let statusPage = R.dispense("status_page");
statusPage.slug = "default";
statusPage.title = title;
statusPage.description = await setting("description");
statusPage.icon = await setting("icon");
statusPage.theme = await setting("statusPageTheme");
statusPage.published = !!await setting("statusPagePublished");
statusPage.search_engine_index = !!await setting("searchEngineIndex");
statusPage.show_tags = !!await setting("statusPageTags");
statusPage.password = null;
if (!statusPage.title) {
statusPage.title = "My Status Page";
}
if (!statusPage.icon) {
statusPage.icon = "";
}
if (!statusPage.theme) {
statusPage.theme = "light";
}
let id = await R.store(statusPage);
await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [
id
]);
await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [
id
]);
await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
// Migrate Entry Page if it is status page
let entryPage = await setting("entryPage");
if (entryPage === "statusPage") {
await setSetting("entryPage", "statusPage-default", "general");
}
console.log("Migrating Status Page - Done");
}
}
/**
* Patch database using new patching process
* Used it patch2() only
* @private
* @param sqlFilename
* @param databasePatchedFiles
* @returns {Promise<void>}
*/
static async patch2Recursion(sqlFilename, databasePatchedFiles) {
let value = this.patchList[sqlFilename];
if (! value) {
console.log(sqlFilename + " skip");
log.info("db", sqlFilename + " skip");
return;
}
// Check if patched
if (! databasePatchedFiles[sqlFilename]) {
console.log(sqlFilename + " is not patched");
log.info("db", sqlFilename + " is not patched");
if (value.parents) {
console.log(sqlFilename + " need parents");
log.info("db", sqlFilename + " need parents");
for (let parentSQLFilename of value.parents) {
await this.patch2Recursion(parentSQLFilename, databasePatchedFiles);
}
@@ -237,24 +342,24 @@ class Database {
this.backup(dayjs().format("YYYYMMDDHHmmss"));
console.log(sqlFilename + " is patching");
log.info("db", sqlFilename + " is patching");
this.patched = true;
await this.importSQLFile("./db/" + sqlFilename);
databasePatchedFiles[sqlFilename] = true;
console.log(sqlFilename + " was patched successfully");
log.info("db", sqlFilename + " was patched successfully");
} else {
debug(sqlFilename + " is already patched, skip");
log.debug("db", sqlFilename + " is already patched, skip");
}
}
/**
* Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself
* @param filename
* Load an SQL file and execute it
* @param filename Filename of SQL file to import
* @returns {Promise<void>}
*/
static async importSQLFile(filename) {
// Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself
await R.getCell("SELECT 1");
let text = fs.readFileSync(filename).toString();
@@ -282,6 +387,10 @@ class Database {
}
}
/**
* Aquire a direct connection to database
* @returns {any}
*/
static getBetterSQLite3Database() {
return R.knex.client.acquireConnection();
}
@@ -296,7 +405,7 @@ class Database {
};
process.addListener("unhandledRejection", listener);
console.log("Closing the database");
log.info("db", "Closing the database");
while (true) {
Database.noReject = true;
@@ -306,10 +415,10 @@ class Database {
if (Database.noReject) {
break;
} else {
console.log("Waiting to close the database");
log.info("db", "Waiting to close the database");
}
}
console.log("SQLite closed");
log.info("db", "SQLite closed");
process.removeListener("unhandledRejection", listener);
}
@@ -317,11 +426,11 @@ class Database {
/**
* One backup one time in this process.
* Reset this.backupPath if you want to backup again
* @param version
* @param {string} version Version code of backup
*/
static backup(version) {
if (! this.backupPath) {
console.info("Backing up the database");
log.info("db", "Backing up the database");
this.backupPath = this.dataDir + "kuma.db.bak" + version;
fs.copyFileSync(Database.path, this.backupPath);
@@ -339,12 +448,10 @@ class Database {
}
}
/**
*
*/
/** Restore from most recent backup */
static restore() {
if (this.backupPath) {
console.error("Patching the database failed!!! Restoring the backup");
log.error("db", "Patching the database failed!!! Restoring the backup");
const shmPath = Database.path + "-shm";
const walPath = Database.path + "-wal";
@@ -363,7 +470,7 @@ class Database {
fs.unlinkSync(walPath);
}
} catch (e) {
console.log("Restore failed; you may need to restore the backup manually");
log.error("db", "Restore failed; you may need to restore the backup manually");
process.exit(1);
}
@@ -379,17 +486,22 @@ class Database {
}
} else {
console.log("Nothing to restore");
log.info("db", "Nothing to restore");
}
}
/** Get the size of the database */
static getSize() {
debug("Database.getSize()");
log.debug("db", "Database.getSize()");
let stats = fs.statSync(Database.path);
debug(stats);
log.debug("db", stats);
return stats.size;
}
/**
* Shrink the database
* @returns {Promise<void>}
*/
static async shrink() {
await R.exec("VACUUM");
}

View File

@@ -3,12 +3,21 @@
Modified with 0 dependencies
*/
let fs = require("fs");
const { log } = require("../src/util");
let ImageDataURI = (() => {
/**
* Decode the data:image/ URI
* @param {string} dataURI data:image/ URI to decode
* @returns {?Object} An object with properties "imageType" and "dataBase64".
* The former is the image type, e.g., "png", and the latter is a base64
* encoded string of the image's binary data. If it fails to parse, returns
* null instead of an object.
*/
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/\"");
log.error("image-data-uri", "It seems that it is not an Image Data URI. Couldn't match \"data:image/\"");
return null;
}
@@ -20,9 +29,16 @@ let ImageDataURI = (() => {
};
}
/**
* Endcode an image into data:image/ URI
* @param {(Buffer|string)} data Data to encode
* @param {string} mediaType Media type of data
* @returns {(string|null)} A string representing the base64-encoded
* version of the given Buffer object or null if an error occurred.
*/
function encode(data, mediaType) {
if (!data || !mediaType) {
console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType ");
log.error("image-data-uri", "Missing some of the required params: data, mediaType");
return null;
}
@@ -33,6 +49,12 @@ let ImageDataURI = (() => {
return dataImgBase64;
}
/**
* Write data URI to file
* @param {string} dataURI data:image/ URI
* @param {string} [filePath] Path to write file to
* @returns {Promise<string>}
*/
function outputFile(dataURI, filePath) {
filePath = filePath || "./";
return new Promise((resolve, reject) => {

View File

@@ -1,7 +1,8 @@
const path = require("path");
const Bree = require("bree");
const { SHARE_ENV } = require("worker_threads");
const { log } = require("../src/util");
let bree;
const jobs = [
{
name: "clear-old-data",
@@ -9,8 +10,13 @@ const jobs = [
},
];
/**
* Initialize background jobs
* @param {Object} args Arguments to pass to workers
* @returns {Bree}
*/
const initBackgroundJobs = function (args) {
const bree = new Bree({
bree = new Bree({
root: path.resolve("server", "jobs"),
jobs,
worker: {
@@ -18,7 +24,7 @@ const initBackgroundJobs = function (args) {
workerData: args,
},
workerMessageHandler: (message) => {
console.log("[Background Job]:", message);
log.info("jobs", message);
}
});
@@ -26,6 +32,13 @@ const initBackgroundJobs = function (args) {
return bree;
};
module.exports = {
initBackgroundJobs
const stopBackgroundJobs = function () {
if (bree) {
bree.stop();
}
};
module.exports = {
initBackgroundJobs,
stopBackgroundJobs
};

View File

@@ -30,7 +30,7 @@ const DEFAULT_KEEP_PERIOD = 180;
try {
await R.exec(
"DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ",
[parsedPeriod]
[ parsedPeriod ]
);
} catch (e) {
log(`Failed to clear old data: ${e.message}`);

View File

@@ -2,14 +2,24 @@ const { parentPort, workerData } = require("worker_threads");
const Database = require("../database");
const path = require("path");
/**
* Send message to parent process for logging
* since worker_thread does not have access to stdout, this is used
* instead of console.log()
* @param {any} any The message to log
*/
const log = function (any) {
if (parentPort) {
parentPort.postMessage(any);
}
};
/**
* Exit the worker process
* @param {number} error The status code to exit
*/
const exit = function (error) {
if (error && error != 0) {
if (error && error !== 0) {
process.exit(error);
} else {
if (parentPort) {
@@ -20,6 +30,7 @@ const exit = function (error) {
}
};
/** Connects to the database */
const connectDb = async function () {
const dbPath = path.join(
process.env.DATA_DIR || workerData["data-dir"] || "./data/"

View File

@@ -3,12 +3,18 @@ const { R } = require("redbean-node");
class Group extends BeanModel {
async toPublicJSON() {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @param {boolean} [showTags=false] Should the JSON include monitor tags
* @returns {Object}
*/
async toPublicJSON(showTags = false) {
let monitorBeanList = await this.getMonitorList();
let monitorList = [];
for (let bean of monitorBeanList) {
monitorList.push(await bean.toPublicJSON());
monitorList.push(await bean.toPublicJSON(showTags));
}
return {
@@ -19,6 +25,10 @@ class Group extends BeanModel {
};
}
/**
* Get all monitors
* @returns {Bean[]}
*/
async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(`
SELECT monitor.* FROM monitor, monitor_group

View File

@@ -13,6 +13,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
*/
class Heartbeat extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
*/
toPublicJSON() {
return {
status: this.status,
@@ -22,6 +27,10 @@ class Heartbeat extends BeanModel {
};
}
/**
* Return an object that ready to parse to JSON
* @returns {Object}
*/
toJSON() {
return {
monitorID: this.monitor_id,

View File

@@ -2,6 +2,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
class Incident extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
*/
toPublicJSON() {
return {
id: this.id,

View File

@@ -6,14 +6,22 @@ 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, setting, errorLog } = require("../util-server");
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync, setSetting, httpNtlm } = require("../util-server");
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification");
const { Proxy } = require("../proxy");
const { demoMode } = require("../config");
const version = require("../../package.json").version;
const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const axiosCachedDnsResolve = require("esm-wallaby")(module)("axios-cached-dns-resolve");
// create an axios client instance with the cached DNS resolve interceptor
const axiosClient = axios.create();
axiosCachedDnsResolve.registerInterceptor(axiosClient);
/**
* status:
@@ -24,20 +32,26 @@ const apicache = require("../modules/apicache");
class Monitor extends BeanModel {
/**
* Return a object that ready to parse to JSON for public
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
*/
async toPublicJSON() {
return {
async toPublicJSON(showTags = false) {
let obj = {
id: this.id,
name: this.name,
};
if (showTags) {
obj.tags = await this.getTags();
}
return obj;
}
/**
* Return a object that ready to parse to JSON
* Return an object that ready to parse to JSON
* @returns {Object}
*/
async toJSON() {
async toJSON(includeSensitiveData = true) {
let notificationIDList = {};
@@ -49,17 +63,13 @@ class Monitor extends BeanModel {
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]);
const tags = await this.getTags();
return {
let data = {
id: this.id,
name: this.name,
url: this.url,
method: this.method,
body: this.body,
headers: this.headers,
basic_auth_user: this.basic_auth_user,
basic_auth_pass: this.basic_auth_pass,
hostname: this.hostname,
port: this.port,
maxretries: this.maxretries,
@@ -69,6 +79,7 @@ class Monitor extends BeanModel {
interval: this.interval,
retryInterval: this.retryInterval,
keyword: this.keyword,
expiryNotification: this.isEnabledExpiryNotification(),
ignoreTls: this.getIgnoreTls(),
upsideDown: this.isUpsideDown(),
maxredirects: this.maxredirects,
@@ -76,10 +87,40 @@ 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,
proxyId: this.proxy_id,
notificationIDList,
tags: tags,
mqttUsername: this.mqttUsername,
mqttPassword: this.mqttPassword,
mqttTopic: this.mqttTopic,
mqttSuccessMessage: this.mqttSuccessMessage,
databaseConnectionString: this.databaseConnectionString,
databaseQuery: this.databaseQuery,
authMethod: this.authMethod,
authWorkstation: this.authWorkstation,
authDomain: this.authDomain,
};
if (includeSensitiveData) {
data = {
...data,
headers: this.headers,
body: this.body,
basic_auth_user: this.basic_auth_user,
basic_auth_pass: this.basic_auth_pass,
pushToken: this.pushToken,
};
}
return data;
}
/**
* Get all tags applied to this monitor
* @returns {Promise<LooseObject<any>[]>}
*/
async getTags() {
return 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 ]);
}
/**
@@ -91,6 +132,14 @@ class Monitor extends BeanModel {
return Buffer.from(user + ":" + pass).toString("base64");
}
/**
* Is the TLS expiry notification enabled?
* @returns {boolean}
*/
isEnabledExpiryNotification() {
return Boolean(this.expiryNotification);
}
/**
* Parse to boolean
* @returns {boolean}
@@ -107,10 +156,18 @@ class Monitor extends BeanModel {
return Boolean(this.upsideDown);
}
/**
* Get accepted status codes
* @returns {Object}
*/
getAcceptedStatuscodes() {
return JSON.parse(this.accepted_statuscodes_json);
}
/**
* Start monitor
* @param {Server} io Socket server instance
*/
start(io) {
let previousBeat = null;
let retries = 0;
@@ -119,11 +176,24 @@ class Monitor extends BeanModel {
const beat = async () => {
let beatInterval = this.interval;
if (! beatInterval) {
beatInterval = 1;
}
if (demoMode) {
if (beatInterval < 20) {
console.log("beat interval too low, reset to 20s");
beatInterval = 20;
}
}
// Expose here for prometheus update
// undefined if not https
let tlsInfo = undefined;
if (! previousBeat) {
if (!previousBeat || this.type === "push") {
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
this.id,
]);
@@ -133,7 +203,7 @@ class Monitor extends BeanModel {
let bean = R.dispense("heartbeat");
bean.monitor_id = this.id;
bean.time = R.isoDateTime(dayjs.utc());
bean.time = R.isoDateTimeMillis(dayjs.utc());
bean.status = DOWN;
if (this.isUpsideDown()) {
@@ -141,7 +211,7 @@ class Monitor extends BeanModel {
}
// Duration
if (! isFirstBeat) {
if (!isFirstBeat) {
bean.duration = dayjs(bean.time).diff(dayjs(previousBeat.time), "second");
} else {
bean.duration = 0;
@@ -154,13 +224,18 @@ class Monitor extends BeanModel {
// HTTP basic auth
let basicAuthHeader = {};
if (this.basic_auth_user) {
if (this.auth_method === "basic") {
basicAuthHeader = {
"Authorization": "Basic " + this.encodeBase64(this.basic_auth_user, this.basic_auth_pass),
};
}
debug(`[${this.name}] Prepare Options for axios`);
const httpsAgentOptions = {
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: !this.getIgnoreTls(),
};
log.debug("monitor", `[${this.name}] Prepare Options for axios`);
const options = {
url: this.url,
@@ -173,47 +248,78 @@ class Monitor extends BeanModel {
...(this.headers ? JSON.parse(this.headers) : {}),
...(basicAuthHeader),
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: ! this.getIgnoreTls(),
}),
maxRedirects: this.maxredirects,
validateStatus: (status) => {
return checkStatusCode(status, this.getAcceptedStatuscodes());
},
};
debug(`[${this.name}] Axios Request`);
let res = await axios.request(options);
if (this.proxy_id) {
const proxy = await R.load("proxy", this.proxy_id);
if (proxy && proxy.active) {
const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, {
httpsAgentOptions: httpsAgentOptions,
});
options.proxy = false;
options.httpAgent = httpAgent;
options.httpsAgent = httpsAgent;
}
}
if (!options.httpsAgent) {
options.httpsAgent = new https.Agent(httpsAgentOptions);
}
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
log.debug("monitor", `[${this.name}] Axios Request`);
let res;
if (this.auth_method === "ntlm") {
options.httpsAgent.keepAlive = true;
res = await httpNtlm(options, {
username: this.basic_auth_user,
password: this.basic_auth_pass,
domain: this.authDomain,
workstation: this.authWorkstation ? this.authWorkstation : undefined
});
} else {
res = await axiosClient.request(options);
}
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
// Check certificate if https is used
let certInfoStartTime = dayjs().valueOf();
if (this.getUrl()?.protocol === "https:") {
debug(`[${this.name}] Check cert`);
log.debug("monitor", `[${this.name}] Check cert`);
try {
let tlsInfoObject = checkCertificate(res);
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
if (!this.getIgnoreTls()) {
debug(`[${this.name}] call sendCertNotification`);
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call sendCertNotification`);
await this.sendCertNotification(tlsInfoObject);
}
} catch (e) {
if (e.message !== "No TLS certificate in response") {
console.error(e.message);
log.error("monitor", "Caught error");
log.error("monitor", e.message);
}
}
}
if (process.env.TIMELOGGER === "1") {
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
log.debug("monitor", "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 (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID === this.id) {
log.info("monitor", res.data);
}
if (this.type === "http") {
@@ -231,7 +337,11 @@ class Monitor extends BeanModel {
bean.msg += ", keyword is found";
bean.status = UP;
} else {
throw new Error(bean.msg + ", but keyword is not found");
data = data.replace(/<[^>]*>?|[\n\r]|\s+/gm, " ");
if (data.length > 50) {
data = data.substring(0, 47) + "...";
}
throw new Error(bean.msg + ", but keyword is not in [" + data + "]");
}
}
@@ -249,27 +359,27 @@ class Monitor extends BeanModel {
let startTime = dayjs().valueOf();
let dnsMessage = "";
let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.dns_resolve_type);
let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.port, this.dns_resolve_type);
bean.ping = dayjs().valueOf() - startTime;
if (this.dns_resolve_type == "A" || this.dns_resolve_type == "AAAA" || this.dns_resolve_type == "TXT") {
if (this.dns_resolve_type === "A" || this.dns_resolve_type === "AAAA" || this.dns_resolve_type === "TXT") {
dnsMessage += "Records: ";
dnsMessage += dnsRes.join(" | ");
} else if (this.dns_resolve_type == "CNAME" || this.dns_resolve_type == "PTR") {
} else if (this.dns_resolve_type === "CNAME" || this.dns_resolve_type === "PTR") {
dnsMessage = dnsRes[0];
} else if (this.dns_resolve_type == "CAA") {
} else if (this.dns_resolve_type === "CAA") {
dnsMessage = dnsRes[0].issue;
} else if (this.dns_resolve_type == "MX") {
} else if (this.dns_resolve_type === "MX") {
dnsRes.forEach(record => {
dnsMessage += `Hostname: ${record.exchange} - Priority: ${record.priority} | `;
});
dnsMessage = dnsMessage.slice(0, -2);
} else if (this.dns_resolve_type == "NS") {
} else if (this.dns_resolve_type === "NS") {
dnsMessage += "Servers: ";
dnsMessage += dnsRes.join(" | ");
} else if (this.dns_resolve_type == "SOA") {
} else if (this.dns_resolve_type === "SOA") {
dnsMessage += `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`;
} else if (this.dns_resolve_type == "SRV") {
} else if (this.dns_resolve_type === "SRV") {
dnsRes.forEach(record => {
dnsMessage += `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight} | `;
});
@@ -286,25 +396,33 @@ class Monitor extends BeanModel {
bean.msg = dnsMessage;
bean.status = UP;
} else if (this.type === "push") { // Type: Push
const time = R.isoDateTime(dayjs.utc().subtract(this.interval, "second"));
log.debug("monitor", `[${this.name}] Checking monitor at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`);
const bufferTime = 1000; // 1s buffer to accommodate clock differences
let heartbeatCount = await R.count("heartbeat", " monitor_id = ? AND time > ? ", [
this.id,
time
]);
if (previousBeat) {
const msSinceLastBeat = dayjs.utc().valueOf() - dayjs.utc(previousBeat.time).valueOf();
debug("heartbeatCount" + heartbeatCount + " " + time);
log.debug("monitor", `[${this.name}] msSinceLastBeat = ${msSinceLastBeat}`);
if (heartbeatCount <= 0) {
// Fix #922, since previous heartbeat could be inserted by api, it should get from database
previousBeat = await Monitor.getPreviousHeartbeat(this.id);
throw new Error("No heartbeat in the time window");
// If the previous beat was down or pending we use the regular
// beatInterval/retryInterval in the setTimeout further below
if (previousBeat.status !== (this.isUpsideDown() ? DOWN : UP) || msSinceLastBeat > beatInterval * 1000 + bufferTime) {
throw new Error("No heartbeat in the time window");
} else {
let timeout = beatInterval * 1000 - msSinceLastBeat;
if (timeout < 0) {
timeout = bufferTime;
} else {
timeout += bufferTime;
}
// No need to insert successful heartbeat for push type, so end here
retries = 0;
log.debug("monitor", `[${this.name}] timeout = ${timeout}`);
this.heartbeatInterval = setTimeout(beat, timeout);
return;
}
} else {
// No need to insert successful heartbeat for push type, so end here
retries = 0;
this.heartbeatInterval = setTimeout(beat, this.interval * 1000);
return;
throw new Error("No heartbeat in the time window");
}
} else if (this.type === "steam") {
@@ -316,7 +434,7 @@ class Monitor extends BeanModel {
throw new Error("Steam API Key not found");
}
let res = await axios.get(steamApiUrl, {
let res = await axiosClient.get(steamApiUrl, {
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "*/*",
@@ -324,7 +442,7 @@ class Monitor extends BeanModel {
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: ! this.getIgnoreTls(),
rejectUnauthorized: !this.getIgnoreTls(),
}),
maxRedirects: this.maxredirects,
validateStatus: (status) => {
@@ -346,7 +464,22 @@ class Monitor extends BeanModel {
} else {
throw new Error("Server not found on Steam");
}
} else if (this.type === "mqtt") {
bean.msg = await mqttAsync(this.hostname, this.mqttTopic, this.mqttSuccessMessage, {
port: this.port,
username: this.mqttUsername,
password: this.mqttPassword,
interval: this.interval,
});
bean.status = UP;
} else if (this.type === "sqlserver") {
let startTime = dayjs().valueOf();
await mssqlQuery(this.databaseConnectionString, this.databaseQuery);
bean.msg = "";
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
} else {
bean.msg = "Unknown Monitor Type";
bean.status = PENDING;
@@ -377,9 +510,7 @@ class Monitor extends BeanModel {
}
}
let beatInterval = this.interval;
debug(`[${this.name}] Check isImportant`);
log.debug("monitor", `[${this.name}] Check isImportant`);
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
// Mark as important if status changed, ignore pending pings,
@@ -387,11 +518,11 @@ class Monitor extends BeanModel {
if (isImportant) {
bean.important = true;
debug(`[${this.name}] sendNotification`);
log.debug("monitor", `[${this.name}] sendNotification`);
await Monitor.sendNotification(isFirstBeat, this, bean);
// Clear Status Page Cache
debug(`[${this.name}] apicache clear`);
log.debug("monitor", `[${this.name}] apicache clear`);
apicache.clear();
} else {
@@ -399,55 +530,48 @@ class Monitor extends BeanModel {
}
if (bean.status === UP) {
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
log.debug("monitor", `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
} else if (bean.status === PENDING) {
if (this.retryInterval > 0) {
beatInterval = this.retryInterval;
}
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`);
log.warn("monitor", `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} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
}
debug(`[${this.name}] Send to socket`);
log.debug("monitor", `[${this.name}] Send to socket`);
io.to(this.user_id).emit("heartbeat", bean.toJSON());
Monitor.sendStats(io, this.id, this.user_id);
debug(`[${this.name}] Store`);
log.debug("monitor", `[${this.name}] Store`);
await R.store(bean);
debug(`[${this.name}] prometheus.update`);
log.debug("monitor", `[${this.name}] prometheus.update`);
prometheus.update(bean, tlsInfo);
previousBeat = bean;
if (! this.isStop) {
if (demoMode) {
if (beatInterval < 20) {
console.log("beat interval too low, reset to 20s");
beatInterval = 20;
}
}
debug(`[${this.name}] SetTimeout for next check.`);
log.debug("monitor", `[${this.name}] SetTimeout for next check.`);
this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000);
} else {
console.log(`[${this.name}] isStop = true, no next check.`);
log.info("monitor", `[${this.name}] isStop = true, no next check.`);
}
};
/** Get a heartbeat and handle errors */
const safeBeat = async () => {
try {
await beat();
} catch (e) {
console.trace(e);
errorLog(e, false);
console.error("Please report to https://github.com/louislam/uptime-kuma/issues");
UptimeKumaServer.errorLog(e, false);
log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues");
if (! this.isStop) {
console.log("Try to restart the monitor");
log.info("monitor", "Try to restart the monitor");
this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
}
}
@@ -463,16 +587,27 @@ class Monitor extends BeanModel {
}
}
/** Stop monitor */
stop() {
clearTimeout(this.heartbeatInterval);
this.isStop = true;
this.prometheus().remove();
}
/**
* Get a new prometheus instance
* @returns {Prometheus}
*/
prometheus() {
return new Prometheus(this);
}
/**
* Helper Method:
* returns URL object for further usage
* returns null if url is invalid
* @returns {null|URL}
* @returns {(null|URL)}
*/
getUrl() {
try {
@@ -485,48 +620,54 @@ class Monitor extends BeanModel {
/**
* Store TLS info to database
* @param checkCertificateResult
* @returns {Promise<object>}
* @returns {Promise<Object>}
*/
async updateTlsInfo(checkCertificateResult) {
let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
this.id,
]);
if (tls_info_bean == null) {
tls_info_bean = R.dispense("monitor_tls_info");
tls_info_bean.monitor_id = this.id;
if (tlsInfoBean == null) {
tlsInfoBean = R.dispense("monitor_tls_info");
tlsInfoBean.monitor_id = this.id;
} else {
// Clear sent history if the cert changed.
try {
let oldCertInfo = JSON.parse(tls_info_bean.info_json);
let oldCertInfo = JSON.parse(tlsInfoBean.info_json);
let isValidObjects = oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo;
if (isValidObjects) {
if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
debug("Resetting sent_history");
log.debug("monitor", "Resetting sent_history");
await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
this.id
]);
} else {
debug("No need to reset sent_history");
debug(oldCertInfo.certInfo.fingerprint256);
debug(checkCertificateResult.certInfo.fingerprint256);
log.debug("monitor", "No need to reset sent_history");
log.debug("monitor", oldCertInfo.certInfo.fingerprint256);
log.debug("monitor", checkCertificateResult.certInfo.fingerprint256);
}
} else {
debug("Not valid object");
log.debug("monitor", "Not valid object");
}
} catch (e) { }
}
tls_info_bean.info_json = JSON.stringify(checkCertificateResult);
await R.store(tls_info_bean);
tlsInfoBean.info_json = JSON.stringify(checkCertificateResult);
await R.store(tlsInfoBean);
return checkCertificateResult;
}
/**
* Send statistics to clients
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
*/
static async sendStats(io, monitorID, userID) {
const hasClients = getTotalClientInRoom(io, userID) > 0;
@@ -536,13 +677,13 @@ class Monitor extends BeanModel {
await Monitor.sendUptime(24 * 30, io, monitorID, userID);
await Monitor.sendCertInfo(io, monitorID, userID);
} else {
debug("No clients in the room, no need to send stats");
log.debug("monitor", "No clients in the room, no need to send stats");
}
}
/**
*
* @param duration : int Hours
* Send the average ping to user
* @param {number} duration Hours
*/
static async sendAvgPing(duration, io, monitorID, userID) {
const timeLogger = new TimeLogger();
@@ -562,12 +703,18 @@ class Monitor extends BeanModel {
io.to(userID).emit("avgPing", monitorID, avgPing);
}
/**
* Send certificate information to client
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
*/
static async sendCertInfo(io, monitorID, userID) {
let tls_info = await R.findOne("monitor_tls_info", "monitor_id = ?", [
let tlsInfo = await R.findOne("monitor_tls_info", "monitor_id = ?", [
monitorID,
]);
if (tls_info != null) {
io.to(userID).emit("certInfo", monitorID, tls_info.info_json);
if (tlsInfo != null) {
io.to(userID).emit("certInfo", monitorID, tlsInfo.info_json);
}
}
@@ -575,7 +722,8 @@ class Monitor extends BeanModel {
* Uptime with calculation
* Calculation based on:
* https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime
* @param duration : int Hours
* @param {number} duration Hours
* @param {number} monitorID ID of monitor to calculate
*/
static async calcUptime(duration, monitorID) {
const timeLogger = new TimeLogger();
@@ -641,13 +789,23 @@ class Monitor extends BeanModel {
/**
* Send Uptime
* @param duration : int Hours
* @param {number} duration Hours
* @param {Server} io Socket server instance
* @param {number} monitorID ID of monitor to send
* @param {number} userID ID of user to send to
*/
static async sendUptime(duration, io, monitorID, userID) {
const uptime = await this.calcUptime(duration, monitorID);
io.to(userID).emit("uptime", monitorID, duration, uptime);
}
/**
* Has status of monitor changed since last beat?
* @param {boolean} isFirstBeat Is this the first beat of this monitor?
* @param {const} previousBeatStatus Status of the previous beat
* @param {const} currentBeatStatus Status of the current beat
* @returns {boolean} True if is an important beat else false
*/
static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
@@ -666,6 +824,12 @@ class Monitor extends BeanModel {
return isImportant;
}
/**
* Send a notification about a monitor
* @param {boolean} isFirstBeat Is this beat the first of this monitor?
* @param {Monitor} monitor The monitor to send a notificaton about
* @param {Bean} bean Status information about monitor
*/
static async sendNotification(isFirstBeat, monitor, bean) {
if (!isFirstBeat || bean.status === DOWN) {
const notificationList = await Monitor.getNotificationList(monitor);
@@ -681,15 +845,20 @@ class Monitor extends BeanModel {
for (let notification of notificationList) {
try {
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON());
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), bean.toJSON());
} catch (e) {
console.error("Cannot send notification to " + notification.name);
console.log(e);
log.error("monitor", "Cannot send notification to " + notification.name);
log.error("monitor", e);
}
}
}
}
/**
* Get list of notification providers for a given monitor
* @param {Monitor} monitor Monitor to get notification providers for
* @returns {Promise<LooseObject<any>[]>}
*/
static async getNotificationList(monitor) {
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
monitor.id,
@@ -697,21 +866,42 @@ class Monitor extends BeanModel {
return notificationList;
}
/**
* Send notification about a certificate
* @param {Object} tlsInfoObject Information about certificate
*/
async sendCertNotification(tlsInfoObject) {
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
const notificationList = await Monitor.getNotificationList(this);
debug("call sendCertNotificationByTargetDays");
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList);
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList);
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList);
let notifyDays = await setting("tlsExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default
setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general");
notifyDays = [ 7, 14, 21 ];
}
if (notifyDays != null && Array.isArray(notifyDays)) {
for (const day of notifyDays) {
log.debug("monitor", "call sendCertNotificationByTargetDays", day);
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList);
}
}
}
}
/**
* Send a certificate notification when certificate expires in less
* than target days
* @param {number} daysRemaining Number of days remaining on certifcate
* @param {number} targetDays Number of days to alert after
* @param {LooseObject<any>[]} notificationList List of notification providers
* @returns {Promise<void>}
*/
async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
if (daysRemaining > targetDays) {
debug(`No need to send cert notification. ${daysRemaining} > ${targetDays}`);
log.debug("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`);
return;
}
@@ -725,21 +915,21 @@ class Monitor extends BeanModel {
// Sent already, no need to send again
if (row) {
debug("Sent already, no need to send again");
log.debug("monitor", "Sent already, no need to send again");
return;
}
let sent = false;
debug("Send certificate notification");
log.debug("monitor", "Send certificate notification");
for (let notification of notificationList) {
try {
debug("Sending to " + notification.name);
log.debug("monitor", "Sending to " + notification.name);
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`);
sent = true;
} catch (e) {
console.error("Cannot send cert notification to " + notification.name);
console.error(e);
log.error("monitor", "Cannot send cert notification to " + notification.name);
log.error("monitor", e);
}
}
@@ -751,10 +941,15 @@ class Monitor extends BeanModel {
]);
}
} else {
debug("No notification, no need to send cert notification");
log.debug("monitor", "No notification, no need to send cert notification");
}
}
/**
* Get the status of the previous heartbeat
* @param {number} monitorID ID of monitor to check
* @returns {Promise<LooseObject<any>>}
*/
static async getPreviousHeartbeat(monitorID) {
return await R.getRow(`
SELECT status, time FROM heartbeat

25
server/model/proxy.js Normal file
View File

@@ -0,0 +1,25 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
class Proxy extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
*/
toJSON() {
return {
id: this._id,
userId: this._user_id,
protocol: this._protocol,
host: this._host,
port: this._port,
auth: !!this._auth,
username: this._username,
password: this._password,
active: !!this._active,
default: !!this._default,
createdDate: this._created_date,
};
}
}
module.exports = Proxy;

259
server/model/status_page.js Normal file
View File

@@ -0,0 +1,259 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
const { R } = require("redbean-node");
const cheerio = require("cheerio");
const { UptimeKumaServer } = require("../uptime-kuma-server");
class StatusPage extends BeanModel {
/**
* Like this: { "test-uptime.kuma.pet": "default" }
* @type {{}}
*/
static domainMappingList = { };
/**
*
* @param {Response} response
* @param {string} indexHTML
* @param {string} slug
*/
static async handleStatusPageResponse(response, indexHTML, slug) {
let statusPage = await R.findOne("status_page", " slug = ? ", [
slug
]);
if (statusPage) {
response.send(await StatusPage.renderHTML(indexHTML, statusPage));
} else {
response.status(404).send(UptimeKumaServer.getInstance().indexHTML);
}
}
/**
* SSR for status pages
* @param {string} indexHTML
* @param {StatusPage} statusPage
*/
static async renderHTML(indexHTML, statusPage) {
const $ = cheerio.load(indexHTML);
const description155 = statusPage.description?.substring(0, 155);
$("title").text(statusPage.title);
$("meta[name=description]").attr("content", description155);
if (statusPage.icon) {
$("link[rel=icon]")
.attr("href", statusPage.icon)
.removeAttr("type");
}
const head = $("head");
// OG Meta Tags
head.append(`<meta property="og:title" content="${statusPage.title}" />`);
head.append(`<meta property="og:description" content="${description155}" />`);
// Preload data
const json = JSON.stringify(await StatusPage.getStatusPageData(statusPage));
head.append(`
<script>
window.preloadData = ${json}
</script>
`);
return $.root().html();
}
/**
* Get all status page data in one call
* @param {StatusPage} statusPage
*/
static async getStatusPageData(statusPage) {
// Incident
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
statusPage.id,
]);
if (incident) {
incident = incident.toPublicJSON();
}
// Public Group List
const publicGroupList = [];
const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id
]);
for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags);
publicGroupList.push(monitorGroup);
}
// Response
return {
config: await statusPage.toPublicJSON(),
incident,
publicGroupList
};
}
/**
* Loads domain mapping from DB
* Return object like this: { "test-uptime.kuma.pet": "default" }
* @returns {Promise<void>}
*/
static async loadDomainMappingList() {
StatusPage.domainMappingList = await R.getAssoc(`
SELECT domain, slug
FROM status_page, status_page_cname
WHERE status_page.id = status_page_cname.status_page_id
`);
}
/**
* Send status page list to client
* @param {Server} io io Socket server instance
* @param {Socket} socket Socket.io instance
* @returns {Promise<Bean[]>}
*/
static async sendStatusPageList(io, socket) {
let result = {};
let list = await R.findAll("status_page", " ORDER BY title ");
for (let item of list) {
result[item.id] = await item.toJSON();
}
io.to(socket.userID).emit("statusPageList", result);
return list;
}
/**
* Update list of domain names
* @param {string[]} domainNameList
* @returns {Promise<void>}
*/
async updateDomainNameList(domainNameList) {
if (!Array.isArray(domainNameList)) {
throw new Error("Invalid array");
}
let trx = await R.begin();
await trx.exec("DELETE FROM status_page_cname WHERE status_page_id = ?", [
this.id,
]);
try {
for (let domain of domainNameList) {
if (typeof domain !== "string") {
throw new Error("Invalid domain");
}
if (domain.trim() === "") {
continue;
}
// If the domain name is used in another status page, delete it
await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [
domain,
]);
let mapping = trx.dispense("status_page_cname");
mapping.status_page_id = this.id;
mapping.domain = domain;
await trx.store(mapping);
}
await trx.commit();
} catch (error) {
await trx.rollback();
throw error;
}
}
/**
* Get list of domain names
* @returns {Object[]}
*/
getDomainNameList() {
let domainList = [];
for (let domain in StatusPage.domainMappingList) {
let s = StatusPage.domainMappingList[domain];
if (this.slug === s) {
domainList.push(domain);
}
}
return domainList;
}
/**
* Return an object that ready to parse to JSON
* @returns {Object}
*/
async toJSON() {
return {
id: this.id,
slug: this.slug,
title: this.title,
description: this.description,
icon: this.getIcon(),
theme: this.theme,
published: !!this.published,
showTags: !!this.show_tags,
domainNameList: this.getDomainNameList(),
customCSS: this.custom_css,
footerText: this.footer_text,
showPoweredBy: !!this.show_powered_by,
};
}
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Object}
*/
async toPublicJSON() {
return {
slug: this.slug,
title: this.title,
description: this.description,
icon: this.getIcon(),
theme: this.theme,
published: !!this.published,
showTags: !!this.show_tags,
customCSS: this.custom_css,
footerText: this.footer_text,
showPoweredBy: !!this.show_powered_by,
};
}
/**
* Convert slug to status page ID
* @param {string} slug
*/
static async slugToID(slug) {
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
slug
]);
}
/**
* Get path to the icon for the page
* @returns {string}
*/
getIcon() {
if (!this.icon) {
return "/icon.svg";
} else {
return this.icon;
}
}
}
module.exports = StatusPage;

View File

@@ -1,6 +1,11 @@
const { BeanModel } = require("redbean-node/dist/bean-model");
class Tag extends BeanModel {
/**
* Return an object that ready to parse to JSON
* @returns {Object}
*/
toJSON() {
return {
id: this._id,

View File

@@ -3,19 +3,30 @@ const passwordHash = require("../password-hash");
const { R } = require("redbean-node");
class User extends BeanModel {
/**
* Reset user password
* Fix #1510, as in the context reset-password.js, there is no auto model mapping. Call this static function instead.
* @param {number} userID ID of user to update
* @param {string} newPassword
* @returns {Promise<void>}
*/
static async resetPassword(userID, newPassword) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
passwordHash.generate(newPassword),
userID
]);
}
/**
* Direct execute, no need R.store()
* @param newPassword
* Reset this users password
* @param {string} newPassword
* @returns {Promise<void>}
*/
async resetPassword(newPassword) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
passwordHash.generate(newPassword),
this.id
]);
await User.resetPassword(this.id, newPassword);
this.password = newPassword;
}
}
module.exports = User;

View File

@@ -13,27 +13,49 @@ let t = {
let instances = [];
/**
* Does a === b
* @param {any} a
* @returns {function(any): boolean}
*/
let matches = function (a) {
return function (b) {
return a === b;
};
};
/**
* Does a!==b
* @param {any} a
* @returns {function(any): boolean}
*/
let doesntMatch = function (a) {
return function (b) {
return !matches(a)(b);
};
};
/**
* Get log duration
* @param {number} d Time in ms
* @param {string} prefix Prefix for log
* @returns {string} Coloured log string
*/
let logDuration = function (d, prefix) {
let str = d > 1000 ? (d / 1000).toFixed(2) + "sec" : d + "ms";
return "\x1b[33m- " + (prefix ? prefix + " " : "") + str + "\x1b[0m";
};
/**
* Get safe headers
* @param {Object} res Express response object
* @returns {Object}
*/
function getSafeHeaders(res) {
return res.getHeaders ? res.getHeaders() : res._headers;
}
/** Constructor for ApiCache instance */
function ApiCache() {
let memCache = new MemoryCache();
@@ -68,6 +90,15 @@ function ApiCache() {
instances.push(this);
this.id = instances.length;
/**
* Logs a message to the console if the `DEBUG` environment variable is set.
* @param {string} a The first argument to log.
* @param {string} b The second argument to log.
* @param {string} c The third argument to log.
* @param {string} d The fourth argument to log, and so on... (optional)
*
* Generated by Trelent
*/
function debug(a, b, c, d) {
let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) {
return arg !== undefined;
@@ -77,6 +108,13 @@ function ApiCache() {
return (globalOptions.debug || debugEnv) && console.log.apply(null, arr);
}
/**
* Returns true if the given request and response should be logged.
* @param {Object} request The HTTP request object.
* @param {Object} response The HTTP response object.
* @param {function(Object, Object):boolean} toggle
* @returns {boolean}
*/
function shouldCacheResponse(request, response, toggle) {
let opt = globalOptions;
let codes = opt.statusCodes;
@@ -99,6 +137,11 @@ function ApiCache() {
return true;
}
/**
* Add key to index array
* @param {string} key Key to add
* @param {Object} req Express request object
*/
function addIndexEntries(key, req) {
let groupName = req.apicacheGroup;
@@ -111,6 +154,16 @@ function ApiCache() {
index.all.unshift(key);
}
/**
* Returns a new object containing only the whitelisted headers.
* @param {Object} headers The original object of header names and
* values.
* @param {string[]} globalOptions.headerWhitelist An array of
* strings representing the whitelisted header names to keep in the
* output object.
*
* Generated by Trelent
*/
function filterBlacklistedHeaders(headers) {
return Object.keys(headers)
.filter(function (key) {
@@ -122,6 +175,14 @@ function ApiCache() {
}, {});
}
/**
* Create a cache object
* @param {Object} headers The response headers to filter.
* @returns {Object} A new object containing only the whitelisted
* response headers.
*
* Generated by Trelent
*/
function createCacheObject(status, headers, data, encoding) {
return {
status: status,
@@ -132,6 +193,15 @@ function ApiCache() {
};
}
/**
* Sets a cache value for the given key.
* @param {string} key The cache key to set.
* @param {any} value The cache value to set.
* @param {number} duration How long in milliseconds the cached
* response should be valid for (defaults to 1 hour).
*
* Generated by Trelent
*/
function cacheResponse(key, value, duration) {
let redis = globalOptions.redisClient;
let expireCallback = globalOptions.events.expire;
@@ -154,6 +224,13 @@ function ApiCache() {
}, Math.min(duration, 2147483647));
}
/**
* Appends content to the response.
* @param {Object} res Express response object
* @param {(string|Buffer)} content The content to append.
*
* Generated by Trelent
*/
function accumulateContent(res, content) {
if (content) {
if (typeof content == "string") {
@@ -179,6 +256,17 @@ function ApiCache() {
}
}
/**
* Monkeypatches the response object to add cache control headers
* and create a cache object.
* @param {Object} req Express request object
* @param {Object} res Express response object
* @param {function} next Function to call next
* @param {string} key Key to add response as
* @param {number} duration Time to cache response for
* @param {string} strDuration Duration in string form
* @param {function(Object, Object):boolean} toggle
*/
function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) {
// monkeypatch res.end to create cache object
res._apicache = {
@@ -245,6 +333,17 @@ function ApiCache() {
next();
}
/**
* Send a cached response to client
* @param {Request} request Express request object
* @param {Response} response Express response object
* @param {object} cacheObject Cache object to send
* @param {function(Object, Object):boolean} toggle
* @param {function} next Function to call next
* @param {number} duration Not used
* @returns {boolean|undefined} true if the request should be
* cached, false otherwise. If undefined, defaults to true.
*/
function sendCachedResponse(request, response, cacheObject, toggle, next, duration) {
if (toggle && !toggle(request, response)) {
return next();
@@ -285,12 +384,19 @@ function ApiCache() {
return response.end(data, cacheObject.encoding);
}
/** Sync caching options */
function syncOptions() {
for (let i in middlewareOptions) {
Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions);
}
}
/**
* Clear key from cache
* @param {string} target Key to clear
* @param {boolean} isAutomatic Is the key being cleared automatically
* @returns {number}
*/
this.clear = function (target, isAutomatic) {
let group = index.groups[target];
let redis = globalOptions.redisClient;
@@ -365,6 +471,14 @@ function ApiCache() {
return this.getIndex();
};
/**
* Converts a duration string to an integer number of milliseconds.
* @param {(string|number)} duration The string to convert.
* @param {number} defaultDuration The default duration to return if
* can't parse duration
* @returns {number} The converted value in milliseconds, or the
* defaultDuration if it can't be parsed.
*/
function parseDuration(duration, defaultDuration) {
if (typeof duration === "number") {
return duration;
@@ -387,17 +501,24 @@ function ApiCache() {
return defaultDuration;
}
/**
* Parse duration
* @param {(number|string)} duration
* @returns {number} Duration parsed to a number
*/
this.getDuration = function (duration) {
return parseDuration(duration, globalOptions.defaultDuration);
};
/**
* Return cache performance statistics (hit rate). Suitable for putting into a route:
* 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>
* @returns {any[]}
*/
this.getPerformance = function () {
return performanceArray.map(function (p) {
@@ -405,6 +526,11 @@ function ApiCache() {
});
};
/**
* Get index of a group
* @param {string} group
* @returns {number}
*/
this.getIndex = function (group) {
if (group) {
return index.groups[group];
@@ -413,6 +539,14 @@ function ApiCache() {
}
};
/**
* Express middleware
* @param {(string|number)} strDuration Duration to cache responses
* for.
* @param {function(Object, Object):boolean} middlewareToggle
* @param {Object} localOptions Options for APICache
* @returns
*/
this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
let duration = instance.getDuration(strDuration);
let opt = {};
@@ -436,63 +570,72 @@ function ApiCache() {
options(localOptions);
/**
* A Function for non tracking performance
*/
* 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.
*/
* 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.
*/
* 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.
*/
* 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.
*/
* 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.
*/
* 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.
*/
* 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
*/
* 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.
*/
* 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.
*/
* The key from the last cache miss. This is useful in
* identifying which route these statistics apply to.
*/
this.lastCacheMiss = null;
/**
* Return performance statistics
*/
* Return performance statistics
* @returns {Object}
*/
this.report = function () {
return {
lastCacheHit: this.lastCacheHit,
@@ -509,10 +652,13 @@ function ApiCache() {
};
/**
* 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
*/
* Computes a cache hit rate from an array of hits and
* misses.
* @param {Uint8Array} array An array representing hits and
* misses.
* @returns {?number} 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;
@@ -538,16 +684,17 @@ function ApiCache() {
};
/**
* 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
*/
* 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
@@ -557,9 +704,11 @@ function ApiCache() {
};
/**
* 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
*/
* 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);
@@ -572,18 +721,18 @@ function ApiCache() {
};
/**
* Records a hit event, setting lastCacheMiss to the given key
* @param {string} key The key that had the cache hit
*/
* 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
*/
* 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;
@@ -594,6 +743,13 @@ function ApiCache() {
performanceArray.push(perf);
/**
* Cache a request
* @param {Object} req Express request object
* @param {Object} res Express response object
* @param {function} next Function to call next
* @returns {any}
*/
let cache = function (req, res, next) {
function bypass() {
debug("bypass detected, skipping cache.");
@@ -701,6 +857,11 @@ function ApiCache() {
return cache;
};
/**
* Process options
* @param {Object} options
* @returns {Object}
*/
this.options = function (options) {
if (options) {
Object.assign(globalOptions, options);
@@ -721,6 +882,7 @@ function ApiCache() {
}
};
/** Reset the index */
this.resetIndex = function () {
index = {
all: [],
@@ -728,6 +890,11 @@ function ApiCache() {
};
};
/**
* Create a new instance of ApiCache
* @param {Object} config Config to pass
* @returns {ApiCache}
*/
this.newInstance = function (config) {
let instance = new ApiCache();
@@ -738,6 +905,7 @@ function ApiCache() {
return instance;
};
/** Clone this instance */
this.clone = function () {
return this.newInstance(this.options());
};

View File

@@ -3,6 +3,15 @@ function MemoryCache() {
this.size = 0;
}
/**
*
* @param {string} key Key to store cache as
* @param {any} value Value to store
* @param {number} time Time to store for
* @param {function(any, string)} timeoutCallback Callback to call in
* case of timeout
* @returns {Object}
*/
MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
let old = this.cache[key];
let instance = this;
@@ -22,6 +31,11 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
return entry;
};
/**
* Delete a cache entry
* @param {string} key Key to delete
* @returns {null}
*/
MemoryCache.prototype.delete = function (key) {
let entry = this.cache[key];
@@ -36,18 +50,32 @@ MemoryCache.prototype.delete = function (key) {
return null;
};
/**
* Get value of key
* @param {string} key
* @returns {Object}
*/
MemoryCache.prototype.get = function (key) {
let entry = this.cache[key];
return entry;
};
/**
* Get value of cache entry
* @param {string} key
* @returns {any}
*/
MemoryCache.prototype.getValue = function (key) {
let entry = this.get(key);
return entry && entry.value;
};
/**
* Clear cache
* @returns {boolean}
*/
MemoryCache.prototype.clear = function () {
Object.keys(this.cache).forEach(function (key) {
this.delete(key);

View File

@@ -0,0 +1,67 @@
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
const axios = require("axios");
class Alerta extends NotificationProvider {
name = "alerta";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let alertaUrl = `${notification.alertaApiEndpoint}`;
let config = {
headers: {
"Content-Type": "application/json;charset=UTF-8",
"Authorization": "Key " + notification.alertaApiKey,
}
};
let data = {
environment: notification.alertaEnvironment,
severity: "critical",
correlate: [],
service: [ "UptimeKuma" ],
value: "Timeout",
tags: [ "uptimekuma" ],
attributes: {},
origin: "uptimekuma",
type: "exceptionAlert",
};
if (heartbeatJSON == null) {
let postData = Object.assign({
event: "msg",
text: msg,
group: "uptimekuma-msg",
resource: "Message",
}, data);
await axios.post(alertaUrl, postData, config);
} else {
let datadup = Object.assign( {
correlate: [ "service_up", "service_down" ],
event: monitorJSON["type"],
group: "uptimekuma-" + monitorJSON["type"],
resource: monitorJSON["name"],
}, data );
if (heartbeatJSON["status"] === DOWN) {
datadup.severity = notification.alertaAlertState; // critical
datadup.text = "Service " + monitorJSON["type"] + " is down.";
await axios.post(alertaUrl, datadup, config);
} else if (heartbeatJSON["status"] === UP) {
datadup.severity = notification.alertaRecoverState; // cleaned
datadup.text = "Service " + monitorJSON["type"] + " is up.";
await axios.post(alertaUrl, datadup, config);
}
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Alerta;

View File

@@ -37,6 +37,12 @@ class AliyunSMS extends NotificationProvider {
}
}
/**
* Send the SMS notification
* @param {BeanModel} notification Notification details
* @param {string} msgbody Message template
* @returns {boolean} True if successful else false
*/
async sendSms(notification, msgbody) {
let params = {
PhoneNumbers: notification.phonenumber,
@@ -64,13 +70,18 @@ class AliyunSMS extends NotificationProvider {
};
let result = await axios(config);
if (result.data.Message == "OK") {
if (result.data.Message === "OK") {
return true;
}
return false;
}
/** Aliyun request sign */
/**
* Aliyun request sign
* @param {Object} param Parameters object to sign
* @param {string} AccessKeySecret Secret key to sign parameters with
* @returns {string}
*/
sign(param, AccessKeySecret) {
let param2 = {};
let data = [];
@@ -82,8 +93,23 @@ class AliyunSMS extends NotificationProvider {
param2[key] = param[key];
}
// Escape more characters than encodeURIComponent does.
// For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded.
// See https://help.aliyun.com/document_detail/315526.html
// This encoding methods as known as RFC 3986 (https://tools.ietf.org/html/rfc3986)
let moreEscapesTable = function (m) {
return {
"!": "%21",
"*": "%2A",
"'": "%27",
"(": "%28",
")": "%29"
}[m];
};
for (let key in param2) {
data.push(`${encodeURIComponent(key)}=${encodeURIComponent(param2[key])}`);
let value = encodeURIComponent(param2[key]).replace(/[!*'()]/g, moreEscapesTable);
data.push(`${encodeURIComponent(key)}=${value}`);
}
let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`;
@@ -93,6 +119,11 @@ class AliyunSMS extends NotificationProvider {
.digest("base64");
}
/**
* Convert status constant to string
* @param {const} status The status constant
* @returns {string}
*/
statusToString(status) {
switch (status) {
case DOWN:

View File

@@ -1,14 +1,19 @@
const NotificationProvider = require("./notification-provider");
const child_process = require("child_process");
const childProcess = 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])
const args = [ "-vv", "-b", msg, notification.appriseURL ];
if (notification.title) {
args.push("-t");
args.push(notification.title);
}
const s = childProcess.spawnSync("apprise", args);
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
if (output) {
@@ -16,7 +21,7 @@ class Apprise extends NotificationProvider {
return "Sent Successfully";
}
throw new Error(output)
throw new Error(output);
} else {
return "No output from apprise";
}

View File

@@ -21,35 +21,35 @@ class Bark extends NotificationProvider {
name = "Bark";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
try {
var barkEndpoint = notification.barkEndpoint;
let barkEndpoint = notification.barkEndpoint;
// check if the endpoint has a "/" suffix, if so, delete it first
if (barkEndpoint.endsWith("/")) {
barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1);
}
// check if the endpoint has a "/" suffix, if so, delete it first
if (barkEndpoint.endsWith("/")) {
barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1);
}
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) {
let title = "UptimeKuma Monitor Up";
return await this.postNotification(title, msg, barkEndpoint);
}
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === UP) {
let title = "UptimeKuma Monitor Up";
return await this.postNotification(title, msg, barkEndpoint);
}
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) {
let title = "UptimeKuma Monitor Down";
return await this.postNotification(title, msg, barkEndpoint);
}
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === DOWN) {
let title = "UptimeKuma Monitor Down";
return await this.postNotification(title, msg, barkEndpoint);
}
if (msg != null) {
let title = "UptimeKuma Message";
return await this.postNotification(title, msg, barkEndpoint);
}
} catch (error) {
throw error;
if (msg != null) {
let title = "UptimeKuma Message";
return await this.postNotification(title, msg, barkEndpoint);
}
}
// add additional parameter for better on device styles (iOS 15 optimized)
/**
* Add additional parameter for better on device styles (iOS 15
* optimized)
* @param {string} postUrl URL to append parameters to
* @returns {string}
*/
appendAdditionalParameters(postUrl) {
// grouping all our notifications
postUrl += "?group=" + barkNotificationGroup;
@@ -60,7 +60,11 @@ class Bark extends NotificationProvider {
return postUrl;
}
// thrown if failed to check result, result code should be in range 2xx
/**
* Check if result is successful
* @param {Object} result Axios response object
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
if (result.status == null) {
throw new Error("Bark notification failed with invalid response!");
@@ -70,6 +74,13 @@ class Bark extends NotificationProvider {
}
}
/**
* Send the message
* @param {string} title Message title
* @param {string} subtitle Message
* @param {string} endpoint Endpoint to send request to
* @returns {string}
*/
async postNotification(title, subtitle, endpoint) {
// url encode title and subtitle
title = encodeURIComponent(title);

View File

@@ -12,7 +12,7 @@ class ClickSendSMS extends NotificationProvider {
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString('base64'),
"Authorization": "Basic " + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString("base64"),
"Accept": "text/json",
}
};

View File

@@ -37,6 +37,12 @@ class DingDing extends NotificationProvider {
}
}
/**
* Send message to DingDing
* @param {BeanModel} notification
* @param {Object} params Parameters of message
* @returns {boolean} True if successful else false
*/
async sendToDingDing(notification, params) {
let timestamp = Date.now();
@@ -50,13 +56,18 @@ class DingDing extends NotificationProvider {
};
let result = await axios(config);
if (result.data.errmsg == "ok") {
if (result.data.errmsg === "ok") {
return true;
}
return false;
}
/** DingDing sign */
/**
* DingDing sign
* @param {Date} timestamp Timestamp of message
* @param {string} secretKey Secret key to sign data with
* @returns {string}
*/
sign(timestamp, secretKey) {
return Crypto
.createHmac("sha256", Buffer.from(secretKey, "utf8"))
@@ -64,7 +75,13 @@ class DingDing extends NotificationProvider {
.digest("base64");
}
/**
* Convert status constant to string
* @param {const} status The status constant
* @returns {string}
*/
statusToString(status) {
// TODO: Move to notification-provider.js to avoid repetition in classes
switch (status) {
case DOWN:
return "DOWN";

View File

@@ -17,25 +17,32 @@ class Discord extends NotificationProvider {
let discordtestdata = {
username: discordDisplayName,
content: msg,
}
await axios.post(notification.discordWebhookUrl, discordtestdata)
};
await axios.post(notification.discordWebhookUrl, discordtestdata);
return okMsg;
}
let url;
let address;
if (monitorJSON["type"] === "port") {
url = monitorJSON["hostname"];
if (monitorJSON["port"]) {
url += ":" + monitorJSON["port"];
}
} else {
url = monitorJSON["url"];
switch (monitorJSON["type"]) {
case "ping":
address = monitorJSON["hostname"];
break;
case "port":
case "dns":
case "steam":
address = monitorJSON["hostname"];
if (monitorJSON["port"]) {
address += ":" + monitorJSON["port"];
}
break;
default:
address = monitorJSON["url"];
break;
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON["status"] == DOWN) {
if (heartbeatJSON["status"] === DOWN) {
let discorddowndata = {
username: discordDisplayName,
embeds: [{
@@ -48,8 +55,8 @@ class Discord extends NotificationProvider {
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url,
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
},
{
name: "Time (UTC)",
@@ -61,16 +68,16 @@ class Discord extends NotificationProvider {
},
],
}],
}
};
if (notification.discordPrefixMessage) {
discorddowndata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discorddowndata)
await axios.post(notification.discordWebhookUrl, discorddowndata);
return okMsg;
} else if (heartbeatJSON["status"] == UP) {
} else if (heartbeatJSON["status"] === UP) {
let discordupdata = {
username: discordDisplayName,
embeds: [{
@@ -83,8 +90,8 @@ class Discord extends NotificationProvider {
value: monitorJSON["name"],
},
{
name: "Service URL",
value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
value: monitorJSON["type"] === "push" ? "Heartbeat" : address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
},
{
name: "Time (UTC)",
@@ -92,21 +99,21 @@ class Discord extends NotificationProvider {
},
{
name: "Ping",
value: heartbeatJSON["ping"] + "ms",
value: heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms",
},
],
}],
}
};
if (notification.discordPrefixMessage) {
discordupdata.content = notification.discordPrefixMessage;
}
await axios.post(notification.discordWebhookUrl, discordupdata)
await axios.post(notification.discordWebhookUrl, discordupdata);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}

View File

@@ -21,7 +21,7 @@ class Feishu extends NotificationProvider {
return okMsg;
}
if (heartbeatJSON["status"] == DOWN) {
if (heartbeatJSON["status"] === DOWN) {
let downdata = {
msg_type: "post",
content: {
@@ -48,7 +48,7 @@ class Feishu extends NotificationProvider {
return okMsg;
}
if (heartbeatJSON["status"] == UP) {
if (heartbeatJSON["status"] === UP) {
let updata = {
msg_type: "post",
content: {

View File

@@ -13,11 +13,11 @@ class GoogleChat extends NotificationProvider {
try {
// Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic
let textMsg = ''
let textMsg = "";
if (heartbeatJSON && heartbeatJSON.status === UP) {
textMsg = `✅ Application is back online\n`;
textMsg = "✅ Application is back online\n";
} else if (heartbeatJSON && heartbeatJSON.status === DOWN) {
textMsg = `🔴 Application went down\n`;
textMsg = "🔴 Application went down\n";
}
if (monitorJSON && monitorJSON.name) {

View File

@@ -0,0 +1,42 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Gorush extends NotificationProvider {
name = "gorush";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let platformMapping = {
"ios": 1,
"android": 2,
"huawei": 3,
};
try {
let data = {
"notifications": [
{
"tokens": [ notification.gorushDeviceToken ],
"platform": platformMapping[notification.gorushPlatform],
"message": msg,
// Optional
"title": notification.gorushTitle,
"priority": notification.gorushPriority,
"retry": parseInt(notification.gorushRetry) || 0,
"topic": notification.gorushTopic,
}
]
};
let config = {};
await axios.post(`${notification.gorushServerURL}/api/push`, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Gorush;

View File

@@ -15,7 +15,7 @@ class Gotify extends NotificationProvider {
"message": msg,
"priority": notification.gotifyPriority || 8,
"title": "Uptime-Kuma",
})
});
return okMsg;

View File

@@ -25,9 +25,9 @@ class Line extends NotificationProvider {
"text": "Test Successful!"
}
]
}
await axios.post(lineAPIUrl, testMessage, config)
} else if (heartbeatJSON["status"] == DOWN) {
};
await axios.post(lineAPIUrl, testMessage, config);
} else if (heartbeatJSON["status"] === DOWN) {
let downMessage = {
"to": notification.lineUserID,
"messages": [
@@ -36,9 +36,9 @@ class Line extends NotificationProvider {
"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) {
};
await axios.post(lineAPIUrl, downMessage, config);
} else if (heartbeatJSON["status"] === UP) {
let upMessage = {
"to": notification.lineUserID,
"messages": [
@@ -47,12 +47,12 @@ class Line extends NotificationProvider {
"text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
}
]
}
await axios.post(lineAPIUrl, upMessage, config)
};
await axios.post(lineAPIUrl, upMessage, config);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}
}

View File

@@ -8,38 +8,38 @@ class LunaSea extends NotificationProvider {
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
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)
};
await axios.post(lunaseadevice, testdata);
return okMsg;
}
if (heartbeatJSON["status"] == DOWN) {
if (heartbeatJSON["status"] === DOWN) {
let downdata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, downdata)
};
await axios.post(lunaseadevice, downdata);
return okMsg;
}
if (heartbeatJSON["status"] == UP) {
if (heartbeatJSON["status"] === UP) {
let updata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
}
await axios.post(lunaseadevice, updata)
};
await axios.post(lunaseadevice, updata);
return okMsg;
}
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}

View File

@@ -1,7 +1,7 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const Crypto = require("crypto");
const { debug } = require("../../src/util");
const { log } = require("../../src/util");
class Matrix extends NotificationProvider {
name = "matrix";
@@ -17,11 +17,11 @@ class Matrix extends NotificationProvider {
.slice(0, size)
);
debug("Random String: " + randomString);
log.debug("notification", "Random String: " + randomString);
const roomId = encodeURIComponent(notification.internalRoomId);
debug("Matrix Room ID: " + roomId);
log.debug("notification", "Matrix Room ID: " + roomId);
try {
let config = {

View File

@@ -15,16 +15,21 @@ class Mattermost extends NotificationProvider {
let mattermostTestData = {
username: mattermostUserName,
text: msg,
}
await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
};
await axios.post(notification.mattermostWebhookUrl, mattermostTestData);
return okMsg;
}
const mattermostChannel = notification.mattermostchannel;
let mattermostChannel;
if (typeof notification.mattermostchannel === "string") {
mattermostChannel = notification.mattermostchannel.toLowerCase();
}
const mattermostIconEmoji = notification.mattermosticonemo;
const mattermostIconUrl = notification.mattermosticonurl;
if (heartbeatJSON["status"] == DOWN) {
if (heartbeatJSON["status"] === DOWN) {
let mattermostdowndata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",
@@ -68,7 +73,7 @@ class Mattermost extends NotificationProvider {
mattermostdowndata
);
return okMsg;
} else if (heartbeatJSON["status"] == UP) {
} else if (heartbeatJSON["status"] === UP) {
let mattermostupdata = {
username: mattermostUserName,
text: "Uptime Kuma Alert",

View File

@@ -7,17 +7,23 @@ class NotificationProvider {
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)
* Send a notification
* @param {BeanModel} notification
* @param {string} msg General Message
* @param {?Object} monitorJSON Monitor details (For Up/Down only)
* @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Return Successful Message
* Throw Error with fail msg
* @throws Error with fail msg
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
throw new Error("Have to override Notification.send(...)");
}
/**
* Throws an error
* @param {any} error The error to throw
* @throws {any} The error specified
*/
throwGeneralAxiosError(error) {
let msg = "Error: " + error + " ";
@@ -25,11 +31,11 @@ class NotificationProvider {
if (typeof error.response.data === "string") {
msg += error.response.data;
} else {
msg += JSON.stringify(error.response.data)
msg += JSON.stringify(error.response.data);
}
}
throw new Error(msg)
throw new Error(msg);
}
}

View File

@@ -0,0 +1,26 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class Ntfy extends NotificationProvider {
name = "ntfy";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
await axios.post(`${notification.ntfyserverurl}`, {
"topic": notification.ntfytopic,
"message": msg,
"priority": notification.ntfyPriority || 4,
"title": "Uptime-Kuma",
});
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Ntfy;

View File

@@ -10,7 +10,7 @@ class Octopush extends NotificationProvider {
try {
// Default - V2
if (notification.octopushVersion == 2 || !notification.octopushVersion) {
if (notification.octopushVersion === 2 || !notification.octopushVersion) {
let config = {
headers: {
"api-key": notification.octopushAPIKey,
@@ -30,14 +30,14 @@ class Octopush extends NotificationProvider {
"purpose": "alert",
"sender": notification.octopushSenderName
};
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
} else if (notification.octopushVersion == 1) {
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",
"sms_type": (notification.octopushDMSMSType === "sms_premium") ? "FR" : "XXX",
"transactional": "1",
//octopush not supporting non ascii char
"sms_text": msg.replace(/[^\x00-\x7F]/g, ""),
@@ -49,7 +49,7 @@ class Octopush extends NotificationProvider {
},
params: data
};
await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config)
await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config);
} else {
throw new Error("Unknown Octopush version!");
}

View File

@@ -0,0 +1,45 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class OneBot extends NotificationProvider {
name = "OneBot";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let httpAddr = notification.httpAddr;
if (!httpAddr.startsWith("http")) {
httpAddr = "http://" + httpAddr;
}
if (!httpAddr.endsWith("/")) {
httpAddr += "/";
}
let onebotAPIUrl = httpAddr + "send_msg";
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notification.accessToken,
}
};
let pushText = "UptimeKuma Alert: " + msg;
let data = {
"auto_escape": true,
"message": pushText,
};
if (notification.msgType === "group") {
data["message_type"] = "group";
data["group_id"] = notification.recieverId;
} else {
data["message_type"] = "private";
data["user_id"] = notification.recieverId;
}
await axios.post(onebotAPIUrl, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = OneBot;

View File

@@ -0,0 +1,113 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
const { setting } = require("../util-server");
let successMessage = "Sent Successfully.";
class PagerDuty extends NotificationProvider {
name = "PagerDuty";
/**
* @inheritdoc
*/
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
try {
if (heartbeatJSON == null) {
const title = "Uptime Kuma Alert";
const monitor = {
type: "ping",
url: "Uptime Kuma Test Button",
};
return this.postNotification(notification, title, msg, monitor);
}
if (heartbeatJSON.status === UP) {
const title = "Uptime Kuma Monitor ✅ Up";
const eventAction = notification.pagerdutyAutoResolve || null;
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, eventAction);
}
if (heartbeatJSON.status === DOWN) {
const title = "Uptime Kuma Monitor 🔴 Down";
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger");
}
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
/**
* Check if result is successful, result code should be in range 2xx
* @param {Object} result Axios response object
* @throws {Error} The status code is not in range 2xx
*/
checkResult(result) {
if (result.status == null) {
throw new Error("PagerDuty notification failed with invalid response!");
}
if (result.status < 200 || result.status >= 300) {
throw new Error("PagerDuty notification failed with status code " + result.status);
}
}
/**
* Send the message
* @param {BeanModel} notification Message title
* @param {string} title Message title
* @param {string} body Message
* @param {Object} monitorInfo Monitor details (For Up/Down only)
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
* @returns {string}
*/
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {
if (eventAction == null) {
return "No action required";
}
let monitorUrl;
if (monitorInfo.type === "port") {
monitorUrl = monitorInfo.hostname;
if (monitorInfo.port) {
monitorUrl += ":" + monitorInfo.port;
}
} else if (monitorInfo.hostname != null) {
monitorUrl = monitorInfo.hostname;
} else {
monitorUrl = monitorInfo.url;
}
const options = {
method: "POST",
url: notification.pagerdutyIntegrationUrl,
headers: { "Content-Type": "application/json" },
data: {
payload: {
summary: `[${title}] [${monitorInfo.name}] ${body}`,
severity: notification.pagerdutyPriority || "warning",
source: monitorUrl,
},
routing_key: notification.pagerdutyIntegrationKey,
event_action: eventAction,
dedup_key: "Uptime Kuma/" + monitorInfo.id,
}
};
const baseURL = await setting("primaryBaseURL");
if (baseURL && monitorInfo) {
options.client = "Uptime Kuma";
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);
}
let result = await axios.request(options);
this.checkResult(result);
if (result.statusText != null) {
return "PagerDuty notification succeed: " + result.statusText;
}
return successMessage;
}
}
module.exports = PagerDuty;

View File

@@ -12,7 +12,7 @@ class PromoSMS extends NotificationProvider {
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString('base64'),
"Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString("base64"),
"Accept": "text/json",
}
};
@@ -30,7 +30,7 @@ class PromoSMS extends NotificationProvider {
let error = "Something gone wrong. Api returned " + resp.data.response.status + ".";
this.throwGeneralAxiosError(error);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);

View File

@@ -23,26 +23,26 @@ class Pushbullet extends NotificationProvider {
"type": "note",
"title": "Uptime Kuma Alert",
"body": "Testing Successful.",
}
await axios.post(pushbulletUrl, testdata, config)
} else if (heartbeatJSON["status"] == DOWN) {
};
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) {
};
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)
};
await axios.post(pushbulletUrl, updata, config);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}
}

View File

@@ -0,0 +1,52 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const { DOWN, UP } = require("../../src/util");
class PushDeer extends NotificationProvider {
name = "PushDeer";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
let pushdeerlink = "https://api2.pushdeer.com/message/push";
let valid = msg != null && monitorJSON != null && heartbeatJSON != null;
let title;
if (valid && heartbeatJSON.status === UP) {
title = "## Uptime Kuma: " + monitorJSON.name + " up";
} else if (valid && heartbeatJSON.status === DOWN) {
title = "## Uptime Kuma: " + monitorJSON.name + " down";
} else {
title = "## Uptime Kuma Message";
}
let data = {
"pushkey": notification.pushdeerKey,
"text": title,
"desp": msg.replace(/\n/g, "\n\n"),
"type": "markdown",
};
try {
let res = await axios.post(pushdeerlink, data);
if ("error" in res.data) {
let error = res.data.error;
this.throwGeneralAxiosError(error);
}
if (res.data.content.result.length === 0) {
let error = "Invalid PushDeer key";
this.throwGeneralAxiosError(error);
} else if (JSON.parse(res.data.content.result[0]).success !== "ok") {
let error = "Unknown error";
this.throwGeneralAxiosError(error);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = PushDeer;

View File

@@ -9,36 +9,31 @@ class Pushover extends NotificationProvider {
let okMsg = "Sent Successfully.";
let pushoverlink = "https://api.pushover.net/1/messages.json";
let data = {
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg,
"user": notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1,
};
if (notification.pushoverdevice) {
data.device = notification.pushoverdevice;
}
try {
if (heartbeatJSON == null) {
let data = {
"message": msg,
"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;
} else {
data.message += "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"];
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);
}

View File

@@ -19,10 +19,10 @@ class Pushy extends NotificationProvider {
"badge": 1,
"sound": "ping.aiff"
}
})
});
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}
}

View File

@@ -2,7 +2,7 @@ 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");
const { getMonitorRelativeURL, DOWN } = require("../../src/util");
class RocketChat extends NotificationProvider {

View File

@@ -16,10 +16,10 @@ class Signal extends NotificationProvider {
};
let config = {};
await axios.post(notification.signalURL, data, config)
await axios.post(notification.signalURL, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}
}

View File

@@ -10,6 +10,7 @@ class Slack extends NotificationProvider {
/**
* Deprecated property notification.slackbutton
* Set it as primary base url if this is not yet set.
* @param {string} url The primary base URL to use
*/
static async deprecateURL(url) {
let currentPrimaryBaseURL = await setting("primaryBaseURL");

View File

@@ -1,6 +1,6 @@
const nodemailer = require("nodemailer");
const NotificationProvider = require("./notification-provider");
const { DOWN, UP } = require("../../src/util");
const { DOWN } = require("../../src/util");
class SMTP extends NotificationProvider {

View File

@@ -5,6 +5,12 @@ const { DOWN, UP } = require("../../src/util");
class Teams extends NotificationProvider {
name = "teams";
/**
* Generate the message to send
* @param {const} status The status constant
* @param {string} monitorName Name of monitor
* @returns {string}
*/
_statusMessageFactory = (status, monitorName) => {
if (status === DOWN) {
return `🔴 Application [${monitorName}] went down`;
@@ -14,6 +20,11 @@ class Teams extends NotificationProvider {
return "Notification";
};
/**
* Select theme color to use based on status
* @param {const} status The status constant
* @returns {string} Selected color in hex RGB format
*/
_getThemeColor = (status) => {
if (status === DOWN) {
return "ff0000";
@@ -24,6 +35,14 @@ class Teams extends NotificationProvider {
return "008cff";
};
/**
* Generate payload for notification
* @param {const} status The status of the monitor
* @param {string} monitorMessage Message to send
* @param {string} monitorName Name of monitor affected
* @param {string} monitorUrl URL of monitor affected
* @returns {Object}
*/
_notificationPayloadFactory = ({
status,
monitorMessage,
@@ -74,10 +93,21 @@ class Teams extends NotificationProvider {
};
};
/**
* Send the notification
* @param {string} webhookUrl URL to send the request to
* @param {Object} payload Payload generated by _notificationPayloadFactory
*/
_sendNotification = async (webhookUrl, payload) => {
await axios.post(webhookUrl, payload);
};
/**
* Send a general notification
* @param {string} webhookUrl URL to send request to
* @param {string} msg Message to send
* @returns {Promise<void>}
*/
_handleGeneralNotification = (webhookUrl, msg) => {
const payload = this._notificationPayloadFactory({
monitorMessage: msg

View File

@@ -0,0 +1,23 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class TechulusPush extends NotificationProvider {
name = "PushByTechulus";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, {
"title": "Uptime-Kuma",
"body": msg,
});
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = TechulusPush;

View File

@@ -14,12 +14,12 @@ class Telegram extends NotificationProvider {
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)
let msg = (error.response.data.description) ? error.response.data.description : "Error without description";
throw new Error(msg);
}
}
}

View File

@@ -24,17 +24,17 @@ class Webhook extends NotificationProvider {
config = {
headers: finalData.getHeaders(),
}
};
} else {
finalData = data;
}
await axios.post(notification.webhookURL, finalData, config)
await axios.post(notification.webhookURL, finalData, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error)
this.throwGeneralAxiosError(error);
}
}

View File

@@ -24,12 +24,18 @@ class WeCom extends NotificationProvider {
}
}
/**
* Generate the message to send
* @param {Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @param {string} msg General message
* @returns {Object}
*/
composeMessage(heartbeatJSON, msg) {
let title;
if (msg != null && heartbeatJSON != null && heartbeatJSON['status'] == UP) {
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === UP) {
title = "UptimeKuma Monitor Up";
}
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) {
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === DOWN) {
title = "UptimeKuma Monitor Down";
}
if (msg != null) {

View File

@@ -2,6 +2,7 @@ const { R } = require("redbean-node");
const Apprise = require("./notification-providers/apprise");
const Discord = require("./notification-providers/discord");
const Gotify = require("./notification-providers/gotify");
const Ntfy = require("./notification-providers/ntfy");
const Line = require("./notification-providers/line");
const LunaSea = require("./notification-providers/lunasea");
const Mattermost = require("./notification-providers/mattermost");
@@ -12,6 +13,7 @@ const ClickSendSMS = require("./notification-providers/clicksendsms");
const Pushbullet = require("./notification-providers/pushbullet");
const Pushover = require("./notification-providers/pushover");
const Pushy = require("./notification-providers/pushy");
const TechulusPush = require("./notification-providers/techulus-push");
const RocketChat = require("./notification-providers/rocket-chat");
const Signal = require("./notification-providers/signal");
const Slack = require("./notification-providers/slack");
@@ -23,17 +25,24 @@ const Feishu = require("./notification-providers/feishu");
const AliyunSms = require("./notification-providers/aliyun-sms");
const DingDing = require("./notification-providers/dingding");
const Bark = require("./notification-providers/bark");
const { log } = require("../src/util");
const SerwerSMS = require("./notification-providers/serwersms");
const Stackfield = require("./notification-providers/stackfield");
const WeCom = require("./notification-providers/wecom");
const GoogleChat = require("./notification-providers/google-chat");
const PagerDuty = require("./notification-providers/pagerduty");
const Gorush = require("./notification-providers/gorush");
const Alerta = require("./notification-providers/alerta");
const OneBot = require("./notification-providers/onebot");
const PushDeer = require("./notification-providers/pushdeer");
class Notification {
providerList = {};
/** Initialize the notification providers */
static init() {
console.log("Prepare Notification Providers");
log.info("notification", "Prepare Notification Providers");
this.providerList = {};
@@ -44,6 +53,7 @@ class Notification {
new Discord(),
new Teams(),
new Gotify(),
new Ntfy(),
new Line(),
new LunaSea(),
new Feishu(),
@@ -55,6 +65,7 @@ class Notification {
new Pushbullet(),
new Pushover(),
new Pushy(),
new TechulusPush(),
new RocketChat(),
new Signal(),
new Slack(),
@@ -65,7 +76,12 @@ class Notification {
new SerwerSMS(),
new Stackfield(),
new WeCom(),
new GoogleChat()
new GoogleChat(),
new PagerDuty(),
new Gorush(),
new Alerta(),
new OneBot(),
new PushDeer(),
];
for (let item of list) {
@@ -81,13 +97,13 @@ class Notification {
}
/**
*
* @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)
* Send a notification
* @param {BeanModel} notification
* @param {string} msg General Message
* @param {Object} monitorJSON Monitor details (For Up/Down only)
* @param {Object} heartbeatJSON Heartbeat details (For Up/Down only)
* @returns {Promise<string>} Successful msg
* Throw Error with fail msg
* @throws Error with fail msg
*/
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
if (this.providerList[notification.type]) {
@@ -97,28 +113,35 @@ class Notification {
}
}
/**
* Save a notification
* @param {Object} notification Notification to save
* @param {?number} notificationID ID of notification to update
* @param {number} userID ID of user who adds notification
* @returns {Promise<Bean>}
*/
static async save(notification, notificationID, userID) {
let bean
let bean;
if (notificationID) {
bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
notificationID,
userID,
])
]);
if (! bean) {
throw new Error("notification not found")
throw new Error("notification not found");
}
} else {
bean = R.dispense("notification")
bean = R.dispense("notification");
}
bean.name = notification.name;
bean.user_id = userID;
bean.config = JSON.stringify(notification);
bean.is_default = notification.isDefault || false;
await R.store(bean)
await R.store(bean);
if (notification.applyExisting) {
await applyNotificationEveryMonitor(bean.id, userID);
@@ -127,19 +150,29 @@ class Notification {
return bean;
}
/**
* Delete a notification
* @param {number} notificationID ID of notification to delete
* @param {number} userID ID of user who created notification
* @returns {Promise<void>}
*/
static async delete(notificationID, userID) {
let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
notificationID,
userID,
])
]);
if (! bean) {
throw new Error("notification not found")
throw new Error("notification not found");
}
await R.trash(bean)
await R.trash(bean);
}
/**
* Check if apprise exists
* @returns {boolean} Does the command apprise exist?
*/
static checkApprise() {
let commandExistsSync = require("command-exists").sync;
let exists = commandExistsSync("apprise");
@@ -148,6 +181,12 @@ class Notification {
}
/**
* Apply the notification to every monitor
* @param {number} notificationID ID of notification to apply
* @param {number} userID ID of user who created notification
* @returns {Promise<void>}
*/
async function applyNotificationEveryMonitor(notificationID, userID) {
let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
userID
@@ -157,17 +196,17 @@ async function applyNotificationEveryMonitor(notificationID, userID) {
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)
await R.store(relation);
}
}
}
module.exports = {
Notification,
}
};

View File

@@ -2,22 +2,42 @@ const passwordHashOld = require("password-hash");
const bcrypt = require("bcryptjs");
const saltRounds = 10;
/**
* Hash a password
* @param {string} password
* @returns {string}
*/
exports.generate = function (password) {
return bcrypt.hashSync(password, saltRounds);
}
};
/**
* Verify a password against a hash
* @param {string} password
* @param {string} hash
* @returns {boolean} Does the password match the hash?
*/
exports.verify = function (password, hash) {
if (isSHA1(hash)) {
return passwordHashOld.verify(password, hash)
return passwordHashOld.verify(password, hash);
}
return bcrypt.compareSync(password, hash);
}
};
/**
* Is the hash a SHA1 hash
* @param {string} hash
* @returns {boolean}
*/
function isSHA1(hash) {
return (typeof hash === "string" && hash.startsWith("sha1"))
return (typeof hash === "string" && hash.startsWith("sha1"));
}
/**
* Does the hash need to be rehashed?
* @returns {boolean}
*/
exports.needRehash = function (hash) {
return isSHA1(hash);
}
};

View File

@@ -8,6 +8,12 @@ const util = require("./util-server");
module.exports = Ping;
/**
* Constructor for ping class
* @param {string} host Host to ping
* @param {object} [options] Options for the ping command
* @param {array|string} [options.args] - Arguments to pass to the ping command
*/
function Ping(host, options) {
if (!host) {
throw new Error("You must specify a host to ping!");
@@ -75,8 +81,17 @@ function Ping(host, options) {
Ping.prototype.__proto__ = events.EventEmitter.prototype;
// SEND A PING
// ===========
/**
* Callback for send
* @callback pingCB
* @param {any} err Any error encountered
* @param {number} ms Ping time in ms
*/
/**
* Send a ping
* @param {pingCB} callback Callback to call with results
*/
Ping.prototype.send = function (callback) {
let self = this;
callback = callback || function (err, ms) {
@@ -125,6 +140,11 @@ Ping.prototype.send = function (callback) {
}
});
/**
* @param {Function} callback
*
* Generated by Trelent
*/
function onEnd() {
let stdout = this.stdout._stdout;
let stderr = this.stderr._stderr;
@@ -145,8 +165,10 @@ Ping.prototype.send = function (callback) {
}
};
// CALL Ping#send(callback) ON A TIMER
// ===================================
/**
* Ping every interval
* @param {pingCB} callback Callback to call with results
*/
Ping.prototype.start = function (callback) {
let self = this;
this._i = setInterval(function () {
@@ -155,8 +177,7 @@ Ping.prototype.start = function (callback) {
self.send(callback);
};
// STOP SENDING PINGS
// ==================
/** Stop sending pings */
Ping.prototype.stop = function () {
clearInterval(this._i);
};
@@ -165,7 +186,7 @@ Ping.prototype.stop = function () {
* 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
* @param {any} data
* @returns {string}
*/
function convertOutput(data) {

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