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)) |
|||
} |
|||
|
@ -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 { 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 { 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) |
|||
library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp); |
|||
|
|||
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, |
|||
"compilerOptions": { |
|||
"target": "ES2018", |
|||
"target": "es2018", |
|||
"module": "commonjs", |
|||
"lib": [ |
|||
"es2020", |
|||
"DOM", |
|||
], |
|||
"removeComments": true, |
|||
"preserveConstEnums": true, |
|||
"sourceMap": false, |
|||
"files.insertFinalNewline": true |
|||
"strict": true |
|||
}, |
|||
"files": [ |
|||
"./server/util.ts" |
|||
"./src/util.ts" |
|||
] |
|||
} |
|||
|
@ -1,14 +1,14 @@ |
|||
import { defineConfig } from 'vite' |
|||
import vue from '@vitejs/plugin-vue' |
|||
import legacy from '@vitejs/plugin-legacy' |
|||
import legacy from "@vitejs/plugin-legacy" |
|||
import vue from "@vitejs/plugin-vue" |
|||
import { defineConfig } from "vite" |
|||
|
|||
// https://vitejs.dev/config/
|
|||
export default defineConfig({ |
|||
plugins: [ |
|||
vue(), |
|||
legacy({ |
|||
targets: ['ie > 11'], |
|||
additionalLegacyPolyfills: ['regenerator-runtime/runtime'] |
|||
targets: ["ie > 11"], |
|||
additionalLegacyPolyfills: ["regenerator-runtime/runtime"] |
|||
}) |
|||
] |
|||
}) |
|||
|
Loading…
Reference in new issue