From 2b8e33caed2fa825ac2c9d74e5c43f68a5cb8b59 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 15 Jul 2021 00:36:44 +0800 Subject: [PATCH 01/75] dockerfile: change the base image to node:14-alpine3.12; add apprise cli, prepare for implementing notification --- .dockerignore | 1 + dockerfile | 30 +++++++++++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.dockerignore b/.dockerignore index b97db4abb..b68a8a694 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ /dist /node_modules /data/kuma.db +/.do diff --git a/dockerfile b/dockerfile index 10e4ac7c6..e017cd98f 100644 --- a/dockerfile +++ b/dockerfile @@ -1,19 +1,35 @@ -FROM node:14-alpine3.14 +# DON'T UPDATE TO alpine3.13, 1.14, see #41. +FROM node:14-alpine3.12 WORKDIR /app -RUN apk add --no-cache make g++ python3 py3-pip python3-dev -RUN ln -s /usr/bin/python3 /usr/bin/python - # split the sqlite install here, so that it can caches the arm prebuilt -RUN npm install sqlite3@5.0.2 +RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + npm install sqlite3@5.0.2 bcrypt@5.0.1 && \ + apk del .build-deps + +# New things add here +# Touching above code may causes sqlite3 re-compile again, painful slow. + +# Install apprise +# Hate pip!!! I never run pip install successfully in first run for anything in my life without Google :/ +# Compilation Fail 1 => Google Search "alpine ffi.h" => Add libffi-dev +# Compilation Fail 2 => Google Search "alpine cargo" => Add cargo +# Compilation Fail 3 => Google Search "alpine opensslv.h" => Add openssl-dev +# Compilation Fail 4 => Google Search "alpine opensslv.h" again => Change to libressl-dev musl-dev +# Compilation Fail 5 => Google Search "ERROR: libressl3.3-libtls-3.3.3-r0: trying to overwrite usr/lib/libtls.so.20 owned by libretls-3.3.3-r0." again => Change back to openssl-dev with musl-dev +ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1 +RUN apk add --no-cache python3 +RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev cargo py3-pip python3-dev && \ + pip3 install apprise && \ + apk del .build-deps COPY . . RUN npm install RUN npm run build -# Remove built tools -RUN apk del make g++ EXPOSE 3001 VOLUME ["/app/data"] CMD ["npm", "run", "start-server"] + From bfb117cb7609e359b55d4c12d096ddd649c62958 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 15 Jul 2021 01:01:47 +0800 Subject: [PATCH 02/75] minor --- dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerfile b/dockerfile index e017cd98f..d21c22bee 100644 --- a/dockerfile +++ b/dockerfile @@ -8,7 +8,6 @@ RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev && \ npm install sqlite3@5.0.2 bcrypt@5.0.1 && \ apk del .build-deps -# New things add here # Touching above code may causes sqlite3 re-compile again, painful slow. # Install apprise @@ -24,11 +23,12 @@ RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev car pip3 install apprise && \ apk del .build-deps +# New things add here + COPY . . RUN npm install RUN npm run build - EXPOSE 3001 VOLUME ["/app/data"] CMD ["npm", "run", "start-server"] From f48f957ba91336de0e58e010cd2091e37c9f6fbd Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 15 Jul 2021 01:44:15 +0800 Subject: [PATCH 03/75] update to 1.0.4 --- README.md | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d13fa169..2e9497392 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ PS: For every new release, it takes some time to build the docker image, please ```bash git fetch --all -git checkout 1.0.3 --force +git checkout 1.0.4 --force npm install npm run build pm2 restart uptime-kuma diff --git a/package.json b/package.json index 46ff2b34b..0dc8452ef 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "uptime-kuma", - "version": "1.0.3", + "version": "1.0.4", "scripts": { "dev": "vite --host", "start-server": "node server/server.js", "update": "", "build": "vite build", "vite-preview-dist": "vite preview --host", - "build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.0.3 . --push", + "build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.0.4 . --push", "build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly . --push", - "setup": "git checkout 1.0.3 && npm install && npm run build", + "setup": "git checkout 1.0.4 && npm install && npm run build", "version-global-replace": "node extra/version-global-replace.js" }, "dependencies": { From b00524067a225c4f8d16b5f6201fb74152b7e98b Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Thu, 15 Jul 2021 10:59:36 +0800 Subject: [PATCH 04/75] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2e9497392..ed971a14a 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,6 @@ It is a self-hosted monitoring tool like "Uptime Robot". ### Docker -⚠ For someone, who are using Raspberry Pi 3<=, please keep using 1.0.1. - ```bash # Create a volume docker volume create uptime-kuma From b3bff8d7357d75d3871aa68bc71db35dd79506a9 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Fri, 16 Jul 2021 01:44:51 +0800 Subject: [PATCH 05/75] add graceful shutdown --- package-lock.json | 11 +++- package.json | 1 + server/server.js | 86 +++++++++++++++++++++++++-- server/util.js | 4 +- src/components/CountUp.vue | 2 +- src/components/NotificationDialog.vue | 6 +- 6 files changed, 98 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index c589c8d9f..e7486fc81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,8 @@ { "name": "uptime-kuma", - "requires": true, + "version": "1.0.4", "lockfileVersion": 1, + "requires": true, "dependencies": { "@babel/helper-validator-identifier": { "version": "7.14.5", @@ -1518,6 +1519,14 @@ "toidentifier": "1.0.0" } }, + "http-graceful-shutdown": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/http-graceful-shutdown/-/http-graceful-shutdown-3.1.2.tgz", + "integrity": "sha512-2vmU3kWOsZqZy4Kn4EZp00CF+6glpNNN/NAYJPkO9bnMX/D8sRl29TsxIu9Vgyo8ygtCWazWJp720zHfqhSdXg==", + "requires": { + "debug": "^4.3.1" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", diff --git a/package.json b/package.json index 0dc8452ef..af0840183 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dayjs": "^1.10.4", "express": "^4.17.1", "form-data": "^4.0.0", + "http-graceful-shutdown": "^3.1.2", "jsonwebtoken": "^8.5.1", "nodemailer": "^6.6.2", "password-hash": "^1.2.2", diff --git a/server/server.js b/server/server.js index 46a6d3714..93e43cd6e 100644 --- a/server/server.js +++ b/server/server.js @@ -1,9 +1,8 @@ +console.log("Welcome to Uptime Kuma ") +console.log("Importing libraries") const express = require('express'); -const app = express(); const http = require('http'); -const server = http.createServer(app); const { Server } = require("socket.io"); -const io = new Server(server); const dayjs = require("dayjs"); const {R} = require("redbean-node"); const passwordHash = require('./password-hash'); @@ -12,12 +11,20 @@ const Monitor = require("./model/monitor"); const fs = require("fs"); const {getSettings} = require("./util-server"); const {Notification} = require("./notification") +const gracefulShutdown = require('http-graceful-shutdown'); +const {sleep} = require("./util"); const args = require('args-parser')(process.argv); const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 +console.log("Version: " + version) + +console.log("Creating express and socket.io instance") +const app = express(); +const server = http.createServer(app); +const io = new Server(server); app.use(express.json()) let totalClient = 0; @@ -539,11 +546,11 @@ async function initDatabase() { const path = './data/kuma.db'; if (! fs.existsSync(path)) { - console.log("Copy Database") + console.log("Copying Database") fs.copyFileSync("./db/kuma.db", path); } - console.log("Connect to Database") + console.log("Connecting to Database") R.setup('sqlite', { filename: path @@ -660,3 +667,72 @@ async function sendImportantHeartbeatList(socket, monitorID) { socket.emit("importantHeartbeatList", monitorID, list) } + + + +const startGracefulShutdown = async () => { + console.log('Shutdown requested'); + + + await (new Promise((resolve) => { + server.close(async function () { + console.log('Stopped Express.'); + process.exit(0) + setTimeout(async () =>{ + await R.close(); + console.log("Stopped DB") + + resolve(); + }, 5000) + + }); + })); + + +} + +let noReject = true; +process.on('unhandledRejection', (reason, p) => { + noReject = false; +}); + +async function shutdownFunction(signal) { + console.log('Called signal: ' + signal); + + console.log("Stopping all monitors") + for (let id in monitorList) { + let monitor = monitorList[id] + monitor.stop() + } + await sleep(2000) + + console.log("Closing DB") + + // Special handle, because tarn.js throw a promise reject that cannot be caught + while (true) { + noReject = true; + await R.close() + await sleep(2000) + + if (noReject) { + break; + } else { + console.log("Waiting...") + } + } + + console.log("OK") +} + +function finalFunction() { + console.log('Graceful Shutdown') +} + +gracefulShutdown(server, { + signals: 'SIGINT SIGTERM', + timeout: 30000, // timeout: 30 secs + development: false, // not in dev mode + forceExit: true, // triggers process.exit() at the end of shutdown process + onShutdown: shutdownFunction, // shutdown function (async) - e.g. for cleanup DB, ... + finally: finalFunction // finally function (sync) - e.g. for logging +}); diff --git a/server/util.js b/server/util.js index dfe4eaa06..d1c9266a8 100644 --- a/server/util.js +++ b/server/util.js @@ -5,11 +5,11 @@ -export function sleep(ms) { +exports.sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -export function ucfirst(str) { +exports.ucfirst = function (str) { if (! str) { return str; } diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue index b929e52eb..b90f430e4 100644 --- a/src/components/CountUp.vue +++ b/src/components/CountUp.vue @@ -5,7 +5,7 @@ diff --git a/src/pages/Details.vue b/src/pages/Details.vue index f925c2849..56b6427d7 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -64,7 +64,7 @@ - + {{ beat.msg }} @@ -75,6 +75,13 @@ + +
+ +
@@ -95,6 +102,7 @@ import Status from "../components/Status.vue"; import Datetime from "../components/Datetime.vue"; import CountUp from "../components/CountUp.vue"; import Uptime from "../components/Uptime.vue"; +import Pagination from "v-pagination-3"; export default { components: { @@ -104,13 +112,16 @@ export default { HeartbeatBar, Confirm, Status, + Pagination, }, mounted() { }, data() { return { - + page: 1, + perPage: 25, + heartBeatList: [], } }, computed: { @@ -154,6 +165,7 @@ export default { importantHeartBeatList() { if (this.$root.importantHeartbeatList[this.monitor.id]) { + this.heartBeatList = this.$root.importantHeartbeatList[this.monitor.id]; return this.$root.importantHeartbeatList[this.monitor.id] } else { return []; @@ -166,8 +178,13 @@ export default { } else { return { } } - } + }, + displayedRecords() { + const startIndex = this.perPage * (this.page - 1); + const endIndex = startIndex + this.perPage; + return this.heartBeatList.slice(startIndex, endIndex); + }, }, methods: { testNotification() { From d94894b7e0b57f345203918f1af3665cca2be00a Mon Sep 17 00:00:00 2001 From: Adam Stachowicz Date: Sun, 18 Jul 2021 03:10:15 +0200 Subject: [PATCH 30/75] Fix `require-v-for-key`, remove unused declarations and double spaces --- server/model/heartbeat.js | 2 -- server/notification.js | 15 ++++++++------- server/server.js | 4 ---- src/components/Datetime.vue | 6 +++--- src/components/NotificationDialog.vue | 10 +++++----- src/pages/Dashboard.vue | 2 +- src/pages/Details.vue | 2 +- src/pages/EditMonitor.vue | 2 +- src/pages/Settings.vue | 8 ++++---- src/util-frontend.js | 6 +++--- 10 files changed, 26 insertions(+), 31 deletions(-) diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index 74e329811..01fb71ff9 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -3,8 +3,6 @@ const utc = require('dayjs/plugin/utc') var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) -const axios = require("axios"); -const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); diff --git a/server/notification.js b/server/notification.js index 99cc1568c..9e6c5e065 100644 --- a/server/notification.js +++ b/server/notification.js @@ -58,7 +58,7 @@ class Notification { finalData = data; } - let res = await axios.post(notification.webhookURL, finalData, config) + await axios.post(notification.webhookURL, finalData, config) return true; } catch (error) { console.log(error) @@ -102,7 +102,8 @@ class Notification { ] }] } - let res = await axios.post(notification.discordWebhookUrl, data) + + await axios.post(notification.discordWebhookUrl, data) return true; } catch(error) { console.log(error) @@ -118,18 +119,18 @@ class Notification { }; let config = {}; - let res = await axios.post(notification.signalURL, data, config) + await axios.post(notification.signalURL, data, config) return true; } catch (error) { console.log(error) return false; } - + } else if (notification.type === "slack") { try { if (heartbeatJSON == null) { let data = {'text': "Uptime Kuma Slack testing successful."} - let res = await axios.post(notification.slackwebhookURL, data) + await axios.post(notification.slackwebhookURL, data) return true; } @@ -170,7 +171,7 @@ class Notification { } ] } - let res = await axios.post(notification.slackwebhookURL, data) + await axios.post(notification.slackwebhookURL, data) return true; } catch (error) { console.log(error) @@ -231,7 +232,7 @@ class Notification { }); // send mail with defined transport object - let info = await transporter.sendMail({ + await transporter.sendMail({ from: `"Uptime Kuma" <${notification.smtpFrom}>`, to: notification.smtpTo, subject: msg, diff --git a/server/server.js b/server/server.js index 93e43cd6e..a64ff512e 100644 --- a/server/server.js +++ b/server/server.js @@ -162,10 +162,6 @@ let needSetup = false; msg: e.message }); } - - - - }); // Auth Only API diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue index 47702236a..e84c877bc 100644 --- a/src/components/Datetime.vue +++ b/src/components/Datetime.vue @@ -4,9 +4,9 @@