Browse Source
# Conflicts: # package-lock.json # package.jsonphilippdormann/feature/release-management
Philipp Dormann
3 years ago
61 changed files with 10213 additions and 14870 deletions
@ -1,4 +1,37 @@ |
|||
/.idea |
|||
/dist |
|||
/node_modules |
|||
/data/kuma.db |
|||
/data |
|||
/.do |
|||
**/.dockerignore |
|||
**/.git |
|||
**/.gitignore |
|||
**/docker-compose* |
|||
**/[Dd]ockerfile* |
|||
LICENSE |
|||
README.md |
|||
.editorconfig |
|||
.vscode |
|||
.eslint* |
|||
.stylelint* |
|||
/.github |
|||
package-lock.json |
|||
yarn.lock |
|||
app.json |
|||
CODE_OF_CONDUCT.md |
|||
CONTRIBUTING.md |
|||
|
|||
### .gitignore content (commented rules are duplicated) |
|||
|
|||
#node_modules |
|||
.DS_Store |
|||
#dist |
|||
dist-ssr |
|||
*.local |
|||
#.idea |
|||
|
|||
#/data |
|||
#!/data/.gitkeep |
|||
#.vscode |
|||
|
|||
### End of .gitignore content |
|||
|
@ -0,0 +1,73 @@ |
|||
module.exports = { |
|||
env: { |
|||
browser: true, |
|||
commonjs: true, |
|||
es2020: true, |
|||
node: true, |
|||
}, |
|||
extends: [ |
|||
"eslint:recommended", |
|||
"plugin:vue/vue3-recommended", |
|||
], |
|||
parser: "vue-eslint-parser", |
|||
parserOptions: { |
|||
parser: "@babel/eslint-parser", |
|||
sourceType: "module", |
|||
requireConfigFile: false, |
|||
}, |
|||
rules: { |
|||
// override/add rules settings here, such as:
|
|||
// 'vue/no-unused-vars': 'error'
|
|||
"no-unused-vars": "warn", |
|||
indent: [ |
|||
"error", |
|||
4, |
|||
{ |
|||
ignoredNodes: ["TemplateLiteral"], |
|||
SwitchCase: 1, |
|||
}, |
|||
], |
|||
quotes: ["warn", "double"], |
|||
//semi: ['off', 'never'],
|
|||
"vue/html-indent": ["warn", 4], // default: 2
|
|||
"vue/max-attributes-per-line": "off", |
|||
"vue/singleline-html-element-content-newline": "off", |
|||
"vue/html-self-closing": "off", |
|||
"no-multi-spaces": ["error", { |
|||
ignoreEOLComments: true, |
|||
}], |
|||
"curly": "error", |
|||
"object-curly-spacing": ["error", "always"], |
|||
"object-curly-newline": "off", |
|||
"object-property-newline": "error", |
|||
"comma-spacing": "error", |
|||
"brace-style": "error", |
|||
"no-var": "error", |
|||
"key-spacing": "warn", |
|||
"keyword-spacing": "warn", |
|||
"space-infix-ops": "warn", |
|||
"arrow-spacing": "warn", |
|||
"no-trailing-spaces": "warn", |
|||
"no-constant-condition": ["error", { |
|||
"checkLoops": false, |
|||
}], |
|||
"space-before-blocks": "warn", |
|||
//'no-console': 'warn',
|
|||
"no-extra-boolean-cast": "off", |
|||
"no-multiple-empty-lines": ["warn", { |
|||
"max": 1, |
|||
"maxBOF": 0, |
|||
}], |
|||
"lines-between-class-members": ["warn", "always", { |
|||
exceptAfterSingleLine: true, |
|||
}], |
|||
"no-unneeded-ternary": "error", |
|||
"no-else-return": ["error", { |
|||
"allowElseIf": false, |
|||
}], |
|||
"array-bracket-newline": ["error", "consistent"], |
|||
"eol-last": ["error", "always"], |
|||
//'prefer-template': 'error',
|
|||
"comma-dangle": ["warn", "only-multiline"], |
|||
}, |
|||
} |
@ -0,0 +1,10 @@ |
|||
--- |
|||
name: Ask for help |
|||
about: You can ask any question related to Uptime Kuma. |
|||
title: '' |
|||
labels: help |
|||
assignees: '' |
|||
|
|||
--- |
|||
|
|||
|
@ -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. |
@ -0,0 +1,20 @@ |
|||
--- |
|||
name: Feature request |
|||
about: Suggest an idea for this project |
|||
title: '' |
|||
labels: enhancement |
|||
assignees: '' |
|||
|
|||
--- |
|||
|
|||
**Is your feature request related to a problem? Please describe.** |
|||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] |
|||
|
|||
**Describe the solution you'd like** |
|||
A clear and concise description of what you want to happen. |
|||
|
|||
**Describe alternatives you've considered** |
|||
A clear and concise description of any alternative solutions or features you've considered. |
|||
|
|||
**Additional context** |
|||
Add any other context or screenshots about the feature request here. |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"extends": "stylelint-config-recommended", |
|||
} |
@ -0,0 +1,128 @@ |
|||
# Contributor Covenant Code of Conduct |
|||
|
|||
## Our Pledge |
|||
|
|||
We as members, contributors, and leaders pledge to make participation in our |
|||
community a harassment-free experience for everyone, regardless of age, body |
|||
size, visible or invisible disability, ethnicity, sex characteristics, gender |
|||
identity and expression, level of experience, education, socio-economic status, |
|||
nationality, personal appearance, race, religion, or sexual identity |
|||
and orientation. |
|||
|
|||
We pledge to act and interact in ways that contribute to an open, welcoming, |
|||
diverse, inclusive, and healthy community. |
|||
|
|||
## Our Standards |
|||
|
|||
Examples of behavior that contributes to a positive environment for our |
|||
community include: |
|||
|
|||
* Demonstrating empathy and kindness toward other people |
|||
* Being respectful of differing opinions, viewpoints, and experiences |
|||
* Giving and gracefully accepting constructive feedback |
|||
* Accepting responsibility and apologizing to those affected by our mistakes, |
|||
and learning from the experience |
|||
* Focusing on what is best not just for us as individuals, but for the |
|||
overall community |
|||
|
|||
Examples of unacceptable behavior include: |
|||
|
|||
* The use of sexualized language or imagery, and sexual attention or |
|||
advances of any kind |
|||
* Trolling, insulting or derogatory comments, and personal or political attacks |
|||
* Public or private harassment |
|||
* Publishing others' private information, such as a physical or email |
|||
address, without their explicit permission |
|||
* Other conduct which could reasonably be considered inappropriate in a |
|||
professional setting |
|||
|
|||
## Enforcement Responsibilities |
|||
|
|||
Community leaders are responsible for clarifying and enforcing our standards of |
|||
acceptable behavior and will take appropriate and fair corrective action in |
|||
response to any behavior that they deem inappropriate, threatening, offensive, |
|||
or harmful. |
|||
|
|||
Community leaders have the right and responsibility to remove, edit, or reject |
|||
comments, commits, code, wiki edits, issues, and other contributions that are |
|||
not aligned to this Code of Conduct, and will communicate reasons for moderation |
|||
decisions when appropriate. |
|||
|
|||
## Scope |
|||
|
|||
This Code of Conduct applies within all community spaces, and also applies when |
|||
an individual is officially representing the community in public spaces. |
|||
Examples of representing our community include using an official e-mail address, |
|||
posting via an official social media account, or acting as an appointed |
|||
representative at an online or offline event. |
|||
|
|||
## Enforcement |
|||
|
|||
Instances of abusive, harassing, or otherwise unacceptable behavior may be |
|||
reported to the community leaders responsible for enforcement at |
|||
louis@uptimekuma.louislam.net. |
|||
All complaints will be reviewed and investigated promptly and fairly. |
|||
|
|||
All community leaders are obligated to respect the privacy and security of the |
|||
reporter of any incident. |
|||
|
|||
## Enforcement Guidelines |
|||
|
|||
Community leaders will follow these Community Impact Guidelines in determining |
|||
the consequences for any action they deem in violation of this Code of Conduct: |
|||
|
|||
### 1. Correction |
|||
|
|||
**Community Impact**: Use of inappropriate language or other behavior deemed |
|||
unprofessional or unwelcome in the community. |
|||
|
|||
**Consequence**: A private, written warning from community leaders, providing |
|||
clarity around the nature of the violation and an explanation of why the |
|||
behavior was inappropriate. A public apology may be requested. |
|||
|
|||
### 2. Warning |
|||
|
|||
**Community Impact**: A violation through a single incident or series |
|||
of actions. |
|||
|
|||
**Consequence**: A warning with consequences for continued behavior. No |
|||
interaction with the people involved, including unsolicited interaction with |
|||
those enforcing the Code of Conduct, for a specified period of time. This |
|||
includes avoiding interactions in community spaces as well as external channels |
|||
like social media. Violating these terms may lead to a temporary or |
|||
permanent ban. |
|||
|
|||
### 3. Temporary Ban |
|||
|
|||
**Community Impact**: A serious violation of community standards, including |
|||
sustained inappropriate behavior. |
|||
|
|||
**Consequence**: A temporary ban from any sort of interaction or public |
|||
communication with the community for a specified period of time. No public or |
|||
private interaction with the people involved, including unsolicited interaction |
|||
with those enforcing the Code of Conduct, is allowed during this period. |
|||
Violating these terms may lead to a permanent ban. |
|||
|
|||
### 4. Permanent Ban |
|||
|
|||
**Community Impact**: Demonstrating a pattern of violation of community |
|||
standards, including sustained inappropriate behavior, harassment of an |
|||
individual, or aggression toward or disparagement of classes of individuals. |
|||
|
|||
**Consequence**: A permanent ban from any sort of public interaction within |
|||
the community. |
|||
|
|||
## Attribution |
|||
|
|||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], |
|||
version 2.0, available at |
|||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. |
|||
|
|||
Community Impact Guidelines were inspired by [Mozilla's code of conduct |
|||
enforcement ladder](https://github.com/mozilla/diversity). |
|||
|
|||
[homepage]: https://www.contributor-covenant.org |
|||
|
|||
For answers to common questions about this code of conduct, see the FAQ at |
|||
https://www.contributor-covenant.org/faq. Translations are available at |
|||
https://www.contributor-covenant.org/translations. |
@ -0,0 +1,104 @@ |
|||
# Project Info |
|||
|
|||
First of all, thank you everyone who made pull requests for Uptime Kuma, I never thought GitHub Community can be that nice! And also because of this, I also never thought other people actually read my code and edit my code. It is not structed and commented so well, lol. Sorry about that. |
|||
|
|||
The project was created with vite.js (vue3). Then I created a sub-directory called "server" for server part. Both frontend and backend share the same package.json. |
|||
|
|||
The frontend code build into "dist" directory. The server uses "dist" as root. This is how production is working. |
|||
|
|||
Your IDE should follow the config in ".editorconfig". The most special thing is I set it to 4 spaces indentation. I know 2 spaces indentation became a kind of standard nowadays for js, but my eyes is not so comfortable for this. In my opinion, there is no callback-hell nowadays, it is good to go back 4 spaces world again. |
|||
|
|||
# Project Styles |
|||
|
|||
I personally do not like something need to learn so much and need to config so much before you can finally start the app. |
|||
|
|||
For example, recently, because I am not a python expert, I spent a 2 hours to resolve all problems in order to install and use the Apprise cli. Apprise requires so many hidden requirements, I have to figure out myself how to solve the problems by Google search for my OS. That is painful. I do not want Uptime Kuma to be like this way, so: |
|||
|
|||
- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort to get it run |
|||
- Single container for Docker users, no very complex docker-composer file. Just map the volume and expose the port, then good to go |
|||
- All settings in frontend. |
|||
- Easy to use |
|||
|
|||
# Tools |
|||
- Node.js >= 14 |
|||
- Git |
|||
- IDE that supports .editorconfig (I am using Intellji Idea) |
|||
- A SQLite tool (I am using SQLite Expert Personal) |
|||
|
|||
# Prepare the dev |
|||
|
|||
```bash |
|||
npm install |
|||
``` |
|||
|
|||
# Backend Dev |
|||
|
|||
```bash |
|||
npm run start-server |
|||
|
|||
# Or |
|||
|
|||
node server/server.js |
|||
|
|||
``` |
|||
|
|||
It binds to 0.0.0.0:3001 by default. |
|||
|
|||
|
|||
## Backend Details |
|||
|
|||
It is mainly a socket.io app + express.js. |
|||
|
|||
express.js is just used for serving the frontend built files (index.html, .js and .css etc.) |
|||
|
|||
# Frontend Dev |
|||
|
|||
Start frontend dev server. Hot-reload enabled in this way. It binds to 0.0.0.0:3000. |
|||
|
|||
```bash |
|||
npm run dev |
|||
``` |
|||
|
|||
PS: You can ignore those scss warnings, those warnings are from Bootstrap that I cannot fix. |
|||
|
|||
You can use Vue Devtool Chrome extension for debugging. |
|||
|
|||
After the frontend server started. It cannot connect to the websocket server even you have started the server. You need to tell the frontend that is a dev env by running this in DevTool console and refresh: |
|||
|
|||
```javascript |
|||
localStorage.dev = "dev"; |
|||
``` |
|||
|
|||
So that the frontend will try to connect websocket server in 3001. |
|||
|
|||
Alternately, you can specific NODE_ENV to "development". |
|||
|
|||
|
|||
## Build the frontend |
|||
|
|||
```bash |
|||
npm run build |
|||
``` |
|||
|
|||
## Frontend Details |
|||
|
|||
Uptime Kuma Frontend is a single page application (SPA). Most paths are handled by Vue Router. |
|||
|
|||
The router in "src/main.js" |
|||
|
|||
As you can see, most data in frontend is stored in root level, even though you changed the current router to any other pages. |
|||
|
|||
The data and socket logic in "src/mixins/socket.js" |
|||
|
|||
# Database Migration |
|||
|
|||
TODO |
|||
|
|||
# Unit Test |
|||
|
|||
Yes, no unit test for now. I know it is very important, but at the same time my spare time is very limited. I want to implement my ideas first. I will go back to this in some points. |
|||
|
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,7 @@ |
|||
{ |
|||
"name": "Uptime Kuma", |
|||
"description": "A fancy self-hosted monitoring tool", |
|||
"repository": "https://github.com/louislam/uptime-kuma", |
|||
"logo": "https://raw.githubusercontent.com/louislam/uptime-kuma/master/public/icon.png", |
|||
"keywords": ["node", "express", "socket-io", "uptime-kuma", "uptime"] |
|||
} |
Binary file not shown.
@ -0,0 +1,37 @@ |
|||
-- You should not modify if this have pushed to Github, unless it does 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; |
@ -0,0 +1,9 @@ |
|||
BEGIN TRANSACTION; |
|||
|
|||
CREATE TABLE monitor_tls_info ( |
|||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
|||
monitor_id INTEGER NOT NULL, |
|||
info_json TEXT |
|||
); |
|||
|
|||
COMMIT; |
@ -0,0 +1,37 @@ |
|||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. |
|||
-- Add maxretries column to monitor |
|||
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), |
|||
maxretries INTEGER NOT NULL DEFAULT 0 |
|||
); |
|||
|
|||
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; |
@ -0,0 +1,40 @@ |
|||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. |
|||
-- OK.... serious wrong, missing maxretries column |
|||
-- Developers should patch it manually if you have missing the maxretries column |
|||
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), |
|||
maxretries INTEGER NOT NULL DEFAULT 0, |
|||
ignore_tls BOOLEAN default 0 not null, |
|||
upside_down BOOLEAN default 0 not null |
|||
); |
|||
|
|||
insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword, maxretries) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword, maxretries 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; |
@ -1,18 +1,31 @@ |
|||
FROM node:14-alpine3.14 |
|||
# DON'T UPDATE TO alpine3.13, 1.14, see #41. |
|||
FROM node:14-alpine3.12 AS release |
|||
WORKDIR /app |
|||
|
|||
# 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 |
|||
# 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 |
|||
|
|||
WORKDIR /app |
|||
COPY . . |
|||
RUN npm install |
|||
RUN npm run build |
|||
# Touching above code may causes sqlite3 re-compile again, painful slow. |
|||
|
|||
# Install apprise |
|||
RUN apk add --no-cache python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib |
|||
RUN pip3 --no-cache-dir install apprise && \ |
|||
rm -rf /root/.cache |
|||
|
|||
# Remove built tools |
|||
RUN apk del make g++ |
|||
# New things add here |
|||
|
|||
COPY . . |
|||
RUN npm install && \ |
|||
npm run build && \ |
|||
npm prune |
|||
|
|||
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 |
|||
|
@ -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(); |
@ -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)) |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -1,66 +1,74 @@ |
|||
{ |
|||
"name": "uptime-kuma", |
|||
"version": "1.0.4", |
|||
"version": "1.0.8", |
|||
"license": "MIT", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/louislam/uptime-kuma.git" |
|||
}, |
|||
"engines": { |
|||
"node": "14.*" |
|||
}, |
|||
"scripts": { |
|||
"dev": "vite --host", |
|||
"release": "release-it", |
|||
"start": "npm run start-server", |
|||
"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.3 . --push", |
|||
"build-docker-nightly": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly . --push", |
|||
"setup": "git checkout 1.0.3 && 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.8 --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.8 && npm install && npm run build", |
|||
"version-global-replace": "node extra/version-global-replace.js", |
|||
"mark-as-nightly": "node extra/mark-as-nightly.js" |
|||
}, |
|||
"dependencies": { |
|||
"@fortawesome/fontawesome-svg-core": "^1.2.35", |
|||
"@fortawesome/free-regular-svg-icons": "^5.15.3", |
|||
"@fortawesome/free-solid-svg-icons": "^5.15.3", |
|||
"@fortawesome/vue-fontawesome": "^3.0.0-4", |
|||
"@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", |
|||
"express-basic-auth": "^1.2.0", |
|||
"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", |
|||
"prom-client": "^13.1.0", |
|||
"prometheus-api-metrics": "^3.2.0", |
|||
"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", |
|||
"v-pagination-3": "^0.1.6", |
|||
"vue": "^3.0.5", |
|||
"vue-confirm-dialog": "^1.0.2", |
|||
"vue-router": "^4.0.10", |
|||
"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", |
|||
"@babel/eslint-parser": "^7.13.10", |
|||
"@types/bootstrap": "^5.0.17", |
|||
"@vitejs/plugin-legacy": "^1.5.0", |
|||
"@vitejs/plugin-vue": "^1.3.0", |
|||
"@vue/compiler-sfc": "^3.1.5", |
|||
"core-js": "^3.15.2", |
|||
"sass": "^1.35.1", |
|||
"vite": "^2.3.7", |
|||
"auto-changelog": "2.3.0", |
|||
"release-it": "14.10.0" |
|||
}, |
|||
"release-it": { |
|||
"git": { |
|||
"commit": true, |
|||
"requireUpstream": false, |
|||
"requireCleanWorkingDir": false, |
|||
"commitMessage": "🚀RELEASE v${version}", |
|||
"push": false, |
|||
"tag": true, |
|||
"tagName": "v${version}", |
|||
"tagAnnotation": "v${version}" |
|||
}, |
|||
"npm": { |
|||
"publish": false |
|||
}, |
|||
"hooks": { |
|||
"after:bump": "auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md" |
|||
} |
|||
"eslint": "^7.31.0", |
|||
"eslint-plugin-vue": "^7.14.0", |
|||
"sass": "^1.36.0", |
|||
"stylelint": "^13.13.1", |
|||
"stylelint-config-recommended": "^5.0.0", |
|||
"stylelint-config-standard": "^22.0.0", |
|||
"typescript": "^4.3.5", |
|||
"vite": "^2.4.4" |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,3 @@ |
|||
# https://www.robotstxt.org/robotstxt.html |
|||
User-agent: * |
|||
Disallow: |
@ -0,0 +1,51 @@ |
|||
const basicAuth = require("express-basic-auth") |
|||
const passwordHash = require("./password-hash"); |
|||
const { R } = require("redbean-node"); |
|||
const { setting } = require("./util-server"); |
|||
const { debug } = require("../src/util"); |
|||
|
|||
/** |
|||
* |
|||
* @param username : string |
|||
* @param password : string |
|||
* @returns {Promise<Bean|null>} |
|||
*/ |
|||
exports.login = async function (username, password) { |
|||
let user = await R.findOne("user", " username = ? AND active = 1 ", [ |
|||
username, |
|||
]) |
|||
|
|||
if (user && passwordHash.verify(password, user.password)) { |
|||
// Upgrade the hash to bcrypt
|
|||
if (passwordHash.needRehash(user.password)) { |
|||
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ |
|||
passwordHash.generate(password), |
|||
user.id, |
|||
]); |
|||
} |
|||
return user; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
function myAuthorizer(username, password, callback) { |
|||
|
|||
setting("disableAuth").then((result) => { |
|||
|
|||
if (result) { |
|||
callback(null, true) |
|||
} else { |
|||
exports.login(username, password).then((user) => { |
|||
callback(null, user != null) |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
} |
|||
|
|||
exports.basicAuth = basicAuth({ |
|||
authorizer: myAuthorizer, |
|||
authorizeAsync: true, |
|||
challenge: true, |
|||
}); |
@ -0,0 +1,120 @@ |
|||
const fs = require("fs"); |
|||
const { sleep } = require("../src/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 = 4; |
|||
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; |
@ -0,0 +1,59 @@ |
|||
const PrometheusClient = require('prom-client'); |
|||
|
|||
const commonLabels = [ |
|||
'monitor_name', |
|||
'monitor_type', |
|||
'monitor_url', |
|||
'monitor_hostname', |
|||
'monitor_port', |
|||
] |
|||
|
|||
const monitor_response_time = new PrometheusClient.Gauge({ |
|||
name: 'monitor_response_time', |
|||
help: 'Monitor Response Time (ms)', |
|||
labelNames: commonLabels |
|||
}); |
|||
|
|||
const monitor_status = new PrometheusClient.Gauge({ |
|||
name: 'monitor_status', |
|||
help: 'Monitor Status (1 = UP, 0= DOWN)', |
|||
labelNames: commonLabels |
|||
}); |
|||
|
|||
class Prometheus { |
|||
monitorLabelValues = {} |
|||
|
|||
constructor(monitor) { |
|||
this.monitorLabelValues = { |
|||
monitor_name: monitor.name, |
|||
monitor_type: monitor.type, |
|||
monitor_url: monitor.url, |
|||
monitor_hostname: monitor.hostname, |
|||
monitor_port: monitor.port |
|||
} |
|||
} |
|||
|
|||
update(heartbeat) { |
|||
try { |
|||
monitor_status.set(this.monitorLabelValues, heartbeat.status) |
|||
} catch (e) { |
|||
console.error(e) |
|||
} |
|||
|
|||
try { |
|||
if (typeof heartbeat.ping === 'number') { |
|||
monitor_response_time.set(this.monitorLabelValues, heartbeat.ping) |
|||
} else { |
|||
// Is it good?
|
|||
monitor_response_time.set(this.monitorLabelValues, -1) |
|||
} |
|||
} catch (e) { |
|||
console.error(e) |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
module.exports = { |
|||
Prometheus |
|||
} |
@ -1,20 +0,0 @@ |
|||
/* |
|||
* Common functions - can be used in frontend or backend |
|||
*/ |
|||
|
|||
|
|||
|
|||
|
|||
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); |
|||
} |
|||
|
@ -1,7 +1,8 @@ |
|||
$primary: #5CDD8B; |
|||
$danger: #DC3545; |
|||
$warning: #f8a306; |
|||
$link-color: #111; |
|||
$border-radius: 50rem; |
|||
|
|||
$highlight: #7ce8a4; |
|||
$highlight-white: #e7faec; |
|||
$highlight-white: #e7faec; |
@ -0,0 +1,10 @@ |
|||
import { library } from "@fortawesome/fontawesome-svg-core" |
|||
import { faCog, faEdit, faList, faPause, faPlay, faPlus, faTachometerAlt, faTrash } from "@fortawesome/free-solid-svg-icons" |
|||
//import { fa } from '@fortawesome/free-regular-svg-icons'
|
|||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" |
|||
|
|||
// Add Free Font Awesome Icons here
|
|||
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
|
|||
library.add(faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList) |
|||
|
|||
export { FontAwesomeIcon } |
@ -0,0 +1,34 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.PENDING = exports.UP = exports.DOWN = void 0; |
|||
exports.DOWN = 0; |
|||
exports.UP = 1; |
|||
exports.PENDING = 2; |
|||
function flipStatus(s) { |
|||
if (s === exports.UP) { |
|||
return exports.DOWN; |
|||
} |
|||
if (s === exports.DOWN) { |
|||
return exports.UP; |
|||
} |
|||
return s; |
|||
} |
|||
exports.flipStatus = flipStatus; |
|||
function sleep(ms) { |
|||
return new Promise(resolve => setTimeout(resolve, ms)); |
|||
} |
|||
exports.sleep = sleep; |
|||
function ucfirst(str) { |
|||
if (!str) { |
|||
return str; |
|||
} |
|||
const firstLetter = str.substr(0, 1); |
|||
return firstLetter.toUpperCase() + str.substr(1); |
|||
} |
|||
exports.ucfirst = ucfirst; |
|||
function debug(msg) { |
|||
if (process.env.NODE_ENV === "development") { |
|||
console.log(msg); |
|||
} |
|||
} |
|||
exports.debug = debug; |
@ -0,0 +1,43 @@ |
|||
// Common Util for frontend and backend
|
|||
// Backend uses the compiled file util.js
|
|||
// Frontend uses util.ts
|
|||
// Need to run "tsc" to compile if there are any changes.
|
|||
|
|||
export const DOWN = 0; |
|||
export const UP = 1; |
|||
export const PENDING = 2; |
|||
|
|||
export function flipStatus(s) { |
|||
if (s === UP) { |
|||
return DOWN; |
|||
} |
|||
|
|||
if (s === DOWN) { |
|||
return UP; |
|||
} |
|||
|
|||
return s; |
|||
} |
|||
|
|||
export function sleep(ms) { |
|||
return new Promise(resolve => setTimeout(resolve, ms)); |
|||
} |
|||
|
|||
/** |
|||
* PHP's ucfirst |
|||
* @param str |
|||
*/ |
|||
export function ucfirst(str) { |
|||
if (! str) { |
|||
return str; |
|||
} |
|||
|
|||
const firstLetter = str.substr(0, 1); |
|||
return firstLetter.toUpperCase() + str.substr(1); |
|||
} |
|||
|
|||
export function debug(msg) { |
|||
if (process.env.NODE_ENV === "development") { |
|||
console.log(msg) |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
{ |
|||
"compileOnSave": true, |
|||
"compilerOptions": { |
|||
"target": "ES2018", |
|||
"module": "commonjs", |
|||
"removeComments": true, |
|||
"preserveConstEnums": true, |
|||
"sourceMap": false, |
|||
"files.insertFinalNewline": true |
|||
}, |
|||
"files": [ |
|||
"./server/util.ts" |
|||
] |
|||
} |
Loading…
Reference in new issue