Compare commits

...

84 Commits
1.0.2 ... 1.0.6

Author SHA1 Message Date
LouisLam
657acf748b update to 1.0.6 2021-07-20 20:04:54 +08:00
Louis Lam
71dd68bb6d Update README.md 2021-07-20 13:04:21 +08:00
Louis Lam
58d029445d Merge pull request #79 from Saibamen/remove_debug_spam
Remove debug spam
2021-07-19 23:12:33 +08:00
LouisLam
77af41bfff env default to production 2021-07-19 23:06:42 +08:00
Louis Lam
058032a26a Merge pull request #81 from Saibamen/use_NODE_ENV
Use `NODE_ENV` from Express and Socket.IO
2021-07-19 22:59:36 +08:00
Louis Lam
cbb9d3f91b set version for docker 2021-07-19 20:18:27 +08:00
Adam Stachowicz
5bd3184ebf Use connect_error event 2021-07-18 20:59:00 +02:00
Adam Stachowicz
59ebe134f1 Fix indentation 2021-07-18 20:46:45 +02:00
Adam Stachowicz
851ceef3d5 Use NODE_ENV from Express and Socket.IO 2021-07-18 20:21:17 +02:00
LouisLam
efd7608ba2 Merge remote-tracking branch 'origin/master' 2021-07-19 00:43:45 +08:00
LouisLam
05fdaf0c96 update package-lock.json 2021-07-19 00:43:25 +08:00
Louis Lam
01b0e82d52 Update README.md 2021-07-19 00:40:10 +08:00
Louis Lam
d2ccfd5366 Update README.md 2021-07-19 00:39:07 +08:00
Adam Stachowicz
7cba9ce231 Remove debug spam 2021-07-18 18:35:40 +02:00
Louis Lam
69e8c56e3e Update README.md 2021-07-19 00:34:48 +08:00
LouisLam
25c370c9ff Merge branch 'update_packages'
# Conflicts:
#	package-lock.json
#	package.json
2021-07-18 22:22:19 +08:00
LouisLam
9227ff6ea3 add nightly build for amd64 only 2021-07-18 22:21:34 +08:00
Louis Lam
f7bd67c413 Merge pull request #77 from Saibamen/fix_docker
Fix Docker build
2021-07-18 20:53:06 +08:00
LouisLam
9ca2444dab improve testing notification response 2021-07-18 20:49:46 +08:00
Adam Stachowicz
1d45a7606d Fix Docker build 2021-07-18 13:51:44 +02:00
Louis Lam
d6b9403f60 Merge pull request #76 from Saibamen/fix_remember_me
Fix multiple labels for `Remember me`
2021-07-18 19:28:48 +08:00
Louis Lam
92e5ddd97c Merge pull request #69 from Saibamen/dockerignore
Update .dockerignore
2021-07-18 19:27:43 +08:00
Louis Lam
23611e540c Merge pull request #70 from Saibamen/fix_npm_warnings
Fix NPM warnings
2021-07-18 19:27:00 +08:00
Adam Stachowicz
f9274557f3 Fix center 2021-07-18 13:22:39 +02:00
Adam Stachowicz
44b66cbd2e Fix Remember me label 2021-07-18 13:00:59 +02:00
LouisLam
66037e236c add apprise support 2021-07-18 18:51:58 +08:00
Adam Stachowicz
386c002fda Merge branch 'master' into fix_npm_warnings 2021-07-18 11:48:08 +02:00
Adam Stachowicz
7c1aab6a15 Merge branch 'master' into dockerignore 2021-07-18 11:47:53 +02:00
Adam Stachowicz
37884cfd08 Merge branch 'master' into update_packages 2021-07-18 11:47:08 +02:00
LouisLam
13c9244e3f fix apprise import issue and loose the healthcheck rule 2021-07-18 17:42:00 +08:00
Louis Lam
13b3a5be9c Merge pull request #68 from Saibamen/use_console_error
Improve printing to console
2021-07-18 14:55:30 +08:00
Louis Lam
2e31e780a1 Merge pull request #67 from Saibamen/lighthouse_improvements
[Lighthouse] Some improvements
2021-07-18 13:48:35 +08:00
Louis Lam
6a2b5f9dd8 Merge pull request #66 from Saibamen/resize_apple_icon
[Lighthouse] Resize apple icon to 192px
2021-07-18 13:47:50 +08:00
Louis Lam
16767dc042 Merge pull request #65 from Saibamen/robots
[Lighthouse] Add robots.txt
2021-07-18 13:47:39 +08:00
Louis Lam
fb3e000dc3 Merge pull request #63 from NiNiyas/docker-healthcheck
Docker Healthcheck
2021-07-18 13:47:25 +08:00
Louis Lam
6f3ea21864 Merge pull request #61 from NiNiyas/slack-enhancements
Slack Enhancements and aligns footer to center
2021-07-18 13:47:04 +08:00
Louis Lam
403137280e Merge pull request #62 from NiNiyas/pushover-support
Pushover support
2021-07-18 12:20:41 +08:00
Adam Stachowicz
9c248776e7 Update dependencies 2021-07-18 00:08:35 +02:00
Adam Stachowicz
309caa4279 Fix indentation 2021-07-17 23:57:48 +02:00
Adam Stachowicz
28b14ceb70 Fix NPM warnings 2021-07-17 23:45:36 +02:00
Adam Stachowicz
9f9c42c30b Update .dockerignore 2021-07-17 23:31:44 +02:00
Adam Stachowicz
2bff62cade Improve printing to console 2021-07-17 23:13:54 +02:00
Adam Stachowicz
bfb4a5bcd4 Add alt="Logo" 2021-07-17 22:56:54 +02:00
Adam Stachowicz
e87b78501b rel="noopener" for external link 2021-07-17 22:55:53 +02:00
Adam Stachowicz
637422494f Add description 2021-07-17 22:53:32 +02:00
Adam Stachowicz
db34484ff2 Add theme-color 2021-07-17 22:51:07 +02:00
Adam Stachowicz
790c071f2c Resize apple icon to 192px 2021-07-17 22:45:28 +02:00
Adam Stachowicz
149688e669 Add robots.txt 2021-07-17 22:24:10 +02:00
Niyas
7dae5279fb Docker healthcheck
Copied from https://scoutapm.com/blog/how-to-use-docker-healthcheck
2021-07-17 20:08:01 +05:30
Niyas
c203317b3b Docker healthcheck
Copied from https://scoutapm.com/blog/how-to-use-docker-healthcheck
2021-07-17 20:07:35 +05:30
Niyas
01f2fccb23 Update notification.js 2021-07-17 18:59:26 +05:30
Niyas
7808aef58f Pushover support 2021-07-17 18:42:54 +05:30
Niyas
ce2d78f45a Pushover support 2021-07-17 17:55:02 +05:30
Niyas
20cad50593 Pushover support 2021-07-17 17:49:56 +05:30
Niyas
829a2a191d Footer center align 2021-07-17 13:42:05 +05:30
Niyas
65b320d06b Slack Enhancements 2021-07-17 12:48:42 +05:30
Niyas
1935da5b16 Slack Enhancements 2021-07-17 12:47:52 +05:30
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
19 changed files with 772 additions and 196 deletions

View File

@@ -2,3 +2,12 @@
/dist
/node_modules
/data/kuma.db
/.do
**/.dockerignore
**/.git
**/.gitignore
**/docker-compose*
**/Dockerfile*
LICENSE
README.md
.editorconfig

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,12 +21,13 @@ It is a self-hosted monitoring tool like "Uptime Robot".
# How to Use
### Docker
```bash
# Create a volume
docker volume create uptime-kuma
# Start the container
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
```
Browse to http://localhost:3001 after started.
@@ -31,7 +35,7 @@ Browse to http://localhost:3001 after started.
Change Port and Volume
```bash
docker run -d --restart=always -p <YOUR_PORT>:3001 -v <YOUR_DIR OR VOLUME>:/app/data --name uptime-kuma louislam/uptime-kuma
docker run -d --restart=always -p <YOUR_PORT>:3001 -v <YOUR_DIR OR VOLUME>:/app/data --name uptime-kuma louislam/uptime-kuma:1
```
### Without Docker
@@ -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.6 --force
npm install
npm run build
pm2 restart uptime-kuma
@@ -103,3 +109,11 @@ Telegram Notification Sample:
If you love this project, please consider giving me a ⭐.
# Contribute
If you want to report a bug or request a new feature. Free feel to open a new issue.
If you want to modify Uptime Kuma, this guideline maybe useful for you: https://github.com/louislam/uptime-kuma/wiki/%5BDev%5D-Setup-Development-Environment
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.

View File

@@ -1,15 +1,41 @@
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
# Runtime Error => ModuleNotFoundError: No module named 'six' => pip3 install six
# Runtime Error 2 => ModuleNotFoundError: No module named 'six' => apk add py3-six
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
RUN apk add --no-cache python3 py3-pip py3-six cargo
RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev python3-dev && \
pip3 install apprise && \
apk del .build-deps
RUN apprise --version
# New things add here
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3001
VOLUME ["/app/data"]
HEALTHCHECK --interval=60s --timeout=30s --start-period=300s CMD node extra/healthcheck.js
CMD ["npm", "run", "start-server"]
FROM release AS nightly
RUN npm run mark-as-nightly

19
extra/healthcheck.js Normal file
View File

@@ -0,0 +1,19 @@
var http = require("http");
var options = {
host: "localhost",
port: "3001",
timeout: 2000,
};
var request = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
if (res.statusCode == 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on("error", function (err) {
console.log("ERROR");
process.exit(1);
});
request.end();

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

@@ -0,0 +1,40 @@
/**
* 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
if (fs.existsSync("README.md")) {
fs.writeFileSync("README.md", fs.readFileSync("README.md", 'utf8').replaceAll(oldVersion, newVersion))
}
}

View File

@@ -3,7 +3,10 @@
<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" />
<meta name="theme-color" content="#5cdd8b" />
<meta name="description" content="Uptime Kuma monitoring tool" />
<title>Uptime Kuma</title>
</head>
<body>

215
package-lock.json generated
View File

@@ -1,7 +1,8 @@
{
"name": "uptime-kuma",
"requires": true,
"version": "1.0.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/helper-validator-identifier": {
"version": "7.14.5",
@@ -39,37 +40,43 @@
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
},
"@types/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg=="
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"@types/cors": {
"version": "2.8.10",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ=="
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
},
"@types/estree": {
"version": "0.0.48",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz",
"integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==",
"dev": true
},
"@types/node": {
"version": "15.12.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz",
"integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA=="
"version": "16.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz",
"integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ=="
},
"@vitejs/plugin-legacy": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.3.tgz",
"integrity": "sha512-lxZUJaMWYMQuqvZM1wPzDP6KABQgA/drVL5fnaygEPcz9adc2OHhfFNN/SvvHQ1V0rP8gybIc7uA+iI1gAdkVQ==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.4.tgz",
"integrity": "sha512-pVYeQUDPG5InWwrTu7acy187WWjGonJnL/GMqMLmeKCFiwkZ6UcsoUjojiKmCUI0nAJTrrKH5lhjTqkccY9Iow==",
"dev": true,
"requires": {
"@babel/standalone": "^7.14.7",
"core-js": "^3.15.1",
"core-js": "^3.15.2",
"magic-string": "^0.25.7",
"regenerator-runtime": "^0.13.7",
"systemjs": "^6.10.1"
"systemjs": "^6.10.2"
}
},
"@vitejs/plugin-vue": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.3.tgz",
"integrity": "sha512-LlnLpObkGKZ+b7dcpL4T24l13nPSHLjo+6Oc7MbZiKz5PMAUzADfNJ3EKfYIQ0l0969nxf2jp/9vsfnuJ7h6fw==",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.5.tgz",
"integrity": "sha512-GIR31mdXTEfvElmBUaRhDc5v7lfdkEdawWQqJRiaRL/5qKsH+xusukglkvJz5y7+c6dEpxgmvcATv2BbB7+fzQ==",
"dev": true
},
"@vue/compiler-core": {
@@ -94,17 +101,18 @@
}
},
"@vue/compiler-sfc": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz",
"integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.5.tgz",
"integrity": "sha512-mtMY6xMvZeSRx9MTa1+NgJWndrkzVTdJ1pQAmAKQuxyb5LsHVvrgP7kcQFvxPHVpLVTORbTJWHaiqoKrJvi1iA==",
"dev": true,
"requires": {
"@babel/parser": "^7.13.9",
"@babel/types": "^7.13.0",
"@vue/compiler-core": "3.1.1",
"@vue/compiler-dom": "3.1.1",
"@vue/compiler-ssr": "3.1.1",
"@vue/shared": "3.1.1",
"@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",
"consolidate": "^0.16.0",
"estree-walker": "^2.0.1",
"hash-sum": "^2.0.0",
@@ -115,16 +123,78 @@
"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.1",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz",
"integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.5.tgz",
"integrity": "sha512-CU5N7Di/a4lyJ18LGJxJYZS2a8PlLdWpWHX9p/XcsjT2TngMpj3QvHVRkuik2u8QrIDZ8OpYmTyj1WDNsOV+Dg==",
"dev": true,
"requires": {
"@vue/compiler-dom": "3.1.1",
"@vue/shared": "3.1.1"
"@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/devtools-api": {
@@ -530,9 +600,9 @@
}
},
"bootstrap": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.1.tgz",
"integrity": "sha512-Fl79+wsLOZKoiU345KeEaWD0ik8WKRI5zm0YSPj2oF1Qr+BO7z0fco6GbUtqjoG1h4VI89PeKJnMsMMVQdKKTw=="
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.2.tgz",
"integrity": "sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q=="
},
"brace-expansion": {
"version": "1.1.11",
@@ -654,6 +724,11 @@
"delayed-stream": "~1.0.0"
}
},
"command-exists": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
},
"commander": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
@@ -752,9 +827,9 @@
}
},
"dayjs": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz",
"integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g=="
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz",
"integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw=="
},
"debug": {
"version": "4.3.1",
@@ -897,9 +972,9 @@
}
},
"engine.io-client": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz",
"integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==",
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.2.tgz",
"integrity": "sha512-blRrgXIE0A/eurWXRzvfCLG7uUFJqfTGFsyJzXSK71srMMGJ2VraBLg8Mdw28uUxSpVicepBN9X7asqpD1mZcQ==",
"requires": {
"base64-arraybuffer": "0.1.4",
"component-emitter": "~1.3.0",
@@ -921,9 +996,9 @@
}
},
"esbuild": {
"version": "0.12.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.9.tgz",
"integrity": "sha512-MWRhAbMOJ9RJygCrt778rz/qNYgA4ZVj6aXnNPxFjs7PmIpb0fuB9Gmg5uWrr6n++XKwwm/RmSz6RR5JL2Ocsw==",
"version": "0.12.15",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz",
"integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==",
"dev": true
},
"escape-html": {
@@ -1518,6 +1593,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",
@@ -2411,9 +2494,9 @@
}
},
"nodemailer": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz",
"integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q=="
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
"integrity": "sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew=="
},
"nopt": {
"version": "3.0.6",
@@ -2966,9 +3049,9 @@
}
},
"rollup": {
"version": "2.52.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.2.tgz",
"integrity": "sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA==",
"version": "2.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz",
"integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
@@ -2993,9 +3076,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sass": {
"version": "1.35.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz",
"integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==",
"version": "1.35.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.35.2.tgz",
"integrity": "sha512-jhO5KAR+AMxCEwIH3v+4zbB2WB0z67V1X0jbapfVwQQdjHZUGUyukpnoM6+iCMfsIUC016w9OPKQ5jrNOS9uXw==",
"dev": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0"
@@ -3216,19 +3299,19 @@
}
},
"socket.io": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.2.tgz",
"integrity": "sha512-xK0SD1C7hFrh9+bYoYCdVt+ncixkSLKtNLCax5aEy1o3r5PaO5yQhVb97exIe67cE7lAK+EpyMytXWTWmyZY8w==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz",
"integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==",
"requires": {
"@types/cookie": "^0.4.0",
"@types/cors": "^2.8.8",
"@types/cors": "^2.8.10",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~5.1.0",
"socket.io-adapter": "~2.3.0",
"socket.io-parser": "~4.0.3"
"engine.io": "~5.1.1",
"socket.io-adapter": "~2.3.1",
"socket.io-parser": "~4.0.4"
}
},
"socket.io-adapter": {
@@ -3237,15 +3320,15 @@
"integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw=="
},
"socket.io-client": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.2.tgz",
"integrity": "sha512-RDpWJP4DQT1XeexmeDyDkm0vrFc0+bUsHDKiVGaNISJvJonhQQOMqV9Vwfg0ZpPJ27LCdan7iqTI92FRSOkFWQ==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.3.tgz",
"integrity": "sha512-hISFn6PDpgDifVUiNklLHVPTMv1LAk8poHArfIUdXa+gKgbr0MZbAlquDFqCqsF30yBqa+jg42wgos2FK50BHA==",
"requires": {
"@types/component-emitter": "^1.2.10",
"backo2": "~1.0.2",
"component-emitter": "~1.3.0",
"debug": "~4.3.1",
"engine.io-client": "~5.1.1",
"engine.io-client": "~5.1.2",
"parseuri": "0.0.6",
"socket.io-parser": "~4.0.4"
}
@@ -3640,14 +3723,14 @@
}
},
"vite": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.3.8.tgz",
"integrity": "sha512-QiEx+iqNnJntSgSF2fWRQvRey9pORIrtNJzNyBJXwc+BdzWs83FQolX84cTBo393cfhObrtWa6180dAa4NLDiQ==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.4.2.tgz",
"integrity": "sha512-2MifxD2I9fjyDmmEzbULOo3kOUoqX90A58cT6mECxoVQlMYFuijZsPQBuA14mqSwvV3ydUsqnq+BRWXyO9Qa+w==",
"dev": true,
"requires": {
"esbuild": "^0.12.8",
"fsevents": "~2.3.2",
"postcss": "^8.3.4",
"postcss": "^8.3.5",
"resolve": "^1.20.0",
"rollup": "^2.38.5"
}

View File

@@ -1,33 +1,42 @@
{
"name": "uptime-kuma",
"version": "1.0.2",
"version": "1.0.6",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/louislam/uptime-kuma.git"
},
"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.6 --target release . --push",
"build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
"build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push",
"setup": "git checkout 1.0.6 && 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",
"args-parser": "^1.3.0",
"axios": "^0.21.1",
"bcrypt": "^5.0.1",
"bootstrap": "^5.0.0",
"dayjs": "^1.10.4",
"bootstrap": "^5.0.2",
"command-exists": "^1.2.9",
"dayjs": "^1.10.6",
"express": "^4.17.1",
"form-data": "^4.0.0",
"http-graceful-shutdown": "^3.1.2",
"jsonwebtoken": "^8.5.1",
"nodemailer": "^6.6.2",
"nodemailer": "^6.6.3",
"password-hash": "^1.2.2",
"redbean-node": "0.0.20",
"socket.io": "^4.0.2",
"socket.io-client": "^4.1.2",
"sqlite3": "^5.0.0",
"socket.io": "^4.1.3",
"socket.io-client": "^4.1.3",
"sqlite3": "^5.0.2",
"tcp-ping": "^0.1.1",
"vue": "^3.0.5",
"vue-confirm-dialog": "^1.0.2",
@@ -35,11 +44,11 @@
"vue-toastification": "^2.0.0-rc.1"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^1.4.3",
"@vitejs/plugin-vue": "^1.2.3",
"@vue/compiler-sfc": "^3.0.5",
"@vitejs/plugin-legacy": "^1.4.4",
"@vitejs/plugin-vue": "^1.2.5",
"@vue/compiler-sfc": "^3.1.5",
"core-js": "^3.15.2",
"sass": "^1.35.1",
"vite": "^2.3.7"
"sass": "^1.35.2",
"vite": "^2.4.2"
}
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

3
public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -2,9 +2,22 @@ const axios = require("axios");
const {R} = require("redbean-node");
const FormData = require('form-data');
const nodemailer = require("nodemailer");
const child_process = require("child_process");
class Notification {
/**
*
* @param notification
* @param msg
* @param monitorJSON
* @param heartbeatJSON
* @returns {Promise<string>} Successful msg
* Throw Error with fail msg
*/
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. ";
if (notification.type === "telegram") {
try {
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
@@ -13,15 +26,32 @@ class Notification {
text: msg,
}
})
return true;
return okMsg;
} catch (error) {
console.log(error)
return false;
let msg = (error.response.data.description) ? error.response.data.description : "Error without description"
throw new Error(msg)
}
} else if (notification.type === "gotify") {
try {
if (notification.gotifyserverurl && 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 okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "webhook") {
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
@@ -43,10 +73,10 @@ class Notification {
}
let res = await axios.post(notification.webhookURL, finalData, config)
return true;
return okMsg;
} catch (error) {
console.log(error)
return false;
throwGeneralAxiosError(error)
}
} else if (notification.type === "smtp") {
@@ -61,7 +91,7 @@ class Notification {
content: msg
}
let res = await axios.post(notification.discordWebhookUrl, data)
return true;
return okMsg;
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if(heartbeatJSON['status'] == 0) {
@@ -87,12 +117,10 @@ class Notification {
}]
}
let res = await axios.post(notification.discordWebhookUrl, data)
return true;
return okMsg;
} catch(error) {
console.log(error)
return false;
throwGeneralAxiosError(error)
}
return await Notification.discord(notification, msg)
} else if (notification.type === "signal") {
try {
@@ -104,12 +132,98 @@ class Notification {
let config = {};
let res = await axios.post(notification.signalURL, data, config)
return true;
return okMsg;
} catch (error) {
console.log(error)
return false;
throwGeneralAxiosError(error)
}
} else if (notification.type === "slack") {
try {
if (heartbeatJSON == null) {
let data = {'text': "Uptime Kuma Slack testing successful.", 'channel': notification.slackchannel, 'username': notification.slackusername, 'icon_emoji': notification.slackiconemo}
let res = await axios.post(notification.slackwebhookURL, data)
return okMsg;
}
const time = heartbeatJSON["time"];
let data = {
"text": "Uptime Kuma Alert",
"channel":notification.slackchannel,
"username": notification.slackusername,
"icon_emoji": notification.slackiconemo,
"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 || "https://github.com/louislam/uptime-kuma"
}
]
}
]
}
let res = await axios.post(notification.slackwebhookURL, data)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "pushover") {
var pushoverlink = 'https://api.pushover.net/1/messages.json'
try {
if (heartbeatJSON == null) {
let data = {'message': "<b>Uptime Kuma Pushover testing successful.</b>",
'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds,
'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1}
let res = await axios.post(pushoverlink, data)
return okMsg;
}
let data = {
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" +msg + '\n<b>Time (UTC)</b>:' +time,
"user":notification.pushoveruserkey,
"token": notification.pushoverapptoken,
"sound": notification.pushoversounds,
"priority": notification.pushoverpriority,
"title": notification.pushovertitle,
"retry": "30",
"expire": "3600",
"html": 1
}
let res = await axios.post(pushoverlink, data)
return okMsg;
} catch (error) {
throwGeneralAxiosError(error)
}
} else if (notification.type === "apprise") {
return Notification.apprise(notification, msg)
} else {
throw new Error("Notification type is not supported")
}
@@ -171,20 +285,47 @@ class Notification {
text: msg,
});
return true;
return "Sent Successfully.";
}
static async discord(notification, msg) {
const client = new Discord.Client();
await client.login(notification.discordToken)
static async apprise(notification, msg) {
let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL])
const channel = await client.channels.fetch(notification.discordChannelID);
await channel.send(msg);
client.destroy()
let output = (s.stdout) ? s.stdout.toString() : 'ERROR: maybe apprise not found';
return true;
if (output) {
if (! output.includes("ERROR")) {
return "Sent Successfully";
} else {
throw new Error(output)
}
} else {
return ""
}
}
static checkApprise() {
let commandExistsSync = require('command-exists').sync;
let exists = commandExistsSync('apprise');
return exists;
}
}
function throwGeneralAxiosError(error) {
let msg = "Error: " + error + " ";
if (error.response && error.response.data) {
if (typeof error.response.data === "string") {
msg += error.response.data;
} else {
msg += JSON.stringify(error.response.data)
}
}
throw new Error(msg)
}
module.exports = {

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.info("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;
@@ -28,19 +35,21 @@ let needSetup = false;
(async () => {
await initDatabase();
console.log("Adding route")
app.use('/', express.static("dist"));
app.get('*', function(request, response, next) {
response.sendFile(process.cwd() + '/dist/index.html');
});
console.log("Adding socket handler")
io.on('connection', async (socket) => {
socket.emit("info", {
version,
})
console.log('a user connected');
totalClient++;
if (needSetup) {
@@ -49,7 +58,6 @@ let needSetup = false;
}
socket.on('disconnect', () => {
console.log('user disconnected');
totalClient--;
});
@@ -229,7 +237,7 @@ let needSetup = false;
});
} catch (e) {
console.log(e)
console.error(e)
callback({
ok: false,
msg: e.message
@@ -430,25 +438,36 @@ let needSetup = false;
try {
checkLogin(socket)
await Notification.send(notification, notification.name + " Testing")
let msg = await Notification.send(notification, notification.name + " Testing")
callback({
ok: true,
msg: "Sent Successfully"
msg
});
} catch (e) {
console.error(e)
callback({
ok: false,
msg: e.message
});
}
});
socket.on("checkApprise", async (callback) => {
try {
checkLogin(socket)
callback(Notification.checkApprise());
} catch (e) {
callback(false);
}
});
});
console.log("Init")
server.listen(port, hostname, () => {
console.log(`Listening on ${hostname}:${port}`);
startMonitors();
});
@@ -539,15 +558,16 @@ 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
});
console.log("Connected")
R.freeze(true)
await R.autoloadModels("./server/model");
@@ -562,6 +582,7 @@ async function initDatabase() {
jwtSecretBean.value = passwordHash.generate(dayjs() + "")
await R.store(jwtSecretBean)
console.log("Stored JWT secret into database")
} else {
console.log("Load JWT secret from database.")
}
@@ -660,3 +681,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

@@ -15,14 +15,14 @@
<label for="floatingPassword">Password</label>
</div>
<div class="form-check mb-3 mt-3" >
<label>
<div class="form-check mb-3 mt-3 d-flex justify-content-center pe-4">
<div class="form-check">
<input type="checkbox" value="remember-me" class="form-check-input" id="remember" v-model="$root.remember">
<label class="form-check-label" for="remember">
Remember me
</label>
</label>
</div>
</div>
<button class="w-100 btn btn-primary" type="submit" :disabled="processing">Login</button>

View File

@@ -10,57 +10,61 @@
</div>
<div class="modal-body">
<div class="mb-3">
<label for="type" class="form-label">Notification Type</label>
<select class="form-select" id="type" v-model="notification.type">
<option value="telegram">Telegram</option>
<option value="webhook">Webhook</option>
<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>
<option value="pushover">Pushover</option>
<option value="apprise">Apprise (Support 50+ Notification services)</option>
</select>
</div>
<div class="mb-3">
<label for="name" class="form-label">Friendly Name</label>
<input type="text" class="form-control" id="name" required v-model="notification.name">
</div>
<template v-if="notification.type === 'telegram'">
<div class="mb-3">
<label for="type" class="form-label">Notification Type</label>
<select class="form-select" id="type" v-model="notification.type">
<option value="telegram">Telegram</option>
<option value="webhook">Webhook</option>
<option value="smtp">Email (SMTP)</option>
<option value="discord">Discord</option>
<option value="signal">Signal</option>
</select>
<label for="telegram-bot-token" class="form-label">Bot Token</label>
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Friendly Name</label>
<input type="text" class="form-control" id="name" required v-model="notification.name">
<label for="telegram-chat-id" class="form-label">Chat ID</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
</div>
<div class="form-text">
Support Direct Chat / Group / Channel's Chat ID
<p style="margin-top: 8px;">
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
</p>
<p style="margin-top: 8px;">
<template v-if="notification.telegramBotToken">
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
</template>
<template v-else>
{{ telegramGetUpdatesURL }}
</template>
</p>
</div>
</div>
<template v-if="notification.type === 'telegram'">
<div class="mb-3">
<label for="telegram-bot-token" class="form-label">Bot Token</label>
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
</div>
<div class="mb-3">
<label for="telegram-chat-id" class="form-label">Chat ID</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
</div>
<div class="form-text">
Support Direct Chat / Group / Channel's Chat ID
<p style="margin-top: 8px;">
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
</p>
<p style="margin-top: 8px;">
<template v-if="notification.telegramBotToken">
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
</template>
<template v-else>
{{ telegramGetUpdatesURL }}
</template>
</p>
</div>
</div>
</template>
</template>
<template v-if="notification.type === 'webhook'">
<div class="mb-3">
@@ -169,6 +173,126 @@
</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">Webhook URL<span style="color:red;"><sup>*</sup></span></label>
<input type="text" class="form-control" id="slack-webhook-url" required v-model="notification.slackwebhookURL">
<label for="slack-username" class="form-label">Username</label>
<input type="text" class="form-control" id="slack-username" v-model="notification.slackusername">
<label for="slack-iconemo" class="form-label">Icon Emoji</label>
<input type="text" class="form-control" id="slack-iconemo" v-model="notification.slackiconemo">
<label for="slack-channel" class="form-label">Channel Name</label>
<input type="text" class="form-control" id="slack-channel-name" v-model="notification.slackchannel">
<label for="slack-button-url" class="form-label">Uptime Kuma URL</label>
<input type="text" class="form-control" id="slack-button" v-model="notification.slackbutton">
<div class="form-text">
<span style="color:red;"><sup>*</sup></span>Required
<p style="margin-top: 8px;">
More info about webhooks on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
</p>
<p style="margin-top: 8px;">
Enter the channel name on Slack Channel Name field if you want to bypass the webhook channel. Ex: #other-channel
</p>
<p style="margin-top: 8px;">
If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
</p>
<p style="margin-top: 8px;">
Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</p>
</div>
</div>
</template>
<template v-if="notification.type === 'pushover'">
<div class="mb-3">
<label for="pushover-app-token" class="form-label">Application Token<span style="color:red;"><sup>*</sup></span></label>
<input type="text" class="form-control" id="pushover-app-token" required v-model="notification.pushoverapptoken">
<label for="pushover-user" class="form-label">User Key<span style="color:red;"><sup>*</sup></span></label>
<input type="text" class="form-control" id="pushover-user" required v-model="notification.pushoveruserkey">
<label for="pushover-device" class="form-label">Device</label>
<input type="text" class="form-control" id="pushover-device" v-model="notification.pushoverdevice">
<label for="pushover-device" class="form-label">Message Title</label>
<input type="text" class="form-control" id="pushover-title" v-model="notification.pushovertitle">
<label for="pushover-priority" class="form-label">Priority</label>
<input type="text" class="form-control" id="pushover-priority" v-model="notification.pushoverpriority">
<label for="pushover-sound" class="form-label">Notification Sound</label>
<select class="form-select" id="pushover-sound" v-model="notification.pushoversounds">
<option>pushover</option>
<option>bike</option>
<option>bugle</option>
<option>cashregister</option>
<option>classical</option>
<option>cosmic</option>
<option>falling</option>
<option>gamelan</option>
<option>incoming</option>
<option>intermission</option>
<option>mechanical</option>
<option>pianobar</option>
<option>siren</option>
<option>spacealarm</option>
<option>tugboat</option>
<option>alien</option>
<option>climb</option>
<option>persistent</option>
<option>echo</option>
<option>updown</option>
<option>vibrate</option>
<option>none</option>
</select>
<div class="form-text">
<span style="color:red;"><sup>*</sup></span>Required
<p style="margin-top: 8px;">
More info on: <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a>
</p>
<p style="margin-top: 8px;">
Emergency priority(2) has default 30 second timeout between retries and will expire after 1 hour.
</p>
</div>
</div>
</template>
<template v-if="notification.type === 'apprise'">
<div class="mb-3">
<label for="gotify-application-token" class="form-label">Apprise URL</label>
<input type="text" class="form-control" id="gotify-application-token" required v-model="notification.appriseURL">
<div class="form-text">
<p>Example: twilio://AccountSid:AuthToken@FromPhoneNo</p>
<p>
Read more: <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
</p>
</div>
</div>
<div class="mb-3">
<p>
Status:
<span class="text-primary" v-if="appriseInstalled">Apprise is installed</span>
<span class="text-danger" v-else>Apprise is not installed. <a href="https://github.com/caronc/apprise">Read more</a></span>
</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 +310,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,18 +329,17 @@ export default {
notification: {
name: "",
type: null,
gotifyPriority: 8
},
appriseInstalled: false,
}
},
mounted() {
this.modal = new Modal(this.$refs.modal)
// TODO: for edit
this.$root.getSocket().emit("getSettings", "notification", (data) => {
// this.notification = data
this.$root.getSocket().emit("checkApprise", (installed) => {
this.appriseInstalled = installed;
})
},
methods: {
@@ -244,6 +367,7 @@ export default {
// Default set to Telegram
this.notification.type = "telegram"
this.notification.gotifyPriority = 8
}
this.modal.show()

View File

@@ -9,7 +9,7 @@
<!-- Desktop header -->
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom" v-if="! $root.isMobile">
<router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg"></object>
<object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg" alt="Logo"></object>
<span class="fs-4 title">Uptime Kuma</span>
</router-link>
@@ -37,7 +37,7 @@
<div class="container-fluid">
Uptime Kuma -
Version: {{ $root.info.version }} -
<a href="https://github.com/louislam/uptime-kuma/releases" target="_blank">Check Update On GitHub</a>
<a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">Check Update On GitHub</a>
</div>
</footer>
@@ -146,6 +146,7 @@ footer {
font-size: 13px;
margin-bottom: 30px;
margin-left: 10px;
text-align: center;
}
</style>

View File

@@ -35,7 +35,8 @@ export default {
window.addEventListener('resize', this.onResize);
let wsHost;
if (localStorage.dev === "dev") {
const env = process.env.NODE_ENV || "production";
if (env === "development" || localStorage.dev === "dev") {
wsHost = ":3001"
} else {
wsHost = ""
@@ -45,6 +46,10 @@ export default {
transports: ['websocket']
});
socket.on("connect_error", (err) => {
console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`);
});
socket.on('info', (info) => {
this.info = info;
});
@@ -158,7 +163,7 @@ export default {
},
getSocket() {
return socket;
return socket;
},
toastRes(res) {

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();
@@ -381,7 +394,7 @@ export function timezoneList() {
time: getTimezoneOffset(timezone),
})
} catch (e) {
console.log(e.message);
console.error(e.message);
console.log("Skip this timezone")
}