Browse Source
# Conflicts: # .do/deploy.template.yaml # README.md # dockerfile # package-lock.json # package.json # server/notification.js # server/server.js # src/assets/app.scss # src/assets/vars.scss # src/components/HeartbeatBar.vue # src/components/NotificationDialog.vue # src/layouts/Layout.vue # src/pages/Dashboard.vue # src/pages/EditMonitor.vue # src/pages/Settings.vueremotes/philippdormann/main
73 changed files with 2951 additions and 837 deletions
@ -1,8 +0,0 @@ |
|||||
spec: |
|
||||
name: uptime-kuma |
|
||||
services: |
|
||||
- dockerfile_path: Dockerfile |
|
||||
git: |
|
||||
branch: main |
|
||||
repo_clone_url: https://github.com/philippdormann/uptime-kuma.git |
|
||||
name: uptime-kuma |
|
@ -1,3 +1,10 @@ |
|||||
{ |
{ |
||||
"extends": "stylelint-config-recommended", |
"extends": "stylelint-config-standard", |
||||
|
"rules": { |
||||
|
"indentation": 4, |
||||
|
"no-descending-specificity": null, |
||||
|
"selector-list-comma-newline-after": null, |
||||
|
"declaration-empty-line-before": null, |
||||
|
"no-duplicate-selectors": null |
||||
|
} |
||||
} |
} |
||||
|
@ -0,0 +1,14 @@ |
|||||
|
# Security Policy |
||||
|
|
||||
|
## Supported Versions |
||||
|
|
||||
|
Use this section to tell people about which versions of your project are |
||||
|
currently being supported with security updates. |
||||
|
|
||||
|
| Version | Supported | |
||||
|
| ------- | ------------------ | |
||||
|
| 1.x.x | :white_check_mark: | |
||||
|
|
||||
|
## Reporting a Vulnerability |
||||
|
|
||||
|
https://github.com/louislam/uptime-kuma/issues |
@ -1,7 +0,0 @@ |
|||||
{ |
|
||||
"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,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,2 @@ |
|||||
|
# Must enable File Sharing in Docker Desktop |
||||
|
docker run -it --rm -v ${pwd}:/app louislam/batsh /usr/bin/batsh bash --output ./install.sh ./extra/install.batsh |
@ -0,0 +1,245 @@ |
|||||
|
// install.sh is generated by ./extra/install.batsh, do not modify it directly. |
||||
|
// "npm run compile-install-script" to compile install.sh |
||||
|
// The command is working on Windows PowerShell and Docker for Windows only. |
||||
|
|
||||
|
|
||||
|
// curl -o kuma_install.sh https://raw.githubusercontent.com/louislam/uptime-kuma/master/install.sh && sudo bash kuma_install.sh |
||||
|
println("====================="); |
||||
|
println("Uptime Kuma Installer"); |
||||
|
println("====================="); |
||||
|
println("Supported OS: CentOS 7/8, Ubuntu >= 16.04 and Debian"); |
||||
|
println("---------------------------------------"); |
||||
|
println("This script is designed for Linux and basic usage."); |
||||
|
println("For advanced usage, please go to https://github.com/louislam/uptime-kuma/wiki/Installation"); |
||||
|
println("---------------------------------------"); |
||||
|
println(""); |
||||
|
println("Local - Install Uptime Kuma in your current machine with git, Node.js 14 and pm2"); |
||||
|
println("Docker - Install Uptime Kuma Docker container"); |
||||
|
println(""); |
||||
|
|
||||
|
if ("$1" != "") { |
||||
|
type = "$1"; |
||||
|
} else { |
||||
|
call("read", "-p", "Which installation method do you prefer? [DOCKER/local]: ", "type"); |
||||
|
} |
||||
|
|
||||
|
defaultPort = "3001"; |
||||
|
|
||||
|
function checkNode() { |
||||
|
bash("nodeVersion=$(node -e 'console.log(process.versions.node.split(`.`)[0])')"); |
||||
|
println("Node Version: " ++ nodeVersion); |
||||
|
|
||||
|
if (nodeVersion < "12") { |
||||
|
println("Error: Required Node.js 14"); |
||||
|
call("exit", "1"); |
||||
|
} |
||||
|
|
||||
|
if (nodeVersion == "12") { |
||||
|
println("Warning: NodeJS " ++ nodeVersion ++ " is not tested."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function deb() { |
||||
|
bash("nodeCheck=$(node -v)"); |
||||
|
bash("apt --yes update"); |
||||
|
|
||||
|
if (nodeCheck != "") { |
||||
|
checkNode(); |
||||
|
} else { |
||||
|
|
||||
|
// Old nodejs binary name is "nodejs" |
||||
|
bash("check=$(nodejs --version)"); |
||||
|
if (check != "") { |
||||
|
println("Error: 'node' command is not found, but 'nodejs' command is found. Your NodeJS should be too old."); |
||||
|
bash("exit 1"); |
||||
|
} |
||||
|
|
||||
|
bash("curlCheck=$(curl --version)"); |
||||
|
if (curlCheck == "") { |
||||
|
println("Installing Curl"); |
||||
|
bash("apt --yes install curl"); |
||||
|
} |
||||
|
|
||||
|
println("Installing Node.js 14"); |
||||
|
bash("curl -sL https://deb.nodesource.com/setup_14.x | bash - > log.txt"); |
||||
|
bash("apt --yes install nodejs"); |
||||
|
bash("node -v"); |
||||
|
|
||||
|
bash("nodeCheckAgain=$(node -v)"); |
||||
|
|
||||
|
if (nodeCheckAgain == "") { |
||||
|
println("Error during Node.js installation"); |
||||
|
bash("exit 1"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bash("check=$(git --version)"); |
||||
|
if (check == "") { |
||||
|
println("Installing Git"); |
||||
|
bash("apt --yes install git"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (type == "local") { |
||||
|
defaultInstallPath = "/opt/uptime-kuma"; |
||||
|
|
||||
|
if (exists("/etc/redhat-release")) { |
||||
|
os = call("cat", "/etc/redhat-release"); |
||||
|
distribution = "rhel"; |
||||
|
|
||||
|
} else if (exists("/etc/issue")) { |
||||
|
bash("os=$(head -n1 /etc/issue | cut -f 1 -d ' ')"); |
||||
|
if (os == "Ubuntu") { |
||||
|
distribution = "ubuntu"; |
||||
|
} |
||||
|
if (os == "Debian") { |
||||
|
distribution = "debian"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bash("arch=$(uname -i)"); |
||||
|
|
||||
|
println("Your OS: " ++ os); |
||||
|
println("Distribution: " ++ distribution); |
||||
|
println("Arch: " ++ arch); |
||||
|
|
||||
|
if ("$3" != "") { |
||||
|
port = "$3"; |
||||
|
} else { |
||||
|
call("read", "-p", "Listening Port [$defaultPort]: ", "port"); |
||||
|
|
||||
|
if (port == "") { |
||||
|
port = defaultPort; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ("$2" != "") { |
||||
|
installPath = "$2"; |
||||
|
} else { |
||||
|
call("read", "-p", "Installation Path [$defaultInstallPath]: ", "installPath"); |
||||
|
|
||||
|
if (installPath == "") { |
||||
|
installPath = defaultInstallPath; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// CentOS |
||||
|
if (distribution == "rhel") { |
||||
|
bash("nodeCheck=$(node -v)"); |
||||
|
|
||||
|
if (nodeCheck != "") { |
||||
|
checkNode(); |
||||
|
} else { |
||||
|
|
||||
|
bash("curlCheck=$(curl --version)"); |
||||
|
if (curlCheck == "") { |
||||
|
println("Installing Curl"); |
||||
|
bash("yum -y -q install curl"); |
||||
|
} |
||||
|
|
||||
|
println("Installing Node.js 14"); |
||||
|
bash("curl -sL https://rpm.nodesource.com/setup_14.x | bash - > log.txt"); |
||||
|
bash("yum install -y -q nodejs"); |
||||
|
bash("node -v"); |
||||
|
|
||||
|
bash("nodeCheckAgain=$(node -v)"); |
||||
|
|
||||
|
if (nodeCheckAgain == "") { |
||||
|
println("Error during Node.js installation"); |
||||
|
bash("exit 1"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bash("check=$(git --version)"); |
||||
|
if (check == "") { |
||||
|
println("Installing Git"); |
||||
|
bash("yum -y -q install git"); |
||||
|
} |
||||
|
|
||||
|
// Ubuntu |
||||
|
} else if (distribution == "ubuntu") { |
||||
|
deb(); |
||||
|
|
||||
|
// Debian |
||||
|
} else if (distribution == "debian") { |
||||
|
deb(); |
||||
|
|
||||
|
} else { |
||||
|
// Unknown distribution |
||||
|
error = 0; |
||||
|
|
||||
|
bash("check=$(git --version)"); |
||||
|
if (check == "") { |
||||
|
error = 1; |
||||
|
println("Error: git is missing"); |
||||
|
} |
||||
|
|
||||
|
bash("check=$(node -v)"); |
||||
|
if (check == "") { |
||||
|
error = 1; |
||||
|
println("Error: node is missing"); |
||||
|
} |
||||
|
|
||||
|
if (error > 0) { |
||||
|
println("Please install above missing software"); |
||||
|
bash("exit 1"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bash("check=$(pm2 --version)"); |
||||
|
if (check == "") { |
||||
|
println("Installing PM2"); |
||||
|
bash("npm install pm2 -g"); |
||||
|
bash("pm2 startup"); |
||||
|
} |
||||
|
|
||||
|
bash("mkdir -p $installPath"); |
||||
|
bash("cd $installPath"); |
||||
|
bash("git clone https://github.com/louislam/uptime-kuma.git ."); |
||||
|
bash("npm run setup"); |
||||
|
|
||||
|
bash("pm2 start npm --name uptime-kuma -- run start-server -- --port=$port"); |
||||
|
|
||||
|
} else { |
||||
|
defaultVolume = "uptime-kuma"; |
||||
|
|
||||
|
bash("check=$(docker -v)"); |
||||
|
if (check == "") { |
||||
|
println("Error: docker is not found!"); |
||||
|
bash("exit 1"); |
||||
|
} |
||||
|
|
||||
|
bash("check=$(docker info)"); |
||||
|
|
||||
|
bash("if [[ \"$check\" == *\"Is the docker daemon running\"* ]]; then |
||||
|
echo \"Error: docker is not running\" |
||||
|
exit 1 |
||||
|
fi"); |
||||
|
|
||||
|
if ("$3" != "") { |
||||
|
port = "$3"; |
||||
|
} else { |
||||
|
call("read", "-p", "Expose Port [$defaultPort]: ", "port"); |
||||
|
|
||||
|
if (port == "") { |
||||
|
port = defaultPort; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ("$2" != "") { |
||||
|
volume = "$2"; |
||||
|
} else { |
||||
|
call("read", "-p", "Volume Name [$defaultVolume]: ", "volume"); |
||||
|
|
||||
|
if (volume == "") { |
||||
|
volume = defaultVolume; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
println("Port: $port"); |
||||
|
println("Volume: $volume"); |
||||
|
bash("docker volume create $volume"); |
||||
|
bash("docker run -d --restart=always -p $port:3001 -v $volume:/app/data --name uptime-kuma louislam/uptime-kuma:1"); |
||||
|
} |
||||
|
|
||||
|
println("http://localhost:$port"); |
@ -0,0 +1,59 @@ |
|||||
|
console.log("== Uptime Kuma Reset Password Tool =="); |
||||
|
|
||||
|
console.log("Loading the database"); |
||||
|
|
||||
|
const Database = require("../server/database"); |
||||
|
const { R } = require("redbean-node"); |
||||
|
const readline = require("readline"); |
||||
|
const { initJWTSecret } = require("../server/util-server"); |
||||
|
const rl = readline.createInterface({ |
||||
|
input: process.stdin, |
||||
|
output: process.stdout |
||||
|
}); |
||||
|
|
||||
|
(async () => { |
||||
|
await Database.connect(); |
||||
|
|
||||
|
try { |
||||
|
const user = await R.findOne("user"); |
||||
|
|
||||
|
if (! user) { |
||||
|
throw new Error("user not found, have you installed?"); |
||||
|
} |
||||
|
|
||||
|
console.log("Found user: " + user.username); |
||||
|
|
||||
|
while (true) { |
||||
|
let password = await question("New Password: "); |
||||
|
let confirmPassword = await question("Confirm New Password: "); |
||||
|
|
||||
|
if (password === confirmPassword) { |
||||
|
await user.resetPassword(password); |
||||
|
|
||||
|
// Reset all sessions by reset jwt secret
|
||||
|
await initJWTSecret(); |
||||
|
|
||||
|
rl.close(); |
||||
|
break; |
||||
|
} else { |
||||
|
console.log("Passwords do not match, please try again."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
console.log("Password reset successfully."); |
||||
|
} catch (e) { |
||||
|
console.error("Error: " + e.message); |
||||
|
} |
||||
|
|
||||
|
await Database.close(); |
||||
|
|
||||
|
console.log("Finished. You should restart the Uptime Kuma server.") |
||||
|
})(); |
||||
|
|
||||
|
function question(question) { |
||||
|
return new Promise((resolve) => { |
||||
|
rl.question(question, (answer) => { |
||||
|
resolve(answer); |
||||
|
}) |
||||
|
}); |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
const pkg = require("../package.json"); |
||||
|
const fs = require("fs"); |
||||
|
const child_process = require("child_process"); |
||||
|
const util = require("../src/util"); |
||||
|
|
||||
|
util.polyfill(); |
||||
|
|
||||
|
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"); |
||||
|
|
||||
|
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)) |
|
||||
} |
|
||||
|
|
@ -1,16 +1,16 @@ |
|||||
<!DOCTYPE html> |
<!DOCTYPE html> |
||||
<html lang="en"> |
<html lang="en"> |
||||
<head> |
<head> |
||||
<meta charset="UTF-8" /> |
<meta charset="UTF-8" /> |
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" /> |
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png"> |
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<meta name="theme-color" content="#5cdd8b" /> |
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> |
||||
|
<link rel="icon" type="image/svg+xml" href="/icon.svg" /> |
||||
|
<meta name="theme-color" id="theme-color" content="" /> |
||||
<meta name="description" content="Uptime Kuma monitoring tool" /> |
<meta name="description" content="Uptime Kuma monitoring tool" /> |
||||
<title>Uptime Kuma</title> |
<title>Uptime Kuma</title> |
||||
</head> |
</head> |
||||
<body> |
<body> |
||||
<div id="app"></div> |
<div id="app"></div> |
||||
<script type="module" src="/src/main.js"></script> |
<script type="module" src="/src/main.js"></script> |
||||
</body> |
</body> |
||||
</html> |
</html> |
||||
|
@ -0,0 +1,203 @@ |
|||||
|
# install.sh is generated by ./extra/install.batsh, do not modify it directly. |
||||
|
# "npm run compile-install-script" to compile install.sh |
||||
|
# The command is working on Windows PowerShell and Docker for Windows only. |
||||
|
# curl -o kuma_install.sh https://raw.githubusercontent.com/louislam/uptime-kuma/master/install.sh && sudo bash kuma_install.sh |
||||
|
"echo" "-e" "=====================" |
||||
|
"echo" "-e" "Uptime Kuma Installer" |
||||
|
"echo" "-e" "=====================" |
||||
|
"echo" "-e" "Supported OS: CentOS 7/8, Ubuntu >= 16.04 and Debian" |
||||
|
"echo" "-e" "---------------------------------------" |
||||
|
"echo" "-e" "This script is designed for Linux and basic usage." |
||||
|
"echo" "-e" "For advanced usage, please go to https://github.com/louislam/uptime-kuma/wiki/Installation" |
||||
|
"echo" "-e" "---------------------------------------" |
||||
|
"echo" "-e" "" |
||||
|
"echo" "-e" "Local - Install Uptime Kuma in your current machine with git, Node.js 14 and pm2" |
||||
|
"echo" "-e" "Docker - Install Uptime Kuma Docker container" |
||||
|
"echo" "-e" "" |
||||
|
if [ "$1" != "" ]; then |
||||
|
type="$1" |
||||
|
else |
||||
|
"read" "-p" "Which installation method do you prefer? [DOCKER/local]: " "type" |
||||
|
fi |
||||
|
defaultPort="3001" |
||||
|
function checkNode { |
||||
|
local _0 |
||||
|
nodeVersion=$(node -e 'console.log(process.versions.node.split(`.`)[0])') |
||||
|
"echo" "-e" "Node Version: ""$nodeVersion" |
||||
|
_0="12" |
||||
|
if [ $(($nodeVersion < $_0)) == 1 ]; then |
||||
|
"echo" "-e" "Error: Required Node.js 14" |
||||
|
"exit" "1" |
||||
|
fi |
||||
|
if [ "$nodeVersion" == "12" ]; then |
||||
|
"echo" "-e" "Warning: NodeJS ""$nodeVersion"" is not tested." |
||||
|
fi |
||||
|
} |
||||
|
function deb { |
||||
|
nodeCheck=$(node -v) |
||||
|
apt --yes update |
||||
|
if [ "$nodeCheck" != "" ]; then |
||||
|
"checkNode" |
||||
|
else |
||||
|
# Old nodejs binary name is "nodejs" |
||||
|
check=$(nodejs --version) |
||||
|
if [ "$check" != "" ]; then |
||||
|
"echo" "-e" "Error: 'node' command is not found, but 'nodejs' command is found. Your NodeJS should be too old." |
||||
|
exit 1 |
||||
|
fi |
||||
|
curlCheck=$(curl --version) |
||||
|
if [ "$curlCheck" == "" ]; then |
||||
|
"echo" "-e" "Installing Curl" |
||||
|
apt --yes install curl |
||||
|
fi |
||||
|
"echo" "-e" "Installing Node.js 14" |
||||
|
curl -sL https://deb.nodesource.com/setup_14.x | bash - > log.txt |
||||
|
apt --yes install nodejs |
||||
|
node -v |
||||
|
nodeCheckAgain=$(node -v) |
||||
|
if [ "$nodeCheckAgain" == "" ]; then |
||||
|
"echo" "-e" "Error during Node.js installation" |
||||
|
exit 1 |
||||
|
fi |
||||
|
fi |
||||
|
check=$(git --version) |
||||
|
if [ "$check" == "" ]; then |
||||
|
"echo" "-e" "Installing Git" |
||||
|
apt --yes install git |
||||
|
fi |
||||
|
} |
||||
|
if [ "$type" == "local" ]; then |
||||
|
defaultInstallPath="/opt/uptime-kuma" |
||||
|
if [ -e "/etc/redhat-release" ]; then |
||||
|
os=$("cat" "/etc/redhat-release") |
||||
|
distribution="rhel" |
||||
|
else |
||||
|
if [ -e "/etc/issue" ]; then |
||||
|
os=$(head -n1 /etc/issue | cut -f 1 -d ' ') |
||||
|
if [ "$os" == "Ubuntu" ]; then |
||||
|
distribution="ubuntu" |
||||
|
fi |
||||
|
if [ "$os" == "Debian" ]; then |
||||
|
distribution="debian" |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
arch=$(uname -i) |
||||
|
"echo" "-e" "Your OS: ""$os" |
||||
|
"echo" "-e" "Distribution: ""$distribution" |
||||
|
"echo" "-e" "Arch: ""$arch" |
||||
|
if [ "$3" != "" ]; then |
||||
|
port="$3" |
||||
|
else |
||||
|
"read" "-p" "Listening Port [$defaultPort]: " "port" |
||||
|
if [ "$port" == "" ]; then |
||||
|
port="$defaultPort" |
||||
|
fi |
||||
|
fi |
||||
|
if [ "$2" != "" ]; then |
||||
|
installPath="$2" |
||||
|
else |
||||
|
"read" "-p" "Installation Path [$defaultInstallPath]: " "installPath" |
||||
|
if [ "$installPath" == "" ]; then |
||||
|
installPath="$defaultInstallPath" |
||||
|
fi |
||||
|
fi |
||||
|
# CentOS |
||||
|
if [ "$distribution" == "rhel" ]; then |
||||
|
nodeCheck=$(node -v) |
||||
|
if [ "$nodeCheck" != "" ]; then |
||||
|
"checkNode" |
||||
|
else |
||||
|
curlCheck=$(curl --version) |
||||
|
if [ "$curlCheck" == "" ]; then |
||||
|
"echo" "-e" "Installing Curl" |
||||
|
yum -y -q install curl |
||||
|
fi |
||||
|
"echo" "-e" "Installing Node.js 14" |
||||
|
curl -sL https://rpm.nodesource.com/setup_14.x | bash - > log.txt |
||||
|
yum install -y -q nodejs |
||||
|
node -v |
||||
|
nodeCheckAgain=$(node -v) |
||||
|
if [ "$nodeCheckAgain" == "" ]; then |
||||
|
"echo" "-e" "Error during Node.js installation" |
||||
|
exit 1 |
||||
|
fi |
||||
|
fi |
||||
|
check=$(git --version) |
||||
|
if [ "$check" == "" ]; then |
||||
|
"echo" "-e" "Installing Git" |
||||
|
yum -y -q install git |
||||
|
fi |
||||
|
# Ubuntu |
||||
|
else |
||||
|
if [ "$distribution" == "ubuntu" ]; then |
||||
|
"deb" |
||||
|
# Debian |
||||
|
else |
||||
|
if [ "$distribution" == "debian" ]; then |
||||
|
"deb" |
||||
|
else |
||||
|
# Unknown distribution |
||||
|
error=$((0)) |
||||
|
check=$(git --version) |
||||
|
if [ "$check" == "" ]; then |
||||
|
error=$((1)) |
||||
|
"echo" "-e" "Error: git is missing" |
||||
|
fi |
||||
|
check=$(node -v) |
||||
|
if [ "$check" == "" ]; then |
||||
|
error=$((1)) |
||||
|
"echo" "-e" "Error: node is missing" |
||||
|
fi |
||||
|
if [ $(($error > 0)) == 1 ]; then |
||||
|
"echo" "-e" "Please install above missing software" |
||||
|
exit 1 |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
check=$(pm2 --version) |
||||
|
if [ "$check" == "" ]; then |
||||
|
"echo" "-e" "Installing PM2" |
||||
|
npm install pm2 -g |
||||
|
pm2 startup |
||||
|
fi |
||||
|
mkdir -p $installPath |
||||
|
cd $installPath |
||||
|
git clone https://github.com/louislam/uptime-kuma.git . |
||||
|
npm run setup |
||||
|
pm2 start npm --name uptime-kuma -- run start-server -- --port=$port |
||||
|
else |
||||
|
defaultVolume="uptime-kuma" |
||||
|
check=$(docker -v) |
||||
|
if [ "$check" == "" ]; then |
||||
|
"echo" "-e" "Error: docker is not found!" |
||||
|
exit 1 |
||||
|
fi |
||||
|
check=$(docker info) |
||||
|
if [[ "$check" == *"Is the docker daemon running"* ]]; then |
||||
|
echo "Error: docker is not running" |
||||
|
exit 1 |
||||
|
fi |
||||
|
if [ "$3" != "" ]; then |
||||
|
port="$3" |
||||
|
else |
||||
|
"read" "-p" "Expose Port [$defaultPort]: " "port" |
||||
|
if [ "$port" == "" ]; then |
||||
|
port="$defaultPort" |
||||
|
fi |
||||
|
fi |
||||
|
if [ "$2" != "" ]; then |
||||
|
volume="$2" |
||||
|
else |
||||
|
"read" "-p" "Volume Name [$defaultVolume]: " "volume" |
||||
|
if [ "$volume" == "" ]; then |
||||
|
volume="$defaultVolume" |
||||
|
fi |
||||
|
fi |
||||
|
"echo" "-e" "Port: $port" |
||||
|
"echo" "-e" "Volume: $volume" |
||||
|
docker volume create $volume |
||||
|
docker run -d --restart=always -p $port:3001 -v $volume:/app/data --name uptime-kuma louislam/uptime-kuma:1 |
||||
|
fi |
||||
|
"echo" "-e" "http://localhost:$port" |
@ -0,0 +1,28 @@ |
|||||
|
# Uptime-Kuma K8s Deployment |
||||
|
## How does it work? |
||||
|
|
||||
|
Kustomize is a tool which builds a complete deployment file for all config elements. |
||||
|
You can edit the files in the ```uptime-kuma``` folder except the ```kustomization.yml``` until you know what you're doing. |
||||
|
If you want to choose another namespace you can edit the ```kustomization.yml``` in the ```kubernetes```-Folder and change the ```namespace: uptime-kuma``` to something you like. |
||||
|
|
||||
|
It creates a certificate with the specified Issuer and creates the Ingress for the Uptime-Kuma ClusterIP-Service |
||||
|
|
||||
|
## What do i have to edit? |
||||
|
You have to edit the ```ingressroute.yml``` to your needs. |
||||
|
This ingressroute.yml is for the [nginx-ingress-controller](https://kubernetes.github.io/ingress-nginx/) in combination with the [cert-manager](https://cert-manager.io/). |
||||
|
|
||||
|
- host |
||||
|
- secrets and secret names |
||||
|
- (Cluster)Issuer (optional) |
||||
|
- the Version in the Deployment-File |
||||
|
- update: |
||||
|
- change to newer version and run the above commands, it will update the pods one after another |
||||
|
|
||||
|
## How To use: |
||||
|
|
||||
|
- install [kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/) |
||||
|
- Edit files mentioned above to your needs |
||||
|
- run ```kustomize build > apply.yml``` |
||||
|
- run ```kubectl apply -f apply.yml``` |
||||
|
|
||||
|
Now you should see some k8s magic and Uptime-Kuma should be available at the specified address. |
@ -0,0 +1,10 @@ |
|||||
|
namespace: uptime-kuma |
||||
|
namePrefix: uptime-kuma- |
||||
|
|
||||
|
commonLabels: |
||||
|
app: uptime-kuma |
||||
|
|
||||
|
bases: |
||||
|
- uptime-kuma |
||||
|
|
||||
|
|
@ -0,0 +1,42 @@ |
|||||
|
apiVersion: apps/v1 |
||||
|
kind: Deployment |
||||
|
metadata: |
||||
|
labels: |
||||
|
component: uptime-kuma |
||||
|
name: deployment |
||||
|
spec: |
||||
|
selector: |
||||
|
matchLabels: |
||||
|
component: uptime-kuma |
||||
|
replicas: 1 |
||||
|
strategy: |
||||
|
type: Recreate |
||||
|
|
||||
|
template: |
||||
|
metadata: |
||||
|
labels: |
||||
|
component: uptime-kuma |
||||
|
spec: |
||||
|
containers: |
||||
|
- name: app |
||||
|
image: louislam/uptime-kuma:1 |
||||
|
ports: |
||||
|
- containerPort: 3001 |
||||
|
volumeMounts: |
||||
|
- mountPath: /app/data |
||||
|
name: storage |
||||
|
livenessProbe: |
||||
|
exec: |
||||
|
command: |
||||
|
- node |
||||
|
- extra/healthcheck.js |
||||
|
readinessProbe: |
||||
|
httpGet: |
||||
|
path: / |
||||
|
port: 3001 |
||||
|
scheme: HTTP |
||||
|
|
||||
|
volumes: |
||||
|
- name: storage |
||||
|
persistentVolumeClaim: |
||||
|
claimName: pvc |
@ -0,0 +1,39 @@ |
|||||
|
apiVersion: networking.k8s.io/v1 |
||||
|
kind: Ingress |
||||
|
metadata: |
||||
|
annotations: |
||||
|
kubernetes.io/ingress.class: nginx |
||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod |
||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" |
||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" |
||||
|
nginx.ingress.kubernetes.io/server-snippets: | |
||||
|
location / { |
||||
|
proxy_set_header Upgrade $http_upgrade; |
||||
|
proxy_http_version 1.1; |
||||
|
proxy_set_header X-Forwarded-Host $http_host; |
||||
|
proxy_set_header X-Forwarded-Proto $scheme; |
||||
|
proxy_set_header X-Forwarded-For $remote_addr; |
||||
|
proxy_set_header Host $host; |
||||
|
proxy_set_header Connection "upgrade"; |
||||
|
proxy_set_header X-Real-IP $remote_addr; |
||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
||||
|
proxy_set_header Upgrade $http_upgrade; |
||||
|
proxy_cache_bypass $http_upgrade; |
||||
|
} |
||||
|
name: ingress |
||||
|
spec: |
||||
|
tls: |
||||
|
- hosts: |
||||
|
- example.com |
||||
|
secretName: example-com-tls |
||||
|
rules: |
||||
|
- host: example.com |
||||
|
http: |
||||
|
paths: |
||||
|
- path: / |
||||
|
pathType: Prefix |
||||
|
backend: |
||||
|
service: |
||||
|
name: service |
||||
|
port: |
||||
|
number: 3001 |
@ -0,0 +1,5 @@ |
|||||
|
resources: |
||||
|
- deployment.yml |
||||
|
- service.yml |
||||
|
- ingressroute.yml |
||||
|
- pvc.yml |
@ -0,0 +1,10 @@ |
|||||
|
apiVersion: v1 |
||||
|
kind: PersistentVolumeClaim |
||||
|
metadata: |
||||
|
name: pvc |
||||
|
spec: |
||||
|
accessModes: |
||||
|
- ReadWriteOnce |
||||
|
resources: |
||||
|
requests: |
||||
|
storage: 4Gi |
@ -0,0 +1,13 @@ |
|||||
|
apiVersion: v1 |
||||
|
kind: Service |
||||
|
metadata: |
||||
|
name: service |
||||
|
spec: |
||||
|
selector: |
||||
|
component: uptime-kuma |
||||
|
type: ClusterIP |
||||
|
ports: |
||||
|
- name: http |
||||
|
port: 3001 |
||||
|
targetPort: 3001 |
||||
|
protocol: TCP |
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 15 KiB |
@ -1,3 +0,0 @@ |
|||||
# https://www.robotstxt.org/robotstxt.html |
|
||||
User-agent: * |
|
||||
Disallow: |
|
@ -0,0 +1,44 @@ |
|||||
|
const { setSetting } = require("./util-server"); |
||||
|
const axios = require("axios"); |
||||
|
const { isDev } = require("../src/util"); |
||||
|
|
||||
|
exports.version = require("../package.json").version; |
||||
|
exports.latestVersion = null; |
||||
|
|
||||
|
let interval; |
||||
|
|
||||
|
exports.startInterval = () => { |
||||
|
let check = async () => { |
||||
|
try { |
||||
|
const res = await axios.get("https://raw.githubusercontent.com/louislam/uptime-kuma/master/package.json"); |
||||
|
|
||||
|
if (typeof res.data === "string") { |
||||
|
res.data = JSON.parse(res.data); |
||||
|
} |
||||
|
|
||||
|
// For debug
|
||||
|
if (process.env.TEST_CHECK_VERSION === "1") { |
||||
|
res.data.version = "1000.0.0" |
||||
|
} |
||||
|
|
||||
|
exports.latestVersion = res.data.version; |
||||
|
console.log("Latest Version: " + exports.latestVersion); |
||||
|
} catch (_) { } |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
check(); |
||||
|
interval = setInterval(check, 3600 * 1000 * 48); |
||||
|
}; |
||||
|
|
||||
|
exports.enableCheckUpdate = async (value) => { |
||||
|
await setSetting("checkUpdate", value); |
||||
|
|
||||
|
clearInterval(interval); |
||||
|
|
||||
|
if (value) { |
||||
|
exports.startInterval(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
exports.socket = null; |
@ -0,0 +1,21 @@ |
|||||
|
const { BeanModel } = require("redbean-node/dist/bean-model"); |
||||
|
const passwordHash = require("../password-hash"); |
||||
|
const { R } = require("redbean-node"); |
||||
|
|
||||
|
class User extends BeanModel { |
||||
|
|
||||
|
/** |
||||
|
* Direct execute, no need R.store() |
||||
|
* @param newPassword |
||||
|
* @returns {Promise<void>} |
||||
|
*/ |
||||
|
async resetPassword(newPassword) { |
||||
|
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ |
||||
|
passwordHash.generate(newPassword), |
||||
|
this.id |
||||
|
]); |
||||
|
this.password = newPassword; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = User; |
@ -0,0 +1,133 @@ |
|||||
|
<template> |
||||
|
<div class="shadow-box list mb-4"> |
||||
|
<div v-if="Object.keys($root.monitorList).length === 0" class="text-center mt-3"> |
||||
|
{{ $t("No Monitors, please") }} <router-link to="/add">{{ $t("add one") }}</router-link> |
||||
|
</div> |
||||
|
|
||||
|
<router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> |
||||
|
<div class="row"> |
||||
|
<div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> |
||||
|
<div class="info"> |
||||
|
<Uptime :monitor="item" type="24" :pill="true" /> |
||||
|
{{ item.name }} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4"> |
||||
|
<HeartbeatBar size="small" :monitor-id="item.id" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> |
||||
|
<div class="col-12"> |
||||
|
<HeartbeatBar size="small" :monitor-id="item.id" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</router-link> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeartbeatBar from "../components/HeartbeatBar.vue"; |
||||
|
import Uptime from "../components/Uptime.vue"; |
||||
|
export default { |
||||
|
components: { |
||||
|
Uptime, |
||||
|
HeartbeatBar, |
||||
|
}, |
||||
|
computed: { |
||||
|
sortedMonitorList() { |
||||
|
let result = Object.values(this.$root.monitorList); |
||||
|
|
||||
|
result.sort((m1, m2) => { |
||||
|
|
||||
|
if (m1.active !== m2.active) { |
||||
|
if (m1.active === 0) { |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
if (m2.active === 0) { |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (m1.weight !== m2.weight) { |
||||
|
if (m1.weight > m2.weight) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (m1.weight < m2.weight) { |
||||
|
return 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return m1.name.localeCompare(m2.name); |
||||
|
}) |
||||
|
|
||||
|
return result; |
||||
|
}, |
||||
|
}, |
||||
|
methods: { |
||||
|
monitorURL(id) { |
||||
|
return "/dashboard/" + id; |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
@import "../assets/vars.scss"; |
||||
|
|
||||
|
.small-padding { |
||||
|
padding-left: 5px !important; |
||||
|
padding-right: 5px !important; |
||||
|
} |
||||
|
|
||||
|
.list { |
||||
|
height: auto; |
||||
|
min-height: calc(100vh - 240px); |
||||
|
|
||||
|
.item { |
||||
|
display: block; |
||||
|
text-decoration: none; |
||||
|
padding: 13px 15px 10px 15px; |
||||
|
border-radius: 10px; |
||||
|
transition: all ease-in-out 0.15s; |
||||
|
|
||||
|
&.disabled { |
||||
|
opacity: 0.3; |
||||
|
} |
||||
|
|
||||
|
.info { |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: $highlight-white; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: #cdf8f4; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.dark { |
||||
|
.list { |
||||
|
.item { |
||||
|
&:hover { |
||||
|
background-color: $dark-bg2; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: $dark-bg2; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.monitorItem { |
||||
|
width: 100%; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,172 @@ |
|||||
|
<template> |
||||
|
<LineChart :chart-data="chartData" :options="chartOptions" /> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { BarController, BarElement, Chart, Filler, LinearScale, LineController, LineElement, PointElement, TimeScale, Tooltip } from "chart.js"; |
||||
|
import dayjs from "dayjs"; |
||||
|
import utc from "dayjs/plugin/utc"; |
||||
|
import timezone from "dayjs/plugin/timezone"; |
||||
|
import "chartjs-adapter-dayjs"; |
||||
|
import { LineChart } from "vue-chart-3"; |
||||
|
dayjs.extend(utc); |
||||
|
dayjs.extend(timezone); |
||||
|
|
||||
|
Chart.register(LineController, BarController, LineElement, PointElement, TimeScale, BarElement, LinearScale, Tooltip, Filler); |
||||
|
|
||||
|
export default { |
||||
|
components: { LineChart }, |
||||
|
props: { |
||||
|
monitorId: { |
||||
|
type: Number, |
||||
|
required: true, |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
chartPeriodHrs: 6, |
||||
|
}; |
||||
|
}, |
||||
|
computed: { |
||||
|
chartOptions() { |
||||
|
return { |
||||
|
responsive: true, |
||||
|
maintainAspectRatio: false, |
||||
|
onResize: (chart) => { |
||||
|
chart.canvas.parentNode.style.position = "relative"; |
||||
|
if (screen.width < 576) { |
||||
|
chart.canvas.parentNode.style.height = "275px"; |
||||
|
} else if (screen.width < 768) { |
||||
|
chart.canvas.parentNode.style.height = "320px"; |
||||
|
} else if (screen.width < 992) { |
||||
|
chart.canvas.parentNode.style.height = "300px"; |
||||
|
} else { |
||||
|
chart.canvas.parentNode.style.height = "250px"; |
||||
|
} |
||||
|
}, |
||||
|
layout: { |
||||
|
padding: { |
||||
|
left: 10, |
||||
|
right: 30, |
||||
|
top: 30, |
||||
|
bottom: 10, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
elements: { |
||||
|
point: { |
||||
|
radius: 0, |
||||
|
}, |
||||
|
bar: { |
||||
|
barThickness: "flex", |
||||
|
} |
||||
|
}, |
||||
|
scales: { |
||||
|
x: { |
||||
|
type: "time", |
||||
|
time: { |
||||
|
minUnit: "minute", |
||||
|
round: "second", |
||||
|
tooltipFormat: "YYYY-MM-DD HH:mm:ss", |
||||
|
displayFormats: { |
||||
|
minute: "HH:mm", |
||||
|
hour: "MM-DD HH:mm", |
||||
|
} |
||||
|
}, |
||||
|
ticks: { |
||||
|
maxRotation: 0, |
||||
|
autoSkipPadding: 30, |
||||
|
}, |
||||
|
bounds: "ticks", |
||||
|
grid: { |
||||
|
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)", |
||||
|
}, |
||||
|
}, |
||||
|
y: { |
||||
|
title: { |
||||
|
display: true, |
||||
|
text: "Resp. Time (ms)", |
||||
|
}, |
||||
|
offset: false, |
||||
|
grid: { |
||||
|
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)", |
||||
|
}, |
||||
|
}, |
||||
|
y1: { |
||||
|
display: false, |
||||
|
position: "right", |
||||
|
grid: { |
||||
|
drawOnChartArea: false, |
||||
|
}, |
||||
|
min: 0, |
||||
|
max: 1, |
||||
|
offset: false, |
||||
|
}, |
||||
|
}, |
||||
|
bounds: "ticks", |
||||
|
plugins: { |
||||
|
tooltip: { |
||||
|
mode: "nearest", |
||||
|
intersect: false, |
||||
|
padding: 10, |
||||
|
filter: function (tooltipItem) { |
||||
|
return tooltipItem.datasetIndex === 0; |
||||
|
}, |
||||
|
callbacks: { |
||||
|
label: (context) => { |
||||
|
return ` ${new Intl.NumberFormat().format(context.parsed.y)} ms` |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
legend: { |
||||
|
display: false, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
chartData() { |
||||
|
let ping_data = []; |
||||
|
let down_data = []; |
||||
|
if (this.monitorId in this.$root.heartbeatList) { |
||||
|
ping_data = this.$root.heartbeatList[this.monitorId] |
||||
|
.filter( |
||||
|
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours"))) |
||||
|
.map((beat) => { |
||||
|
return { |
||||
|
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"), |
||||
|
y: beat.ping, |
||||
|
}; |
||||
|
}); |
||||
|
down_data = this.$root.heartbeatList[this.monitorId] |
||||
|
.filter( |
||||
|
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours"))) |
||||
|
.map((beat) => { |
||||
|
return { |
||||
|
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"), |
||||
|
y: beat.status === 0 ? 1 : 0, |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
return { |
||||
|
datasets: [ |
||||
|
{ |
||||
|
data: ping_data, |
||||
|
fill: "origin", |
||||
|
tension: 0.2, |
||||
|
borderColor: "#5CDD8B", |
||||
|
backgroundColor: "#5CDD8B38", |
||||
|
yAxisID: "y", |
||||
|
}, |
||||
|
{ |
||||
|
type: "bar", |
||||
|
data: down_data, |
||||
|
borderColor: "#00000000", |
||||
|
backgroundColor: "#DC354568", |
||||
|
yAxisID: "y1", |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
@ -1,10 +1,10 @@ |
|||||
import { library } from "@fortawesome/fontawesome-svg-core" |
import { library } from "@fortawesome/fontawesome-svg-core" |
||||
import { faCog, faEdit, faList, faPause, faPlay, faPlus, faTachometerAlt, faTrash } from "@fortawesome/free-solid-svg-icons" |
import { faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp } from "@fortawesome/free-solid-svg-icons" |
||||
//import { fa } from '@fortawesome/free-regular-svg-icons'
|
//import { fa } from '@fortawesome/free-regular-svg-icons'
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" |
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" |
||||
|
|
||||
// Add Free Font Awesome Icons here
|
// Add Free Font Awesome Icons here
|
||||
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
|
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
|
||||
library.add(faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList) |
library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp); |
||||
|
|
||||
export { FontAwesomeIcon } |
export { FontAwesomeIcon } |
||||
|
@ -0,0 +1,12 @@ |
|||||
|
export default { |
||||
|
languageName: "English", |
||||
|
checkEverySecond: "Check every {0} seconds.", |
||||
|
"Avg.": "Avg. ", |
||||
|
retriesDescription: "Maximum retries before the service is marked as down and a notification is sent", |
||||
|
ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites", |
||||
|
upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.", |
||||
|
maxRedirectDescription: "Maximum number of redirects to follow. Set to 0 to disable redirects.", |
||||
|
acceptedStatusCodesDescription: "Select status codes which are considered as a successful response.", |
||||
|
passwordNotMatchMsg: "The repeat password does not match.", |
||||
|
notificationDescription: "Please assign a notification to monitor(s) to get it to work.", |
||||
|
} |
@ -0,0 +1,97 @@ |
|||||
|
export default { |
||||
|
languageName: "繁體中文 (香港)", |
||||
|
Settings: "設定", |
||||
|
Dashboard: "錶板", |
||||
|
"New Update": "有更新", |
||||
|
Language: "語言", |
||||
|
Appearance: "外觀", |
||||
|
Theme: "主題", |
||||
|
General: "一般", |
||||
|
Version: "版本", |
||||
|
"Check Update On GitHub": "到 Github 查看更新", |
||||
|
List: "列表", |
||||
|
Add: "新增", |
||||
|
"Add New Monitor": "新增監測器", |
||||
|
"Quick Stats": "綜合數據", |
||||
|
Up: "上線", |
||||
|
Down: "離線", |
||||
|
Pending: "待定", |
||||
|
Unknown: "不明", |
||||
|
Pause: "暫停", |
||||
|
Name: "名稱", |
||||
|
Status: "狀態", |
||||
|
DateTime: "日期時間", |
||||
|
Message: "內容", |
||||
|
"No important events": "沒有重要事件", |
||||
|
Resume: "恢復", |
||||
|
Edit: "編輯", |
||||
|
Delete: "刪除", |
||||
|
Current: "目前", |
||||
|
Uptime: "上線率", |
||||
|
"Cert Exp.": "証書期限", |
||||
|
days: "日", |
||||
|
day: "日", |
||||
|
"-day": "日", |
||||
|
hour: "小時", |
||||
|
"-hour": "小時", |
||||
|
checkEverySecond: "每 {0} 秒檢查一次", |
||||
|
"Avg.": "平均", |
||||
|
Response: "反應時間", |
||||
|
Ping: "反應時間", |
||||
|
"Monitor Type": "監測器類型", |
||||
|
Keyword: "關鍵字", |
||||
|
"Friendly Name": "名稱", |
||||
|
URL: "網址 URL", |
||||
|
Hostname: "Hostname", |
||||
|
Port: "Port", |
||||
|
"Heartbeat Interval": "檢查間距", |
||||
|
Retries: "重試數次確定為離線", |
||||
|
retriesDescription: "重試多少次後才判定為離線及傳送通知。如數值為 0 會即判定為離線及傳送通知。", |
||||
|
Advanced: "進階", |
||||
|
ignoreTLSError: "忽略 TLS/SSL 錯誤", |
||||
|
"Upside Down Mode": "反轉模式", |
||||
|
upsideDownModeDescription: "反轉狀態,如網址是可正常瀏覽,會被判定為 '離線/DOWN'", |
||||
|
"Max. Redirects": "跟隨重新導向 (Redirect) 的次數", |
||||
|
maxRedirectDescription: "設為 0 即不跟蹤", |
||||
|
"Accepted Status Codes": "接受為上線的 HTTP 狀態碼", |
||||
|
acceptedStatusCodesDescription: "可多選", |
||||
|
Save: "儲存", |
||||
|
Notifications: "通知", |
||||
|
"Not available, please setup.": "無法使用,需要設定", |
||||
|
"Setup Notification": "設定通知", |
||||
|
Light: "明亮", |
||||
|
Dark: "暗黑", |
||||
|
Auto: "自動", |
||||
|
"Theme - Heartbeat Bar": "監測器列表 狀態條外觀", |
||||
|
Normal: "一般", |
||||
|
Bottom: "下方", |
||||
|
None: "沒有", |
||||
|
Timezone: "時區", |
||||
|
"Search Engine Visibility": "是否允許搜尋器索引", |
||||
|
"Allow indexing": "允許索引", |
||||
|
"Discourage search engines from indexing site": "不建議搜尋器索引", |
||||
|
"Change Password": "變更密碼", |
||||
|
"Current Password": "目前密碼", |
||||
|
"New Password": "新密碼", |
||||
|
"Repeat New Password": "確認新密碼", |
||||
|
passwordNotMatchMsg: "密碼不一致", |
||||
|
"Update Password": "更新密碼", |
||||
|
"Disable Auth": "取消登入認証", |
||||
|
"Enable Auth": "開啟登入認証", |
||||
|
Logout: "登出", |
||||
|
notificationDescription: "新增後,你需要在監測器裡啟用。", |
||||
|
Leave: "離開", |
||||
|
"I understand, please disable": "我明白,請取消登入認証", |
||||
|
Confirm: "確認", |
||||
|
Yes: "是", |
||||
|
No: "否", |
||||
|
Username: "帳號", |
||||
|
Password: "密碼", |
||||
|
"Remember me": "記住我", |
||||
|
Login: "登入", |
||||
|
"No Monitors, please": "沒有監測器,請", |
||||
|
"add one": "新增", |
||||
|
"Notification Type": "通知類型", |
||||
|
"Email": "電郵", |
||||
|
"Test": "測試", |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
import dayjs from "dayjs"; |
||||
|
import utc from "dayjs/plugin/utc"; |
||||
|
import timezone from "dayjs/plugin/timezone"; |
||||
|
import relativeTime from "dayjs/plugin/relativeTime"; |
||||
|
dayjs.extend(utc); |
||||
|
dayjs.extend(timezone); |
||||
|
dayjs.extend(relativeTime); |
||||
|
|
||||
|
/** |
||||
|
* DateTime Mixin |
||||
|
* Handled timezone and format |
||||
|
*/ |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
userTimezone: localStorage.timezone || "auto", |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
datetime(value) { |
||||
|
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss"); |
||||
|
}, |
||||
|
|
||||
|
date(value) { |
||||
|
return this.datetimeFormat(value, "YYYY-MM-DD"); |
||||
|
}, |
||||
|
|
||||
|
time(value, second = true) { |
||||
|
let secondString; |
||||
|
if (second) { |
||||
|
secondString = ":ss"; |
||||
|
} else { |
||||
|
secondString = ""; |
||||
|
} |
||||
|
return this.datetimeFormat(value, "HH:mm" + secondString); |
||||
|
}, |
||||
|
|
||||
|
datetimeFormat(value, format) { |
||||
|
if (value !== undefined && value !== "") { |
||||
|
return dayjs.utc(value).tz(this.timezone).format(format); |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
timezone() { |
||||
|
if (this.userTimezone === "auto") { |
||||
|
return dayjs.tz.guess() |
||||
|
} |
||||
|
|
||||
|
return this.userTimezone |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
export default { |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
windowWidth: window.innerWidth, |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
created() { |
||||
|
window.addEventListener("resize", this.onResize); |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
onResize() { |
||||
|
this.windowWidth = window.innerWidth; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
isMobile() { |
||||
|
return this.windowWidth <= 767.98; |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
export default { |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
system: (window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light", |
||||
|
userTheme: localStorage.theme, |
||||
|
userHeartbeatBar: localStorage.heartbeatBarTheme, |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
// Default Light
|
||||
|
if (! this.userTheme) { |
||||
|
this.userTheme = "light"; |
||||
|
} |
||||
|
|
||||
|
// Default Heartbeat Bar
|
||||
|
if (!this.userHeartbeatBar) { |
||||
|
this.userHeartbeatBar = "normal"; |
||||
|
} |
||||
|
|
||||
|
document.body.classList.add(this.theme); |
||||
|
this.updateThemeColorMeta(); |
||||
|
}, |
||||
|
|
||||
|
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); |
||||
|
this.updateThemeColorMeta(); |
||||
|
}, |
||||
|
|
||||
|
userHeartbeatBar(to, from) { |
||||
|
localStorage.heartbeatBarTheme = to; |
||||
|
}, |
||||
|
|
||||
|
heartbeatBarTheme(to, from) { |
||||
|
document.body.classList.remove(from); |
||||
|
document.body.classList.add(this.heartbeatBarTheme); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
updateThemeColorMeta() { |
||||
|
if (this.theme === "dark") { |
||||
|
document.querySelector("#theme-color").setAttribute("content", "#161B22"); |
||||
|
} else { |
||||
|
document.querySelector("#theme-color").setAttribute("content", "#5cdd8b"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,16 @@ |
|||||
|
<template> |
||||
|
<transition name="slide-fade" appear> |
||||
|
<MonitorList /> |
||||
|
</transition> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import MonitorList from "../components/MonitorList.vue"; |
||||
|
|
||||
|
export default { |
||||
|
components: { |
||||
|
MonitorList, |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
FROM alpine:3 |
||||
|
RUN apk add --update nodejs npm git |
||||
|
COPY ./install.sh . |
||||
|
RUN /bin/sh install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -0,0 +1,4 @@ |
|||||
|
FROM centos:7 |
||||
|
|
||||
|
COPY ./install.sh . |
||||
|
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -0,0 +1,4 @@ |
|||||
|
FROM centos:8 |
||||
|
|
||||
|
COPY ./install.sh . |
||||
|
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -0,0 +1,10 @@ |
|||||
|
FROM debian |
||||
|
|
||||
|
# Test invalid node version, these commands install nodejs 10 |
||||
|
# RUN apt-get update |
||||
|
# RUN apt --yes install nodejs |
||||
|
# RUN ln -s /usr/bin/nodejs /usr/bin/node |
||||
|
# RUN node -v |
||||
|
|
||||
|
COPY ./install.sh . |
||||
|
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -0,0 +1,10 @@ |
|||||
|
FROM ubuntu |
||||
|
|
||||
|
# Test invalid node version, these commands install nodejs 10 |
||||
|
# RUN apt-get update |
||||
|
# RUN apt --yes install nodejs |
||||
|
# RUN ln -s /usr/bin/nodejs /usr/bin/node |
||||
|
# RUN node -v |
||||
|
|
||||
|
COPY ./install.sh . |
||||
|
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -0,0 +1,10 @@ |
|||||
|
FROM ubuntu:16.04 |
||||
|
|
||||
|
# Test invalid node version, these commands install nodejs 10 |
||||
|
RUN apt-get update |
||||
|
RUN apt --yes install nodejs |
||||
|
# RUN ln -s /usr/bin/nodejs /usr/bin/node |
||||
|
# RUN node -v |
||||
|
|
||||
|
COPY ./install.sh . |
||||
|
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0 |
@ -1,14 +1,18 @@ |
|||||
{ |
{ |
||||
"compileOnSave": true, |
"compileOnSave": true, |
||||
"compilerOptions": { |
"compilerOptions": { |
||||
"target": "ES2018", |
"target": "es2018", |
||||
"module": "commonjs", |
"module": "commonjs", |
||||
|
"lib": [ |
||||
|
"es2020", |
||||
|
"DOM", |
||||
|
], |
||||
"removeComments": true, |
"removeComments": true, |
||||
"preserveConstEnums": true, |
"preserveConstEnums": true, |
||||
"sourceMap": false, |
"sourceMap": false, |
||||
"files.insertFinalNewline": true |
"strict": true |
||||
}, |
}, |
||||
"files": [ |
"files": [ |
||||
"./server/util.ts" |
"./src/util.ts" |
||||
] |
] |
||||
} |
} |
||||
|
@ -1,14 +1,14 @@ |
|||||
import { defineConfig } from 'vite' |
import legacy from "@vitejs/plugin-legacy" |
||||
import vue from '@vitejs/plugin-vue' |
import vue from "@vitejs/plugin-vue" |
||||
import legacy from '@vitejs/plugin-legacy' |
import { defineConfig } from "vite" |
||||
|
|
||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||
export default defineConfig({ |
export default defineConfig({ |
||||
plugins: [ |
plugins: [ |
||||
vue(), |
vue(), |
||||
legacy({ |
legacy({ |
||||
targets: ['ie > 11'], |
targets: ["ie > 11"], |
||||
additionalLegacyPolyfills: ['regenerator-runtime/runtime'] |
additionalLegacyPolyfills: ["regenerator-runtime/runtime"] |
||||
}) |
}) |
||||
] |
] |
||||
}) |
}) |
||||
|
Loading…
Reference in new issue