add graceful shutdown

pull/64/head
LouisLam 3 years ago
parent f2af5bc064
commit b3bff8d735

11
package-lock.json generated

@ -1,7 +1,8 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"requires": true, "version": "1.0.4",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true,
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.14.5", "version": "7.14.5",
@ -1518,6 +1519,14 @@
"toidentifier": "1.0.0" "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": { "http-signature": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",

@ -21,6 +21,7 @@
"dayjs": "^1.10.4", "dayjs": "^1.10.4",
"express": "^4.17.1", "express": "^4.17.1",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"http-graceful-shutdown": "^3.1.2",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"nodemailer": "^6.6.2", "nodemailer": "^6.6.2",
"password-hash": "^1.2.2", "password-hash": "^1.2.2",

@ -1,9 +1,8 @@
console.log("Welcome to Uptime Kuma ")
console.log("Importing libraries")
const express = require('express'); const express = require('express');
const app = express();
const http = require('http'); const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io"); const { Server } = require("socket.io");
const io = new Server(server);
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const {R} = require("redbean-node"); const {R} = require("redbean-node");
const passwordHash = require('./password-hash'); const passwordHash = require('./password-hash');
@ -12,12 +11,20 @@ const Monitor = require("./model/monitor");
const fs = require("fs"); const fs = require("fs");
const {getSettings} = require("./util-server"); const {getSettings} = require("./util-server");
const {Notification} = require("./notification") const {Notification} = require("./notification")
const gracefulShutdown = require('http-graceful-shutdown');
const {sleep} = require("./util");
const args = require('args-parser')(process.argv); const args = require('args-parser')(process.argv);
const version = require('../package.json').version; const version = require('../package.json').version;
const hostname = args.host || "0.0.0.0" const hostname = args.host || "0.0.0.0"
const port = args.port || 3001 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()) app.use(express.json())
let totalClient = 0; let totalClient = 0;
@ -539,11 +546,11 @@ async function initDatabase() {
const path = './data/kuma.db'; const path = './data/kuma.db';
if (! fs.existsSync(path)) { if (! fs.existsSync(path)) {
console.log("Copy Database") console.log("Copying Database")
fs.copyFileSync("./db/kuma.db", path); fs.copyFileSync("./db/kuma.db", path);
} }
console.log("Connect to Database") console.log("Connecting to Database")
R.setup('sqlite', { R.setup('sqlite', {
filename: path filename: path
@ -660,3 +667,72 @@ async function sendImportantHeartbeatList(socket, monitorID) {
socket.emit("importantHeartbeatList", monitorID, list) 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
});

@ -5,11 +5,11 @@
export function sleep(ms) { exports.sleep = function (ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
export function ucfirst(str) { exports.ucfirst = function (str) {
if (! str) { if (! str) {
return str; return str;
} }

@ -5,7 +5,7 @@
<script> <script>
import {sleep} from "../../server/util"; const {sleep} = require("../../server/util")
export default { export default {

@ -188,7 +188,7 @@
<input type="number" class="form-control" id="gotify-priority" v-model="notification.gotifyPriority" required min="0" max="10" step="1"> <input type="number" class="form-control" id="gotify-priority" v-model="notification.gotifyPriority" required min="0" max="10" step="1">
</div> </div>
</template> </template>
<template v-if="notification.type === 'slack'"> <template v-if="notification.type === 'slack'">
<div class="mb-3"> <div class="mb-3">
<label for="slack-webhook-url" class="form-label">Slack Webhook URL</label> <label for="slack-webhook-url" class="form-label">Slack Webhook URL</label>
@ -220,7 +220,7 @@
<script> <script>
import { Modal } from 'bootstrap' import { Modal } from 'bootstrap'
import { ucfirst } from "../../server/util"; const {ucfirst} = require("../../server/util")
import axios from "axios"; import axios from "axios";
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import Confirm from "./Confirm.vue"; import Confirm from "./Confirm.vue";
@ -276,7 +276,7 @@ export default {
name: "", name: "",
type: null, type: null,
} }
// Default set to Telegram // Default set to Telegram
this.notification.type = "telegram" this.notification.type = "telegram"
this.notification.gotifyPriority = 8 this.notification.gotifyPriority = 8

Loading…
Cancel
Save