Merge branch 'master' into DeeJayPee_master

This commit is contained in:
LouisLam 2021-10-08 01:27:06 +08:00
commit 5b4af550fb
68 changed files with 1622 additions and 521 deletions

View file

@ -27,7 +27,7 @@ CNAME
install.sh install.sh
SECURITY.md SECURITY.md
tsconfig.json tsconfig.json
.env
### .gitignore content (commented rules are duplicated) ### .gitignore content (commented rules are duplicated)

View file

@ -10,11 +10,12 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
build: auto-test:
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
node-version: [14.x, 15.x, 16.x] node-version: [14.x, 15.x, 16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ dist-ssr
/private /private
/out /out
/tmp /tmp
.env

View file

@ -38,6 +38,7 @@ If you are not sure, feel free to create an empty pull request draft first.
- Add a new notification - Add a new notification
- Add a chart - Add a chart
- Fix a bug - Fix a bug
- Translations
### *️⃣ Requires one more reviewer ### *️⃣ Requires one more reviewer
@ -172,3 +173,7 @@ npm install
Since previously updating vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only. Since previously updating vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only.
Patch release = the third digit Patch release = the third digit
# Translations
Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages

View file

@ -9,7 +9,7 @@
It is a self-hosted monitoring tool like "Uptime Robot". It is a self-hosted monitoring tool like "Uptime Robot".
<img src="https://louislam.net/uptimekuma/1.jpg" width="512" alt="" /> <img src="https://uptime.kuma.pet/img/dark.jpg" width="700" alt="" />
## 🥔 Live Demo ## 🥔 Live Demo
@ -86,9 +86,13 @@ https://github.com/louislam/uptime-kuma/projects/1
## 🖼 More Screenshots ## 🖼 More Screenshots
Dark Mode: Light Mode:
<img src="https://user-images.githubusercontent.com/1336778/128710166-908f8d88-9256-43f3-9c49-bfc2c56011d2.png" width="400" alt="" /> <img src="https://uptime.kuma.pet/img/light.jpg" width="512" alt="" />
Status Page:
<img src="https://user-images.githubusercontent.com/1336778/133384019-962e1120-6c3a-481f-9d07-d7df765e9ba4.png" width="512" alt="" />
Settings Page: Settings Page:

View file

@ -7,7 +7,8 @@ currently being supported with security updates.
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ------------------ |
| 1.x.x | :white_check_mark: | | 1.7.X | :white_check_mark: |
| < 1.7 | |
## Reporting a Vulnerability ## Reporting a Vulnerability
Please report security issues to uptime@kuma.pet. Please report security issues to uptime@kuma.pet.

View file

@ -1,6 +1,8 @@
FROM louislam/uptime-kuma:base-debian AS build FROM louislam/uptime-kuma:base-debian AS build
WORKDIR /app WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . . COPY . .
RUN npm install --legacy-peer-deps && \ RUN npm install --legacy-peer-deps && \
npm run build && \ npm run build && \

View file

@ -1,6 +1,8 @@
FROM louislam/uptime-kuma:base-alpine AS build FROM louislam/uptime-kuma:base-alpine AS build
WORKDIR /app WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
COPY . . COPY . .
RUN npm install --legacy-peer-deps && \ RUN npm install --legacy-peer-deps && \
npm run build && \ npm run build && \

View file

@ -19,6 +19,7 @@ if (! newVersion) {
const exists = tagExists(newVersion); const exists = tagExists(newVersion);
if (! exists) { if (! exists) {
// Process package.json // Process package.json
pkg.version = newVersion; pkg.version = newVersion;
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion); pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
@ -29,8 +30,11 @@ if (! exists) {
commit(newVersion); commit(newVersion);
tag(newVersion); tag(newVersion);
updateWiki(oldVersion, newVersion);
} else { } else {
console.log("version exists") console.log("version exists");
} }
function commit(version) { function commit(version) {
@ -38,16 +42,16 @@ function commit(version) {
let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]); let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]);
let stdout = res.stdout.toString().trim(); let stdout = res.stdout.toString().trim();
console.log(stdout) console.log(stdout);
if (stdout.includes("no changes added to commit")) { if (stdout.includes("no changes added to commit")) {
throw new Error("commit error") throw new Error("commit error");
} }
} }
function tag(version) { function tag(version) {
let res = child_process.spawnSync("git", ["tag", version]); let res = child_process.spawnSync("git", ["tag", version]);
console.log(res.stdout.toString().trim()) console.log(res.stdout.toString().trim());
} }
function tagExists(version) { function tagExists(version) {
@ -59,3 +63,38 @@ function tagExists(version) {
return res.stdout.toString().trim() === version; return res.stdout.toString().trim() === version;
} }
function updateWiki(oldVersion, newVersion) {
const wikiDir = "./tmp/wiki";
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
safeDelete(wikiDir);
child_process.spawnSync("git", ["clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir]);
let content = fs.readFileSync(howToUpdateFilename).toString();
content = content.replaceAll(`git checkout ${oldVersion}`, `git checkout ${newVersion}`);
fs.writeFileSync(howToUpdateFilename, content);
child_process.spawnSync("git", ["add", "-A"], {
cwd: wikiDir,
});
child_process.spawnSync("git", ["commit", "-m", `Update to ${newVersion} from ${oldVersion}`], {
cwd: wikiDir,
});
console.log("Pushing to Github");
child_process.spawnSync("git", ["push"], {
cwd: wikiDir,
});
safeDelete(wikiDir);
}
function safeDelete(dir) {
if (fs.existsSync(dir)) {
fs.rmdirSync(dir, {
recursive: true,
});
}
}

622
package-lock.json generated
View file

@ -13,7 +13,7 @@
"@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-4", "@fortawesome/vue-fontawesome": "~3.0.0-4",
"@louislam/sqlite3": "~5.0.6", "@louislam/sqlite3": "~6.0.0",
"@popperjs/core": "~2.10.2", "@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.21.4", "axios": "~0.21.4",
@ -48,7 +48,7 @@
"vue-chart-3": "~0.5.8", "vue-chart-3": "~0.5.8",
"vue-confirm-dialog": "~1.0.2", "vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4", "vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.1.8", "vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3", "vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2", "vue-multiselect": "~3.0.0-alpha.2",
"vue-qrcode": "~1.0.0", "vue-qrcode": "~1.0.0",
@ -59,7 +59,7 @@
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "~7.15.7", "@babel/eslint-parser": "~7.15.7",
"@types/bootstrap": "~5.1.6", "@types/bootstrap": "~5.1.6",
"@vitejs/plugin-legacy": "~1.5.3", "@vitejs/plugin-legacy": "~1.6.1",
"@vitejs/plugin-vue": "~1.9.2", "@vitejs/plugin-vue": "~1.9.2",
"@vue/compiler-sfc": "~3.2.19", "@vue/compiler-sfc": "~3.2.19",
"core-js": "~3.18.1", "core-js": "~3.18.1",
@ -74,7 +74,7 @@
"stylelint": "~13.13.1", "stylelint": "~13.13.1",
"stylelint-config-standard": "~22.0.0", "stylelint-config-standard": "~22.0.0",
"typescript": "~4.4.3", "typescript": "~4.4.3",
"vite": "~2.5.10" "vite": "~2.6.4"
}, },
"engines": { "engines": {
"node": "14.*" "node": "14.*"
@ -844,39 +844,39 @@
"dev": true "dev": true
}, },
"node_modules/@intlify/core-base": { "node_modules/@intlify/core-base": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
"integrity": "sha512-k+q6nUOD9HPTMr8AKEn4d/EgoNSCMVuc/dG97tFXrifT4+QbxoLNRjXovyC60rb4q+7D/cGF+5R6Tjby4t5gng==", "integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
"dependencies": { "dependencies": {
"@intlify/devtools-if": "9.1.8", "@intlify/devtools-if": "9.1.9",
"@intlify/message-compiler": "9.1.8", "@intlify/message-compiler": "9.1.9",
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/runtime": "9.1.8", "@intlify/runtime": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"@intlify/vue-devtools": "9.1.8" "@intlify/vue-devtools": "9.1.9"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@intlify/devtools-if": { "node_modules/@intlify/devtools-if": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
"integrity": "sha512-17REiNoQ5dWnYECbKgkMHtX8GnNCkMNxfImXrJNnUhSH3GitheNkYn0o14AZmZWk7Fw/1IOdV5v7K0QVnYx5fw==", "integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
"dependencies": { "dependencies": {
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@intlify/message-compiler": { "node_modules/@intlify/message-compiler": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
"integrity": "sha512-x/vfvHqz2v/ngE0slaD/QnJORHzrlKt6p6TdoY+/1zBQ/TOH+6BlkdtrSIrSfwJfP+3Qe6C8uw6yJknQ6cpabA==", "integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
"dependencies": { "dependencies": {
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"source-map": "0.6.1" "source-map": "0.6.1"
}, },
"engines": { "engines": {
@ -884,42 +884,42 @@
} }
}, },
"node_modules/@intlify/message-resolver": { "node_modules/@intlify/message-resolver": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
"integrity": "sha512-4tHBo5U2/oDG85tNv9z8bS/5ThMw+wADPLyBVOLplUqw8Q+N5tkrTL23Pa8hg5Ekd2crvyIxFHFwt1gbT8TT6w==", "integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==",
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@intlify/runtime": { "node_modules/@intlify/runtime": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
"integrity": "sha512-Q9WvSjRFhxxCen5cj3jOZEKAYlXjYZ+wZbTEfBQhDtcBwrS7xd9tyFos4ZRNNvF7G0H0sNDzXmSdZkoCpoU0iA==", "integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
"dependencies": { "dependencies": {
"@intlify/message-compiler": "9.1.8", "@intlify/message-compiler": "9.1.9",
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@intlify/shared": { "node_modules/@intlify/shared": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
"integrity": "sha512-o9nksOx3yIMDNvYzcPv87NR+U62ka775/Ufjl3U2g4NsMORN8+VacbVJ/oAF6CYfzZALpArBBZdk5jafzcLkvw==", "integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==",
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@intlify/vue-devtools": { "node_modules/@intlify/vue-devtools": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
"integrity": "sha512-SypF7tpWFxIQzKrqv6O8JIk5xrU53E0/xNVR5LFCHLIDIKe62uMTrzXvjW5zoYcJjRTZ87BZLXEM1n2CaLSBsg==", "integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
"dependencies": { "dependencies": {
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/runtime": "9.1.8", "@intlify/runtime": "9.1.9",
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
@ -1199,9 +1199,9 @@
} }
}, },
"node_modules/@louislam/sqlite3": { "node_modules/@louislam/sqlite3": {
"version": "5.0.6", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-5.0.6.tgz", "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-6.0.0.tgz",
"integrity": "sha512-uitL0jdbki5XSrmGKGgvHVMHEe00O6GAMoPrVOnh4KTcFOJ1T8SWypbnyqSxBr7PrjAVfgnIGu3kzYCCqIxd4g==", "integrity": "sha512-jKNkg7olyL4vM0yqVBiyPrtHALfWkCLAASASDJpghBE5Ri6qOh9bXmzaKyTrYH5cSsFB7R39XGC4O9XdAmTe4Q==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0", "@mapbox/node-pre-gyp": "^1.0.0",
@ -1685,16 +1685,16 @@
} }
}, },
"node_modules/@vitejs/plugin-legacy": { "node_modules/@vitejs/plugin-legacy": {
"version": "1.5.3", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.1.tgz",
"integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==", "integrity": "sha512-isBi2ti+AlCZUpfA1P6L8gseltBy/qi6Rsi92aDzeL2elpwXgN4Hv/xLS2UUSSj9F0mFmxXCYPWlBPaJnlYamQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/standalone": "^7.14.9", "@babel/standalone": "^7.15.7",
"core-js": "^3.16.0", "core-js": "^3.18.1",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"regenerator-runtime": "^0.13.9", "regenerator-runtime": "^0.13.9",
"systemjs": "^6.10.2" "systemjs": "^6.10.3"
}, },
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=12.0.0"
@ -3512,15 +3512,241 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.12.29", "version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.4.tgz",
"integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", "integrity": "sha512-wMA5eUwpavTBiNl+It6j8OQuKVh69l6z4DKDLzoTIqC+gChnPpcmqdA8WNHptUHRnfyML+mKEQPlW7Mybj8gHg==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
},
"optionalDependencies": {
"esbuild-android-arm64": "0.13.4",
"esbuild-darwin-64": "0.13.4",
"esbuild-darwin-arm64": "0.13.4",
"esbuild-freebsd-64": "0.13.4",
"esbuild-freebsd-arm64": "0.13.4",
"esbuild-linux-32": "0.13.4",
"esbuild-linux-64": "0.13.4",
"esbuild-linux-arm": "0.13.4",
"esbuild-linux-arm64": "0.13.4",
"esbuild-linux-mips64le": "0.13.4",
"esbuild-linux-ppc64le": "0.13.4",
"esbuild-openbsd-64": "0.13.4",
"esbuild-sunos-64": "0.13.4",
"esbuild-windows-32": "0.13.4",
"esbuild-windows-64": "0.13.4",
"esbuild-windows-arm64": "0.13.4"
} }
}, },
"node_modules/esbuild-android-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.4.tgz",
"integrity": "sha512-elDJt+jNyoHFId0/dKsuVYUPke3EcquIyUwzJCH17a3ERglN3A9aMBI5zbz+xNZ+FbaDNdpn0RaJHCFLbZX+fA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/esbuild-darwin-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.4.tgz",
"integrity": "sha512-zJQGyHRAdZUXlRzbN7W+7ykmEiGC+bq3Gc4GxKYjjWTgDRSEly98ym+vRNkDjXwXYD3gGzSwvH35+MiHAtWvLA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.4.tgz",
"integrity": "sha512-r8oYvAtqSGq8HNTZCAx4TdLE7jZiGhX9ooGi5AQAey37MA6XNaP8ZNlw9OCpcgpx3ryU2WctXwIqPzkHO7a8dg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/esbuild-freebsd-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.4.tgz",
"integrity": "sha512-u9DRGkn09EN8+lCh6z7FKle7awi17PJRBuAKdRNgSo5ZrH/3m+mYaJK2PR2URHMpAfXiwJX341z231tSdVe3Yw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.4.tgz",
"integrity": "sha512-q3B2k68Uf6gfjATjcK16DqxvjqRQkHL8aPoOfj4op+lSqegdXvBacB1d8jw8PxbWJ8JHpdTLdAVUYU80kotQXA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/esbuild-linux-32": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.4.tgz",
"integrity": "sha512-UUYJPHSiKAO8KoN3Ls/iZtgDLZvK5HarES96aolDPWZnq9FLx4dIHM/x2z4Rxv9IYqQ/DxlPoE2Co1UPBIYYeA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-linux-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.4.tgz",
"integrity": "sha512-+RnohAKiiUW4UHLGRkNR1AnENW1gCuDWuygEtd4jxTNPIoeC7lbXGor7rtgjj9AdUzFgOEvAXyNNX01kJ8NueQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-linux-arm": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.4.tgz",
"integrity": "sha512-BH5gKve4jglS7UPSsfwHSX79I5agC/lm4eKoRUEyo8lwQs89frQSRp2Xup+6SFQnxt3md5EsKcd2Dbkqeb3gPA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-linux-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.4.tgz",
"integrity": "sha512-+A188cAdd6QuSRxMIwRrWLjgphQA0LDAQ/ECVlrPVJwnx+1i64NjDZivoqPYLOTkSPIKntiWwMhhf0U5/RrPHQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.4.tgz",
"integrity": "sha512-0xkwtPaUkG5xMTFGaQPe1AadSe5QAiQuD4Gix1O9k5Xo/U8xGIkw9UFUTvfEUeu71vFb6ZgsIacfP1NLoFjWNw==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.4.tgz",
"integrity": "sha512-E1+oJPP7A+j23GPo3CEpBhGwG1bni4B8IbTA3/3rvzjURwUMZdcN3Fhrz24rnjzdLSHmULtOE4VsbT42h1Om4Q==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/esbuild-openbsd-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.4.tgz",
"integrity": "sha512-xEkI1o5HYxDzbv9jSox0EsDxpwraG09SRiKKv0W8pH6O3bt+zPSlnoK7+I7Q69tkvONkpIq5n2o+c55uq0X7cw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/esbuild-sunos-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.4.tgz",
"integrity": "sha512-bjXUMcODMnB6hQicLBBmmnBl7OMDyVpFahKvHGXJfDChIi5udiIRKCmFUFIRn+AUAKVlfrofRKdyPC7kBsbvGQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
]
},
"node_modules/esbuild-windows-32": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.4.tgz",
"integrity": "sha512-z4CH07pfyVY0XF98TCsGmLxKCl0kyvshKDbdpTekW9f2d+dJqn5mmoUyWhpSVJ0SfYWJg86FoD9nMbbaMVyGdg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/esbuild-windows-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.4.tgz",
"integrity": "sha512-uVL11vORRPjocGLYam67rwFLd0LvkrHEs+JG+1oJN4UD9MQmNGZPa4gBHo6hDpF+kqRJ9kXgQSeDqUyRy0tj/Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/esbuild-windows-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.4.tgz",
"integrity": "sha512-vA6GLvptgftRcDcWngD5cMlL4f4LbL8JjU2UMT9yJ0MT5ra6hdZNFWnOeOoEtY4GtJ6OjZ0i+81sTqhAB0fMkg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -10066,15 +10292,15 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "2.5.10", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.5.10.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.4.tgz",
"integrity": "sha512-0ObiHTi5AHyXdJcvZ67HMsDgVpjT5RehvVKv6+Q0jFZ7zDI28PF5zK9mYz2avxdA+4iJMdwCz6wnGNnn4WX5Gg==", "integrity": "sha512-zNGZgjKGprdLKJ1g1taAvNt51JbGAdrAUU9hpLzgtlks+cXBxTZUsEAGEtLbF3UvlYOVAPXS8r9E9gxYAv6z+A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.12.17", "esbuild": "^0.13.2",
"postcss": "^8.3.6", "postcss": "^8.3.8",
"resolve": "^1.20.0", "resolve": "^1.20.0",
"rollup": "^2.38.5" "rollup": "^2.57.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
@ -10084,6 +10310,22 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
}
} }
}, },
"node_modules/vue": { "node_modules/vue": {
@ -10207,13 +10449,13 @@
} }
}, },
"node_modules/vue-i18n": { "node_modules/vue-i18n": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.8.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
"integrity": "sha512-Gmjkt/4ZQtKyCLTjJNGVp+w/dhMpuwuPtgW/Xm9DY0ppSwHRgqMRR6GspnCIYWBxQjzsTT8tIWLPc3SUfKifLA==", "integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
"dependencies": { "dependencies": {
"@intlify/core-base": "9.1.8", "@intlify/core-base": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"@intlify/vue-devtools": "9.1.8", "@intlify/vue-devtools": "9.1.9",
"@vue/devtools-api": "^6.0.0-beta.7" "@vue/devtools-api": "^6.0.0-beta.7"
}, },
"engines": { "engines": {
@ -11229,64 +11471,64 @@
"dev": true "dev": true
}, },
"@intlify/core-base": { "@intlify/core-base": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
"integrity": "sha512-k+q6nUOD9HPTMr8AKEn4d/EgoNSCMVuc/dG97tFXrifT4+QbxoLNRjXovyC60rb4q+7D/cGF+5R6Tjby4t5gng==", "integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
"requires": { "requires": {
"@intlify/devtools-if": "9.1.8", "@intlify/devtools-if": "9.1.9",
"@intlify/message-compiler": "9.1.8", "@intlify/message-compiler": "9.1.9",
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/runtime": "9.1.8", "@intlify/runtime": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"@intlify/vue-devtools": "9.1.8" "@intlify/vue-devtools": "9.1.9"
} }
}, },
"@intlify/devtools-if": { "@intlify/devtools-if": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
"integrity": "sha512-17REiNoQ5dWnYECbKgkMHtX8GnNCkMNxfImXrJNnUhSH3GitheNkYn0o14AZmZWk7Fw/1IOdV5v7K0QVnYx5fw==", "integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
"requires": { "requires": {
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
} }
}, },
"@intlify/message-compiler": { "@intlify/message-compiler": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
"integrity": "sha512-x/vfvHqz2v/ngE0slaD/QnJORHzrlKt6p6TdoY+/1zBQ/TOH+6BlkdtrSIrSfwJfP+3Qe6C8uw6yJknQ6cpabA==", "integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
"requires": { "requires": {
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"source-map": "0.6.1" "source-map": "0.6.1"
} }
}, },
"@intlify/message-resolver": { "@intlify/message-resolver": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
"integrity": "sha512-4tHBo5U2/oDG85tNv9z8bS/5ThMw+wADPLyBVOLplUqw8Q+N5tkrTL23Pa8hg5Ekd2crvyIxFHFwt1gbT8TT6w==" "integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA=="
}, },
"@intlify/runtime": { "@intlify/runtime": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
"integrity": "sha512-Q9WvSjRFhxxCen5cj3jOZEKAYlXjYZ+wZbTEfBQhDtcBwrS7xd9tyFos4ZRNNvF7G0H0sNDzXmSdZkoCpoU0iA==", "integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
"requires": { "requires": {
"@intlify/message-compiler": "9.1.8", "@intlify/message-compiler": "9.1.9",
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
} }
}, },
"@intlify/shared": { "@intlify/shared": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
"integrity": "sha512-o9nksOx3yIMDNvYzcPv87NR+U62ka775/Ufjl3U2g4NsMORN8+VacbVJ/oAF6CYfzZALpArBBZdk5jafzcLkvw==" "integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw=="
}, },
"@intlify/vue-devtools": { "@intlify/vue-devtools": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.8.tgz", "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
"integrity": "sha512-SypF7tpWFxIQzKrqv6O8JIk5xrU53E0/xNVR5LFCHLIDIKe62uMTrzXvjW5zoYcJjRTZ87BZLXEM1n2CaLSBsg==", "integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
"requires": { "requires": {
"@intlify/message-resolver": "9.1.8", "@intlify/message-resolver": "9.1.9",
"@intlify/runtime": "9.1.8", "@intlify/runtime": "9.1.9",
"@intlify/shared": "9.1.8" "@intlify/shared": "9.1.9"
} }
}, },
"@istanbuljs/load-nyc-config": { "@istanbuljs/load-nyc-config": {
@ -11507,9 +11749,9 @@
} }
}, },
"@louislam/sqlite3": { "@louislam/sqlite3": {
"version": "5.0.6", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-5.0.6.tgz", "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-6.0.0.tgz",
"integrity": "sha512-uitL0jdbki5XSrmGKGgvHVMHEe00O6GAMoPrVOnh4KTcFOJ1T8SWypbnyqSxBr7PrjAVfgnIGu3kzYCCqIxd4g==", "integrity": "sha512-jKNkg7olyL4vM0yqVBiyPrtHALfWkCLAASASDJpghBE5Ri6qOh9bXmzaKyTrYH5cSsFB7R39XGC4O9XdAmTe4Q==",
"requires": { "requires": {
"@mapbox/node-pre-gyp": "^1.0.0", "@mapbox/node-pre-gyp": "^1.0.0",
"node-addon-api": "^3.0.0", "node-addon-api": "^3.0.0",
@ -11951,16 +12193,16 @@
} }
}, },
"@vitejs/plugin-legacy": { "@vitejs/plugin-legacy": {
"version": "1.5.3", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.1.tgz",
"integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==", "integrity": "sha512-isBi2ti+AlCZUpfA1P6L8gseltBy/qi6Rsi92aDzeL2elpwXgN4Hv/xLS2UUSSj9F0mFmxXCYPWlBPaJnlYamQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/standalone": "^7.14.9", "@babel/standalone": "^7.15.7",
"core-js": "^3.16.0", "core-js": "^3.18.1",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"regenerator-runtime": "^0.13.9", "regenerator-runtime": "^0.13.9",
"systemjs": "^6.10.2" "systemjs": "^6.10.3"
} }
}, },
"@vitejs/plugin-vue": { "@vitejs/plugin-vue": {
@ -13373,10 +13615,140 @@
} }
}, },
"esbuild": { "esbuild": {
"version": "0.12.29", "version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.4.tgz",
"integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", "integrity": "sha512-wMA5eUwpavTBiNl+It6j8OQuKVh69l6z4DKDLzoTIqC+gChnPpcmqdA8WNHptUHRnfyML+mKEQPlW7Mybj8gHg==",
"dev": true "dev": true,
"requires": {
"esbuild-android-arm64": "0.13.4",
"esbuild-darwin-64": "0.13.4",
"esbuild-darwin-arm64": "0.13.4",
"esbuild-freebsd-64": "0.13.4",
"esbuild-freebsd-arm64": "0.13.4",
"esbuild-linux-32": "0.13.4",
"esbuild-linux-64": "0.13.4",
"esbuild-linux-arm": "0.13.4",
"esbuild-linux-arm64": "0.13.4",
"esbuild-linux-mips64le": "0.13.4",
"esbuild-linux-ppc64le": "0.13.4",
"esbuild-openbsd-64": "0.13.4",
"esbuild-sunos-64": "0.13.4",
"esbuild-windows-32": "0.13.4",
"esbuild-windows-64": "0.13.4",
"esbuild-windows-arm64": "0.13.4"
}
},
"esbuild-android-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.4.tgz",
"integrity": "sha512-elDJt+jNyoHFId0/dKsuVYUPke3EcquIyUwzJCH17a3ERglN3A9aMBI5zbz+xNZ+FbaDNdpn0RaJHCFLbZX+fA==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.4.tgz",
"integrity": "sha512-zJQGyHRAdZUXlRzbN7W+7ykmEiGC+bq3Gc4GxKYjjWTgDRSEly98ym+vRNkDjXwXYD3gGzSwvH35+MiHAtWvLA==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.4.tgz",
"integrity": "sha512-r8oYvAtqSGq8HNTZCAx4TdLE7jZiGhX9ooGi5AQAey37MA6XNaP8ZNlw9OCpcgpx3ryU2WctXwIqPzkHO7a8dg==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.4.tgz",
"integrity": "sha512-u9DRGkn09EN8+lCh6z7FKle7awi17PJRBuAKdRNgSo5ZrH/3m+mYaJK2PR2URHMpAfXiwJX341z231tSdVe3Yw==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.4.tgz",
"integrity": "sha512-q3B2k68Uf6gfjATjcK16DqxvjqRQkHL8aPoOfj4op+lSqegdXvBacB1d8jw8PxbWJ8JHpdTLdAVUYU80kotQXA==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.4.tgz",
"integrity": "sha512-UUYJPHSiKAO8KoN3Ls/iZtgDLZvK5HarES96aolDPWZnq9FLx4dIHM/x2z4Rxv9IYqQ/DxlPoE2Co1UPBIYYeA==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.4.tgz",
"integrity": "sha512-+RnohAKiiUW4UHLGRkNR1AnENW1gCuDWuygEtd4jxTNPIoeC7lbXGor7rtgjj9AdUzFgOEvAXyNNX01kJ8NueQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.4.tgz",
"integrity": "sha512-BH5gKve4jglS7UPSsfwHSX79I5agC/lm4eKoRUEyo8lwQs89frQSRp2Xup+6SFQnxt3md5EsKcd2Dbkqeb3gPA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.4.tgz",
"integrity": "sha512-+A188cAdd6QuSRxMIwRrWLjgphQA0LDAQ/ECVlrPVJwnx+1i64NjDZivoqPYLOTkSPIKntiWwMhhf0U5/RrPHQ==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.4.tgz",
"integrity": "sha512-0xkwtPaUkG5xMTFGaQPe1AadSe5QAiQuD4Gix1O9k5Xo/U8xGIkw9UFUTvfEUeu71vFb6ZgsIacfP1NLoFjWNw==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.4.tgz",
"integrity": "sha512-E1+oJPP7A+j23GPo3CEpBhGwG1bni4B8IbTA3/3rvzjURwUMZdcN3Fhrz24rnjzdLSHmULtOE4VsbT42h1Om4Q==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.4.tgz",
"integrity": "sha512-xEkI1o5HYxDzbv9jSox0EsDxpwraG09SRiKKv0W8pH6O3bt+zPSlnoK7+I7Q69tkvONkpIq5n2o+c55uq0X7cw==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.4.tgz",
"integrity": "sha512-bjXUMcODMnB6hQicLBBmmnBl7OMDyVpFahKvHGXJfDChIi5udiIRKCmFUFIRn+AUAKVlfrofRKdyPC7kBsbvGQ==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.4.tgz",
"integrity": "sha512-z4CH07pfyVY0XF98TCsGmLxKCl0kyvshKDbdpTekW9f2d+dJqn5mmoUyWhpSVJ0SfYWJg86FoD9nMbbaMVyGdg==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.4.tgz",
"integrity": "sha512-uVL11vORRPjocGLYam67rwFLd0LvkrHEs+JG+1oJN4UD9MQmNGZPa4gBHo6hDpF+kqRJ9kXgQSeDqUyRy0tj/Q==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.4.tgz",
"integrity": "sha512-vA6GLvptgftRcDcWngD5cMlL4f4LbL8JjU2UMT9yJ0MT5ra6hdZNFWnOeOoEtY4GtJ6OjZ0i+81sTqhAB0fMkg==",
"dev": true,
"optional": true
}, },
"escalade": { "escalade": {
"version": "3.1.1", "version": "3.1.1",
@ -18379,16 +18751,16 @@
} }
}, },
"vite": { "vite": {
"version": "2.5.10", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.5.10.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.4.tgz",
"integrity": "sha512-0ObiHTi5AHyXdJcvZ67HMsDgVpjT5RehvVKv6+Q0jFZ7zDI28PF5zK9mYz2avxdA+4iJMdwCz6wnGNnn4WX5Gg==", "integrity": "sha512-zNGZgjKGprdLKJ1g1taAvNt51JbGAdrAUU9hpLzgtlks+cXBxTZUsEAGEtLbF3UvlYOVAPXS8r9E9gxYAv6z+A==",
"dev": true, "dev": true,
"requires": { "requires": {
"esbuild": "^0.12.17", "esbuild": "^0.13.2",
"fsevents": "~2.3.2", "fsevents": "~2.3.2",
"postcss": "^8.3.6", "postcss": "^8.3.8",
"resolve": "^1.20.0", "resolve": "^1.20.0",
"rollup": "^2.38.5" "rollup": "^2.57.0"
} }
}, },
"vue": { "vue": {
@ -18469,13 +18841,13 @@
} }
}, },
"vue-i18n": { "vue-i18n": {
"version": "9.1.8", "version": "9.1.9",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.8.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
"integrity": "sha512-Gmjkt/4ZQtKyCLTjJNGVp+w/dhMpuwuPtgW/Xm9DY0ppSwHRgqMRR6GspnCIYWBxQjzsTT8tIWLPc3SUfKifLA==", "integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
"requires": { "requires": {
"@intlify/core-base": "9.1.8", "@intlify/core-base": "9.1.9",
"@intlify/shared": "9.1.8", "@intlify/shared": "9.1.9",
"@intlify/vue-devtools": "9.1.8", "@intlify/vue-devtools": "9.1.9",
"@vue/devtools-api": "^6.0.0-beta.7" "@vue/devtools-api": "^6.0.0-beta.7"
} }
}, },

View file

@ -21,6 +21,7 @@
"start-server-dev": "cross-env NODE_ENV=development node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"build": "vite build", "build": "vite build",
"test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
"test-with-build": "npm run build && npm test",
"jest": "node test/prepare-jest.js && jest", "jest": "node test/prepare-jest.js && jest",
"tsc": "tsc", "tsc": "tsc",
"vite-preview-dist": "vite preview --host", "vite-preview-dist": "vite preview --host",
@ -52,7 +53,7 @@
"@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-4", "@fortawesome/vue-fontawesome": "~3.0.0-4",
"@louislam/sqlite3": "~5.0.6", "@louislam/sqlite3": "~6.0.0",
"@popperjs/core": "~2.10.2", "@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.21.4", "axios": "~0.21.4",
@ -87,7 +88,7 @@
"vue-chart-3": "~0.5.8", "vue-chart-3": "~0.5.8",
"vue-confirm-dialog": "~1.0.2", "vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4", "vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.1.8", "vue-i18n": "~9.1.9",
"vue-image-crop-upload": "~3.0.3", "vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2", "vue-multiselect": "~3.0.0-alpha.2",
"vue-qrcode": "~1.0.0", "vue-qrcode": "~1.0.0",
@ -98,7 +99,7 @@
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "~7.15.7", "@babel/eslint-parser": "~7.15.7",
"@types/bootstrap": "~5.1.6", "@types/bootstrap": "~5.1.6",
"@vitejs/plugin-legacy": "~1.5.3", "@vitejs/plugin-legacy": "~1.6.1",
"@vitejs/plugin-vue": "~1.9.2", "@vitejs/plugin-vue": "~1.9.2",
"@vue/compiler-sfc": "~3.2.19", "@vue/compiler-sfc": "~3.2.19",
"core-js": "~3.18.1", "core-js": "~3.18.1",
@ -113,7 +114,7 @@
"stylelint": "~13.13.1", "stylelint": "~13.13.1",
"stylelint-config-standard": "~22.0.0", "stylelint-config-standard": "~22.0.0",
"typescript": "~4.4.3", "typescript": "~4.4.3",
"vite": "~2.5.10" "vite": "~2.6.4"
}, },
"jest": { "jest": {
"verbose": true, "verbose": true,
@ -123,6 +124,6 @@
}, },
"testRegex": "./test/*.spec.js", "testRegex": "./test/*.spec.js",
"rootDir": ".", "rootDir": ".",
"testTimeout": 15000 "testTimeout": 30000
} }
} }

View file

@ -166,7 +166,9 @@ class Monitor extends BeanModel {
} }
} }
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); if (process.env.TIMELOGGER === "1") {
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (this.type === "http") { if (this.type === "http") {
bean.status = UP; bean.status = UP;
@ -530,6 +532,7 @@ class Monitor extends BeanModel {
const uptime = await this.calcUptime(duration, monitorID); const uptime = await this.calcUptime(duration, monitorID);
io.to(userID).emit("uptime", monitorID, duration, uptime); io.to(userID).emit("uptime", monitorID, duration, uptime);
} }
} }
module.exports = Monitor; module.exports = Monitor;

View file

@ -1,5 +1,8 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const Slack = require("./slack");
const { setting } = require("../util-server");
const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util");
class RocketChat extends NotificationProvider { class RocketChat extends NotificationProvider {
@ -10,16 +13,17 @@ class RocketChat extends NotificationProvider {
try { try {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let data = { let data = {
"text": "Uptime Kuma Rocket.chat testing successful.", "text": msg,
"channel": notification.rocketchannel, "channel": notification.rocketchannel,
"username": notification.rocketusername, "username": notification.rocketusername,
"icon_emoji": notification.rocketiconemo, "icon_emoji": notification.rocketiconemo,
} };
await axios.post(notification.rocketwebhookURL, data) await axios.post(notification.rocketwebhookURL, data);
return okMsg; return okMsg;
} }
const time = heartbeatJSON["time"]; const time = heartbeatJSON["time"];
let data = { let data = {
"text": "Uptime Kuma Alert", "text": "Uptime Kuma Alert",
"channel": notification.rocketchannel, "channel": notification.rocketchannel,
@ -28,16 +32,32 @@ class RocketChat extends NotificationProvider {
"attachments": [ "attachments": [
{ {
"title": "Uptime Kuma Alert *Time (UTC)*\n" + time, "title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
"title_link": notification.rocketbutton,
"text": "*Message*\n" + msg, "text": "*Message*\n" + msg,
"color": "#32cd32"
} }
] ]
};
// Color
if (heartbeatJSON.status === DOWN) {
data.attachments[0].color = "#ff0000";
} else {
data.attachments[0].color = "#32cd32";
} }
await axios.post(notification.rocketwebhookURL, data)
if (notification.rocketbutton) {
await Slack.deprecateURL(notification.rocketbutton);
}
const baseURL = await setting("primaryBaseURL");
if (baseURL) {
data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id);
}
await axios.post(notification.rocketwebhookURL, data);
return okMsg; return okMsg;
} catch (error) { } catch (error) {
this.throwGeneralAxiosError(error) this.throwGeneralAxiosError(error);
} }
} }

View file

@ -1,21 +1,40 @@
const NotificationProvider = require("./notification-provider"); const NotificationProvider = require("./notification-provider");
const axios = require("axios"); const axios = require("axios");
const { setSettings, setting } = require("../util-server");
const { getMonitorRelativeURL } = require("../../src/util");
class Slack extends NotificationProvider { class Slack extends NotificationProvider {
name = "slack"; name = "slack";
/**
* Deprecated property notification.slackbutton
* Set it as primary base url if this is not yet set.
*/
static async deprecateURL(url) {
let currentPrimaryBaseURL = await setting("primaryBaseURL");
if (!currentPrimaryBaseURL) {
console.log("Move the url to be the primary base URL");
await setSettings("general", {
primaryBaseURL: url,
});
} else {
console.log("Already there, no need to move the primary base URL");
}
}
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully. ";
try { try {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let data = { let data = {
"text": "Uptime Kuma Slack testing successful.", "text": msg,
"channel": notification.slackchannel, "channel": notification.slackchannel,
"username": notification.slackusername, "username": notification.slackusername,
"icon_emoji": notification.slackiconemo, "icon_emoji": notification.slackiconemo,
} };
await axios.post(notification.slackwebhookURL, data) await axios.post(notification.slackwebhookURL, data);
return okMsg; return okMsg;
} }
@ -42,26 +61,35 @@ class Slack extends NotificationProvider {
"type": "mrkdwn", "type": "mrkdwn",
"text": "*Time (UTC)*\n" + time, "text": "*Time (UTC)*\n" + time,
}], }],
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit Uptime Kuma",
},
"value": "Uptime-Kuma",
"url": notification.slackbutton || "https://github.com/louislam/uptime-kuma",
},
],
}], }],
};
if (notification.slackbutton) {
await Slack.deprecateURL(notification.slackbutton);
} }
await axios.post(notification.slackwebhookURL, data)
const baseURL = await setting("primaryBaseURL");
// Button
if (baseURL) {
data.blocks.push({
"type": "actions",
"elements": [{
"type": "button",
"text": {
"type": "plain_text",
"text": "Visit Uptime Kuma",
},
"value": "Uptime-Kuma",
"url": baseURL + getMonitorRelativeURL(monitorJSON.id),
}],
});
}
await axios.post(notification.slackwebhookURL, data);
return okMsg; return okMsg;
} catch (error) { } catch (error) {
this.throwGeneralAxiosError(error) this.throwGeneralAxiosError(error);
} }
} }

View file

@ -321,7 +321,7 @@ h2 {
.item { .item {
display: block; display: block;
text-decoration: none; text-decoration: none;
padding: 14px 15px; padding: 13px 15px 10px 15px;
border-radius: 10px; border-radius: 10px;
transition: all ease-in-out 0.15s; transition: all ease-in-out 0.15s;

View file

@ -38,7 +38,7 @@ export default {
beatMargin: 4, beatMargin: 4,
move: false, move: false,
maxBeat: -1, maxBeat: -1,
}; }
}, },
computed: { computed: {
@ -69,12 +69,12 @@ export default {
if (start < 0) { if (start < 0) {
// Add empty placeholder // Add empty placeholder
for (let i = start; i < 0; i++) { for (let i = start; i < 0; i++) {
placeholders.push(0); placeholders.push(0)
} }
start = 0; start = 0;
} }
return placeholders.concat(this.beatList.slice(start)); return placeholders.concat(this.beatList.slice(start))
}, },
wrapStyle() { wrapStyle() {
@ -84,7 +84,7 @@ export default {
return { return {
padding: `${topBottom}px ${leftRight}px`, padding: `${topBottom}px ${leftRight}px`,
width: "100%", width: "100%",
}; }
}, },
barStyle() { barStyle() {
@ -94,12 +94,12 @@ export default {
return { return {
transition: "all ease-in-out 0.25s", transition: "all ease-in-out 0.25s",
transform: `translateX(${width}px)`, transform: `translateX(${width}px)`,
}; }
} }
return { return {
transform: "translateX(0)", transform: "translateX(0)",
}; }
}, },
@ -109,7 +109,7 @@ export default {
height: this.beatHeight + "px", height: this.beatHeight + "px",
margin: this.beatMargin + "px", margin: this.beatMargin + "px",
"--hover-scale": this.hoverScale, "--hover-scale": this.hoverScale,
}; }
}, },
}, },
@ -120,7 +120,7 @@ export default {
setTimeout(() => { setTimeout(() => {
this.move = false; this.move = false;
}, 300); }, 300)
}, },
deep: true, deep: true,
}, },
@ -162,7 +162,7 @@ export default {
methods: { methods: {
resize() { resize() {
if (this.$refs.wrap) { if (this.$refs.wrap) {
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2))
} }
}, },
@ -170,7 +170,7 @@ export default {
return `${this.$root.datetime(beat.time)} - ${beat.msg}`; return `${this.$root.datetime(beat.time)} - ${beat.msg}`;
} }
}, },
}; }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -183,9 +183,6 @@ export default {
} }
.hp-bar-big { .hp-bar-big {
display: flex;
justify-content: flex-end;
.beat { .beat {
display: inline-block; display: inline-block;
background-color: $primary; background-color: $primary;

View file

@ -3,10 +3,10 @@
<div class="list-header"> <div class="list-header">
<div class="placeholder"></div> <div class="placeholder"></div>
<div class="search-wrapper"> <div class="search-wrapper">
<a v-if="!searchText" class="search-icon"> <a v-if="searchText == ''" class="search-icon">
<font-awesome-icon icon="search" /> <font-awesome-icon icon="search" />
</a> </a>
<a v-if="searchText" class="search-icon" @click="clearSearchText"> <a v-if="searchText != ''" class="search-icon" @click="clearSearchText">
<font-awesome-icon icon="times" /> <font-awesome-icon icon="times" />
</a> </a>
<input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" /> <input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" />
@ -19,21 +19,21 @@
<router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> <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="row">
<div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar === 'bottom' || $root.userHeartbeatBar === 'none' }"> <div class="col-9 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
<div class="info"> <div class="info">
<Uptime :monitor="item" type="24" :pill="true" /> <Uptime :monitor="item" type="24" :pill="true" />
<span class="ms-1">{{ item.name }}</span> {{ item.name }}
</div> </div>
<div class="tags"> <div class="tags">
<Tag v-for="tag in item.tags" :key="tag" :item="tag" :size="'sm'" /> <Tag v-for="tag in item.tags" :key="tag" :item="tag" :size="'sm'" />
</div> </div>
</div> </div>
<div v-show="$root.userHeartbeatBar === 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4 small-padding"> <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-3 col-md-4">
<HeartbeatBar size="small" :monitor-id="item.id" /> <HeartbeatBar size="small" :monitor-id="item.id" />
</div> </div>
</div> </div>
<div v-if="$root.userHeartbeatBar === 'bottom'" class="row"> <div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
<div class="col-12"> <div class="col-12">
<HeartbeatBar size="small" :monitor-id="item.id" /> <HeartbeatBar size="small" :monitor-id="item.id" />
</div> </div>
@ -47,6 +47,7 @@
import HeartbeatBar from "../components/HeartbeatBar.vue"; import HeartbeatBar from "../components/HeartbeatBar.vue";
import Uptime from "../components/Uptime.vue"; import Uptime from "../components/Uptime.vue";
import Tag from "../components/Tag.vue"; import Tag from "../components/Tag.vue";
import { getMonitorRelativeURL } from "../util.ts";
export default { export default {
components: { components: {
@ -95,7 +96,7 @@ export default {
// Simple filter by search text // Simple filter by search text
// finds monitor name, tag name or tag value // finds monitor name, tag name or tag value
if (this.searchText) { if (this.searchText != "") {
const loweredSearchText = this.searchText.toLowerCase(); const loweredSearchText = this.searchText.toLowerCase();
result = result.filter(monitor => { result = result.filter(monitor => {
return monitor.name.toLowerCase().includes(loweredSearchText) return monitor.name.toLowerCase().includes(loweredSearchText)
@ -109,7 +110,7 @@ export default {
}, },
methods: { methods: {
monitorURL(id) { monitorURL(id) {
return "/dashboard/" + id; return getMonitorRelativeURL(id);
}, },
clearSearchText() { clearSearchText() {
this.searchText = ""; this.searchText = "";

View file

@ -68,11 +68,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Modal } from "bootstrap" import { Modal } from "bootstrap";
import { ucfirst } from "../util.ts" import { ucfirst } from "../util.ts";
import Confirm from "./Confirm.vue"; import Confirm from "./Confirm.vue";
import NotificationFormList from "./notifications" import NotificationFormList from "./notifications";
export default { export default {
components: { components: {
@ -93,41 +93,40 @@ export default {
isDefault: false, isDefault: false,
// Do not set default value here, please scroll to show() // Do not set default value here, please scroll to show()
} }
} };
}, },
computed: { computed: {
currentForm() { currentForm() {
if (!this.notification.type) { if (!this.notification.type) {
return null return null;
} }
return NotificationFormList[this.notification.type] return NotificationFormList[this.notification.type];
} }
}, },
watch: { watch: {
"notification.type"(to, from) { "notification.type"(to, from) {
let oldName; let oldName;
if (from) { if (from) {
oldName = `My ${ucfirst(from)} Alert (1)`; oldName = this.getUniqueDefaultName(from);
} else { } else {
oldName = ""; oldName = "";
} }
if (! this.notification.name || this.notification.name === oldName) { if (! this.notification.name || this.notification.name === oldName) {
this.notification.name = `My ${ucfirst(to)} Alert (1)` this.notification.name = this.getUniqueDefaultName(to);
} }
}, },
}, },
mounted() { mounted() {
this.modal = new Modal(this.$refs.modal) this.modal = new Modal(this.$refs.modal);
}, },
methods: { methods: {
deleteConfirm() { deleteConfirm() {
this.modal.hide(); this.modal.hide();
this.$refs.confirmDelete.show() this.$refs.confirmDelete.show();
}, },
show(notificationID) { show(notificationID) {
@ -146,19 +145,19 @@ export default {
name: "", name: "",
type: null, type: null,
isDefault: false, isDefault: false,
} };
// Set Default value here // Set Default value here
this.notification.type = this.notificationTypes[0]; this.notification.type = this.notificationTypes[0];
} }
this.modal.show() this.modal.show();
}, },
submit() { submit() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => { this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
this.$root.toastRes(res) this.$root.toastRes(res);
this.processing = false; this.processing = false;
if (res.ok) { if (res.ok) {
@ -170,30 +169,45 @@ export default {
} }
} }
}) });
}, },
test() { test() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("testNotification", this.notification, (res) => { this.$root.getSocket().emit("testNotification", this.notification, (res) => {
this.$root.toastRes(res) this.$root.toastRes(res);
this.processing = false; this.processing = false;
}) });
}, },
deleteNotification() { deleteNotification() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("deleteNotification", this.id, (res) => { this.$root.getSocket().emit("deleteNotification", this.id, (res) => {
this.$root.toastRes(res) this.$root.toastRes(res);
this.processing = false; this.processing = false;
if (res.ok) { if (res.ok) {
this.modal.hide() this.modal.hide();
} }
}) });
}, },
/**
* @param {keyof NotificationFormList} notificationKey
* @return {string}
*/
getUniqueDefaultName(notificationKey) {
let index = 1;
let name = "";
do {
name = this.$t("defaultNotificationName", {
notification: this.$t(notificationKey).replace(/\(.+\)/, "").trim(),
number: index++
});
} while (this.$root.notificationList.find(it => it.name === name));
return name;
}
}, },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<h4 class="mb-3">{{ $t("Tags") }}</h4> <h4 class="mt-5 mb-3">{{ $t("Tags") }}</h4>
<div class="mb-3 p-1"> <div v-if="selectedTags.length > 0" class="mb-2 p-1">
<tag <tag
v-for="item in selectedTags" v-for="item in selectedTags"
:key="item.id" :key="item.id"
@ -124,8 +124,8 @@
import { Modal } from "bootstrap"; import { Modal } from "bootstrap";
import VueMultiselect from "vue-multiselect"; import VueMultiselect from "vue-multiselect";
import Tag from "../components/Tag.vue"; import Tag from "../components/Tag.vue";
import { useToast } from "vue-toastification" import { useToast } from "vue-toastification";
const toast = useToast() const toast = useToast();
export default { export default {
components: { components: {
@ -186,7 +186,7 @@ export default {
color: "#7C3AED" }, color: "#7C3AED" },
{ name: this.$t("Pink"), { name: this.$t("Pink"),
color: "#DB2777" }, color: "#DB2777" },
] ];
}, },
validateDraftTag() { validateDraftTag() {
let nameInvalid = false; let nameInvalid = false;
@ -227,7 +227,7 @@ export default {
invalid, invalid,
nameInvalid, nameInvalid,
valueInvalid, valueInvalid,
} };
}, },
}, },
mounted() { mounted() {
@ -243,7 +243,7 @@ export default {
if (res.ok) { if (res.ok) {
this.existingTags = res.tags; this.existingTags = res.tags;
} else { } else {
toast.error(res.msg) toast.error(res.msg);
} }
}); });
}, },
@ -277,7 +277,7 @@ export default {
name: this.newDraftTag.select.name, name: this.newDraftTag.select.name,
value: this.newDraftTag.value, value: this.newDraftTag.value,
new: true, new: true,
}) });
} }
} else { } else {
// Add new Tag // Add new Tag
@ -286,7 +286,7 @@ export default {
name: this.newDraftTag.name.trim(), name: this.newDraftTag.name.trim(),
value: this.newDraftTag.value, value: this.newDraftTag.value,
new: true, new: true,
}) });
} }
this.clearDraftTag(); this.clearDraftTag();
}, },
@ -348,7 +348,7 @@ export default {
if (tag.name == newTag.name && tag.color == newTag.color) { if (tag.name == newTag.name && tag.color == newTag.color) {
tag.id = newTagResult.id; tag.id = newTagResult.id;
} }
}) });
} else { } else {
tagId = newTag.id; tagId = newTag.id;
} }

View file

@ -22,33 +22,33 @@ export default {
return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%"; return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%";
} }
return this.$t("notAvailableShort"); return this.$t("notAvailableShort")
}, },
color() { color() {
if (this.lastHeartBeat.status === 0) { if (this.lastHeartBeat.status === 0) {
return "danger"; return "danger"
} }
if (this.lastHeartBeat.status === 1) { if (this.lastHeartBeat.status === 1) {
return "primary"; return "primary"
} }
if (this.lastHeartBeat.status === 2) { if (this.lastHeartBeat.status === 2) {
return "warning"; return "warning"
} }
return "secondary"; return "secondary"
}, },
lastHeartBeat() { lastHeartBeat() {
if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) { if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) {
return this.$root.lastHeartbeatList[this.monitor.id]; return this.$root.lastHeartbeatList[this.monitor.id]
} }
return { return {
status: -1, status: -1,
}; }
}, },
className() { className() {
@ -59,7 +59,7 @@ export default {
return ""; return "";
}, },
}, },
}; }
</script> </script>
<style> <style>

View file

@ -1,20 +1,21 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="apprise-url" class="form-label">Apprise URL</label> <label for="apprise-url" class="form-label">{{ $t("Apprise URL") }}</label>
<input id="apprise-url" v-model="$parent.notification.appriseURL" type="text" class="form-control" required> <input id="apprise-url" v-model="$parent.notification.appriseURL" type="text" class="form-control" required>
<div class="form-text"> <div class="form-text">
<p>Example: twilio://AccountSid:AuthToken@FromPhoneNo</p> <p>{{ $t("Example:", ["twilio://AccountSid:AuthToken@FromPhoneNo"]) }}</p>
<p> <i18n-t tag="p" keypath="Read more:">
Read more: <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a> <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
</p> </i18n-t>
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<p> <i18n-t tag="p" keypath="Status:">
Status: <span v-if="appriseInstalled" class="text-primary">{{ $t("appriseInstalled") }}</span>
<span v-if="appriseInstalled" class="text-primary">Apprise is installed</span> <i18n-t v-else tag="span" keypath="appriseNotInstalled" class="text-danger">
<span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise" target="_blank">Read more</a></span> <a href="https://github.com/caronc/apprise" target="_blank">{{ $t("Read more") }}</a>
</p> </i18n-t>
</i18n-t>
</div> </div>
</template> </template>
@ -23,12 +24,12 @@ export default {
data() { data() {
return { return {
appriseInstalled: false appriseInstalled: false
} };
}, },
mounted() { mounted() {
this.$root.getSocket().emit("checkApprise", (installed) => { this.$root.getSocket().emit("checkApprise", (installed) => {
this.appriseInstalled = installed; this.appriseInstalled = installed;
}) });
}, },
} };
</script> </script>

View file

@ -1,20 +1,19 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="discord-webhook-url" class="form-label">Discord Webhook URL</label> <label for="discord-webhook-url" class="form-label">{{ $t("Discord Webhook URL") }}</label>
<input id="discord-webhook-url" v-model="$parent.notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false"> <input id="discord-webhook-url" v-model="$parent.notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false">
<div class="form-text"> <div class="form-text">
You can get this by going to Server Settings -> Integrations -> Create Webhook {{ $t("wayToGetDiscordURL") }}
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="discord-username" class="form-label">Bot Display Name</label> <label for="discord-username" class="form-label">{{ $t("Bot Display Name") }}</label>
<input id="discord-username" v-model="$parent.notification.discordUsername" type="text" class="form-control" autocomplete="false" :placeholder="$root.appName"> <input id="discord-username" v-model="$parent.notification.discordUsername" type="text" class="form-control" autocomplete="false" :placeholder="$root.appName">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="discord-prefix-message" class="form-label">Prefix Custom Message</label> <label for="discord-prefix-message" class="form-label">{{ $t("Prefix Custom Message") }}</label>
<input id="discord-prefix-message" v-model="$parent.notification.discordPrefixMessage" type="text" class="form-control" autocomplete="false" placeholder="Hello @everyone is..."> <input id="discord-prefix-message" v-model="$parent.notification.discordPrefixMessage" type="text" class="form-control" autocomplete="false" :placeholder="$t('Hello @everyone is...')">
</div> </div>
</template> </template>

View file

@ -1,17 +1,17 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="gotify-application-token" class="form-label">Application Token</label> <label for="gotify-application-token" class="form-label">{{ $t("Application Token") }}</label>
<HiddenInput id="gotify-application-token" v-model="$parent.notification.gotifyapplicationToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="gotify-application-token" v-model="$parent.notification.gotifyapplicationToken" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="gotify-server-url" class="form-label">Server URL</label> <label for="gotify-server-url" class="form-label">{{ $t("Server URL") }}</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input id="gotify-server-url" v-model="$parent.notification.gotifyserverurl" type="text" class="form-control" required> <input id="gotify-server-url" v-model="$parent.notification.gotifyserverurl" type="text" class="form-control" required>
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="gotify-priority" class="form-label">Priority</label> <label for="gotify-priority" class="form-label">{{ $t("Priority") }}</label>
<input id="gotify-priority" v-model="$parent.notification.gotifyPriority" type="number" class="form-control" required min="0" max="10" step="1"> <input id="gotify-priority" v-model="$parent.notification.gotifyPriority" type="number" class="form-control" required min="0" max="10" step="1">
</div> </div>
</template> </template>

View file

@ -1,21 +1,21 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="line-channel-access-token" class="form-label">Channel access token</label> <label for="line-channel-access-token" class="form-label">{{ $t("Channel access token") }}</label>
<HiddenInput id="line-channel-access-token" v-model="$parent.notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="line-channel-access-token" v-model="$parent.notification.lineChannelAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<div class="form-text"> <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
Line Developers Console - <b>Basic Settings</b> <b>{{ $t("Basic Settings") }}</b>
</div> </i18n-t>
<div class="mb-3" style="margin-top: 12px;"> <div class="mb-3" style="margin-top: 12px;">
<label for="line-user-id" class="form-label">User ID</label> <label for="line-user-id" class="form-label">User ID</label>
<input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required> <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
</div> </div>
<div class="form-text"> <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
Line Developers Console - <b>Messaging API</b> <b>{{ $t("Messaging API") }}</b>
</div> </i18n-t>
<div class="form-text" style="margin-top: 8px;"> <i18n-t tag="div" keypath="wayToGetLineChannelToken" class="form-text" style="margin-top: 8px;">
First access the <a href="https://developers.line.biz/console/" target="_blank">Line Developers Console</a>, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items. <a href="https://developers.line.biz/console/" target="_blank">{{ $t("Line Developers Console") }}</a>
</div> </i18n-t>
</template> </template>
<script> <script>
@ -25,5 +25,5 @@ export default {
components: { components: {
HiddenInput, HiddenInput,
}, },
} };
</script> </script>

View file

@ -1,9 +1,9 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="lunasea-device" class="form-label">LunaSea Device ID<span style="color: red;"><sup>*</sup></span></label> <label for="lunasea-device" class="form-label">{{ $t("LunaSea Device ID") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="lunasea-device" v-model="$parent.notification.lunaseaDevice" type="text" class="form-control" required> <input id="lunasea-device" v-model="$parent.notification.lunaseaDevice" type="text" class="form-control" required>
<div class="form-text"> <div class="form-text">
<p><span style="color: red;"><sup>*</sup></span>Required</p> <p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,32 +1,32 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="mattermost-webhook-url" class="form-label">Webhook URL<span style="color:red;"><sup>*</sup></span></label> <label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color:red;"><sup>*</sup></span></label>
<input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required> <input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required>
<label for="mattermost-username" class="form-label">Username</label> <label for="mattermost-username" class="form-label">{{ $t("Username") }}</label>
<input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control"> <input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control">
<label for="mattermost-iconurl" class="form-label">Icon URL</label> <label for="mattermost-iconurl" class="form-label">{{ $t("Icon URL") }}</label>
<input id="mattermost-iconurl" v-model="$parent.notification.mattermosticonurl" type="text" class="form-control"> <input id="mattermost-iconurl" v-model="$parent.notification.mattermosticonurl" type="text" class="form-control">
<label for="mattermost-iconemo" class="form-label">Icon Emoji</label> <label for="mattermost-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
<input id="mattermost-iconemo" v-model="$parent.notification.mattermosticonemo" type="text" class="form-control"> <input id="mattermost-iconemo" v-model="$parent.notification.mattermosticonemo" type="text" class="form-control">
<label for="mattermost-channel" class="form-label">Channel Name</label> <label for="mattermost-channel" class="form-label">{{ $t("Channel Name") }}</label>
<input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control"> <input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control">
<div class="form-text"> <div class="form-text">
<span style="color:red;"><sup>*</sup></span>Required <span style="color:red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
<a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
</i18n-t>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
More info about webhooks on: <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a> {{ $t("aboutMattermostChannelName") }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
You can override the default channel that webhook posts to by entering the channel name into "Channel Name" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel {{ $t("aboutKumaURL") }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
If you leave the Uptime Kuma URL field blank, it will default to the Project Github page. {{ $t("aboutIconURL") }}
</p>
<p style="margin-top: 8px;">
You can provide a link to a picture in "Icon URL" to override the default profile picture. Will not be used if Icon Emoji is set.
</p>
<p style="margin-top: 8px;">
Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a> Note: emoji takes preference over Icon URL.
</p> </p>
<i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
<a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</i18n-t>
</div> </div>
</div> </div>
</template> </template>

View file

@ -16,27 +16,27 @@
<input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required> <input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="octopush-type-sms" class="form-label">SMS Type</label> <label for="octopush-type-sms" class="form-label">{{ $t("SMS Type") }}</label>
<select id="octopush-type-sms" v-model="$parent.notification.octopushSMSType" class="form-select"> <select id="octopush-type-sms" v-model="$parent.notification.octopushSMSType" class="form-select">
<option value="sms_premium">Premium (Fast - recommended for alerting)</option> <option value="sms_premium">{{ $t("octopushTypePremium") }}</option>
<option value="sms_low_cost">Low Cost (Slow, sometimes blocked by operator)</option> <option value="sms_low_cost">{{ $t("octopushTypeLowCost") }}</option>
</select> </select>
<div class="form-text"> <i18n-t tag="div" keypath="Check octopush prices" class="form-text">
Check octopush prices <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>. <a href="https://octopush.com/tarifs-sms-international/" target="_blank">https://octopush.com/tarifs-sms-international/</a>
</div> </i18n-t>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="octopush-phone-number" class="form-label">Phone number (intl format, eg : +33612345678) </label> <label for="octopush-phone-number" class="form-label">{{ $t("octopushPhoneNumber") }}</label>
<input id="octopush-phone-number" v-model="$parent.notification.octopushPhoneNumber" type="text" class="form-control" required> <input id="octopush-phone-number" v-model="$parent.notification.octopushPhoneNumber" type="text" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="octopush-sender-name" class="form-label">SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)</label> <label for="octopush-sender-name" class="form-label">{{ $t("octopushSMSSender") }}</label>
<input id="octopush-sender-name" v-model="$parent.notification.octopushSenderName" type="text" minlength="3" maxlength="11" class="form-control"> <input id="octopush-sender-name" v-model="$parent.notification.octopushSenderName" type="text" minlength="3" maxlength="11" class="form-control">
</div> </div>
<p style="margin-top: 8px;"> <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
More info on: <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a> <a href="https://octopush.com/api-sms-documentation/envoi-de-sms/" target="_blank">https://octopush.com/api-sms-documentation/envoi-de-sms/</a>
</p> </i18n-t>
</template> </template>
<script> <script>
@ -46,5 +46,5 @@ export default {
components: { components: {
HiddenInput, HiddenInput,
}, },
} };
</script> </script>

View file

@ -1,12 +1,12 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="pushbullet-access-token" class="form-label">Access Token</label> <label for="pushbullet-access-token" class="form-label">{{ $t("Access Token") }}</label>
<HiddenInput id="pushbullet-access-token" v-model="$parent.notification.pushbulletAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushbullet-access-token" v-model="$parent.notification.pushbulletAccessToken" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<p style="margin-top: 8px;"> <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
More info on: <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a> <a href="https://docs.pushbullet.com" target="_blank">https://docs.pushbullet.com</a>
</p> </i18n-t>
</template> </template>
<script> <script>
@ -16,5 +16,5 @@ export default {
components: { components: {
HiddenInput, HiddenInput,
}, },
} };
</script> </script>

View file

@ -1,14 +1,14 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="pushover-user" class="form-label">User Key<span style="color: red;"><sup>*</sup></span></label> <label for="pushover-user" class="form-label">{{ $t("User Key") }}<span style="color: red;"><sup>*</sup></span></label>
<HiddenInput id="pushover-user" v-model="$parent.notification.pushoveruserkey" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushover-user" v-model="$parent.notification.pushoveruserkey" :required="true" autocomplete="one-time-code"></HiddenInput>
<label for="pushover-app-token" class="form-label">Application Token<span style="color: red;"><sup>*</sup></span></label> <label for="pushover-app-token" class="form-label">{{ $t("Application Token") }}<span style="color: red;"><sup>*</sup></span></label>
<HiddenInput id="pushover-app-token" v-model="$parent.notification.pushoverapptoken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushover-app-token" v-model="$parent.notification.pushoverapptoken" :required="true" autocomplete="one-time-code"></HiddenInput>
<label for="pushover-device" class="form-label">Device</label> <label for="pushover-device" class="form-label">{{ $t("Device") }}</label>
<input id="pushover-device" v-model="$parent.notification.pushoverdevice" type="text" class="form-control"> <input id="pushover-device" v-model="$parent.notification.pushoverdevice" type="text" class="form-control">
<label for="pushover-device" class="form-label">Message Title</label> <label for="pushover-device" class="form-label">{{ $t("Message Title") }}</label>
<input id="pushover-title" v-model="$parent.notification.pushovertitle" type="text" class="form-control"> <input id="pushover-title" v-model="$parent.notification.pushovertitle" type="text" class="form-control">
<label for="pushover-priority" class="form-label">Priority</label> <label for="pushover-priority" class="form-label">{{ $t("Priority") }}</label>
<select id="pushover-priority" v-model="$parent.notification.pushoverpriority" class="form-select"> <select id="pushover-priority" v-model="$parent.notification.pushoverpriority" class="form-select">
<option>-2</option> <option>-2</option>
<option>-1</option> <option>-1</option>
@ -16,7 +16,7 @@
<option>1</option> <option>1</option>
<option>2</option> <option>2</option>
</select> </select>
<label for="pushover-sound" class="form-label">Notification Sound</label> <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
<select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select"> <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
<option>pushover</option> <option>pushover</option>
<option>bike</option> <option>bike</option>
@ -42,15 +42,15 @@
<option>none</option> <option>none</option>
</select> </select>
<div class="form-text"> <div class="form-text">
<span style="color: red;"><sup>*</sup></span>Required <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
<a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a>
</i18n-t>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
More info on: <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a> {{ $t("pushoverDesc1") }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour. {{ $t("pushoverDesc2") }}
</p>
<p style="margin-top: 8px;">
If you want to send notifications to different devices, fill out Device field.
</p> </p>
</div> </div>
</div> </div>

View file

@ -10,9 +10,9 @@
<HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
</div> </div>
<p style="margin-top: 8px;"> <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
More info on: <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a> <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a>
</p> </i18n-t>
</template> </template>
<script> <script>
@ -22,5 +22,5 @@ export default {
components: { components: {
HiddenInput, HiddenInput,
}, },
} };
</script> </script>

View file

@ -1,29 +1,27 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="rocket-webhook-url" class="form-label">Webhook URL<span style="color: red;"><sup>*</sup></span></label> <label for="rocket-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="rocket-webhook-url" v-model="$parent.notification.rocketwebhookURL" type="text" class="form-control" required> <input id="rocket-webhook-url" v-model="$parent.notification.rocketwebhookURL" type="text" class="form-control" required>
<label for="rocket-username" class="form-label">Username</label> <label for="rocket-username" class="form-label">{{ $t("Username") }}</label>
<input id="rocket-username" v-model="$parent.notification.rocketusername" type="text" class="form-control"> <input id="rocket-username" v-model="$parent.notification.rocketusername" type="text" class="form-control">
<label for="rocket-iconemo" class="form-label">Icon Emoji</label> <label for="rocket-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
<input id="rocket-iconemo" v-model="$parent.notification.rocketiconemo" type="text" class="form-control"> <input id="rocket-iconemo" v-model="$parent.notification.rocketiconemo" type="text" class="form-control">
<label for="rocket-channel" class="form-label">Channel Name</label> <label for="rocket-channel" class="form-label">{{ $t("Channel Name") }}</label>
<input id="rocket-channel-name" v-model="$parent.notification.rocketchannel" type="text" class="form-control"> <input id="rocket-channel-name" v-model="$parent.notification.rocketchannel" type="text" class="form-control">
<label for="rocket-button-url" class="form-label">Uptime Kuma URL</label>
<input id="rocket-button" v-model="$parent.notification.rocketbutton" type="text" class="form-control">
<div class="form-text"> <div class="form-text">
<span style="color: red;"><sup>*</sup></span>Required <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
<a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a>
</i18n-t>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
More info about webhooks on: <a href="https://docs.rocket.chat/guides/administration/administration/integrations" target="_blank">https://api.slack.com/messaging/webhooks</a> {{ $t("aboutChannelName", [$t("rocket.chat")]) }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
Enter the channel name on Rocket.chat Channel Name field if you want to bypass the webhook channel. Ex: #other-channel {{ $t("aboutKumaURL") }}
</p>
<p style="margin-top: 8px;">
If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
</p>
<p style="margin-top: 8px;">
Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</p> </p>
<i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
<a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</i18n-t>
</div> </div>
</div> </div>
</template> </template>

View file

@ -12,8 +12,8 @@
<div class="mb-3"> <div class="mb-3">
<label for="secure" class="form-label">Secure</label> <label for="secure" class="form-label">Secure</label>
<select id="secure" v-model="$parent.notification.smtpSecure" class="form-select"> <select id="secure" v-model="$parent.notification.smtpSecure" class="form-select">
<option :value="false">None / STARTTLS (25, 587)</option> <option :value="false">{{ $t("secureOptionNone") }}</option>
<option :value="true">TLS (465)</option> <option :value="true">{{ $t("secureOptionTLS") }}</option>
</select> </select>
</div> </div>
@ -21,7 +21,7 @@
<div class="form-check"> <div class="form-check">
<input id="ignore-tls-error" v-model="$parent.notification.smtpIgnoreTLSError" class="form-check-input" type="checkbox" value=""> <input id="ignore-tls-error" v-model="$parent.notification.smtpIgnoreTLSError" class="form-check-input" type="checkbox" value="">
<label class="form-check-label" for="ignore-tls-error"> <label class="form-check-label" for="ignore-tls-error">
Ignore TLS Error {{ $t("Ignore TLS Error") }}
</label> </label>
</div> </div>
</div> </div>
@ -37,24 +37,24 @@
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="from-email" class="form-label">From Email</label> <label for="from-email" class="form-label">{{ $t("From Email") }}</label>
<input id="from-email" v-model="$parent.notification.smtpFrom" type="text" class="form-control" required autocomplete="false" placeholder="&quot;Uptime Kuma&quot; &lt;example@kuma.pet&gt;"> <input id="from-email" v-model="$parent.notification.smtpFrom" type="text" class="form-control" required autocomplete="false" placeholder="&quot;Uptime Kuma&quot; &lt;example@kuma.pet&gt;">
<div class="form-text"> <div class="form-text">
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="to-email" class="form-label">To Email</label> <label for="to-email" class="form-label">{{ $t("To Email") }}</label>
<input id="to-email" v-model="$parent.notification.smtpTo" type="text" class="form-control" autocomplete="false" placeholder="example2@kuma.pet, example3@kuma.pet" :required="!hasRecipient"> <input id="to-email" v-model="$parent.notification.smtpTo" type="text" class="form-control" autocomplete="false" placeholder="example2@kuma.pet, example3@kuma.pet" :required="!hasRecipient">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="to-cc" class="form-label">CC</label> <label for="to-cc" class="form-label">{{ $t("smtpCC") }}</label>
<input id="to-cc" v-model="$parent.notification.smtpCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient"> <input id="to-cc" v-model="$parent.notification.smtpCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="to-bcc" class="form-label">BCC</label> <label for="to-bcc" class="form-label">{{ $t("smtpBCC") }}</label>
<input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient"> <input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient">
</div> </div>
</template> </template>

View file

@ -1,23 +1,25 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="signal-url" class="form-label">Post URL</label> <label for="signal-url" class="form-label">{{ $t("Post URL") }}</label>
<input id="signal-url" v-model="$parent.notification.signalURL" type="url" pattern="https?://.+" class="form-control" required> <input id="signal-url" v-model="$parent.notification.signalURL" type="url" pattern="https?://.+" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="signal-number" class="form-label">Number</label> <label for="signal-number" class="form-label">{{ $t("Number") }}</label>
<input id="signal-number" v-model="$parent.notification.signalNumber" type="text" class="form-control" required> <input id="signal-number" v-model="$parent.notification.signalNumber" type="text" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="signal-recipients" class="form-label">Recipients</label> <label for="signal-recipients" class="form-label">{{ $t("Recipients") }}</label>
<input id="signal-recipients" v-model="$parent.notification.signalRecipients" type="text" class="form-control" required> <input id="signal-recipients" v-model="$parent.notification.signalRecipients" type="text" class="form-control" required>
<div class="form-text"> <div class="form-text">
You need to have a signal client with REST API. <p style="margin-top: 8px;">
{{ $t("needSignalAPI") }}
</p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
You can check this url to view how to setup one: {{ $t("wayToCheckSignalURL") }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
@ -25,7 +27,7 @@
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
IMPORTANT: You cannot mix groups and numbers in recipients! {{ $t("signalImportant") }}
</p> </p>
</div> </div>
</div> </div>

View file

@ -1,29 +1,28 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="slack-webhook-url" class="form-label">Webhook URL<span style="color: red;"><sup>*</sup></span></label> <label for="slack-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="slack-webhook-url" v-model="$parent.notification.slackwebhookURL" type="text" class="form-control" required> <input id="slack-webhook-url" v-model="$parent.notification.slackwebhookURL" type="text" class="form-control" required>
<label for="slack-username" class="form-label">Username</label> <label for="slack-username" class="form-label">{{ $t("Username") }}</label>
<input id="slack-username" v-model="$parent.notification.slackusername" type="text" class="form-control"> <input id="slack-username" v-model="$parent.notification.slackusername" type="text" class="form-control">
<label for="slack-iconemo" class="form-label">Icon Emoji</label> <label for="slack-iconemo" class="form-label">{{ $t("Icon Emoji") }}</label>
<input id="slack-iconemo" v-model="$parent.notification.slackiconemo" type="text" class="form-control"> <input id="slack-iconemo" v-model="$parent.notification.slackiconemo" type="text" class="form-control">
<label for="slack-channel" class="form-label">Channel Name</label> <label for="slack-channel" class="form-label">{{ $t("Channel Name") }}</label>
<input id="slack-channel-name" v-model="$parent.notification.slackchannel" type="text" class="form-control"> <input id="slack-channel-name" v-model="$parent.notification.slackchannel" type="text" class="form-control">
<label for="slack-button-url" class="form-label">Uptime Kuma URL</label>
<input id="slack-button" v-model="$parent.notification.slackbutton" type="text" class="form-control">
<div class="form-text"> <div class="form-text">
<span style="color: red;"><sup>*</sup></span>Required <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
<a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a>
</i18n-t>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
More info about webhooks on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a> {{ $t("aboutChannelName", [$t("slack")]) }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
Enter the channel name on Slack Channel Name field if you want to bypass the webhook channel. Ex: #other-channel {{ $t("aboutKumaURL") }}
</p>
<p style="margin-top: 8px;">
If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
</p>
<p style="margin-top: 8px;">
Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</p> </p>
<i18n-t tag="p" keypath="emojiCheatSheet" style="margin-top: 8px;">
<a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a>
</i18n-t>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="teams-webhookurl" class="form-label">Webhook URL</label> <label for="teams-webhookurl" class="form-label">{{ $t("Webhook URL") }}</label>
<input <input
id="teams-webhookurl" id="teams-webhookurl"
v-model="$parent.notification.webhookUrl" v-model="$parent.notification.webhookUrl"
@ -8,12 +8,11 @@
class="form-control" class="form-control"
required required
/> />
<div class="form-text"> <i18n-t tag="div" keypath="wayToGetTeamsURL" class="form-text">
You can learn how to create a webhook url
<a <a
href="https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook" href="https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook"
target="_blank" target="_blank"
>here</a>. >{{ $t("here") }}</a>
</div> </i18n-t>
</div> </div>
</template> </template>

View file

@ -1,14 +1,14 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="telegram-bot-token" class="form-label">Bot Token</label> <label for="telegram-bot-token" class="form-label">{{ $t("Bot Token") }}</label>
<HiddenInput id="telegram-bot-token" v-model="$parent.notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="telegram-bot-token" v-model="$parent.notification.telegramBotToken" :required="true" autocomplete="one-time-code"></HiddenInput>
<div class="form-text"> <div class="form-text">
You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>. {{ $t("You can get a token from") }} <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="telegram-chat-id" class="form-label">Chat ID</label> <label for="telegram-chat-id" class="form-label">{{ $t("Chat ID") }}</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input id="telegram-chat-id" v-model="$parent.notification.telegramChatID" type="text" class="form-control" required> <input id="telegram-chat-id" v-model="$parent.notification.telegramChatID" type="text" class="form-control" required>
@ -18,10 +18,10 @@
</div> </div>
<div class="form-text"> <div class="form-text">
Support Direct Chat / Group / Channel's Chat ID {{ $t("supportTelegramChatID") }}
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
You can get your chat id by sending message to the bot and go to this url to view the chat_id: {{ $t("wayToGetTelegramChatID") }}
</p> </p>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
@ -49,7 +49,7 @@ export default {
}, },
computed: { computed: {
telegramGetUpdatesURL() { telegramGetUpdatesURL() {
let token = "<YOUR BOT TOKEN HERE>" let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`
if (this.$parent.notification.telegramBotToken) { if (this.$parent.notification.telegramBotToken) {
token = this.$parent.notification.telegramBotToken; token = this.$parent.notification.telegramBotToken;
@ -71,11 +71,11 @@ export default {
} else if (update.message) { } else if (update.message) {
this.notification.telegramChatID = update.message.chat.id; this.notification.telegramChatID = update.message.chat.id;
} else { } else {
throw new Error("Chat ID is not found, please send a message to this bot first") throw new Error(this.$t("chatIDNotFound"))
} }
} else { } else {
throw new Error("Chat ID is not found, please send a message to this bot first") throw new Error(this.$t("chatIDNotFound"))
} }
} catch (error) { } catch (error) {

View file

@ -1,11 +1,11 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="webhook-url" class="form-label">Post URL</label> <label for="webhook-url" class="form-label">{{ $t("Post URL") }}</label>
<input id="webhook-url" v-model="$parent.notification.webhookURL" type="url" pattern="https?://.+" class="form-control" required> <input id="webhook-url" v-model="$parent.notification.webhookURL" type="url" pattern="https?://.+" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="webhook-content-type" class="form-label">Content Type</label> <label for="webhook-content-type" class="form-label">{{ $t("Content Type") }}</label>
<select id="webhook-content-type" v-model="$parent.notification.webhookContentType" class="form-select" required> <select id="webhook-content-type" v-model="$parent.notification.webhookContentType" class="form-select" required>
<option value="json"> <option value="json">
application/json application/json
@ -17,7 +17,12 @@
<div class="form-text"> <div class="form-text">
<p>"application/json" is good for any modern http servers such as express.js</p> <p>"application/json" is good for any modern http servers such as express.js</p>
<p>"multipart/form-data" is good for PHP, you just need to parse the json by <strong>json_decode($_POST['data'])</strong></p> <i18n-t tag="p" keypath="webhookFormDataDesc">
<template #multipart>"multipart/form-data"</template>
<template #decodeFunction>
<strong>json_decode($_POST['data'])</strong>
</template>
</i18n-t>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,12 +1,10 @@
import { createI18n } from "vue-i18n"; import { createI18n } from "vue-i18n/index";
import bgBG from "./languages/bg-BG";
import daDK from "./languages/da-DK"; import daDK from "./languages/da-DK";
import deDE from "./languages/de-DE"; import deDE from "./languages/de-DE";
import en from "./languages/en"; import en from "./languages/en";
import fa from "./languages/fa";
import esEs from "./languages/es-ES"; import esEs from "./languages/es-ES";
import ptBR from "./languages/pt-BR";
import etEE from "./languages/et-EE"; import etEE from "./languages/et-EE";
import fa from "./languages/fa";
import frFR from "./languages/fr-FR"; import frFR from "./languages/fr-FR";
import hu from "./languages/hu"; import hu from "./languages/hu";
import itIT from "./languages/it-IT"; import itIT from "./languages/it-IT";
@ -14,11 +12,13 @@ import ja from "./languages/ja";
import koKR from "./languages/ko-KR"; import koKR from "./languages/ko-KR";
import nlNL from "./languages/nl-NL"; import nlNL from "./languages/nl-NL";
import pl from "./languages/pl"; import pl from "./languages/pl";
import ptBR from "./languages/pt-BR";
import bgBG from "./languages/bg-BG";
import ruRU from "./languages/ru-RU"; import ruRU from "./languages/ru-RU";
import sr from "./languages/sr"; import sr from "./languages/sr";
import srLatn from "./languages/sr-latn"; import srLatn from "./languages/sr-latn";
import trTR from "./languages/tr-TR";
import svSE from "./languages/sv-SE"; import svSE from "./languages/sv-SE";
import trTR from "./languages/tr-TR";
import zhCN from "./languages/zh-CN"; import zhCN from "./languages/zh-CN";
import zhHK from "./languages/zh-HK"; import zhHK from "./languages/zh-HK";
@ -48,12 +48,13 @@ const languageList = {
}; };
const rtlLangs = ["fa"]; const rtlLangs = ["fa"];
export const currentLocale = () => localStorage.locale || "en"; export const currentLocale = () => localStorage.locale || "en";
export const localeDirection = () => { export const localeDirection = () => {
return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr" return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr";
} };
export const i18n = createI18n({ export const i18n = createI18n({
locale: currentLocale(), locale: currentLocale(),
fallbackLocale: "en", fallbackLocale: "en",

View file

@ -1,6 +1,6 @@
export default { export default {
languageName: "Български", languageName: "Български",
checkEverySecond: "Проверявай на всеки {0} секунди.", checkEverySecond: "Ще се извършва на всеки {0} секунди.",
retryCheckEverySecond: "Повторен опит на всеки {0} секунди.", retryCheckEverySecond: "Повторен опит на всеки {0} секунди.",
retriesDescription: "Максимакен брой опити преди услугата да бъде маркирана като недостъпна и да бъде изпратено известие", retriesDescription: "Максимакен брой опити преди услугата да бъде маркирана като недостъпна и да бъде изпратено известие",
ignoreTLSError: "Игнорирай TLS/SSL грешки за HTTPS уебсайтове", ignoreTLSError: "Игнорирай TLS/SSL грешки за HTTPS уебсайтове",
@ -9,19 +9,19 @@ export default {
acceptedStatusCodesDescription: "Изберете статус кодове, които се считат за успешен отговор.", acceptedStatusCodesDescription: "Изберете статус кодове, които се считат за успешен отговор.",
passwordNotMatchMsg: "Повторената парола не съвпада.", passwordNotMatchMsg: "Повторената парола не съвпада.",
notificationDescription: "Моля, задайте известието към монитор(и), за да функционира.", notificationDescription: "Моля, задайте известието към монитор(и), за да функционира.",
keywordDescription: "Търсете ключова дума в обикновен html или JSON отговор - чувствителна е към регистъра", keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра",
pauseDashboardHome: "Пауза", pauseDashboardHome: "Пауза",
deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?", deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?",
deleteNotificationMsg: "Наистина ли желаете да изтриете известието за всички монитори?", deleteNotificationMsg: "Наистина ли желаете да изтриете това известяване за всички монитори?",
resoverserverDescription: "Cloudflare е сървърът по подразбиране, можете да промените сървъра по всяко време.", resoverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.",
rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате", rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате",
pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?", pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?",
enableDefaultNotificationDescription: "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да изключите известието за всеки отделен монитор.", enableDefaultNotificationDescription: "За всеки нов монитор това известяване ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.",
clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?", clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?",
clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?", clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?",
confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?", confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?",
importHandleDescription: "Изберете 'Пропусни съществуващите', ако искате да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.", importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известяване със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известяване.",
confirmImportMsg: "Сигурни ли сте за импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.", confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.",
twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи", twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи",
tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.", tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.",
confirmEnableTwoFAMsg: "Сигурни ли сте, че желаете да активирате 2FA?", confirmEnableTwoFAMsg: "Сигурни ли сте, че желаете да активирате 2FA?",
@ -34,32 +34,32 @@ export default {
Theme: "Тема", Theme: "Тема",
General: "Общи", General: "Общи",
Version: "Версия", Version: "Версия",
"Check Update On GitHub": "Провери за актуализация в GitHub", "Check Update On GitHub": "Проверка за актуализация в GitHub",
List: "Списък", List: "Списък",
Add: "Добави", Add: "Добави",
"Add New Monitor": "Добави монитор", "Add New Monitor": "Добави монитор",
"Quick Stats": "Кратка статистика", "Quick Stats": "Кратка статистика",
Up: "Достъпни", Up: "Достъпен",
Down: "Недостъпни", Down: "Недостъпен",
Pending: "В изчакване", Pending: "Изчаква",
Unknown: "Неизвестни", Unknown: "Неизвестен",
Pause: "В пауза", Pause: "Пауза",
Name: "Име", Name: "Име",
Status: "Статус", Status: "Статус",
DateTime: "Дата и час", DateTime: "Дата и час",
Message: "Съобщение", Message: "Отговор",
"No important events": "Няма важни събития", "No important events": "Няма важни събития",
Resume: "Възобнови", Resume: "Възобнови",
Edit: "Редактирай", Edit: "Редактирай",
Delete: "Изтрий", Delete: "Изтрий",
Current: "Текущ", Current: "Текущ",
Uptime: "Време на работа", Uptime: "Достъпност",
"Cert Exp.": "Вал. сертификат", "Cert Exp.": "Вал. сертификат",
days: "дни", days: "дни",
day: "ден", day: "ден",
"-day": "-ден", "-day": "-денa",
hour: "час", hour: "час",
"-hour": "-час", "-hour": "-часa",
Response: "Отговор", Response: "Отговор",
Ping: "Пинг", Ping: "Пинг",
"Monitor Type": "Монитор тип", "Monitor Type": "Монитор тип",
@ -78,7 +78,7 @@ export default {
Save: "Запази", Save: "Запази",
Notifications: "Известявания", Notifications: "Известявания",
"Not available, please setup.": "Не е налично. Моля, настройте.", "Not available, please setup.": "Не е налично. Моля, настройте.",
"Setup Notification": "Настройка за известяване", "Setup Notification": "Настройки за известявания",
Light: "Светла", Light: "Светла",
Dark: "Тъмна", Dark: "Тъмна",
Auto: "Автоматично", Auto: "Автоматично",
@ -98,9 +98,9 @@ export default {
"Disable Auth": "Изключи удостоверяване", "Disable Auth": "Изключи удостоверяване",
"Enable Auth": "Включи удостоверяване", "Enable Auth": "Включи удостоверяване",
Logout: "Изход от профила", Logout: "Изход от профила",
Leave: "Напускам", Leave: "Отказ",
"I understand, please disable": "Разбирам. Моля, изключи", "I understand, please disable": "Разбирам. Моля, изключи",
Confirm: "Потвърди", Confirm: "Потвърдете",
Yes: "Да", Yes: "Да",
No: "Не", No: "Не",
Username: "Потребител", Username: "Потребител",
@ -127,7 +127,7 @@ export default {
"Default enabled": "Включен по подразбиране", "Default enabled": "Включен по подразбиране",
"Apply on all existing monitors": "Приложи върху всички съществуващи монитори", "Apply on all existing monitors": "Приложи върху всички съществуващи монитори",
Create: "Създай", Create: "Създай",
"Clear Data": "Изчисти данни", "Clear Data": "Изтрий данни",
Events: "Събития", Events: "Събития",
Heartbeats: "Проверки", Heartbeats: "Проверки",
"Auto Get": "Автоматияно получаване", "Auto Get": "Автоматияно получаване",
@ -136,7 +136,7 @@ export default {
backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.",
alertNoFile: "Моля, изберете файл за импортиране.", alertNoFile: "Моля, изберете файл за импортиране.",
alertWrongFileType: "Моля, изберете JSON файл.", alertWrongFileType: "Моля, изберете JSON файл.",
"Clear all statistics": "Изчисти всички статистики", "Clear all statistics": "Изтрий цялата статистика",
"Skip existing": "Пропусни съществуващите", "Skip existing": "Пропусни съществуващите",
Overwrite: "Презапиши", Overwrite: "Презапиши",
Options: "Опции", Options: "Опции",
@ -152,7 +152,7 @@ export default {
Token: "Токен код", Token: "Токен код",
"Show URI": "Покажи URI", "Show URI": "Покажи URI",
Tags: "Етикети", Tags: "Етикети",
"Add New below or Select...": "Добави нов по-долу или избери...", "Add New below or Select...": "Добавете нов по-долу или изберете...",
"Tag with this name already exist.": "Етикет с това име вече съществува.", "Tag with this name already exist.": "Етикет с това име вече съществува.",
"Tag with this value already exist.": "Етикет с тази стойност вече съществува.", "Tag with this value already exist.": "Етикет с тази стойност вече съществува.",
color: "цвят", color: "цвят",
@ -171,11 +171,29 @@ export default {
"Entry Page": "Основна страница", "Entry Page": "Основна страница",
statusPageNothing: "Все още няма нищо тук. Моля, добавете група или монитор.", statusPageNothing: "Все още няма нищо тук. Моля, добавете група или монитор.",
"No Services": "Няма Услуги", "No Services": "Няма Услуги",
"All Systems Operational": "Всички системи функционират", "All Systems Operational": "Всички услуги са достъпни",
"Partially Degraded Service": "Частично влошена услуга", "Partially Degraded Service": "Част от услугите са недостъпни",
"Degraded Service": "Влошена услуга", "Degraded Service": "Всички услуги са недостъпни",
"Add Group": "Добави група", "Add Group": "Добави група",
"Add a monitor": "Добави монитор", "Add a monitor": "Добави монитор",
"Edit Status Page": "Редактирай статус страница", "Edit Status Page": "Редактиране Статус страница",
"Go to Dashboard": "Към Таблото", "Go to Dashboard": "Към Таблото",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Поддържа 50+ услуги за инвестяване)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"Status Page": "Статус страница",
}; };

View file

@ -1,5 +1,5 @@
export default { export default {
languageName: "Danish", languageName: "Danish (Danmark)",
Settings: "Indstillinger", Settings: "Indstillinger",
Dashboard: "Dashboard", Dashboard: "Dashboard",
"New Update": "Opdatering tilgængelig", "New Update": "Opdatering tilgængelig",
@ -170,7 +170,7 @@ export default {
"Avg. Ping": "Gns. Ping", "Avg. Ping": "Gns. Ping",
"Avg. Response": "Gns. Respons", "Avg. Response": "Gns. Respons",
"Entry Page": "Entry Side", "Entry Page": "Entry Side",
"statusPageNothing": "Intet her, tilføj venligst en Gruppe eller en Overvåger.", statusPageNothing: "Intet her, tilføj venligst en Gruppe eller en Overvåger.",
"No Services": "Ingen Tjenester", "No Services": "Ingen Tjenester",
"All Systems Operational": "Alle Systemer i Drift", "All Systems Operational": "Alle Systemer i Drift",
"Partially Degraded Service": "Delvist Forringet Service", "Partially Degraded Service": "Delvist Forringet Service",
@ -179,4 +179,22 @@ export default {
"Add a monitor": "Tilføj en Overvåger", "Add a monitor": "Tilføj en Overvåger",
"Edit Status Page": "Rediger Statusside", "Edit Status Page": "Rediger Statusside",
"Go to Dashboard": "Gå til Dashboard", "Go to Dashboard": "Gå til Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -1,5 +1,5 @@
export default { export default {
languageName: "German", languageName: "Deutsch (Deutschland)",
Settings: "Einstellungen", Settings: "Einstellungen",
Dashboard: "Dashboard", Dashboard: "Dashboard",
"New Update": "Update Verfügbar", "New Update": "Update Verfügbar",
@ -178,4 +178,22 @@ export default {
"Add a monitor": "Monitor hinzufügen", "Add a monitor": "Monitor hinzufügen",
"Edit Status Page": "Bearbeite Statusseite", "Edit Status Page": "Bearbeite Statusseite",
"Go to Dashboard": "Gehe zum Dashboard", "Go to Dashboard": "Gehe zum Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -178,21 +178,67 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
// Start notification form
defaultNotificationName: "My {notification} Alert ({number})",
here: "here",
"Required": "Required",
"telegram": "Telegram", "telegram": "Telegram",
"Bot Token": "Bot Token",
"You can get a token from": "You can get a token from",
"Chat ID": "Chat ID",
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
wayToGetTelegramChatID: "You can get your chat id by sending message to the bot and go to this url to view the chat_id:",
"YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
chatIDNotFound: "Chat ID is not found, please send a message to this bot first",
"webhook": "Webhook", "webhook": "Webhook",
"Post URL": "Post URL",
"Content Type": "Content Type",
webhookJsonDesc: "{0} is good for any modern http servers such as express.js",
webhookFormDataDesc: "{multipart} is good for PHP, you just need to parse the json by {decodeFunction}",
"smtp": "Email (SMTP)", "smtp": "Email (SMTP)",
secureOptionNone: "None / STARTTLS (25, 587)",
secureOptionTLS: "TLS (465)",
"Ignore TLS Error": "Ignore TLS Error",
"From Email": "From Email",
"To Email": "To Email",
smtpCC: "CC",
smtpBCC: "BCC",
"discord": "Discord", "discord": "Discord",
"Discord Webhook URL": "Discord Webhook URL",
wayToGetDiscordURL: "You can get this by going to Server Settings -> Integrations -> Create Webhook",
"Bot Display Name": "Bot Display Name",
"Prefix Custom Message": "Prefix Custom Message",
"Hello @everyone is...": "Hello {'@'}everyone is...",
"teams": "Microsoft Teams", "teams": "Microsoft Teams",
"Webhook URL": "Webhook URL",
wayToGetTeamsURL: "You can learn how to create a webhook url {0}.",
"signal": "Signal", "signal": "Signal",
"Number": "Number",
"Recipients": "Recipients",
needSignalAPI: "You need to have a signal client with REST API.",
wayToCheckSignalURL: "You can check this url to view how to setup one:",
signalImportant: "IMPORTANT: You cannot mix groups and numbers in recipients!",
"gotify": "Gotify", "gotify": "Gotify",
"Application Token": "Application Token",
"Server URL": "Server URL",
"Priority": "Priority",
"slack": "Slack", "slack": "Slack",
"Icon Emoji": "Icon Emoji",
"Channel Name": "Channel Name",
"Uptime Kuma URL": "Uptime Kuma URL",
aboutWebhooks: "More info about webhooks on: {0}",
aboutChannelName: "Enter the channel name on {0} Channel Name field if you want to bypass the webhook channel. Ex: #other-channel",
aboutKumaURL: "If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.",
emojiCheatSheet: "Emoji cheat sheet: {0}",
"rocket.chat": "Rocket.chat", "rocket.chat": "Rocket.chat",
"pushover": "Pushover", pushover: "Pushover",
"pushy": "Pushy", pushy: "Pushy",
"octopush": "Octopush", octopush: "Octopush",
"lunasea": "LunaSea", lunasea: "LunaSea",
"apprise": "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
"pushbullet": "Pushbullet", pushbullet: "Pushbullet",
"line": "Line Messenger", line: "Line Messenger",
"mattermost": "Mattermost", mattermost: "Mattermost",
// End notification form
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -16,7 +16,7 @@ export default {
rrtypeDescription: "Vali kirje tüüp, mida soovid jälgida.", rrtypeDescription: "Vali kirje tüüp, mida soovid jälgida.",
pauseMonitorMsg: "Kas soovid peatada seire?", pauseMonitorMsg: "Kas soovid peatada seire?",
Settings: "Seaded", Settings: "Seaded",
"Status Page": "Ülevaade", // hääletuse tulemus, teine: seisundileht, kolmas: Olukord/Olek "Status Page": "Ülevaade",
Dashboard: "Töölaud", Dashboard: "Töölaud",
"New Update": "Uuem tarkvara versioon on saadaval.", "New Update": "Uuem tarkvara versioon on saadaval.",
Language: "Keel", Language: "Keel",
@ -44,7 +44,7 @@ export default {
Edit: "Muuda", Edit: "Muuda",
Delete: "Eemalda", Delete: "Eemalda",
Current: "Hetkeseisund", Current: "Hetkeseisund",
Uptime: "Eluiga", // todo: launchpad? Uptime: "Eluiga",
"Cert Exp.": "Sert. aegumine", "Cert Exp.": "Sert. aegumine",
days: "päeva", days: "päeva",
day: "päev", day: "päev",
@ -109,7 +109,7 @@ export default {
"Create your admin account": "Admininstraatori konto loomine", "Create your admin account": "Admininstraatori konto loomine",
"Repeat Password": "korda salasõna", "Repeat Password": "korda salasõna",
respTime: "Reageerimisaeg (ms)", respTime: "Reageerimisaeg (ms)",
notAvailableShort: "N/A", // tõlkimata, umbkaudu rahvusvaheline termin peaks sobima piisavalt notAvailableShort: "N/A",
enableDefaultNotificationDescription: "Kõik järgnevalt lisatud seired kasutavad seda teavitusteenuset. Seiretelt võib teavitusteenuse ühekaupa eemaldada.", enableDefaultNotificationDescription: "Kõik järgnevalt lisatud seired kasutavad seda teavitusteenuset. Seiretelt võib teavitusteenuse ühekaupa eemaldada.",
clearEventsMsg: "Kas soovid seire kõik sündmused kustutada?", clearEventsMsg: "Kas soovid seire kõik sündmused kustutada?",
clearHeartbeatsMsg: "Kas soovid seire kõik tuksed kustutada?", clearHeartbeatsMsg: "Kas soovid seire kõik tuksed kustutada?",
@ -122,7 +122,7 @@ export default {
"Clear Data": "Eemalda andmed", "Clear Data": "Eemalda andmed",
Events: "Sündmused", Events: "Sündmused",
Heartbeats: "Tuksed", Heartbeats: "Tuksed",
"Auto Get": "Hangi automaatselt", // hangi? kõlab liiga otsetõlge "Auto Get": "Hangi automaatselt",
backupDescription: "Varunda kõik seired ja teavitused JSON faili.", backupDescription: "Varunda kõik seired ja teavitused JSON faili.",
backupDescription2: "PS: Varukoopia EI sisalda seirete ajalugu ja sündmustikku.", backupDescription2: "PS: Varukoopia EI sisalda seirete ajalugu ja sündmustikku.",
backupDescription3: "Varukoopiad sisaldavad teavitusteenusete pääsuvõtmeid.", backupDescription3: "Varukoopiad sisaldavad teavitusteenusete pääsuvõtmeid.",
@ -140,7 +140,7 @@ export default {
"Two Factor Authentication": "Kaksikautentimine", "Two Factor Authentication": "Kaksikautentimine",
Active: "kasutusel", Active: "kasutusel",
Inactive: "seadistamata", Inactive: "seadistamata",
Token: "kaksikautentimise kood", // needs to compensate for no title Token: "kaksikautentimise kood",
"Show URI": "Näita URId", "Show URI": "Näita URId",
"Clear all statistics": "Tühjenda ajalugu", "Clear all statistics": "Tühjenda ajalugu",
importHandleDescription: "'kombineeri' täiendab varukoopiast ja kirjutab üle samanimelised seireid ja teavitusteenused; 'lisa praegustele' jätab olemasolevad puutumata; 'asenda' kustutab ja asendab kõik seired ja teavitusteenused.", importHandleDescription: "'kombineeri' täiendab varukoopiast ja kirjutab üle samanimelised seireid ja teavitusteenused; 'lisa praegustele' jätab olemasolevad puutumata; 'asenda' kustutab ja asendab kõik seired ja teavitusteenused.",
@ -150,14 +150,14 @@ export default {
"Export Backup": "Varukoopia eksportimine", "Export Backup": "Varukoopia eksportimine",
"Skip existing": "lisa praegustele", "Skip existing": "lisa praegustele",
Overwrite: "asenda", Overwrite: "asenda",
Options: "Mestimisviis", // reusal of key would be chaos Options: "Mestimisviis",
"Keep both": "kombineeri", "Keep both": "kombineeri",
Tags: "Sildid", Tags: "Sildid",
"Add New below or Select...": "Leia või lisa all uus…", "Add New below or Select...": "Leia või lisa all uus…",
"Tag with this name already exist.": "Selle nimega silt on juba olemas.", "Tag with this name already exist.": "Selle nimega silt on juba olemas.",
"Tag with this value already exist.": "Selle väärtusega silt on juba olemas.", "Tag with this value already exist.": "Selle väärtusega silt on juba olemas.",
color: "värvus", color: "värvus",
"value (optional)": "väärtus (fakultatiivne)", // milline sõna! "value (optional)": "väärtus (fakultatiivne)",
Gray: "hall", Gray: "hall",
Red: "punane", Red: "punane",
Orange: "oranž", Orange: "oranž",
@ -167,7 +167,7 @@ export default {
Purple: "lilla", Purple: "lilla",
Pink: "roosa", Pink: "roosa",
"Search...": "Otsi…", "Search...": "Otsi…",
"Avg. Ping": "Keskmine ping", // pikk, aga nagunii kahel real "Avg. Ping": "Keskmine ping",
"Avg. Response": "Keskmine reaktsiooniaeg", "Avg. Response": "Keskmine reaktsiooniaeg",
"Entry Page": "Avaleht", "Entry Page": "Avaleht",
statusPageNothing: "Kippu ega kõppu; siia saab lisada seireid või -gruppe.", statusPageNothing: "Kippu ega kõppu; siia saab lisada seireid või -gruppe.",
@ -178,4 +178,22 @@ export default {
"Add Group": "Lisa grupp", "Add Group": "Lisa grupp",
"Edit Status Page": "Muuda lehte", "Edit Status Page": "Muuda lehte",
"Go to Dashboard": "Töölauale", "Go to Dashboard": "Töölauale",
checkEverySecond: "Kontrolli peale tõrget {0} sekundilise vahega.",
telegram: "Telegram",
webhook: "Webhook",
smtp: "elektronpost (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (vahendab üle 65 teavitusteenust)",
pushbullet: "Pushbullet",
line: "LINE",
mattermost: "Mattermost",
}; };

View file

@ -186,5 +186,22 @@ export default {
First: "اولین", First: "اولین",
Last: "آخرین", Last: "آخرین",
Info: "اطلاعات", Info: "اطلاعات",
"Powered By": "نیرو گرفته از", "Powered by": "نیرو گرفته از",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -170,7 +170,7 @@ export default {
"Avg. Ping": "Ping moyen", "Avg. Ping": "Ping moyen",
"Avg. Response": "Réponse moyenne", "Avg. Response": "Réponse moyenne",
"Entry Page": "Page d'accueil", "Entry Page": "Page d'accueil",
"statusPageNothing": "Rien ici, veuillez ajouter un groupe ou une sonde.", statusPageNothing: "Rien ici, veuillez ajouter un groupe ou une sonde.",
"No Services": "Aucun service", "No Services": "Aucun service",
"All Systems Operational": "Tous les systèmes sont opérationnels", "All Systems Operational": "Tous les systèmes sont opérationnels",
"Partially Degraded Service": "Service partiellement dégradé", "Partially Degraded Service": "Service partiellement dégradé",
@ -179,4 +179,22 @@ export default {
"Add a monitor": "Ajouter une sonde", "Add a monitor": "Ajouter une sonde",
"Edit Status Page": "Modifier la page de statut", "Edit Status Page": "Modifier la page de statut",
"Go to Dashboard": "Accéder au tableau de bord", "Go to Dashboard": "Accéder au tableau de bord",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -178,4 +178,22 @@ export default {
"Add a monitor": "Figyelő hozzáadása", "Add a monitor": "Figyelő hozzáadása",
"Edit Status Page": "Sátusz oldal szerkesztése", "Edit Status Page": "Sátusz oldal szerkesztése",
"Go to Dashboard": "Menj az irányítópulthoz", "Go to Dashboard": "Menj az irányítópulthoz",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
"Status Page": "Status Page",
}; };

View file

@ -169,7 +169,7 @@ export default {
"Avg. Ping": "Ping medio", "Avg. Ping": "Ping medio",
"Avg. Response": "Risposta media", "Avg. Response": "Risposta media",
"Entry Page": "Entry Page", "Entry Page": "Entry Page",
"statusPageNothing": "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.", statusPageNothing: "Non c'è nulla qui, aggiungere un gruppo oppure un monitoraggio.",
"No Services": "Nessun Servizio", "No Services": "Nessun Servizio",
"All Systems Operational": "Tutti i sistemi sono operativi", "All Systems Operational": "Tutti i sistemi sono operativi",
"Partially Degraded Service": "Servizio parzialmente degradato", "Partially Degraded Service": "Servizio parzialmente degradato",
@ -178,4 +178,22 @@ export default {
"Add a monitor": "Aggiungi un monitoraggio", "Add a monitor": "Aggiungi un monitoraggio",
"Edit Status Page": "Modifica pagina di stato", "Edit Status Page": "Modifica pagina di stato",
"Go to Dashboard": "Vai al Cruscotto", "Go to Dashboard": "Vai al Cruscotto",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -169,14 +169,32 @@ export default {
"Search...": "Szukaj...", "Search...": "Szukaj...",
"Avg. Ping": "Średni ping", "Avg. Ping": "Średni ping",
"Avg. Response": "Średnia odpowiedź", "Avg. Response": "Średnia odpowiedź",
"Entry Page": "Wejdź na stronę", "Entry Page": "Strona główna",
"statusPageNothing": "Nic tu nie ma, dodaj monitor lub grupę.", statusPageNothing: "Nic tu nie ma, dodaj grupę lub monitor.",
"No Services": "Brak usług", "No Services": "Brak usług",
"All Systems Operational": "Wszystkie systemy działają", "All Systems Operational": "Wszystkie systemy działają",
"Partially Degraded Service": "Częściowy błąd usługi", "Partially Degraded Service": "Częściowy błąd usługi",
"Degraded Service": "Błąd usługi", "Degraded Service": "Błąd usługi",
"Add Group": "Dodaj grupę", "Add Group": "Dodaj grupę",
"Add a monitor": "Dodaj monitoe", "Add a monitor": "Dodaj monitor",
"Edit Status Page": "Edytuj stronę statusu", "Edit Status Page": "Edytuj stronę statusu",
"Go to Dashboard": "Idź do panelu", "Go to Dashboard": "Idź do panelu",
"Status Page": "Strona statusu",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (obsługuje 50+ usług powiadamiania)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,21 @@ export default {
"Add a monitor": "Adicionar um monitor", "Add a monitor": "Adicionar um monitor",
"Edit Status Page": "Editar Página de Status", "Edit Status Page": "Editar Página de Status",
"Go to Dashboard": "Ir para a dashboard", "Go to Dashboard": "Ir para a dashboard",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -43,7 +43,7 @@ export default {
Delete: "Удалить", Delete: "Удалить",
Current: "Текущий", Current: "Текущий",
Uptime: "Аптайм", Uptime: "Аптайм",
"Cert Exp.": "Сертификат просрочен", "Cert Exp.": "Сертификат истекает",
days: "дней", days: "дней",
day: "день", day: "день",
"-day": " дней", "-day": " дней",
@ -180,8 +180,25 @@ export default {
"Edit Status Page": "Редактировать", "Edit Status Page": "Редактировать",
"Go to Dashboard": "Панель мониторов", "Go to Dashboard": "Панель мониторов",
"Status Page": "Статус сервисов", "Status Page": "Статус сервисов",
"Discard": "Отмена", Discard: "Отмена",
"Create Incident": "Создать инцидент", "Create Incident": "Создать инцидент",
"Switch to Dark Theme": "Тёмная тема", "Switch to Dark Theme": "Тёмная тема",
"Switch to Light Theme": "Светлая тема", "Switch to Light Theme": "Светлая тема",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -179,4 +179,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -178,4 +178,22 @@ export default {
"Add a monitor": "Add a monitor", "Add a monitor": "Add a monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Edit Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Go to Dashboard",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -170,7 +170,7 @@ export default {
"Avg. Ping": "平均Ping", "Avg. Ping": "平均Ping",
"Avg. Response": "平均响应", "Avg. Response": "平均响应",
"Entry Page": "入口页面", "Entry Page": "入口页面",
"statusPageNothing": "这里什么也没有,请添加一个分组或一个监控项。", statusPageNothing: "这里什么也没有,请添加一个分组或一个监控项。",
"No Services": "无服务", "No Services": "无服务",
"All Systems Operational": "所有服务运行正常", "All Systems Operational": "所有服务运行正常",
"Partially Degraded Service": "部分服务出现故障", "Partially Degraded Service": "部分服务出现故障",
@ -179,4 +179,22 @@ export default {
"Add a monitor": "添加监控项", "Add a monitor": "添加监控项",
"Edit Status Page": "编辑状态页", "Edit Status Page": "编辑状态页",
"Go to Dashboard": "前往仪表盘", "Go to Dashboard": "前往仪表盘",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "Email (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -1,7 +1,7 @@
export default { export default {
languageName: "繁體中文 (香港)", languageName: "繁體中文 (香港)",
Settings: "設定", Settings: "設定",
Dashboard: "錶板", Dashboard: "主控台",
"New Update": "有更新", "New Update": "有更新",
Language: "語言", Language: "語言",
Appearance: "外觀", Appearance: "外觀",
@ -104,7 +104,7 @@ export default {
rrtypeDescription: "請選擇 DNS 記錄類型", rrtypeDescription: "請選擇 DNS 記錄類型",
pauseMonitorMsg: "是否確定暫停?", pauseMonitorMsg: "是否確定暫停?",
"Last Result": "最後結果", "Last Result": "最後結果",
"Create your admin account": "製作你的管理員帳號", "Create your admin account": "建立管理員帳號",
"Repeat Password": "重複密碼", "Repeat Password": "重複密碼",
respTime: "反應時間 (ms)", respTime: "反應時間 (ms)",
notAvailableShort: "N/A", notAvailableShort: "N/A",
@ -146,37 +146,55 @@ export default {
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.", importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.",
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.", confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.",
"Heartbeat Retry Interval": "Heartbeat Retry Interval", "Heartbeat Retry Interval": "Heartbeat Retry Interval",
"Import Backup": "Import Backup", "Import Backup": "匯入備份",
"Export Backup": "Export Backup", "Export Backup": "匯出備份",
"Skip existing": "Skip existing", "Skip existing": "略過已存在的",
Overwrite: "Overwrite", Overwrite: "覆蓋",
Options: "Options", Options: "選項",
"Keep both": "Keep both", "Keep both": "兩者並存",
Tags: "Tags", Tags: "標籤",
"Add New below or Select...": "Add New below or Select...", "Add New below or Select...": "Add New below or Select...",
"Tag with this name already exist.": "Tag with this name already exist.", "Tag with this name already exist.": "Tag with this name already exist.",
"Tag with this value already exist.": "Tag with this value already exist.", "Tag with this value already exist.": "Tag with this value already exist.",
color: "color", color: "顏色",
"value (optional)": "value (optional)", "value (optional)": "值 (非必需)",
Gray: "Gray", Gray: "",
Red: "Red", Red: "",
Orange: "Orange", Orange: "",
Green: "Green", Green: "",
Blue: "Blue", Blue: "",
Indigo: "Indigo", Indigo: "",
Purple: "Purple", Purple: "",
Pink: "Pink", Pink: "粉紅",
"Search...": "Search...", "Search...": "搜尋...",
"Avg. Ping": "Avg. Ping", "Avg. Ping": "平均反應時間",
"Avg. Response": "Avg. Response", "Avg. Response": "平均反應時間",
"Entry Page": "Entry Page", "Entry Page": "Entry Page",
statusPageNothing: "Nothing here, please add a group or a monitor.", statusPageNothing: "Nothing here, please add a group or a monitor.",
"No Services": "No Services", "No Services": "沒有服務",
"All Systems Operational": "All Systems Operational", "All Systems Operational": "一切正常",
"Partially Degraded Service": "Partially Degraded Service", "Partially Degraded Service": "部份服務受阻",
"Degraded Service": "Degraded Service", "Degraded Service": "服務受阻",
"Add Group": "Add Group", "Add Group": "新增群組",
"Add a monitor": "Add a monitor", "Add a monitor": " 新增監測器",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "編輯 Status Page",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "前往主控台",
"Status Page": "Status Page",
telegram: "Telegram",
webhook: "Webhook",
smtp: "電郵 (SMTP)",
discord: "Discord",
teams: "Microsoft Teams",
signal: "Signal",
gotify: "Gotify",
slack: "Slack",
"rocket.chat": "Rocket.chat",
pushover: "Pushover",
pushy: "Pushy",
octopush: "Octopush",
lunasea: "LunaSea",
apprise: "Apprise (支援 50 多種通知)",
pushbullet: "Pushbullet",
line: "Line Messenger",
mattermost: "Mattermost",
}; };

View file

@ -1,7 +1,7 @@
import "bootstrap"; import "bootstrap";
import { createApp, h } from "vue"; import { createApp, h } from "vue";
import contenteditable from "vue-contenteditable";
import Toast from "vue-toastification"; import Toast from "vue-toastification";
import contenteditable from "vue-contenteditable"
import "vue-toastification/dist/index.css"; import "vue-toastification/dist/index.css";
import App from "./App.vue"; import App from "./App.vue";
import "./assets/app.scss"; import "./assets/app.scss";
@ -9,10 +9,9 @@ import { i18n } from "./i18n";
import { FontAwesomeIcon } from "./icon.js"; import { FontAwesomeIcon } from "./icon.js";
import datetime from "./mixins/datetime"; import datetime from "./mixins/datetime";
import mobile from "./mixins/mobile"; import mobile from "./mixins/mobile";
import publicMixin from "./mixins/public";
import socket from "./mixins/socket"; import socket from "./mixins/socket";
import theme from "./mixins/theme"; import theme from "./mixins/theme";
import publicMixin from "./mixins/public";
import { router } from "./router"; import { router } from "./router";
import { appName } from "./util.ts"; import { appName } from "./util.ts";
@ -27,10 +26,10 @@ const app = createApp({
data() { data() {
return { return {
appName: appName appName: appName
} };
}, },
render: () => h(App), render: () => h(App),
}) });
app.use(router); app.use(router);
app.use(i18n); app.use(i18n);

View file

@ -256,7 +256,7 @@ export default {
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
ipRegexPattern: "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))", ipRegexPattern: "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))",
// Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address // Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
hostnameRegexPattern: "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$" hostnameRegexPattern: "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$"
}; };
}, },

View file

@ -52,9 +52,12 @@
</div> </div>
</div> </div>
<!-- General Settings -->
<h2 class="mt-5 mb-2">{{ $t("General") }}</h2> <h2 class="mt-5 mb-2">{{ $t("General") }}</h2>
<form class="mb-3" @submit.prevent="saveGeneral"> <form class="mb-3" @submit.prevent="saveGeneral">
<div class="mb-3"> <!-- Timezone -->
<div class="mb-4">
<label for="timezone" class="form-label">{{ $t("Timezone") }}</label> <label for="timezone" class="form-label">{{ $t("Timezone") }}</label>
<select id="timezone" v-model="$root.userTimezone" class="form-select"> <select id="timezone" v-model="$root.userTimezone" class="form-select">
<option value="auto"> <option value="auto">
@ -66,7 +69,8 @@
</select> </select>
</div> </div>
<div class="mb-3"> <!-- Search Engine -->
<div class="mb-4">
<label class="form-label">{{ $t("Search Engine Visibility") }}</label> <label class="form-label">{{ $t("Search Engine Visibility") }}</label>
<div class="form-check"> <div class="form-check">
@ -83,7 +87,8 @@
</div> </div>
</div> </div>
<div class="mb-3"> <!-- Entry Page -->
<div class="mb-4">
<label class="form-label">{{ $t("Entry Page") }}</label> <label class="form-label">{{ $t("Entry Page") }}</label>
<div class="form-check"> <div class="form-check">
@ -101,6 +106,19 @@
</div> </div>
</div> </div>
<!-- Primary Base URL -->
<div class="mb-4">
<label class="form-label" for="primaryBaseURL">Primary Base URL</label>
<div class="input-group mb-3">
<input id="primaryBaseURL" v-model="settings.primaryBaseURL" class="form-control" name="primaryBaseURL" placeholder="https://" pattern="https?://.+">
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">Auto Get</button>
</div>
<div class="form-text">
</div>
</div>
<div> <div>
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
{{ $t("Save") }} {{ $t("Save") }}
@ -109,6 +127,7 @@
</form> </form>
<template v-if="loaded"> <template v-if="loaded">
<!-- Change Password -->
<template v-if="! settings.disableAuth"> <template v-if="! settings.disableAuth">
<h2 class="mt-5 mb-2">{{ $t("Change Password") }}</h2> <h2 class="mt-5 mb-2">{{ $t("Change Password") }}</h2>
<form class="mb-3" @submit.prevent="savePassword"> <form class="mb-3" @submit.prevent="savePassword">
@ -196,36 +215,40 @@
<h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2> <h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
<div class="mb-3"> <div class="mb-3">
<button v-if="settings.disableAuth" class="btn btn-outline-primary me-1 mb-1" @click="enableAuth">{{ $t("Enable Auth") }}</button> <button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button>
<button v-if="! settings.disableAuth" class="btn btn-primary me-1 mb-1" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button> <button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
<button v-if="! settings.disableAuth" class="btn btn-danger me-1 mb-1" @click="$root.logout">{{ $t("Logout") }}</button> <button v-if="! settings.disableAuth" class="btn btn-danger me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button>
<button class="btn btn-outline-danger me-1 mb-1" @click="confirmClearStatistics">{{ $t("Clear all statistics") }}</button> <button class="btn btn-outline-danger me-1 mb-1" @click="confirmClearStatistics">{{ $t("Clear all statistics") }}</button>
</div> </div>
</template> </template>
</div> </div>
<div class="notification-list col-md-6"> <div class="col-md-6">
<div v-if="$root.isMobile" class="mt-3" /> <div v-if="$root.isMobile" class="mt-3" />
<h2>{{ $t("Notifications") }}</h2> <!-- Notifications -->
<p v-if="$root.notificationList.length === 0"> <div class="notification-list ">
{{ $t("Not available, please setup.") }} <h2>{{ $t("Notifications") }}</h2>
</p> <p v-if="$root.notificationList.length === 0">
<p v-else> {{ $t("Not available, please setup.") }}
{{ $t("notificationDescription") }} </p>
</p> <p v-else>
{{ $t("notificationDescription") }}
</p>
<ul class="list-group mb-3" style="border-radius: 1rem;"> <ul class="list-group mb-3" style="border-radius: 1rem;">
<li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item"> <li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
{{ notification.name }}<br> {{ notification.name }}<br>
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a> <a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
</li> </li>
</ul> </ul>
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()"> <button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
{{ $t("Setup Notification") }} {{ $t("Setup Notification") }}
</button> </button>
</div>
<!-- Info -->
<h2 class="mt-5">{{ $t("Info") }}</h2> <h2 class="mt-5">{{ $t("Info") }}</h2>
{{ $t("Version") }}: {{ $root.info.version }} <br /> {{ $t("Version") }}: {{ $root.info.version }} <br />
@ -325,7 +348,7 @@
<template v-else-if="$i18n.locale === 'bg-BG' "> <template v-else-if="$i18n.locale === 'bg-BG' ">
<p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p> <p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p>
<p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p> <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p>
<p>Моля, използвайте внимателно.</p> <p>Моля, използвайте с повишено внимание.</p>
</template> </template>
<template v-else-if="$i18n.locale === 'hu' "> <template v-else-if="$i18n.locale === 'hu' ">
@ -400,7 +423,7 @@ export default {
"$i18n.locale"() { "$i18n.locale"() {
localStorage.locale = this.$i18n.locale; localStorage.locale = this.$i18n.locale;
setPageLocale() setPageLocale();
}, },
}, },
@ -531,6 +554,10 @@ export default {
} }
}); });
}, },
autoGetPrimaryBaseURL() {
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
}
}, },
}; };
</script> </script>

View file

@ -197,7 +197,7 @@
</div> </div>
<footer class="mt-5 mb-4"> <footer class="mt-5 mb-4">
{{ $t("Powered By") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" )}}</a> {{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
</footer> </footer>
</div> </div>
</template> </template>

View file

@ -7,7 +7,7 @@
// Backend uses the compiled file util.js // Backend uses the compiled file util.js
// Frontend uses util.ts // Frontend uses util.ts
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
const _dayjs = require("dayjs"); const _dayjs = require("dayjs");
const dayjs = _dayjs; const dayjs = _dayjs;
exports.isDev = process.env.NODE_ENV === "development"; exports.isDev = process.env.NODE_ENV === "development";
@ -74,7 +74,7 @@ class TimeLogger {
this.startTime = dayjs().valueOf(); this.startTime = dayjs().valueOf();
} }
print(name) { print(name) {
if (exports.isDev) { if (exports.isDev && process && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
} }
} }
@ -112,3 +112,7 @@ function genSecret(length = 64) {
return secret; return secret;
} }
exports.genSecret = genSecret; exports.genSecret = genSecret;
function getMonitorRelativeURL(id) {
return "/dashboard/" + id;
}
exports.getMonitorRelativeURL = getMonitorRelativeURL;

View file

@ -86,7 +86,7 @@ export class TimeLogger {
} }
print(name: string) { print(name: string) {
if (isDev) { if (isDev && process && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms") console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms")
} }
} }
@ -123,3 +123,7 @@ export function genSecret(length = 64) {
} }
return secret; return secret;
} }
export function getMonitorRelativeURL(id: string) {
return "/dashboard/" + id;
}

View file

@ -1,6 +1,7 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const { Page } = require("puppeteer"); const { Page, Browser } = require("puppeteer");
const { sleep } = require("../src/util"); const { sleep } = require("../src/util");
const axios = require("axios");
/** /**
* Set back the correct data type for page object * Set back the correct data type for page object
@ -8,14 +9,17 @@ const { sleep } = require("../src/util");
*/ */
page; page;
beforeAll(() => { /**
if (process.env.JUST_FOR_TEST) { * @type {Browser}
console.log(process.env.JUST_FOR_TEST); */
browser;
if (process.env.JUST_FOR_TEST === "JUST_FOR_TEST_HELLO") { beforeAll(async () => {
console.log("secret ok"); await page.setViewport({
} width: 1280,
} height: 720,
deviceScaleFactor: 1,
});
}); });
afterAll(() => { afterAll(() => {
@ -35,6 +39,7 @@ describe("Init", () => {
await expect(page.title()).resolves.toMatch(title); await expect(page.title()).resolves.toMatch(title);
}); });
// Setup Page
it("Setup", async () => { it("Setup", async () => {
// Create an Admin // Create an Admin
await page.waitForSelector("#floatingInput"); await page.waitForSelector("#floatingInput");
@ -49,20 +54,25 @@ describe("Init", () => {
// Go to /setup again // Go to /setup again
await page.goto(baseURL + "/setup"); await page.goto(baseURL + "/setup");
await sleep(3000); await sleep(3000);
const pathname = await page.evaluate(() => location.pathname); let pathname = await page.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard"); expect(pathname).toEqual("/dashboard");
// Go to / // Go to /
await page.goto(baseURL); await page.goto(baseURL);
await sleep(3000);
pathname = await page.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard"); expect(pathname).toEqual("/dashboard");
}); });
// Settings Page
describe("Settings", () => { describe("Settings", () => {
beforeAll(async () => { beforeAll(async () => {
await page.goto(baseURL + "/settings"); await page.goto(baseURL + "/settings");
}); });
it("Change Language", async () => { it("Change Language", async () => {
await page.waitForSelector("#language");
await page.select("#language", "zh-HK"); await page.select("#language", "zh-HK");
let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText); let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText);
expect(languageTitle).toMatch("語言"); expect(languageTitle).toMatch("語言");
@ -73,19 +83,135 @@ describe("Init", () => {
}); });
it("Change Theme", async () => { it("Change Theme", async () => {
// Light await sleep(1000);
await page.click(".btn[for=btncheck1]");
await page.waitForSelector("div.light", {
timeout: 2000
});
await page.click(".btn[for=btncheck2]"); // Dark
await page.waitForSelector("div.dark", { await click(page, ".btn[for=btncheck2]");
timeout: 2000 await page.waitForSelector("div.dark");
});
await sleep(1000);
// Light
await click(page, ".btn[for=btncheck1]");
await page.waitForSelector("div.light");
}); });
// TODO: Heartbeat Bar Style
// TODO: Timezone
it("Search Engine Visibility", async () => {
// Default
let res = await axios.get(baseURL + "/robots.txt");
expect(res.data).toMatch("Disallow: /");
// Yes
await click(page, "#searchEngineIndexYes");
await click(page, "form > div > .btn[type=submit]");
await sleep(2000);
res = await axios.get(baseURL + "/robots.txt");
expect(res.data).not.toMatch("Disallow: /");
// No
await click(page, "#searchEngineIndexNo");
await click(page, "form > div > .btn[type=submit]");
await sleep(2000);
res = await axios.get(baseURL + "/robots.txt");
expect(res.data).toMatch("Disallow: /");
});
it("Entry Page", async () => {
const newPage = await browser.newPage();
// Default
await newPage.goto(baseURL);
await sleep(3000);
let pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard");
// Status Page
await click(page, "#entryPageNo");
await click(page, "form > div > .btn[type=submit]");
await sleep(4000);
await newPage.goto(baseURL);
await sleep(4000);
pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/status");
// Back to Dashboard
await click(page, "#entryPageYes");
await click(page, "form > div > .btn[type=submit]");
await sleep(4000);
await newPage.goto(baseURL);
await sleep(4000);
pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard");
await newPage.close();
});
it("Change Password (wrong current password)", async () => {
await page.goto(baseURL + "/settings");
await page.waitForSelector("#current-password");
await page.type("#current-password", "wrong_passw$$d");
await page.type("#new-password", "new_password123");
await page.type("#repeat-new-password", "new_password123");
// Save
await click(page, "form > div > .btn[type=submit]", 1);
await sleep(4000);
await click(page, ".btn-danger.btn.me-2");
await login("admin", "new_password123");
let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
expect(elementCount).toEqual(1);
await login("admin", "admin123");
});
it("Change Password (wrong repeat)", async () => {
await page.goto(baseURL + "/settings");
await page.waitForSelector("#current-password");
await page.type("#current-password", "admin123");
await page.type("#new-password", "new_password123");
await page.type("#repeat-new-password", "new_password1234567898797898");
await click(page, "form > div > .btn[type=submit]", 1);
await sleep(4000);
await click(page, ".btn-danger.btn.me-2");
await login("admin", "new_password123");
let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
expect(elementCount).toEqual(1);
await login("admin", "admin123");
await sleep(3000);
});
// TODO: 2FA
// TODO: Export Backup
// TODO: Import Backup
// TODO: Disable Auth
// TODO: Clear Stats
}); });
/*
* TODO
* Create Monitor - All type
* Edit Monitor
* Delete Monitor
*
* Create Notification (token problem, maybe hard to test)
*
*/
describe("Status Page", () => { describe("Status Page", () => {
const title = "Uptime Kuma"; const title = "Uptime Kuma";
beforeAll(async () => { beforeAll(async () => {
@ -97,3 +223,28 @@ describe("Init", () => {
}); });
}); });
async function login(username, password) {
await input(page, "#floatingInput", username);
await input(page, "#floatingPassword", password);
await page.click(".btn-primary[type=submit]");
await sleep(5000);
}
async function click(page, selector, elementIndex = 0) {
await page.waitForSelector(selector, {
timeout: 5000,
});
return await page.evaluate((s, i) => {
return document.querySelectorAll(s)[i].click();
}, selector, elementIndex);
}
async function input(page, selector, text) {
await page.waitForSelector(selector, {
timeout: 5000,
});
const element = await page.$(selector);
await element.click({ clickCount: 3 });
await page.keyboard.press("Backspace");
await page.type(selector, text);
}

View file

@ -1,10 +1,10 @@
FROM ubuntu:16.04 FROM ubuntu:16.04
RUN apt-get update
RUN apt --yes install curl
# Test invalid node version, these commands install nodejs 10 # Test invalid node version, these commands install nodejs 10
RUN apt-get update #RUN apt --yes install nodejs
RUN apt --yes install nodejs
# RUN ln -s /usr/bin/nodejs /usr/bin/node # RUN ln -s /usr/bin/nodejs /usr/bin/node
# RUN node -v # RUN node -v
COPY ./install.sh . RUN curl -o kuma_install.sh http://git.kuma.pet/install.sh && bash kuma_install.sh local /opt/uptime-kuma 3000 0.0.0.0
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0