58 changed files with 6676 additions and 1875 deletions
@ -1,14 +1,37 @@ |
|||||
/.idea |
/.idea |
||||
/dist |
/dist |
||||
/node_modules |
/node_modules |
||||
/data/kuma.db |
/data |
||||
/.do |
/.do |
||||
**/.dockerignore |
**/.dockerignore |
||||
**/.git |
**/.git |
||||
**/.gitignore |
**/.gitignore |
||||
**/docker-compose* |
**/docker-compose* |
||||
**/Dockerfile* |
**/[Dd]ockerfile* |
||||
LICENSE |
LICENSE |
||||
README.md |
README.md |
||||
.editorconfig |
.editorconfig |
||||
.vscode |
.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,70 @@ |
|||||
|
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", |
||||
|
"array-bracket-newline": ["error", "consistent"], |
||||
|
"eol-last": ["error", "always"], |
||||
|
//'prefer-template': 'error',
|
||||
|
"comma-dangle": ["warn", "only-multiline"], |
||||
|
}, |
||||
|
} |
@ -1,10 +0,0 @@ |
|||||
--- |
|
||||
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 |
|
@ -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,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,11 @@ |
|||||
|
name: reviewdog |
||||
|
on: [pull_request] |
||||
|
jobs: |
||||
|
eslint: |
||||
|
name: runner / eslint |
||||
|
runs-on: ubuntu-latest |
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- uses: reviewdog/action-eslint@v1 |
||||
|
with: |
||||
|
reporter: github-pr-review |
@ -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"] |
||||
|
} |
@ -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; |
@ -0,0 +1,70 @@ |
|||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. |
||||
|
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 default (DATETIME('now')) not null, |
||||
|
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, |
||||
|
keyword, |
||||
|
maxretries, |
||||
|
ignore_tls, |
||||
|
upside_down |
||||
|
) |
||||
|
select |
||||
|
id, |
||||
|
name, |
||||
|
active, |
||||
|
user_id, |
||||
|
interval, |
||||
|
url, |
||||
|
type, |
||||
|
weight, |
||||
|
hostname, |
||||
|
port, |
||||
|
keyword, |
||||
|
maxretries, |
||||
|
ignore_tls, |
||||
|
upside_down |
||||
|
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,74 @@ |
|||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. |
||||
|
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 default (DATETIME('now')) not null, |
||||
|
keyword VARCHAR(255), |
||||
|
maxretries INTEGER NOT NULL DEFAULT 0, |
||||
|
ignore_tls BOOLEAN default 0 not null, |
||||
|
upside_down BOOLEAN default 0 not null, |
||||
|
maxredirects INTEGER default 10 not null, |
||||
|
accepted_statuscodes_json TEXT default '["200-299"]' not null |
||||
|
); |
||||
|
|
||||
|
insert into |
||||
|
monitor_dg_tmp( |
||||
|
id, |
||||
|
name, |
||||
|
active, |
||||
|
user_id, |
||||
|
interval, |
||||
|
url, |
||||
|
type, |
||||
|
weight, |
||||
|
hostname, |
||||
|
port, |
||||
|
created_date, |
||||
|
keyword, |
||||
|
maxretries, |
||||
|
ignore_tls, |
||||
|
upside_down |
||||
|
) |
||||
|
select |
||||
|
id, |
||||
|
name, |
||||
|
active, |
||||
|
user_id, |
||||
|
interval, |
||||
|
url, |
||||
|
type, |
||||
|
weight, |
||||
|
hostname, |
||||
|
port, |
||||
|
created_date, |
||||
|
keyword, |
||||
|
maxretries, |
||||
|
ignore_tls, |
||||
|
upside_down |
||||
|
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,78 @@ |
|||||
|
/** |
||||
|
* 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 child_process = require("child_process"); |
||||
|
const oldVersion = pkg.version; |
||||
|
const newVersion = process.argv[2]; |
||||
|
|
||||
|
console.log("Old Version: " + oldVersion); |
||||
|
console.log("New Version: " + newVersion); |
||||
|
|
||||
|
if (! newVersion) { |
||||
|
console.error("invalid version"); |
||||
|
process.exit(1); |
||||
|
} |
||||
|
|
||||
|
const exists = tagExists(newVersion); |
||||
|
|
||||
|
if (! exists) { |
||||
|
// Process package.json
|
||||
|
pkg.version = newVersion; |
||||
|
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion); |
||||
|
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion); |
||||
|
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n"); |
||||
|
|
||||
|
// Process README.md
|
||||
|
fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion)); |
||||
|
|
||||
|
commit(newVersion); |
||||
|
tag(newVersion); |
||||
|
} else { |
||||
|
console.log("version exists") |
||||
|
} |
||||
|
|
||||
|
function commit(version) { |
||||
|
let msg = "update to " + version; |
||||
|
|
||||
|
let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]); |
||||
|
let stdout = res.stdout.toString().trim(); |
||||
|
console.log(stdout) |
||||
|
|
||||
|
if (stdout.includes("no changes added to commit")) { |
||||
|
throw new Error("commit error") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function tag(version) { |
||||
|
let res = child_process.spawnSync("git", ["tag", version]); |
||||
|
console.log(res.stdout.toString().trim()) |
||||
|
} |
||||
|
|
||||
|
function tagExists(version) { |
||||
|
if (! version) { |
||||
|
throw new Error("invalid version"); |
||||
|
} |
||||
|
|
||||
|
let res = child_process.spawnSync("git", ["tag", "-l", version]); |
||||
|
|
||||
|
return res.stdout.toString().trim() === version; |
||||
|
} |
@ -1,39 +0,0 @@ |
|||||
/** |
|
||||
* 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 = process.argv[2] |
|
||||
|
|
||||
console.log("Old Version: " + oldVersion) |
|
||||
console.log("New Version: " + newVersion) |
|
||||
|
|
||||
if (newVersion) { |
|
||||
// Process package.json
|
|
||||
pkg.version = newVersion |
|
||||
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion) |
|
||||
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion) |
|
||||
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n") |
|
||||
|
|
||||
// Process README.md
|
|
||||
fs.writeFileSync("README.md", fs.readFileSync("README.md", 'utf8').replaceAll(oldVersion, newVersion)) |
|
||||
} |
|
||||
|
|
File diff suppressed because it is too large
@ -1,55 +1,75 @@ |
|||||
{ |
{ |
||||
"name": "uptime-kuma", |
"name": "uptime-kuma", |
||||
"version": "1.0.6", |
"version": "1.0.10", |
||||
"license": "MIT", |
"license": "MIT", |
||||
"repository": { |
"repository": { |
||||
"type": "git", |
"type": "git", |
||||
"url": "https://github.com/louislam/uptime-kuma.git" |
"url": "https://github.com/louislam/uptime-kuma.git" |
||||
}, |
}, |
||||
|
"engines": { |
||||
|
"node": "14.*" |
||||
|
}, |
||||
"scripts": { |
"scripts": { |
||||
"dev": "vite --host", |
"dev": "vite --host", |
||||
|
"start": "npm run start-server", |
||||
"start-server": "node server/server.js", |
"start-server": "node server/server.js", |
||||
"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.6 --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.10 --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.6 && npm install && npm run build", |
"setup": "git checkout 1.0.10 && npm install && npm run build", |
||||
"version-global-replace": "node extra/version-global-replace.js", |
"update-version": "node extra/update-version.js", |
||||
"mark-as-nightly": "node extra/mark-as-nightly.js" |
"mark-as-nightly": "node extra/mark-as-nightly.js" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@popperjs/core": "^2.9.2", |
"@fortawesome/fontawesome-svg-core": "^1.2.36", |
||||
|
"@fortawesome/free-regular-svg-icons": "^5.15.4", |
||||
|
"@fortawesome/free-solid-svg-icons": "^5.15.4", |
||||
|
"@fortawesome/vue-fontawesome": "^3.0.0-4", |
||||
|
"@popperjs/core": "^2.9.3", |
||||
"args-parser": "^1.3.0", |
"args-parser": "^1.3.0", |
||||
"axios": "^0.21.1", |
"axios": "^0.21.1", |
||||
"bcrypt": "^5.0.1", |
"bcrypt": "^5.0.1", |
||||
"bootstrap": "^5.0.2", |
"bootstrap": "^5.1.0", |
||||
"command-exists": "^1.2.9", |
"command-exists": "^1.2.9", |
||||
"dayjs": "^1.10.6", |
"dayjs": "^1.10.6", |
||||
"express": "^4.17.1", |
"express": "^4.17.1", |
||||
|
"express-basic-auth": "^1.2.0", |
||||
"form-data": "^4.0.0", |
"form-data": "^4.0.0", |
||||
"http-graceful-shutdown": "^3.1.2", |
"http-graceful-shutdown": "^3.1.3", |
||||
"jsonwebtoken": "^8.5.1", |
"jsonwebtoken": "^8.5.1", |
||||
"nodemailer": "^6.6.3", |
"nodemailer": "^6.6.3", |
||||
"password-hash": "^1.2.2", |
"password-hash": "^1.2.2", |
||||
"redbean-node": "0.0.20", |
"prom-client": "^13.1.0", |
||||
|
"prometheus-api-metrics": "^3.2.0", |
||||
|
"redbean-node": "0.0.21", |
||||
"socket.io": "^4.1.3", |
"socket.io": "^4.1.3", |
||||
"socket.io-client": "^4.1.3", |
"socket.io-client": "^4.1.3", |
||||
"sqlite3": "^5.0.2", |
"@louislam/sqlite3": "^5.0.3", |
||||
"tcp-ping": "^0.1.1", |
"tcp-ping": "^0.1.1", |
||||
"v-pagination-3": "^0.1.6", |
"v-pagination-3": "^0.1.6", |
||||
"vue": "^3.0.5", |
"vue": "^3.1.5", |
||||
"vue-confirm-dialog": "^1.0.2", |
"vue-confirm-dialog": "^1.0.2", |
||||
|
"vue-multiselect": "^3.0.0-alpha.2", |
||||
"vue-router": "^4.0.10", |
"vue-router": "^4.0.10", |
||||
"vue-toastification": "^2.0.0-rc.1" |
"vue-toastification": "^2.0.0-rc.1" |
||||
}, |
}, |
||||
"devDependencies": { |
"devDependencies": { |
||||
"@vitejs/plugin-legacy": "^1.4.4", |
"@babel/eslint-parser": "^7.15.0", |
||||
"@vitejs/plugin-vue": "^1.2.5", |
"@types/bootstrap": "^5.0.17", |
||||
|
"@vitejs/plugin-legacy": "^1.5.1", |
||||
|
"@vitejs/plugin-vue": "^1.3.0", |
||||
"@vue/compiler-sfc": "^3.1.5", |
"@vue/compiler-sfc": "^3.1.5", |
||||
"core-js": "^3.15.2", |
"core-js": "^3.16.0", |
||||
"sass": "^1.35.2", |
"eslint": "^7.32.0", |
||||
"vite": "^2.4.2" |
"eslint-plugin-vue": "^7.15.1", |
||||
|
"sass": "^1.37.5", |
||||
|
"stylelint": "^13.13.1", |
||||
|
"stylelint-config-recommended": "^5.0.0", |
||||
|
"stylelint-config-standard": "^22.0.0", |
||||
|
"typescript": "^4.3.5", |
||||
|
"vite": "^2.4.4" |
||||
} |
} |
||||
} |
} |
||||
|
@ -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,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,26 +0,0 @@ |
|||||
// Common JS cannot be used in frontend sadly
|
|
||||
// sleep, ucfirst is duplicated in ../src/util-frontend.js
|
|
||||
|
|
||||
exports.DOWN = 0; |
|
||||
exports.UP = 1; |
|
||||
exports.PENDING = 2; |
|
||||
|
|
||||
exports.sleep = function (ms) { |
|
||||
return new Promise(resolve => setTimeout(resolve, ms)); |
|
||||
} |
|
||||
|
|
||||
exports.ucfirst = function (str) { |
|
||||
if (! str) { |
|
||||
return str; |
|
||||
} |
|
||||
|
|
||||
const firstLetter = str.substr(0, 1); |
|
||||
return firstLetter.toUpperCase() + str.substr(1); |
|
||||
} |
|
||||
|
|
||||
exports.debug = (msg) => { |
|
||||
if (process.env.NODE_ENV === "development") { |
|
||||
console.log(msg) |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -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,39 @@ |
|||||
|
export default { |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
system: (window.matchMedia("(prefers-color-scheme: dark)")) ? "dark" : "light", |
||||
|
userTheme: localStorage.theme, |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
// Default Light
|
||||
|
if (! this.userTheme) { |
||||
|
this.userTheme = "light"; |
||||
|
} |
||||
|
|
||||
|
document.body.classList.add(this.theme); |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
theme() { |
||||
|
if (this.userTheme === "auto") { |
||||
|
return this.system; |
||||
|
} |
||||
|
return this.userTheme; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
userTheme(to, from) { |
||||
|
localStorage.theme = to; |
||||
|
}, |
||||
|
|
||||
|
theme(to, from) { |
||||
|
document.body.classList.remove(from); |
||||
|
document.body.classList.add(this.theme); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,35 @@ |
|||||
|
"use strict"; |
||||
|
Object.defineProperty(exports, "__esModule", { value: true }); |
||||
|
exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.PENDING = exports.UP = exports.DOWN = exports.appName = void 0; |
||||
|
exports.appName = "Uptime Kuma"; |
||||
|
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,44 @@ |
|||||
|
// 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 appName = "Uptime Kuma"; |
||||
|
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