Compare commits

..

15 Commits
1.0.4 ... 1.0.5

Author SHA1 Message Date
LouisLam
78f5d2cd8b update to 1.0.5 2021-07-17 02:31:31 +08:00
LouisLam
f62b70c9a9 add nightly to version number 2021-07-17 02:30:16 +08:00
LouisLam
dfa9b3a0ca fix require() actually not working after build in the frontend 2021-07-17 00:51:28 +08:00
LouisLam
b3bff8d735 add graceful shutdown 2021-07-16 01:44:51 +08:00
Louis Lam
f2af5bc064 Merge pull request #46 from NiNiyas/slack-webhook
Added Slack webhook notification
2021-07-15 11:59:41 +08:00
Louis Lam
91b736f391 Merge pull request #52 from philippdormann/feature/gotify-upstream-merge
customize Gotify priority
2021-07-15 11:59:20 +08:00
Louis Lam
275f77d4bb Merge pull request #45 from R0GGER/master
Apple icon for iPhone/iPad
2021-07-15 11:03:46 +08:00
Louis Lam
b00524067a Update README.md 2021-07-15 10:59:36 +08:00
Philipp Dormann
25a93b05dc easier merging 🤞 2021-07-14 22:00:15 +02:00
Philipp Dormann
53e203d2f9 add gotify priority
ref https://github.com/louislam/uptime-kuma/pull/43
closes https://github.com/louislam/uptime-kuma/issues/50
2021-07-14 21:56:38 +02:00
Niyas
60493f0f86 Updated Slack test notification 2021-07-14 21:59:16 +05:30
Niyas
63c6e29e62 Added Slack Webhook support 2021-07-14 21:08:38 +05:30
Niyas
5f6d5588a6 Added Slack Webhook support 2021-07-14 21:07:14 +05:30
R0GGER
18744d834f Add files via upload 2021-07-14 14:35:59 +02:00
R0GGER
8dd5b97b79 Apple icon 2021-07-14 14:34:50 +02:00
13 changed files with 236 additions and 27 deletions

View File

@@ -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
@@ -82,7 +80,7 @@ PS: For every new release, it takes some time to build the docker image, please
```bash
git fetch --all
git checkout 1.0.4 --force
git checkout 1.0.5 --force
npm install
npm run build
pm2 restart uptime-kuma

View File

@@ -1,5 +1,5 @@
# DON'T UPDATE TO alpine3.13, 1.14, see #41.
FROM node:14-alpine3.12
FROM node:14-alpine3.12 AS release
WORKDIR /app
# split the sqlite install here, so that it can caches the arm prebuilt
@@ -33,3 +33,5 @@ EXPOSE 3001
VOLUME ["/app/data"]
CMD ["npm", "run", "start-server"]
FROM release AS nightly
RUN npm run mark-as-nightly

39
extra/mark-as-nightly.js Normal file
View File

@@ -0,0 +1,39 @@
/**
* String.prototype.replaceAll() polyfill
* https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
* @author Chris Ferdinandi
* @license MIT
*/
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(str, newStr){
// If a regex pattern
if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
return this.replace(str, newStr);
}
// If a string
return this.replace(new RegExp(str, 'g'), newStr);
};
}
const pkg = require('../package.json');
const fs = require("fs");
const oldVersion = pkg.version
const newVersion = oldVersion + "-nightly"
console.log("Old Version: " + oldVersion)
console.log("New Version: " + newVersion)
if (newVersion) {
// Process package.json
pkg.version = newVersion
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion)
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion)
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n")
// Process README.md
fs.writeFileSync("README.md", fs.readFileSync("README.md", 'utf8').replaceAll(oldVersion, newVersion))
}

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Uptime Kuma</title>
</head>

11
package-lock.json generated
View File

@@ -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",

View File

@@ -1,16 +1,17 @@
{
"name": "uptime-kuma",
"version": "1.0.4",
"version": "1.0.5",
"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.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.4 && npm install && npm run build",
"version-global-replace": "node extra/version-global-replace.js"
"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.5 --target release . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly --target nightly . --push",
"setup": "git checkout 1.0.5 && npm install && npm run build",
"version-global-replace": "node extra/version-global-replace.js",
"mark-as-nightly": "node extra/mark-as-nightly.js"
},
"dependencies": {
"@popperjs/core": "^2.9.2",
@@ -21,6 +22,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",

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -26,7 +26,7 @@ class Notification {
}
await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, {
"message": msg,
"priority": 8,
"priority": notification.gotifyPriority || 8,
"title": "Uptime-Kuma"
})
return true;
@@ -124,6 +124,58 @@ class Notification {
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)
return true;
}
const time = heartbeatJSON["time"];
let data = {
"blocks": [{
"type": "header",
"text": {
"type": "plain_text",
"text": "Uptime Kuma Alert"
}
},
{
"type": "section",
"fields": [{
"type": "mrkdwn",
"text": '*Message*\n'+msg
},
{
"type": "mrkdwn",
"text": "*Time (UTC)*\n"+time
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit Uptime Kuma",
},
"value": "Uptime-Kuma",
"url": notification.slackbutton
}
]
}
]
}
let res = await axios.post(notification.slackwebhookURL, data)
return true;
} catch (error) {
console.log(error)
return false;
}
} else {
throw new Error("Notification type is not supported")

View File

@@ -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
});

View File

@@ -1,15 +1,11 @@
/*
* Common functions - can be used in frontend or backend
*/
// Common JS cannot be used in frontend sadly
// sleep, ucfirst is duplicated in ../src/util-frontend.js
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;
}

View File

@@ -5,7 +5,7 @@
<script>
import {sleep} from "../../server/util";
import {sleep} from '../util-frontend'
export default {

View File

@@ -19,6 +19,7 @@
<option value="discord">Discord</option>
<option value="signal">Signal</option>
<option value="gotify">Gotify</option>
<option value="slack">Slack</option>
</select>
</div>
@@ -175,15 +176,33 @@
<label for="gotify-application-token" class="form-label">Application Token</label>
<input type="text" class="form-control" id="gotify-application-token" required v-model="notification.gotifyapplicationToken">
</div>
<div class="mb-3">
<label for="gotify-server-url" class="form-label">Server URL</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="gotify-server-url" required v-model="notification.gotifyserverurl">
</div>
</div>
<div class="mb-3">
<label for="gotify-priority" class="form-label">Priority</label>
<input type="number" class="form-control" id="gotify-priority" v-model="notification.gotifyPriority" required min="0" max="10" step="1">
</div>
</template>
<template v-if="notification.type === 'slack'">
<div class="mb-3">
<label for="slack-webhook-url" class="form-label">Slack Webhook URL</label>
<input type="text" class="form-control" id="slack-webhook-url" required v-model="notification.slackwebhookURL" autocomplete="false">
<label for="gotify-server-url" class="form-label">Uptime Kuma URL</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="slack-button" required v-model="notification.slackbutton" autocomplete="false">
</div>
<p style="margin-top: 8px;">
More info on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
</p>
</div>
</template>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @click="deleteConfirm" :disabled="processing" v-if="id">Delete</button>
@@ -201,7 +220,7 @@
<script>
import { Modal } from 'bootstrap'
import { ucfirst } from "../../server/util";
import { ucfirst } from '../util-frontend'
import axios from "axios";
import { useToast } from 'vue-toastification'
import Confirm from "./Confirm.vue";
@@ -220,6 +239,7 @@ export default {
notification: {
name: "",
type: null,
gotifyPriority: 8
},
}
},
@@ -259,6 +279,7 @@ export default {
// Default set to Telegram
this.notification.type = "telegram"
this.notification.gotifyPriority = 8
}
this.modal.show()

View File

@@ -5,6 +5,19 @@ import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone)
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export function ucfirst(str) {
if (! str) {
return str;
}
const firstLetter = str.substr(0, 1);
return firstLetter.toUpperCase() + str.substr(1);
}
function getTimezoneOffset(timeZone) {
const now = new Date();