Merge branch 'master' into simple_pagination

This commit is contained in:
LouisLam 2021-07-22 10:42:30 +08:00
commit afd4cf2425
12 changed files with 409 additions and 106 deletions

View file

@ -0,0 +1,10 @@
---
name: ⚠ Please go to "Discussions" Tab if you want to ask or share something
about: BUG REPORT ONLY HERE
title: ''
labels: ''
assignees: ''
---
BUG REPORT ONLY HERE

34
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,34 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- Uptime Kuma Version:
- Using Docker?: Yes/No
- OS:
- Browser:
**Additional context**
Add any other context about the problem here.

View file

@ -27,7 +27,7 @@ It is a self-hosted monitoring tool like "Uptime Robot".
docker volume create uptime-kuma docker volume create uptime-kuma
# Start the container # 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. Browse to http://localhost:3001 after started.
@ -35,7 +35,7 @@ Browse to http://localhost:3001 after started.
Change Port and Volume Change Port and Volume
```bash ```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 ### Without Docker
@ -80,12 +80,17 @@ PS: For every new release, it takes some time to build the docker image, please
```bash ```bash
git fetch --all git fetch --all
git checkout 1.0.5 --force git checkout 1.0.6 --force
npm install npm install
npm run build npm run build
pm2 restart uptime-kuma pm2 restart uptime-kuma
``` ```
# What's Next?
I will mark requests/issues to the next milestone.
https://github.com/louislam/uptime-kuma/milestones
# More Screenshots # More Screenshots
Settings Page: Settings Page:
@ -109,3 +114,11 @@ Telegram Notification Sample:
If you love this project, please consider giving me a ⭐. 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.

37
db/patch1.sql Normal file
View file

@ -0,0 +1,37 @@
-- You should not modify if this have pushed to Github, unless it do serious wrong with the db.
-- Change Monitor.created_date from "TIMESTAMP" to "DATETIME"
-- SQL Generated by Intellij Idea
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
create table monitor_dg_tmp
(
id INTEGER not null
primary key autoincrement,
name VARCHAR(150),
active BOOLEAN default 1 not null,
user_id INTEGER
references user
on update cascade on delete set null,
interval INTEGER default 20 not null,
url TEXT,
type VARCHAR(20),
weight INTEGER default 2000,
hostname VARCHAR(255),
port INTEGER,
created_date DATETIME,
keyword VARCHAR(255)
);
insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword from monitor;
drop table monitor;
alter table monitor_dg_tmp rename to monitor;
create index user_id on monitor (user_id);
COMMIT;
PRAGMA foreign_keys=on;

199
package-lock.json generated
View file

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

View file

@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.0.5", "version": "1.0.6",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@ -12,10 +12,10 @@
"update": "", "update": "",
"build": "vite build", "build": "vite build",
"vite-preview-dist": "vite preview --host", "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.5 --target release . --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.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": "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", "build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push",
"setup": "git checkout 1.0.5 && npm install && npm run build", "setup": "git checkout 1.0.6 && npm install && npm run build",
"version-global-replace": "node extra/version-global-replace.js", "version-global-replace": "node extra/version-global-replace.js",
"mark-as-nightly": "node extra/mark-as-nightly.js" "mark-as-nightly": "node extra/mark-as-nightly.js"
}, },

119
server/database.js Normal file
View file

@ -0,0 +1,119 @@
const fs = require("fs");
const {sleep} = require("./util");
const {R} = require("redbean-node");
const {setSetting, setting} = require("./util-server");
class Database {
static templatePath = "./db/kuma.db"
static path = './data/kuma.db';
static latestVersion = 1;
static noReject = true;
static async patch() {
let version = parseInt(await setting("database_version"));
if (! version) {
version = 0;
}
console.info("Your database version: " + version);
console.info("Latest database version: " + this.latestVersion);
if (version === this.latestVersion) {
console.info("Database no need to patch");
} else {
console.info("Database patch is needed")
console.info("Backup the db")
const backupPath = "./data/kuma.db.bak" + version;
fs.copyFileSync(Database.path, backupPath);
// Try catch anything here, if gone wrong, restore the backup
try {
for (let i = version + 1; i <= this.latestVersion; i++) {
const sqlFile = `./db/patch${i}.sql`;
console.info(`Patching ${sqlFile}`);
await Database.importSQLFile(sqlFile);
console.info(`Patched ${sqlFile}`);
await setSetting("database_version", i);
}
console.log("Database Patched Successfully");
} catch (ex) {
await Database.close();
console.error("Patch db failed!!! Restoring the backup")
fs.copyFileSync(backupPath, Database.path);
console.error(ex)
console.error("Start Uptime-Kuma failed due to patch db failed")
console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues")
process.exit(1);
}
}
}
/**
* Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself
* @param filename
* @returns {Promise<void>}
*/
static async importSQLFile(filename) {
await R.getCell("SELECT 1");
let text = fs.readFileSync(filename).toString();
// Remove all comments (--)
let lines = text.split("\n");
lines = lines.filter((line) => {
return ! line.startsWith("--")
});
// Split statements by semicolon
// Filter out empty line
text = lines.join("\n")
let statements = text.split(";")
.map((statement) => {
return statement.trim();
})
.filter((statement) => {
return statement !== "";
})
for (let statement of statements) {
await R.exec(statement);
}
}
/**
* Special handle, because tarn.js throw a promise reject that cannot be caught
* @returns {Promise<void>}
*/
static async close() {
const listener = (reason, p) => {
Database.noReject = false;
};
process.addListener('unhandledRejection', listener);
console.log("Closing DB")
while (true) {
Database.noReject = true;
await R.close()
await sleep(2000)
if (Database.noReject) {
break;
} else {
console.log("Waiting to close the db")
}
}
console.log("SQLite closed")
process.removeListener('unhandledRejection', listener);
}
}
module.exports = Database;

View file

@ -48,8 +48,6 @@ class Monitor extends BeanModel {
let previousBeat = null; let previousBeat = null;
const beat = async () => { const beat = async () => {
console.log(`Monitor ${this.id}: Heartbeat`)
if (! previousBeat) { if (! previousBeat) {
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
this.id this.id
@ -145,6 +143,12 @@ class Monitor extends BeanModel {
bean.important = false; bean.important = false;
} }
if (bean.status === 1) {
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`)
} else {
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`)
}
io.to(this.user_id).emit("heartbeat", bean.toJSON()); io.to(this.user_id).emit("heartbeat", bean.toJSON());
await R.store(bean) await R.store(bean)

View file

@ -12,6 +12,7 @@ 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 gracefulShutdown = require('http-graceful-shutdown');
const Database = require("./database");
const {sleep} = require("./util"); const {sleep} = require("./util");
const args = require('args-parser')(process.argv); const args = require('args-parser')(process.argv);
@ -27,9 +28,28 @@ const server = http.createServer(app);
const io = new Server(server); const io = new Server(server);
app.use(express.json()) app.use(express.json())
/**
* Total WebSocket client connected to server currently, no actual use
* @type {number}
*/
let totalClient = 0; let totalClient = 0;
/**
* Use for decode the auth object
* @type {null}
*/
let jwtSecret = null; let jwtSecret = null;
/**
* Main monitor list
* @type {{}}
*/
let monitorList = {}; let monitorList = {};
/**
* Show Setup Page
* @type {boolean}
*/
let needSetup = false; let needSetup = false;
(async () => { (async () => {
@ -50,7 +70,6 @@ let needSetup = false;
version, version,
}) })
console.log('a user connected');
totalClient++; totalClient++;
if (needSetup) { if (needSetup) {
@ -59,7 +78,6 @@ let needSetup = false;
} }
socket.on('disconnect', () => { socket.on('disconnect', () => {
console.log('user disconnected');
totalClient--; totalClient--;
}); });
@ -557,19 +575,21 @@ function checkLogin(socket) {
} }
async function initDatabase() { async function initDatabase() {
const path = './data/kuma.db'; if (! fs.existsSync(Database.path)) {
if (! fs.existsSync(path)) {
console.log("Copying Database") console.log("Copying Database")
fs.copyFileSync("./db/kuma.db", path); fs.copyFileSync(Database.templatePath, Database.path);
} }
console.log("Connecting to Database") console.log("Connecting to Database")
R.setup('sqlite', { R.setup('sqlite', {
filename: path filename: Database.path
}); });
console.log("Connected") console.log("Connected")
// Patch the database
await Database.patch()
// Auto map the model to a bean object
R.freeze(true) R.freeze(true)
await R.autoloadModels("./server/model"); await R.autoloadModels("./server/model");
@ -589,6 +609,7 @@ async function initDatabase() {
console.log("Load JWT secret from database.") console.log("Load JWT secret from database.")
} }
// If there is no record in user table, it is a new Uptime Kuma instance, need to setup
if ((await R.count("user")) === 0) { if ((await R.count("user")) === 0) {
console.log("No user, need setup") console.log("No user, need setup")
needSetup = true; needSetup = true;
@ -707,11 +728,6 @@ const startGracefulShutdown = async () => {
} }
let noReject = true;
process.on('unhandledRejection', (reason, p) => {
noReject = false;
});
async function shutdownFunction(signal) { async function shutdownFunction(signal) {
console.log('Called signal: ' + signal); console.log('Called signal: ' + signal);
@ -720,24 +736,8 @@ async function shutdownFunction(signal) {
let monitor = monitorList[id] let monitor = monitorList[id]
monitor.stop() monitor.stop()
} }
await sleep(2000) await sleep(2000);
await Database.close();
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() { function finalFunction() {

View file

@ -45,6 +45,18 @@ exports.setting = async function (key) {
]) ])
} }
exports.setSetting = async function (key, value) {
let bean = await R.findOne("setting", " `key` = ? ", [
key
])
if (! bean) {
bean = R.dispense("setting")
bean.key = key;
}
bean.value = value;
await R.store(bean)
}
exports.getSettings = async function (type) { exports.getSettings = async function (type) {
let list = await R.getAll("SELECT * FROM setting WHERE `type` = ? ", [ let list = await R.getAll("SELECT * FROM setting WHERE `type` = ? ", [
type type

View file

@ -55,7 +55,7 @@
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
<template v-if="notification.telegramBotToken"> <template v-if="notification.telegramBotToken">
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a> <a :href="telegramGetUpdatesURL" target="_blank" style="word-break: break-word;">{{ telegramGetUpdatesURL }}</a>
</template> </template>
<template v-else> <template v-else>

View file

@ -35,7 +35,8 @@ export default {
window.addEventListener('resize', this.onResize); window.addEventListener('resize', this.onResize);
let wsHost; let wsHost;
if (localStorage.dev === "dev") { const env = process.env.NODE_ENV || "production";
if (env === "development" || localStorage.dev === "dev") {
wsHost = ":3001" wsHost = ":3001"
} else { } else {
wsHost = "" wsHost = ""
@ -45,6 +46,10 @@ export default {
transports: ['websocket'] 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) => { socket.on('info', (info) => {
this.info = info; this.info = info;
}); });