mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-28 01:04:05 +00:00
Merge branch 'louislam:master' into master
This commit is contained in:
commit
91d4c15b4d
11 changed files with 528 additions and 200 deletions
48
README.md
48
README.md
|
@ -10,16 +10,16 @@ It is a self-hosted monitoring tool like "Uptime Robot".
|
|||
|
||||
<img src="https://louislam.net/uptimekuma/1.jpg" width="512" alt="" />
|
||||
|
||||
## Features
|
||||
## ⭐ Features
|
||||
|
||||
* Monitoring uptime for HTTP(s) / TCP / Ping.
|
||||
* Fancy, Reactive, Fast UI/UX.
|
||||
* Notifications via Webhook, Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP) and more by Apprise.
|
||||
* 20 seconds interval.
|
||||
|
||||
## How to Use
|
||||
## 🔧 How to Install
|
||||
|
||||
### Docker
|
||||
### 🐳 Docker
|
||||
|
||||
```bash
|
||||
# Create a volume
|
||||
|
@ -31,18 +31,13 @@ docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name upti
|
|||
|
||||
Browse to http://localhost:3001 after started.
|
||||
|
||||
Change Port and Volume
|
||||
|
||||
```bash
|
||||
docker run -d --restart=always -p <YOUR_PORT>:3001 -v <YOUR_DIR OR VOLUME>:/app/data --name uptime-kuma louislam/uptime-kuma:1
|
||||
```
|
||||
If you want to change port and volume, or need to browse via a reserve proxy, please read: https://github.com/louislam/uptime-kuma/wiki/Installation.
|
||||
|
||||
### Without Docker (x86/x64 only)
|
||||
### 💪🏻 Without Docker (Recommanded for x86/x64 only)
|
||||
|
||||
Required Tools: Node.js >= 14, git and pm2.
|
||||
|
||||
(**Not recommanded for ARM CPU users.** Since there is no prebuilt for node-sqlite3, it is hard to get it running)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/louislam/uptime-kuma.git
|
||||
cd uptime-kuma
|
||||
|
@ -56,33 +51,15 @@ npm run start-server
|
|||
# Install PM2 if you don't have: npm install pm2 -g
|
||||
pm2 start npm --name uptime-kuma -- run start-server
|
||||
|
||||
# Listen to different port or hostname
|
||||
pm2 start npm --name uptime-kuma -- run start-server -- --port=80 --hostname=0.0.0.0
|
||||
|
||||
```
|
||||
|
||||
More useful commands if you have installed.
|
||||
|
||||
```bash
|
||||
pm2 start uptime-kuma
|
||||
pm2 restart uptime-kuma
|
||||
pm2 stop uptime-kuma
|
||||
```
|
||||
|
||||
Browse to http://localhost:3001 after started.
|
||||
|
||||
### (Optional) One more step for Reverse Proxy
|
||||
If you want to change port and hostname, or need to browse via a reserve proxy, please read: https://github.com/louislam/uptime-kuma/wiki/Installation.
|
||||
|
||||
This is optional for someone who want to do reverse proxy.
|
||||
## 🆙 How to Update
|
||||
|
||||
Unlikely other web apps, Uptime Kuma is based on WebSocket. You need two more headers **"Upgrade"** and **"Connection"** in order to reverse proxy WebSocket.
|
||||
|
||||
Please read wiki for more info:
|
||||
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy
|
||||
|
||||
## How to Update
|
||||
|
||||
### Docker
|
||||
### 🆙🐳 Docker
|
||||
|
||||
Re-pull the latest docker image and create another container with the same volume.
|
||||
|
||||
|
@ -97,9 +74,10 @@ docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name upti
|
|||
|
||||
PS: For every new release, it takes some time to build the docker image, please be patient if it is not available yet.
|
||||
|
||||
### Without Docker
|
||||
### 🆙 💪🏻 Without Docker
|
||||
|
||||
```bash
|
||||
cd <uptime-kuma-directory>
|
||||
git fetch --all
|
||||
git checkout 1.1.0 --force
|
||||
npm install
|
||||
|
@ -107,12 +85,12 @@ npm run build
|
|||
pm2 restart uptime-kuma
|
||||
```
|
||||
|
||||
## What's Next?
|
||||
## 🆕 What's Next?
|
||||
|
||||
I will mark requests/issues to the next milestone.
|
||||
https://github.com/louislam/uptime-kuma/milestones
|
||||
|
||||
## More Screenshots
|
||||
## 🖼 More Screenshots
|
||||
|
||||
Dark Mode:
|
||||
|
||||
|
@ -144,3 +122,5 @@ If you want to report a bug or request a new feature. Free feel to open a new is
|
|||
If you want to modify Uptime Kuma, this guideline maybe useful for you: https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md
|
||||
|
||||
English proofreading is needed too, because my grammar is not that great sadly. Feel free to correct my grammar in this Readme, source code or wiki.
|
||||
|
||||
🐻
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#5cdd8b"/>
|
||||
<meta name="theme-color" id="theme-color" content="" />
|
||||
<meta name="description" content="Uptime Kuma monitoring tool" />
|
||||
<title>Uptime Kuma</title>
|
||||
</head>
|
||||
|
|
286
package-lock.json
generated
286
package-lock.json
generated
|
@ -756,9 +756,9 @@
|
|||
}
|
||||
},
|
||||
"@types/bootstrap": {
|
||||
"version": "5.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.0.17.tgz",
|
||||
"integrity": "sha512-uQQQ3p+zw10VjZLvtCuKWI6QgVCYEnK/yHnno3gyEhikfQdiZexS2XPxjWRboGmX135o470GkmCta9eAgQMVLQ==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.1.1.tgz",
|
||||
"integrity": "sha512-W/fEBlqwaJFh+3sCz/H88LPsLt/zLsEECFlrAOkrRPjWuo/ETl8u0JefIerCdc8+WukowQS1f60eIJOwkCBwhg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@popperjs/core": "^2.9.2",
|
||||
|
@ -960,45 +960,45 @@
|
|||
}
|
||||
},
|
||||
"@vitejs/plugin-vue": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.3.0.tgz",
|
||||
"integrity": "sha512-wJvuJdTBjvucUX0vK4fuy60t+A9bJSZxc59vp1Y+8kiOd0NU5kFt4lay72gMWPeR+lSUjrTmGUq8Uzb99Jbw3A==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.4.0.tgz",
|
||||
"integrity": "sha512-RkqfJHz9wdLKBp5Yi+kQL8BAljdrvPoccQm2PTZc/UcL4EjD11xsv2PPCduYx2oV1a/bpSKA3sD5sxOHFhz+LA==",
|
||||
"dev": true
|
||||
},
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.1.tgz",
|
||||
"integrity": "sha512-UEJf2ZGww5wGVdrWIXIZo04KdJFGPmI2bHRUsBZ3AdyCAqJ5ykRXKOBn1OR1hvA2YzimudOEyHM+DpbBv91Kww==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.2.tgz",
|
||||
"integrity": "sha512-QhCI0ZU5nAR0LMcLgzW3v75374tIrHGp8XG5CzJS7Nsy+iuignbE4MZ2XJfh5TGIrtpuzfWA4eTIfukZf/cRdg==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.12.0",
|
||||
"@babel/types": "^7.12.0",
|
||||
"@vue/shared": "3.2.1",
|
||||
"@vue/shared": "3.2.2",
|
||||
"estree-walker": "^2.0.1",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.1.tgz",
|
||||
"integrity": "sha512-tXg8tkPb3j54zNfWqoao9T1JI41yWPz8TROzmif/QNNA46eq8/SRuRsBd36i47GWaz7mh+yg3vOJ87/YBjcMyQ==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.2.tgz",
|
||||
"integrity": "sha512-ggcc+NV/ENIE0Uc3TxVE/sKrhYVpLepMAAmEiQ047332mbKOvUkowz4TTFZ+YkgOIuBOPP0XpCxmCMg7p874mA==",
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.2.1",
|
||||
"@vue/shared": "3.2.1"
|
||||
"@vue/compiler-core": "3.2.2",
|
||||
"@vue/shared": "3.2.2"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-sfc": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.5.tgz",
|
||||
"integrity": "sha512-mtMY6xMvZeSRx9MTa1+NgJWndrkzVTdJ1pQAmAKQuxyb5LsHVvrgP7kcQFvxPHVpLVTORbTJWHaiqoKrJvi1iA==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.2.tgz",
|
||||
"integrity": "sha512-hrtqpQ5L6IPn5v7yVRo7uvLcQxv0z1+KBjZBWMBOcrXz4t+PKUxU/SWd6Tl9T8FDmYlunzKUh6lcx+2CLo6f5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.13.9",
|
||||
"@babel/types": "^7.13.0",
|
||||
"@types/estree": "^0.0.48",
|
||||
"@vue/compiler-core": "3.1.5",
|
||||
"@vue/compiler-dom": "3.1.5",
|
||||
"@vue/compiler-ssr": "3.1.5",
|
||||
"@vue/shared": "3.1.5",
|
||||
"@vue/compiler-core": "3.2.2",
|
||||
"@vue/compiler-dom": "3.2.2",
|
||||
"@vue/compiler-ssr": "3.2.2",
|
||||
"@vue/shared": "3.2.2",
|
||||
"consolidate": "^0.16.0",
|
||||
"estree-walker": "^2.0.1",
|
||||
"hash-sum": "^2.0.0",
|
||||
|
@ -1009,116 +1009,54 @@
|
|||
"postcss-modules": "^4.0.0",
|
||||
"postcss-selector-parser": "^6.0.4",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.5.tgz",
|
||||
"integrity": "sha512-TXBhFinoBaXKDykJzY26UEuQU1K07FOp/0Ie+OXySqqk0bS0ZO7Xvl7UmiTUPYcLrWbxWBR7Bs/y55AI0MNc2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.12.0",
|
||||
"@babel/types": "^7.12.0",
|
||||
"@vue/shared": "3.1.5",
|
||||
"estree-walker": "^2.0.1",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.5.tgz",
|
||||
"integrity": "sha512-ZsL3jqJ52OjGU/YiT/9XiuZAmWClKInZM2aFJh9gnsAPqOrj2JIELMbkIFpVKR/CrVO/f2VxfPiiQdQTr65jcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.1.5",
|
||||
"@vue/shared": "3.1.5"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/compiler-ssr": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.5.tgz",
|
||||
"integrity": "sha512-CU5N7Di/a4lyJ18LGJxJYZS2a8PlLdWpWHX9p/XcsjT2TngMpj3QvHVRkuik2u8QrIDZ8OpYmTyj1WDNsOV+Dg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.2.tgz",
|
||||
"integrity": "sha512-rVl1agMFhdEN3Go0bCriXo+3cysxKIuRP0yh1Wd8ysRrKfAmokyDhUA8PrGSq2Ymj/LdZTh+4OKfj3p2+C+hlA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.1.5",
|
||||
"@vue/shared": "3.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.5.tgz",
|
||||
"integrity": "sha512-TXBhFinoBaXKDykJzY26UEuQU1K07FOp/0Ie+OXySqqk0bS0ZO7Xvl7UmiTUPYcLrWbxWBR7Bs/y55AI0MNc2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.12.0",
|
||||
"@babel/types": "^7.12.0",
|
||||
"@vue/shared": "3.1.5",
|
||||
"estree-walker": "^2.0.1",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.5.tgz",
|
||||
"integrity": "sha512-ZsL3jqJ52OjGU/YiT/9XiuZAmWClKInZM2aFJh9gnsAPqOrj2JIELMbkIFpVKR/CrVO/f2VxfPiiQdQTr65jcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.1.5",
|
||||
"@vue/shared": "3.1.5"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"dev": true
|
||||
}
|
||||
"@vue/compiler-dom": "3.2.2",
|
||||
"@vue/shared": "3.2.2"
|
||||
}
|
||||
},
|
||||
"@vue/devtools-api": {
|
||||
"version": "6.0.0-beta.14",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.14.tgz",
|
||||
"integrity": "sha512-44fPrrN1cqcs6bFkT0C+yxTM6PZXLbR+ESh1U1j8UD22yO04gXvxH62HApMjLbS3WqJO/iCNC+CYT+evPQh2EQ=="
|
||||
"version": "6.0.0-beta.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.15.tgz",
|
||||
"integrity": "sha512-quBx4Jjpexo6KDiNUGFr/zF/2A4srKM9S9v2uHgMXSU//hjgq1eGzqkIFql8T9gfX5ZaVOUzYBP3jIdIR3PKIA=="
|
||||
},
|
||||
"@vue/reactivity": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.1.tgz",
|
||||
"integrity": "sha512-4Lja2KmyiKvuraDed6dXK2A6+r/7x7xGDA7vVR2Aqc8hQVu0+FWeVX+IBfiVOSpbZXFlHLNmCBFkbuWLQSlgxg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.2.tgz",
|
||||
"integrity": "sha512-IHjhtmrhK6dzacj/EnLQDWOaA3HuzzVk6w84qgV8EpS4uWGIJXiRalMRg6XvGW2ykJvIl3pLsF0aBFlTMRiLOA==",
|
||||
"requires": {
|
||||
"@vue/shared": "3.2.1"
|
||||
"@vue/shared": "3.2.2"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.1.tgz",
|
||||
"integrity": "sha512-IsgelRM/5hYeRhz5+ECi66XvYDdjG2t4lARjHvCXw5s9Q4N6uIbjLMwtLzAWRxYf3/y258BrD+ehxAi943ScJg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.2.tgz",
|
||||
"integrity": "sha512-/aUk1+GO/VPX0oVxhbzSWE1zrf3/wGCsO1ALNisVokYftKqfqLDjbJHE6mrI2hx3MiuwbHrWjJClkGUVTIOPEQ==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.2.1",
|
||||
"@vue/shared": "3.2.1"
|
||||
"@vue/reactivity": "3.2.2",
|
||||
"@vue/shared": "3.2.2"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.1.tgz",
|
||||
"integrity": "sha512-bUAHUSe49A5wYdHQ8wsLU1CMPXaG2fRuv2661mx/6Q9+20QxglT3ss8ZeL6AVRu16JNJMcdvTTsNpbnMbVc/lQ==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.2.tgz",
|
||||
"integrity": "sha512-1Le/NpCfawCOfePfJezvWUF+oCVLU8N+IHN4oFDOxRe6/PgHNJ+yT+YdxFifBfI+TIAoXI/9PsnqzmJZV+xsmw==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.2.1",
|
||||
"@vue/shared": "3.2.1",
|
||||
"@vue/runtime-core": "3.2.2",
|
||||
"@vue/shared": "3.2.2",
|
||||
"csstype": "^2.6.8"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.1.tgz",
|
||||
"integrity": "sha512-INN92dVBNgd0TW9BqfQQKx/HWGCHhUUbAV5EZ5FgSCiEdwuZsJbGt1mdnaD9IxGhpiyOjP2ClxGG8SFp7ELcWg=="
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.2.tgz",
|
||||
"integrity": "sha512-dvYb318tk9uOzHtSaT3WII/HscQSIRzoCZ5GyxEb3JlkEXASpAUAQwKnvSe2CudnF8XHFRTB7VITWSnWNLZUtA=="
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
|
@ -1779,6 +1717,16 @@
|
|||
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
|
||||
"dev": true
|
||||
},
|
||||
"chart.js": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.0.tgz",
|
||||
"integrity": "sha512-J1a4EAb1Gi/KbhwDRmoovHTRuqT8qdF0kZ4XgwxpGethJHUdDrkqyPYwke0a+BuvSeUxPf8Cos6AX2AB8H8GLA=="
|
||||
},
|
||||
"chartjs-adapter-dayjs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-dayjs/-/chartjs-adapter-dayjs-1.0.0.tgz",
|
||||
"integrity": "sha512-EnbVqTJGFKLpg1TROLdCEufrzbmIa2oeLGx8O2Wdjw2EoMudoOo9+YFu+6CM0Z0hQ/v3yq/e/Y6efQMu22n8Jg=="
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
|
||||
|
@ -1945,9 +1893,9 @@
|
|||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz",
|
||||
"integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.1.tgz",
|
||||
"integrity": "sha512-AAkP8i35EbefU+JddyWi12AWE9f2N/qr/pwnDtWz4nyUIBGMJPX99ANFFRSw6FefM374lDujdtLDyhN2A/btHw==",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
|
@ -2441,9 +2389,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-vue": {
|
||||
"version": "7.15.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.15.1.tgz",
|
||||
"integrity": "sha512-4/r+n/i+ovyeW2gVRRH92kpy4lkpFbyPR4BMxGBTLtGnwqOKKzjSo6EMSaT0RhWPvEjK9uifcY8e7z5n8BIEgw==",
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.16.0.tgz",
|
||||
"integrity": "sha512-0E2dVvVC7I2Xm1HXyx+ZwPj9CNX4NJjs4K4r+GVsHWyt5Pew3JLD4fI7A91b2jeL0TXE7LlszrwLSTJU9eqehw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-utils": "^2.1.0",
|
||||
|
@ -4789,9 +4737,9 @@
|
|||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz",
|
||||
"integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==",
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz",
|
||||
"integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colorette": "^1.2.2",
|
||||
|
@ -4874,9 +4822,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"postcss-modules": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz",
|
||||
"integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==",
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.2.2.tgz",
|
||||
"integrity": "sha512-/H08MGEmaalv/OU8j6bUKi/kZr2kqGF6huAW8m9UAgOLWtpFdhA14+gPBoymtqyv+D4MLsmqaF2zvIegdCxJXg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"generic-names": "^2.0.1",
|
||||
|
@ -5148,9 +5096,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"prom-client": {
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-13.1.0.tgz",
|
||||
"integrity": "sha512-jT9VccZCWrJWXdyEtQddCDszYsiuWj5T0ekrPszi/WEegj3IZy6Mm09iOOVM86A4IKMWq8hZkT2dD9MaSe+sng==",
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-13.2.0.tgz",
|
||||
"integrity": "sha512-wGr5mlNNdRNzEhRYXgboUU2LxHWIojxscJKmtG3R8f4/KiWqyYgXTLHs0+Ted7tG3zFT7pgHJbtomzZ1L0ARaQ==",
|
||||
"requires": {
|
||||
"tdigest": "^0.1.1"
|
||||
}
|
||||
|
@ -6805,20 +6753,94 @@
|
|||
}
|
||||
},
|
||||
"vue": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.1.tgz",
|
||||
"integrity": "sha512-0jhXluF5mzTAK5bXw/8yq4McvsI8HwEWI4cnQwJeN8NYGRbwh9wwuE4FNv1Kej9pxBB5ajTNsWr0M6DPs5EJZg==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.2.tgz",
|
||||
"integrity": "sha512-D/LuzAV30CgNJYGyNheE/VUs5N4toL2IgmS6c9qeOxvyh0xyn4exyRqizpXIrsvfx34zG9x5gCI2tdRHCGvF9w==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.2",
|
||||
"@vue/runtime-dom": "3.2.2",
|
||||
"@vue/shared": "3.2.2"
|
||||
}
|
||||
},
|
||||
"vue-chart-3": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-chart-3/-/vue-chart-3-0.5.7.tgz",
|
||||
"integrity": "sha512-BccfPv2rodY6IOppYHvMluVmIJE1CHfp5uW2DXrHrm1kIzaafLwpQ5SwdrxuCevn/QhKoi7azzcxwRcoWbX9hg==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "^3.2.1",
|
||||
"@vue/runtime-dom": "^3.2.1",
|
||||
"csstype": "^3.0.8",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^3.1.23",
|
||||
"vue-demi": "^0.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/reactivity": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.1.tgz",
|
||||
"integrity": "sha512-4Lja2KmyiKvuraDed6dXK2A6+r/7x7xGDA7vVR2Aqc8hQVu0+FWeVX+IBfiVOSpbZXFlHLNmCBFkbuWLQSlgxg==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.1",
|
||||
"@vue/runtime-dom": "3.2.1",
|
||||
"@vue/shared": "3.2.1"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-core": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.1.tgz",
|
||||
"integrity": "sha512-IsgelRM/5hYeRhz5+ECi66XvYDdjG2t4lARjHvCXw5s9Q4N6uIbjLMwtLzAWRxYf3/y258BrD+ehxAi943ScJg==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.2.1",
|
||||
"@vue/shared": "3.2.1"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.1.tgz",
|
||||
"integrity": "sha512-bUAHUSe49A5wYdHQ8wsLU1CMPXaG2fRuv2661mx/6Q9+20QxglT3ss8ZeL6AVRu16JNJMcdvTTsNpbnMbVc/lQ==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.2.1",
|
||||
"@vue/shared": "3.2.1",
|
||||
"csstype": "^2.6.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "2.6.17",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz",
|
||||
"integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.1.tgz",
|
||||
"integrity": "sha512-INN92dVBNgd0TW9BqfQQKx/HWGCHhUUbAV5EZ5FgSCiEdwuZsJbGt1mdnaD9IxGhpiyOjP2ClxGG8SFp7ELcWg=="
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz",
|
||||
"integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw=="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.1.23",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||
"integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-confirm-dialog": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-confirm-dialog/-/vue-confirm-dialog-1.0.2.tgz",
|
||||
"integrity": "sha512-gTo1bMDWOLd/6ihmWv8VlPxhc9QaKoE5YqlsKydUOfrrQ3Q3taljF6yI+1TMtAtJLrvZ8DYrePhgBhY1VCJzbQ=="
|
||||
},
|
||||
"vue-demi": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.10.1.tgz",
|
||||
"integrity": "sha512-L6Oi+BvmMv6YXvqv5rJNCFHEKSVu7llpWWJczqmAQYOdmPPw5PNYoz1KKS//Fxhi+4QP64dsPjtmvnYGo1jemA=="
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.10.0.tgz",
|
||||
|
@ -6865,9 +6887,9 @@
|
|||
"integrity": "sha512-Xp9fGJECns45v+v8jXbCIsAkCybYkEg0lNwr7Z6HDUSMyx2TEIK2giipPE+qXiShEc1Ipn+ZtttH2iq9hwXP4Q=="
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.10.tgz",
|
||||
"integrity": "sha512-YbPf6QnZpyyWfnk7CUt2Bme+vo7TLfg1nGZNkvYqKYh4vLaFw6Gn8bPGdmt5m4qrGnKoXLqc4htAsd3dIukICA==",
|
||||
"version": "4.0.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.11.tgz",
|
||||
"integrity": "sha512-sha6I8fx9HWtvTrFZfxZkiQQBpqSeT+UCwauYjkdOQYRvwsGwimlQQE2ayqUwuuXGzquFpCPoXzYKWlzL4OuXg==",
|
||||
"requires": {
|
||||
"@vue/devtools-api": "^6.0.0-beta.14"
|
||||
}
|
||||
|
|
21
package.json
21
package.json
|
@ -31,11 +31,14 @@
|
|||
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.0-4",
|
||||
"@louislam/sqlite3": "^5.0.3",
|
||||
"@popperjs/core": "^2.9.3",
|
||||
"args-parser": "^1.3.0",
|
||||
"axios": "^0.21.1",
|
||||
"bcrypt": "^5.0.1",
|
||||
"bootstrap": "^5.1.0",
|
||||
"chart.js": "^3.5.0",
|
||||
"chartjs-adapter-dayjs": "^1.0.0",
|
||||
"command-exists": "^1.2.9",
|
||||
"dayjs": "^1.10.6",
|
||||
"express": "^4.17.1",
|
||||
|
@ -45,29 +48,29 @@
|
|||
"jsonwebtoken": "^8.5.1",
|
||||
"nodemailer": "^6.6.3",
|
||||
"password-hash": "^1.2.2",
|
||||
"prom-client": "^13.1.0",
|
||||
"prom-client": "^13.2.0",
|
||||
"prometheus-api-metrics": "^3.2.0",
|
||||
"redbean-node": "0.0.21",
|
||||
"socket.io": "^4.1.3",
|
||||
"socket.io-client": "^4.1.3",
|
||||
"@louislam/sqlite3": "^5.0.3",
|
||||
"tcp-ping": "^0.1.1",
|
||||
"v-pagination-3": "^0.1.6",
|
||||
"vue": "^3.2.1",
|
||||
"vue": "^3.2.2",
|
||||
"vue-chart-3": "^0.5.7",
|
||||
"vue-confirm-dialog": "^1.0.2",
|
||||
"vue-multiselect": "^3.0.0-alpha.2",
|
||||
"vue-router": "^4.0.10",
|
||||
"vue-router": "^4.0.11",
|
||||
"vue-toastification": "^2.0.0-rc.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.15.0",
|
||||
"@types/bootstrap": "^5.0.17",
|
||||
"@types/bootstrap": "^5.1.1",
|
||||
"@vitejs/plugin-legacy": "^1.5.1",
|
||||
"@vitejs/plugin-vue": "^1.3.0",
|
||||
"@vue/compiler-sfc": "^3.1.5",
|
||||
"core-js": "^3.16.0",
|
||||
"@vitejs/plugin-vue": "^1.4.0",
|
||||
"@vue/compiler-sfc": "^3.2.2",
|
||||
"core-js": "^3.16.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^7.15.1",
|
||||
"eslint-plugin-vue": "^7.16.0",
|
||||
"sass": "^1.37.5",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-recommended": "^5.0.0",
|
||||
|
|
|
@ -11,7 +11,7 @@ const { tcping, ping, checkCertificate, checkStatusCode } = require("../util-ser
|
|||
const { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { Notification } = require("../notification")
|
||||
const version = require("../package.json").version;
|
||||
const version = require("../../package.json").version;
|
||||
|
||||
/**
|
||||
* status:
|
||||
|
@ -80,6 +80,10 @@ class Monitor extends BeanModel {
|
|||
|
||||
const beat = async () => {
|
||||
|
||||
// Expose here for prometheus update
|
||||
// undefined if not https
|
||||
let tlsInfo = undefined;
|
||||
|
||||
if (! previousBeat) {
|
||||
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
||||
this.id,
|
||||
|
@ -133,7 +137,7 @@ class Monitor extends BeanModel {
|
|||
let certInfoStartTime = dayjs().valueOf();
|
||||
if (this.getUrl()?.protocol === "https:") {
|
||||
try {
|
||||
await this.updateTlsInfo(checkCertificate(res));
|
||||
tlsInfo = await this.updateTlsInfo(checkCertificate(res));
|
||||
} catch (e) {
|
||||
if (e.message !== "No TLS certificate in response") {
|
||||
console.error(e.message)
|
||||
|
@ -255,7 +259,7 @@ class Monitor extends BeanModel {
|
|||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`)
|
||||
}
|
||||
|
||||
prometheus.update(bean)
|
||||
prometheus.update(bean, tlsInfo)
|
||||
|
||||
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
||||
|
||||
|
@ -290,7 +294,7 @@ class Monitor extends BeanModel {
|
|||
/**
|
||||
* Store TLS info to database
|
||||
* @param checkCertificateResult
|
||||
* @returns {Promise<void>}
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async updateTlsInfo(checkCertificateResult) {
|
||||
let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
|
||||
|
@ -302,6 +306,8 @@ class Monitor extends BeanModel {
|
|||
}
|
||||
tls_info_bean.info_json = JSON.stringify(checkCertificateResult);
|
||||
await R.store(tls_info_bean);
|
||||
|
||||
return checkCertificateResult;
|
||||
}
|
||||
|
||||
static async sendStats(io, monitorID, userID) {
|
||||
|
|
|
@ -193,6 +193,34 @@ class Notification {
|
|||
console.log(error)
|
||||
return false;
|
||||
}
|
||||
} else if (notification.type === "octopush") {
|
||||
try {
|
||||
let config = {
|
||||
headers: {
|
||||
'api-key': notification.octopushAPIKey,
|
||||
'api-login': notification.octopushLogin,
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
let data = {
|
||||
"recipients": [
|
||||
{
|
||||
"phone_number": notification.octopushPhoneNumber
|
||||
}
|
||||
],
|
||||
//octopush not supporting non ascii char
|
||||
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||
"type": notification.octopushSMSType,
|
||||
"purpose": "alert",
|
||||
"sender": notification.octopushSenderName
|
||||
};
|
||||
|
||||
await axios.post(`https://api.octopush.com/v1/public/sms-campaign/send`, data, config)
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return false;
|
||||
}
|
||||
} else if (notification.type === "slack") {
|
||||
try {
|
||||
if (heartbeatJSON == null) {
|
||||
|
@ -326,6 +354,41 @@ class Notification {
|
|||
throwGeneralAxiosError(error)
|
||||
}
|
||||
|
||||
} else if (notification.type === "pushbullet") {
|
||||
try {
|
||||
let pushbulletUrl = `https://api.pushbullet.com/v2/pushes`;
|
||||
let config = {
|
||||
headers: {
|
||||
'Access-Token': notification.pushbulletAccessToken,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
if (heartbeatJSON == null) {
|
||||
let testdata = {
|
||||
"type": "note",
|
||||
"title": "Uptime Kuma Alert",
|
||||
"body": "Testing Successful.",
|
||||
}
|
||||
await axios.post(pushbulletUrl, testdata, config)
|
||||
} else if (heartbeatJSON["status"] == 0) {
|
||||
let downdata = {
|
||||
"type": "note",
|
||||
"title": "UptimeKuma Alert:" + monitorJSON["name"],
|
||||
"body": "[🔴 Down]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
|
||||
}
|
||||
await axios.post(pushbulletUrl, downdata, config)
|
||||
} else if (heartbeatJSON["status"] == 1) {
|
||||
let updata = {
|
||||
"type": "note",
|
||||
"title": "UptimeKuma Alert:" + monitorJSON["name"],
|
||||
"body": "[✅ Up]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
|
||||
}
|
||||
await axios.post(pushbulletUrl, updata, config)
|
||||
}
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
throwGeneralAxiosError(error)
|
||||
}
|
||||
} else {
|
||||
throw new Error("Notification type is not supported")
|
||||
}
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
const PrometheusClient = require('prom-client');
|
||||
const PrometheusClient = require("prom-client");
|
||||
|
||||
const commonLabels = [
|
||||
'monitor_name',
|
||||
'monitor_type',
|
||||
'monitor_url',
|
||||
'monitor_hostname',
|
||||
'monitor_port',
|
||||
"monitor_name",
|
||||
"monitor_type",
|
||||
"monitor_url",
|
||||
"monitor_hostname",
|
||||
"monitor_port",
|
||||
]
|
||||
|
||||
const monitor_cert_days_remaining = new PrometheusClient.Gauge({
|
||||
name: "monitor_cert_days_remaining",
|
||||
help: "The number of days remaining until the certificate expires",
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
const monitor_cert_is_valid = new PrometheusClient.Gauge({
|
||||
name: "monitor_cert_is_valid",
|
||||
help: "Is the certificate still valid? (1 = Yes, 0= No)",
|
||||
labelNames: commonLabels
|
||||
});
|
||||
const monitor_response_time = new PrometheusClient.Gauge({
|
||||
name: 'monitor_response_time',
|
||||
help: 'Monitor Response Time (ms)',
|
||||
name: "monitor_response_time",
|
||||
help: "Monitor Response Time (ms)",
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
const monitor_status = new PrometheusClient.Gauge({
|
||||
name: 'monitor_status',
|
||||
help: 'Monitor Status (1 = UP, 0= DOWN)',
|
||||
name: "monitor_status",
|
||||
help: "Monitor Status (1 = UP, 0= DOWN)",
|
||||
labelNames: commonLabels
|
||||
});
|
||||
|
||||
|
@ -33,7 +44,27 @@ class Prometheus {
|
|||
}
|
||||
}
|
||||
|
||||
update(heartbeat) {
|
||||
update(heartbeat, tlsInfo) {
|
||||
if (typeof tlsInfo !== "undefined") {
|
||||
try {
|
||||
let is_valid = 0
|
||||
if (tlsInfo.valid == true) {
|
||||
is_valid = 1
|
||||
} else {
|
||||
is_valid = 0
|
||||
}
|
||||
monitor_cert_is_valid.set(this.monitorLabelValues, is_valid)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
try {
|
||||
monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.daysRemaining)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
monitor_status.set(this.monitorLabelValues, heartbeat.status)
|
||||
} catch (e) {
|
||||
|
@ -41,7 +72,7 @@ class Prometheus {
|
|||
}
|
||||
|
||||
try {
|
||||
if (typeof heartbeat.ping === 'number') {
|
||||
if (typeof heartbeat.ping === "number") {
|
||||
monitor_response_time.set(this.monitorLabelValues, heartbeat.ping)
|
||||
} else {
|
||||
// Is it good?
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
<option value="slack">Slack</option>
|
||||
<option value="pushover">Pushover</option>
|
||||
<option value="pushy">Pushy</option>
|
||||
<option value="octopush">Octopush</option>
|
||||
<option value="lunasea">LunaSea</option>
|
||||
<option value="apprise">Apprise (Support 50+ Notification services)</option>
|
||||
<option value="pushbullet">Pushbullet</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
@ -252,6 +254,37 @@
|
|||
</p>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type === 'octopush'">
|
||||
<div class="mb-3">
|
||||
<label for="octopush-key" class="form-label">API KEY</label>
|
||||
<input id="octopush-key" v-model="notification.octopushAPIKey" type="text" class="form-control" required>
|
||||
<label for="octopush-login" class="form-label">API LOGIN</label>
|
||||
<input id="octopush-login" v-model="notification.octopushLogin" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="octopush-type-sms" class="form-label">SMS Type</label>
|
||||
<select id="octopush-type-sms" v-model="notification.octopushSMSType" class="form-select">
|
||||
<option value="sms_premium">Premium (Fast - recommended for alerting)</option>
|
||||
<option value="sms_low_cost">Low Cost (Slow, sometimes blocked by operator)</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
Check octopush prices <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="octopush-phone-number" class="form-label">Phone number (intl format, eg : +33612345678) </label>
|
||||
<input id="octopush-phone-number" v-model="notification.octopushPhoneNumber" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="octopush-sender-name" class="form-label">SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)</label>
|
||||
<input id="octopush-sender-name" v-model="notification.octopushSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 8px;">
|
||||
More info on: <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type === 'pushover'">
|
||||
<div class="mb-3">
|
||||
<label for="pushover-user" class="form-label">User Key<span style="color:red;"><sup>*</sup></span></label>
|
||||
|
@ -339,6 +372,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type === 'pushbullet'">
|
||||
<div class="mb-3">
|
||||
<label for="pushbullet-access-token" class="form-label">Access Token</label>
|
||||
<input id="pushbullet-access-token" v-model="notification.pushbulletAccessToken" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 8px;">
|
||||
More info on: <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a>
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button v-if="id" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm">
|
||||
|
|
152
src/components/PingChart.vue
Normal file
152
src/components/PingChart.vue
Normal file
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<LineChart :chart-data="chartData" :height="100" :options="chartOptions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BarController, BarElement, Chart, Filler, LinearScale, LineController, LineElement, PointElement, TimeScale, Tooltip } from "chart.js";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import "chartjs-adapter-dayjs";
|
||||
import { LineChart } from "vue-chart-3";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
Chart.register(LineController, BarController, LineElement, PointElement, TimeScale, BarElement, LinearScale, Tooltip, Filler);
|
||||
|
||||
export default {
|
||||
components: { LineChart },
|
||||
props: {
|
||||
monitorId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartPeriodHrs: 6,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
chartOptions() {
|
||||
return {
|
||||
responsive: true,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 10,
|
||||
right: 30,
|
||||
top: 30,
|
||||
bottom: 10,
|
||||
},
|
||||
},
|
||||
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0,
|
||||
},
|
||||
bar: {
|
||||
barThickness: "flex",
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "minute",
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
autoSkipPadding: 10,
|
||||
},
|
||||
grid: {
|
||||
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: "Response Time (ms)",
|
||||
},
|
||||
offset: false,
|
||||
grid: {
|
||||
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)",
|
||||
},
|
||||
},
|
||||
y1: {
|
||||
display: false,
|
||||
position: "right",
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
min: 0,
|
||||
max: 1,
|
||||
offset: false,
|
||||
},
|
||||
},
|
||||
bounds: "ticks",
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
padding: 10,
|
||||
filter: function (tooltipItem) {
|
||||
return tooltipItem.datasetIndex === 0;
|
||||
},
|
||||
callbacks: {
|
||||
label: (context) => {
|
||||
return ` ${new Intl.NumberFormat().format(context.parsed.y)} ms`
|
||||
},
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
chartData() {
|
||||
let ping_data = [];
|
||||
let down_data = [];
|
||||
if (this.monitorId in this.$root.heartbeatList) {
|
||||
ping_data = this.$root.heartbeatList[this.monitorId]
|
||||
.filter(
|
||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
||||
.map((beat) => {
|
||||
return {
|
||||
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"),
|
||||
y: beat.ping,
|
||||
};
|
||||
});
|
||||
down_data = this.$root.heartbeatList[this.monitorId]
|
||||
.filter(
|
||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
||||
.map((beat) => {
|
||||
return {
|
||||
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"),
|
||||
y: beat.status === 0 ? 1 : 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
return {
|
||||
datasets: [
|
||||
{
|
||||
data: ping_data,
|
||||
fill: "origin",
|
||||
tension: 0.2,
|
||||
borderColor: "#5CDD8B",
|
||||
backgroundColor: "#5CDD8B38",
|
||||
yAxisID: "y",
|
||||
},
|
||||
{
|
||||
type: "bar",
|
||||
data: down_data,
|
||||
borderColor: "#00000000",
|
||||
backgroundColor: "#DC354568",
|
||||
yAxisID: "y1",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -2,7 +2,7 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
system: (window.matchMedia("(prefers-color-scheme: dark)")) ? "dark" : "light",
|
||||
system: (window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light",
|
||||
userTheme: localStorage.theme,
|
||||
};
|
||||
},
|
||||
|
@ -14,6 +14,7 @@ export default {
|
|||
}
|
||||
|
||||
document.body.classList.add(this.theme);
|
||||
this.updateThemeColorMeta();
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -33,6 +34,17 @@ export default {
|
|||
theme(to, from) {
|
||||
document.body.classList.remove(from);
|
||||
document.body.classList.add(this.theme);
|
||||
this.updateThemeColorMeta();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateThemeColorMeta() {
|
||||
if (this.theme === "dark") {
|
||||
document.querySelector("#theme-color").setAttribute("content", "#161B22");
|
||||
} else {
|
||||
document.querySelector("#theme-color").setAttribute("content", "#5cdd8b");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,11 @@
|
|||
<div class="col">
|
||||
<h4>{{ pingTitle }}</h4>
|
||||
<p>(Current)</p>
|
||||
<span class="num"><CountUp :value="ping" /></span>
|
||||
<span class="num">
|
||||
<a href="#" @click.prevent="showPingChartBox = !showPingChartBox">
|
||||
<CountUp :value="ping" />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Avg. {{ pingTitle }}</h4>
|
||||
|
@ -70,6 +74,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showPingChartBox" class="shadow-box big-padding text-center">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<PingChart :monitor-id="monitor.id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showCertInfoBox" class="shadow-box big-padding text-center">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
@ -155,6 +167,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import { useToast } from "vue-toastification"
|
||||
const toast = useToast()
|
||||
import Confirm from "../components/Confirm.vue";
|
||||
|
@ -164,6 +177,7 @@ import Datetime from "../components/Datetime.vue";
|
|||
import CountUp from "../components/CountUp.vue";
|
||||
import Uptime from "../components/Uptime.vue";
|
||||
import Pagination from "v-pagination-3";
|
||||
const PingChart = defineAsyncComponent(() => import("../components/PingChart.vue"));
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -174,6 +188,7 @@ export default {
|
|||
Confirm,
|
||||
Status,
|
||||
Pagination,
|
||||
PingChart,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -181,6 +196,7 @@ export default {
|
|||
perPage: 25,
|
||||
heartBeatList: [],
|
||||
toggleCertInfoBox: false,
|
||||
showPingChartBox: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
Loading…
Reference in a new issue