Compare commits

...

27 Commits
1.0.2 ... 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
LouisLam
f48f957ba9 update to 1.0.4 2021-07-15 01:44:15 +08:00
LouisLam
bfb117cb76 minor 2021-07-15 01:01:47 +08:00
LouisLam
2b8e33caed dockerfile: change the base image to node:14-alpine3.12; add apprise cli, prepare for implementing notification 2021-07-15 00:36:44 +08: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
Louis Lam
386c8bfdf1 Merge pull request #43 from philippdormann/feature/gotify-upstream-merge
 Gotify Support
2021-07-14 17:36:21 +08:00
Philipp Dormann
126f00e739 added Gotify Support 2021-07-14 11:25:10 +02:00
Louis Lam
80466ac957 Update README.md 2021-07-14 17:10:51 +08:00
LouisLam
3b52433202 cache the sqlite built when docker build 2021-07-14 12:42:52 +08:00
Louis Lam
137f5da3da Update README.md 2021-07-14 01:48:55 +08:00
Louis Lam
338d002d42 Update README.md 2021-07-14 01:39:04 +08:00
Louis Lam
77ab9fbc57 Add some shields by shields.io 2021-07-14 01:36:25 +08:00
LouisLam
b6b7835d7e update to 1.0.3 2021-07-13 23:34:33 +08:00
LouisLam
d4fe5908f5 fix merging problem 2021-07-13 23:29:40 +08:00
14 changed files with 298 additions and 30 deletions

View File

@@ -2,3 +2,4 @@
/dist
/node_modules
/data/kuma.db
/.do

View File

@@ -1,5 +1,8 @@
# Uptime Kuma
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a>
<div align="center" width="100%">
<img src="./public/icon.svg" width="128" alt="" />
</div>
@@ -18,6 +21,7 @@ It is a self-hosted monitoring tool like "Uptime Robot".
# How to Use
### Docker
```bash
# Create a volume
docker volume create uptime-kuma
@@ -70,11 +74,13 @@ Choose Cheapest Plan is enough. (US$ 5)
Re-pull the latest docker image and create another container with the same volume.
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
```bash
git fetch --all
git checkout 1.0.2 --force
git checkout 1.0.5 --force
npm install
npm run build
pm2 restart uptime-kuma

View File

@@ -1,11 +1,30 @@
FROM node:14-alpine3.14
# sqlite have to build on arm
# TODO: use prebuilt sqlite for arm, because it is very very slow.
RUN apk add --no-cache make g++ python3
RUN ln -s /usr/bin/python3 /usr/bin/python
# DON'T UPDATE TO alpine3.13, 1.14, see #41.
FROM node:14-alpine3.12 AS release
WORKDIR /app
# split the sqlite install here, so that it can caches the arm prebuilt
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
# 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
# New things add here
COPY . .
RUN npm install
RUN npm run build
@@ -13,3 +32,6 @@ RUN npm run build
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.2",
"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.2 . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly . --push",
"setup": "git checkout 1.0.2 && 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

@@ -19,6 +19,22 @@ class Notification {
return false;
}
} else if (notification.type === "gotify") {
try {
if (notification.gotifyserverurl.endsWith("/")) {
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);
}
await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, {
"message": msg,
"priority": notification.gotifyPriority || 8,
"title": "Uptime-Kuma"
})
return true;
} catch (error) {
console.log(error)
return false;
}
} else if (notification.type === "webhook") {
try {
@@ -92,7 +108,6 @@ class Notification {
console.log(error)
return false;
}
return await Notification.discord(notification, msg)
} else if (notification.type === "signal") {
try {
@@ -109,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

@@ -18,6 +18,8 @@
<option value="smtp">Email (SMTP)</option>
<option value="discord">Discord</option>
<option value="signal">Signal</option>
<option value="gotify">Gotify</option>
<option value="slack">Slack</option>
</select>
</div>
@@ -169,6 +171,38 @@
</div>
</template>
<template v-if="notification.type === 'gotify'">
<div class="mb-3">
<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>
@@ -186,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";
@@ -205,6 +239,7 @@ export default {
notification: {
name: "",
type: null,
gotifyPriority: 8
},
}
},
@@ -244,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();