diff --git a/README.md b/README.md index d1c8b383f..8561b26d1 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,16 @@ It is a self-hosted monitoring tool like "Uptime Robot". -## 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 :3001 -v :/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 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. + +🐻 diff --git a/index.html b/index.html index 2a4d4e5d9..61d0f42b0 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,12 @@ - - + + - - - - + + + Uptime Kuma diff --git a/package-lock.json b/package-lock.json index c3fbf0633..ea3a57fbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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,13 +6753,82 @@ } }, "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.1", - "@vue/runtime-dom": "3.2.1", - "@vue/shared": "3.2.1" + "@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/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": { @@ -6819,6 +6836,11 @@ "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" } diff --git a/package.json b/package.json index 0f8929b52..7fe438cc5 100644 --- a/package.json +++ b/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", diff --git a/server/model/monitor.js b/server/model/monitor.js index 4c667342d..94b2f0d04 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -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} + * @returns {Promise} */ 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) { diff --git a/server/notification.js b/server/notification.js index 05dae8ad1..ea065b2fa 100644 --- a/server/notification.js +++ b/server/notification.js @@ -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") } diff --git a/server/prometheus.js b/server/prometheus.js index f60ec45a6..3e4767b3d 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -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? diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 6c481681d..5d2dbd6b8 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -22,8 +22,10 @@ + + @@ -252,6 +254,37 @@

+ + + + +
+
+
+ +
+
+
+
@@ -155,6 +167,7 @@