Compare commits
No commits in common. "master" and "1.20.X" have entirely different histories.
18
.github/workflows/auto-test.yml
vendored
18
.github/workflows/auto-test.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
node: [ 14, 16, 18, 19 ]
|
node: [ 14, 16, 17, 18 ]
|
||||||
# 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/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -66,19 +66,3 @@ jobs:
|
|||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run cy:test
|
- run: npm run cy:test
|
||||||
|
|
||||||
frontend-unit-tests:
|
|
||||||
needs: [ check-linters ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- run: git config --global core.autocrlf false # Mainly for Windows
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Use Node.js 14
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
cache: 'npm'
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build
|
|
||||||
- run: npm run cy:run:unit
|
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
<img src="./public/icon.svg" width="128" alt="" />
|
<img src="./public/icon.svg" width="128" alt="" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Uptime Kuma is an easy-to-use self-hosted monitoring tool.
|
It is a self-hosted monitoring tool like "Uptime Robot".
|
||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/1336778/212262296-e6205815-ad62-488c-83ec-a5b0d0689f7c.jpg" width="700" alt="" />
|
<img src="https://uptime.kuma.pet/img/dark.jpg" width="700" alt="" />
|
||||||
|
|
||||||
## 🥔 Live Demo
|
## 🥔 Live Demo
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
const { defineConfig } = require("cypress");
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
|
||||||
e2e: {
|
|
||||||
supportFile: false,
|
|
||||||
specPattern: [
|
|
||||||
"test/cypress/unit/**/*.js"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,16 +0,0 @@
|
|||||||
############################################
|
|
||||||
# Build in Golang
|
|
||||||
# Run npm run build-healthcheck-armv7 in the host first, another it will be super slow where it is building the armv7 healthcheck
|
|
||||||
############################################
|
|
||||||
FROM golang:1.19.4-buster
|
|
||||||
WORKDIR /app
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
COPY ./extra/ ./extra/
|
|
||||||
|
|
||||||
# Compile healthcheck.go
|
|
||||||
RUN apt update && \
|
|
||||||
apt --yes --no-install-recommends install curl && \
|
|
||||||
curl -sL https://deb.nodesource.com/setup_18.x | bash && \
|
|
||||||
apt --yes --no-install-recommends install nodejs && \
|
|
||||||
node ./extra/build-healthcheck.js $TARGETPLATFORM && \
|
|
||||||
apt --yes remove nodejs
|
|
@ -1,9 +1,19 @@
|
|||||||
############################################
|
############################################
|
||||||
# Build in Golang
|
# Build in Golang
|
||||||
# Run npm run build-healthcheck-armv7 in the host first, another it will be super slow where it is building the armv7 healthcheck
|
# Run npm run build-healthcheck-armv7 in the host first, another it will be super slow where it is building the armv7 healthcheck
|
||||||
# Check file: builder-go.dockerfile
|
|
||||||
############################################
|
############################################
|
||||||
FROM louislam/uptime-kuma:builder-go AS build_healthcheck
|
FROM golang:1.19.4-buster AS build_healthcheck
|
||||||
|
WORKDIR /app
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
COPY ./extra/ ./extra/
|
||||||
|
|
||||||
|
# Compile healthcheck.go
|
||||||
|
RUN apt update
|
||||||
|
RUN apt --yes --no-install-recommends install curl
|
||||||
|
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash
|
||||||
|
RUN apt --yes --no-install-recommends install nodejs
|
||||||
|
RUN node -v
|
||||||
|
RUN node ./extra/build-healthcheck.js $TARGETPLATFORM
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# Build in Node.js
|
# Build in Node.js
|
||||||
@ -12,13 +22,10 @@ FROM louislam/uptime-kuma:base-debian AS build
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
||||||
COPY .npmrc .npmrc
|
|
||||||
COPY package.json package.json
|
|
||||||
COPY package-lock.json package-lock.json
|
|
||||||
RUN npm ci --omit=dev
|
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck
|
COPY --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck
|
||||||
RUN chmod +x /app/extra/entrypoint.sh
|
RUN npm ci --production && \
|
||||||
|
chmod +x /app/extra/entrypoint.sh
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# ⭐ Main Image
|
# ⭐ Main Image
|
||||||
|
@ -3,12 +3,10 @@ WORKDIR /app
|
|||||||
|
|
||||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
|
||||||
|
|
||||||
COPY .npmrc .npmrc
|
|
||||||
COPY package.json package.json
|
|
||||||
COPY package-lock.json package-lock.json
|
|
||||||
RUN npm ci --omit=dev
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN chmod +x /app/extra/entrypoint.sh
|
RUN npm ci --production && \
|
||||||
|
chmod +x /app/extra/entrypoint.sh
|
||||||
|
|
||||||
|
|
||||||
FROM louislam/uptime-kuma:base-alpine AS release
|
FROM louislam/uptime-kuma:base-alpine AS release
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -32,10 +32,6 @@ if (! exists) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Commit updated files
|
|
||||||
* @param {string} version Version to update to
|
|
||||||
*/
|
|
||||||
function commit(version) {
|
function commit(version) {
|
||||||
let msg = "Update to " + version;
|
let msg = "Update to " + version;
|
||||||
|
|
||||||
@ -51,10 +47,6 @@ function commit(version) {
|
|||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a tag with the specified version
|
|
||||||
* @param {string} version Tag to create
|
|
||||||
*/
|
|
||||||
function tag(version) {
|
function tag(version) {
|
||||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
@ -63,11 +55,6 @@ function tag(version) {
|
|||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a tag exists for the specified version
|
|
||||||
* @param {string} version Version to check
|
|
||||||
* @returns {boolean} Does the tag already exist
|
|
||||||
*/
|
|
||||||
function tagExists(version) {
|
function tagExists(version) {
|
||||||
if (! version) {
|
if (! version) {
|
||||||
throw new Error("invalid version");
|
throw new Error("invalid version");
|
||||||
|
@ -25,10 +25,6 @@ if (platform === "linux/amd64") {
|
|||||||
const file = fs.createWriteStream("cloudflared.deb");
|
const file = fs.createWriteStream("cloudflared.deb");
|
||||||
get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb");
|
get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb");
|
||||||
|
|
||||||
/**
|
|
||||||
* Download specified file
|
|
||||||
* @param {string} url URL to request
|
|
||||||
*/
|
|
||||||
function get(url) {
|
function get(url) {
|
||||||
http.get(url, function (res) {
|
http.get(url, function (res) {
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
/*
|
|
||||||
* If changed, have to run `npm run build-docker-builder-go`.
|
|
||||||
* This script should be run after a period of time (180s), because the server may need some time to prepare.
|
|
||||||
*/
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -43,11 +43,6 @@ const main = async () => {
|
|||||||
console.log("Finished.");
|
console.log("Finished.");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask question of user
|
|
||||||
* @param {string} question Question to ask
|
|
||||||
* @returns {Promise<string>} Users response
|
|
||||||
*/
|
|
||||||
function question(question) {
|
function question(question) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
rl.question(question, (answer) => {
|
rl.question(question, (answer) => {
|
||||||
|
@ -53,11 +53,6 @@ const main = async () => {
|
|||||||
console.log("Finished.");
|
console.log("Finished.");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask question of user
|
|
||||||
* @param {string} question Question to ask
|
|
||||||
* @returns {Promise<string>} Users response
|
|
||||||
*/
|
|
||||||
function question(question) {
|
function question(question) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
rl.question(question, (answer) => {
|
rl.question(question, (answer) => {
|
||||||
|
@ -135,11 +135,6 @@ server.listen({
|
|||||||
udp: 5300
|
udp: 5300
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Get human readable request type from request code
|
|
||||||
* @param {number} code Request code to translate
|
|
||||||
* @returns {string} Human readable request type
|
|
||||||
*/
|
|
||||||
function type(code) {
|
function type(code) {
|
||||||
for (let name in Packet.TYPE) {
|
for (let name in Packet.TYPE) {
|
||||||
if (Packet.TYPE[name] === code) {
|
if (Packet.TYPE[name] === code) {
|
||||||
|
@ -11,7 +11,6 @@ class SimpleMqttServer {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start the MQTT server */
|
|
||||||
start() {
|
start() {
|
||||||
this.server.listen(this.port, () => {
|
this.server.listen(this.port, () => {
|
||||||
console.log("server started and listening on port ", this.port);
|
console.log("server started and listening on port ", this.port);
|
||||||
|
@ -36,8 +36,10 @@ if (! exists) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commit updated files
|
* Updates the version number in package.json and commits it to git.
|
||||||
* @param {string} version Version to update to
|
* @param {string} version - The new version number
|
||||||
|
*
|
||||||
|
* Generated by Trelent
|
||||||
*/
|
*/
|
||||||
function commit(version) {
|
function commit(version) {
|
||||||
let msg = "Update to " + version;
|
let msg = "Update to " + version;
|
||||||
@ -51,19 +53,16 @@ function commit(version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a tag with the specified version
|
|
||||||
* @param {string} version Tag to create
|
|
||||||
*/
|
|
||||||
function tag(version) {
|
function tag(version) {
|
||||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||||
console.log(res.stdout.toString().trim());
|
console.log(res.stdout.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a tag exists for the specified version
|
* Checks if a given version is already tagged in the git repository.
|
||||||
* @param {string} version Version to check
|
* @param {string} version - The version to check for.
|
||||||
* @returns {boolean} Does the tag already exist
|
*
|
||||||
|
* Generated by Trelent
|
||||||
*/
|
*/
|
||||||
function tagExists(version) {
|
function tagExists(version) {
|
||||||
if (! version) {
|
if (! version) {
|
||||||
|
@ -10,10 +10,6 @@ if (!newVersion) {
|
|||||||
|
|
||||||
updateWiki(newVersion);
|
updateWiki(newVersion);
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the wiki with new version number
|
|
||||||
* @param {string} newVersion Version to update to
|
|
||||||
*/
|
|
||||||
function updateWiki(newVersion) {
|
function updateWiki(newVersion) {
|
||||||
const wikiDir = "./tmp/wiki";
|
const wikiDir = "./tmp/wiki";
|
||||||
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
|
const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md";
|
||||||
@ -43,10 +39,6 @@ function updateWiki(newVersion) {
|
|||||||
safeDelete(wikiDir);
|
safeDelete(wikiDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a directory exists and then delete it
|
|
||||||
* @param {string} dir Directory to delete
|
|
||||||
*/
|
|
||||||
function safeDelete(dir) {
|
function safeDelete(dir) {
|
||||||
if (fs.existsSync(dir)) {
|
if (fs.existsSync(dir)) {
|
||||||
fs.rm(dir, {
|
fs.rm(dir, {
|
||||||
|
2733
package-lock.json
generated
2733
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uptime-kuma",
|
"name": "uptime-kuma",
|
||||||
"version": "1.19.6",
|
"version": "1.19.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -31,7 +31,6 @@
|
|||||||
"build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",
|
"build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",
|
||||||
"build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
|
"build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
|
||||||
"build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
|
"build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
|
||||||
"build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push",
|
|
||||||
"build-docker-alpine": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:$VERSION-alpine --target release . --push",
|
"build-docker-alpine": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:$VERSION-alpine --target release . --push",
|
||||||
"build-docker-debian": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push",
|
"build-docker-debian": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push",
|
||||||
"build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
|
"build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
|
||||||
@ -39,7 +38,7 @@
|
|||||||
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
||||||
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
|
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
|
||||||
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||||
"setup": "git checkout 1.19.6 && npm ci --production && npm run download-dist",
|
"setup": "git checkout 1.19.3 && npm ci --production && npm run download-dist",
|
||||||
"download-dist": "node extra/download-dist.js",
|
"download-dist": "node extra/download-dist.js",
|
||||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||||
"reset-password": "node extra/reset-password.js",
|
"reset-password": "node extra/reset-password.js",
|
||||||
@ -61,13 +60,11 @@
|
|||||||
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
|
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
|
||||||
"cy:test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --e2e",
|
"cy:test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --e2e",
|
||||||
"cy:run": "npx cypress run --browser chrome --headless --config-file ./config/cypress.config.js",
|
"cy:run": "npx cypress run --browser chrome --headless --config-file ./config/cypress.config.js",
|
||||||
"cy:run:unit": "npx cypress run --browser chrome --headless --config-file ./config/cypress.frontend.config.js",
|
|
||||||
"cypress-open": "concurrently -k -r \"node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/\" \"cypress open --config-file ./config/cypress.config.js\"",
|
"cypress-open": "concurrently -k -r \"node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/\" \"cypress open --config-file ./config/cypress.config.js\"",
|
||||||
"build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go"
|
"build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "~1.7.3",
|
"@grpc/grpc-js": "~1.7.3",
|
||||||
"@louislam/ping": "~0.4.2-mod.1",
|
|
||||||
"@louislam/sqlite3": "15.1.2",
|
"@louislam/sqlite3": "15.1.2",
|
||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.27.0",
|
"axios": "~0.27.0",
|
||||||
@ -96,7 +93,6 @@
|
|||||||
"jsonwebtoken": "~9.0.0",
|
"jsonwebtoken": "~9.0.0",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"limiter": "~2.1.0",
|
"limiter": "~2.1.0",
|
||||||
"mongodb": "~4.13.0",
|
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~8.1.4",
|
||||||
"mysql2": "~2.3.3",
|
"mysql2": "~2.3.3",
|
||||||
@ -107,11 +103,11 @@
|
|||||||
"password-hash": "~1.2.2",
|
"password-hash": "~1.2.2",
|
||||||
"pg": "~8.8.0",
|
"pg": "~8.8.0",
|
||||||
"pg-connection-string": "~2.5.0",
|
"pg-connection-string": "~2.5.0",
|
||||||
|
"ping": "~0.4.2",
|
||||||
"prom-client": "~13.2.0",
|
"prom-client": "~13.2.0",
|
||||||
"prometheus-api-metrics": "~3.2.1",
|
"prometheus-api-metrics": "~3.2.1",
|
||||||
"protobufjs": "~7.1.1",
|
"protobufjs": "~7.1.1",
|
||||||
"redbean-node": "~0.2.0",
|
"redbean-node": "0.1.4",
|
||||||
"redis": "~4.5.1",
|
|
||||||
"socket.io": "~4.5.3",
|
"socket.io": "~4.5.3",
|
||||||
"socket.io-client": "~4.5.3",
|
"socket.io-client": "~4.5.3",
|
||||||
"socks-proxy-agent": "6.1.1",
|
"socks-proxy-agent": "6.1.1",
|
||||||
|
@ -63,12 +63,6 @@ function myAuthorizer(username, password, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use basic auth if auth is not disabled
|
|
||||||
* @param {express.Request} req Express request object
|
|
||||||
* @param {express.Response} res Express response object
|
|
||||||
* @param {express.NextFunction} next
|
|
||||||
*/
|
|
||||||
exports.basicAuth = async function (req, res, next) {
|
exports.basicAuth = async function (req, res, next) {
|
||||||
const middleware = basicAuth({
|
const middleware = basicAuth({
|
||||||
authorizer: myAuthorizer,
|
authorizer: myAuthorizer,
|
||||||
|
@ -37,10 +37,6 @@ class CacheableDnsHttpAgent {
|
|||||||
this.enable = isEnable;
|
this.enable = isEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach cacheable to HTTP agent
|
|
||||||
* @param {http.Agent} agent Agent to install
|
|
||||||
*/
|
|
||||||
static install(agent) {
|
static install(agent) {
|
||||||
this.cacheable.install(agent);
|
this.cacheable.install(agent);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ const initBackgroundJobs = function (args) {
|
|||||||
return bree;
|
return bree;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Stop all background jobs if running */
|
|
||||||
const stopBackgroundJobs = function () {
|
const stopBackgroundJobs = function () {
|
||||||
if (bree) {
|
if (bree) {
|
||||||
bree.stop();
|
bree.stop();
|
||||||
|
@ -25,20 +25,15 @@ const DEFAULT_KEEP_PERIOD = 180;
|
|||||||
parsedPeriod = DEFAULT_KEEP_PERIOD;
|
parsedPeriod = DEFAULT_KEEP_PERIOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedPeriod < 1) {
|
log(`Clearing Data older than ${parsedPeriod} days...`);
|
||||||
log(`Data deletion has been disabled as period is less than 1. Period is ${parsedPeriod} days.`);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
log(`Clearing Data older than ${parsedPeriod} days...`);
|
try {
|
||||||
|
await R.exec(
|
||||||
try {
|
"DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ",
|
||||||
await R.exec(
|
[ parsedPeriod ]
|
||||||
"DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ",
|
);
|
||||||
[ parsedPeriod ]
|
} catch (e) {
|
||||||
);
|
log(`Failed to clear old data: ${e.message}`);
|
||||||
} catch (e) {
|
|
||||||
log(`Failed to clear old data: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit();
|
exit();
|
||||||
|
@ -112,11 +112,6 @@ class Maintenance extends BeanModel {
|
|||||||
return this.toPublicJSON(timezone);
|
return this.toPublicJSON(timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of weekdays that the maintenance is active for
|
|
||||||
* Monday=1, Tuesday=2 etc.
|
|
||||||
* @returns {number[]} Array of active weekdays
|
|
||||||
*/
|
|
||||||
getDayOfWeekList() {
|
getDayOfWeekList() {
|
||||||
log.debug("timeslot", "List: " + this.weekdays);
|
log.debug("timeslot", "List: " + this.weekdays);
|
||||||
return JSON.parse(this.weekdays).sort(function (a, b) {
|
return JSON.parse(this.weekdays).sort(function (a, b) {
|
||||||
@ -124,20 +119,12 @@ class Maintenance extends BeanModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of days in month that maintenance is active for
|
|
||||||
* @returns {number[]} Array of active days in month
|
|
||||||
*/
|
|
||||||
getDayOfMonthList() {
|
getDayOfMonthList() {
|
||||||
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the start date and time for maintenance
|
|
||||||
* @returns {dayjs.Dayjs} Start date and time
|
|
||||||
*/
|
|
||||||
getStartDateTime() {
|
getStartDateTime() {
|
||||||
let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm");
|
let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm");
|
||||||
log.debug("timeslot", "startOfTheDay: " + startOfTheDay);
|
log.debug("timeslot", "startOfTheDay: " + startOfTheDay);
|
||||||
@ -150,10 +137,6 @@ class Maintenance extends BeanModel {
|
|||||||
return dayjs.utc(this.start_date).add(startTimeSecond, "second");
|
return dayjs.utc(this.start_date).add(startTimeSecond, "second");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the duraction of maintenance in seconds
|
|
||||||
* @returns {number} Duration of maintenance
|
|
||||||
*/
|
|
||||||
getDuration() {
|
getDuration() {
|
||||||
let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second");
|
let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second");
|
||||||
// Add 24hours if it is across day
|
// Add 24hours if it is across day
|
||||||
@ -163,12 +146,6 @@ class Maintenance extends BeanModel {
|
|||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert data from socket to bean
|
|
||||||
* @param {Bean} bean Bean to fill in
|
|
||||||
* @param {Object} obj Data to fill bean with
|
|
||||||
* @returns {Bean} Filled bean
|
|
||||||
*/
|
|
||||||
static jsonToBean(bean, obj) {
|
static jsonToBean(bean, obj) {
|
||||||
if (obj.id) {
|
if (obj.id) {
|
||||||
bean.id = obj.id;
|
bean.id = obj.id;
|
||||||
|
@ -6,11 +6,6 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
|
|||||||
|
|
||||||
class MaintenanceTimeslot extends BeanModel {
|
class MaintenanceTimeslot extends BeanModel {
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an object that ready to parse to JSON for public
|
|
||||||
* Only show necessary data to public
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
async toPublicJSON() {
|
async toPublicJSON() {
|
||||||
const serverTimezoneOffset = UptimeKumaServer.getInstance().getTimezoneOffset();
|
const serverTimezoneOffset = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||||
|
|
||||||
@ -26,10 +21,6 @@ class MaintenanceTimeslot extends BeanModel {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an object that ready to parse to JSON
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
async toJSON() {
|
async toJSON() {
|
||||||
return await this.toPublicJSON();
|
return await this.toPublicJSON();
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ const dayjs = require("dayjs");
|
|||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const { Prometheus } = require("../prometheus");
|
const { Prometheus } = require("../prometheus");
|
||||||
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } = require("../../src/util");
|
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } = require("../../src/util");
|
||||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery,
|
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery } = require("../util-server");
|
||||||
redisPingAsync, mongodbPing,
|
|
||||||
} = require("../util-server");
|
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||||
const { Notification } = require("../notification");
|
const { Notification } = require("../notification");
|
||||||
@ -38,6 +36,7 @@ class Monitor extends BeanModel {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
sendUrl: this.sendUrl,
|
sendUrl: this.sendUrl,
|
||||||
|
maintenance: await Monitor.isUnderMaintenance(this.id),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.sendUrl) {
|
if (this.sendUrl) {
|
||||||
@ -495,17 +494,13 @@ class Monitor extends BeanModel {
|
|||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: `/containers/${this.docker_container}/json`,
|
url: `/containers/${this.docker_container}/json`,
|
||||||
timeout: this.interval * 1000 * 0.8,
|
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"User-Agent": "Uptime-Kuma/" + version,
|
"User-Agent": "Uptime-Kuma/" + version,
|
||||||
},
|
},
|
||||||
httpsAgent: CacheableDnsHttpAgent.getHttpsAgent({
|
httpsAgent: new https.Agent({
|
||||||
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
||||||
rejectUnauthorized: !this.getIgnoreTls(),
|
rejectUnauthorized: ! this.getIgnoreTls(),
|
||||||
}),
|
|
||||||
httpAgent: CacheableDnsHttpAgent.getHttpAgent({
|
|
||||||
maxCachedSessions: 0,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -586,15 +581,6 @@ class Monitor extends BeanModel {
|
|||||||
bean.msg = "";
|
bean.msg = "";
|
||||||
bean.status = UP;
|
bean.status = UP;
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
bean.ping = dayjs().valueOf() - startTime;
|
||||||
} else if (this.type === "mongodb") {
|
|
||||||
let startTime = dayjs().valueOf();
|
|
||||||
|
|
||||||
await mongodbPing(this.databaseConnectionString);
|
|
||||||
|
|
||||||
bean.msg = "";
|
|
||||||
bean.status = UP;
|
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
|
||||||
|
|
||||||
} else if (this.type === "radius") {
|
} else if (this.type === "radius") {
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
|
||||||
@ -631,12 +617,6 @@ class Monitor extends BeanModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
bean.ping = dayjs().valueOf() - startTime;
|
||||||
} else if (this.type === "redis") {
|
|
||||||
let startTime = dayjs().valueOf();
|
|
||||||
|
|
||||||
bean.msg = await redisPingAsync(this.databaseConnectionString);
|
|
||||||
bean.status = UP;
|
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
|
||||||
} else {
|
} else {
|
||||||
bean.msg = "Unknown Monitor Type";
|
bean.msg = "Unknown Monitor Type";
|
||||||
bean.status = PENDING;
|
bean.status = PENDING;
|
||||||
@ -768,13 +748,6 @@ class Monitor extends BeanModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a request using axios
|
|
||||||
* @param {Object} options Options for Axios
|
|
||||||
* @param {boolean} finalCall Should this be the final call i.e
|
|
||||||
* don't retry on faliure
|
|
||||||
* @returns {Object} Axios response
|
|
||||||
*/
|
|
||||||
async makeAxiosRequest(options, finalCall = false) {
|
async makeAxiosRequest(options, finalCall = false) {
|
||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
@ -1256,7 +1229,6 @@ class Monitor extends BeanModel {
|
|||||||
return maintenance.count !== 0;
|
return maintenance.count !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Make sure monitor interval is between bounds */
|
|
||||||
validate() {
|
validate() {
|
||||||
if (this.interval > MAX_INTERVAL_SECOND) {
|
if (this.interval > MAX_INTERVAL_SECOND) {
|
||||||
throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
|
throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
|
||||||
|
@ -8,14 +8,6 @@ class PromoSMS extends NotificationProvider {
|
|||||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
let okMsg = "Sent Successfully.";
|
let okMsg = "Sent Successfully.";
|
||||||
|
|
||||||
if (notification.promosmsAllowLongSMS === undefined) {
|
|
||||||
notification.promosmsAllowLongSMS = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Add option for enabling special characters. It will decrese message max length from 160 to 70 chars.
|
|
||||||
//Lets remove non ascii char
|
|
||||||
let cleanMsg = msg.replace(/[^\x00-\x7F]/g, "");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let config = {
|
let config = {
|
||||||
headers: {
|
headers: {
|
||||||
@ -26,9 +18,8 @@ class PromoSMS extends NotificationProvider {
|
|||||||
};
|
};
|
||||||
let data = {
|
let data = {
|
||||||
"recipients": [ notification.promosmsPhoneNumber ],
|
"recipients": [ notification.promosmsPhoneNumber ],
|
||||||
//Trim message to maximum length of 1 SMS or 4 if we allowed long messages
|
//Lets remove non ascii char
|
||||||
"text": notification.promosmsAllowLongSMS ? cleanMsg.substring(0, 639) : cleanMsg.substring(0, 159),
|
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||||
"long-sms": notification.promosmsAllowLongSMS,
|
|
||||||
"type": Number(notification.promosmsSMSType),
|
"type": Number(notification.promosmsSMSType),
|
||||||
"sender": notification.promosmsSenderName
|
"sender": notification.promosmsSenderName
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@ class Pushover extends NotificationProvider {
|
|||||||
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
let pushoverlink = "https://api.pushover.net/1/messages.json";
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
"message": "<b>Message</b>:" + msg,
|
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg,
|
||||||
"user": notification.pushoveruserkey,
|
"user": notification.pushoveruserkey,
|
||||||
"token": notification.pushoverapptoken,
|
"token": notification.pushoverapptoken,
|
||||||
"sound": notification.pushoversounds,
|
"sound": notification.pushoversounds,
|
||||||
|
@ -21,12 +21,6 @@ class ServerChan extends NotificationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the formatted title for message
|
|
||||||
* @param {?Object} monitorJSON Monitor details (For Up/Down only)
|
|
||||||
* @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only)
|
|
||||||
* @returns {string} Formatted title
|
|
||||||
*/
|
|
||||||
checkStatus(heartbeatJSON, monitorJSON) {
|
checkStatus(heartbeatJSON, monitorJSON) {
|
||||||
let title = "UptimeKuma Message";
|
let title = "UptimeKuma Message";
|
||||||
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
|
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
const NotificationProvider = require("./notification-provider");
|
|
||||||
const axios = require("axios");
|
|
||||||
const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util");
|
|
||||||
const { setting } = require("../util-server");
|
|
||||||
let successMessage = "Sent Successfully.";
|
|
||||||
|
|
||||||
class Splunk extends NotificationProvider {
|
|
||||||
name = "Splunk";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
|
||||||
try {
|
|
||||||
if (heartbeatJSON == null) {
|
|
||||||
const title = "Uptime Kuma Alert";
|
|
||||||
const monitor = {
|
|
||||||
type: "ping",
|
|
||||||
url: "Uptime Kuma Test Button",
|
|
||||||
};
|
|
||||||
return this.postNotification(notification, title, msg, monitor, "trigger");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (heartbeatJSON.status === UP) {
|
|
||||||
const title = "Uptime Kuma Monitor ✅ Up";
|
|
||||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "recovery");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (heartbeatJSON.status === DOWN) {
|
|
||||||
const title = "Uptime Kuma Monitor 🔴 Down";
|
|
||||||
return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.throwGeneralAxiosError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if result is successful, result code should be in range 2xx
|
|
||||||
* @param {Object} result Axios response object
|
|
||||||
* @throws {Error} The status code is not in range 2xx
|
|
||||||
*/
|
|
||||||
checkResult(result) {
|
|
||||||
if (result.status == null) {
|
|
||||||
throw new Error("Splunk notification failed with invalid response!");
|
|
||||||
}
|
|
||||||
if (result.status < 200 || result.status >= 300) {
|
|
||||||
throw new Error("Splunk notification failed with status code " + result.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the message
|
|
||||||
* @param {BeanModel} notification Message title
|
|
||||||
* @param {string} title Message title
|
|
||||||
* @param {string} body Message
|
|
||||||
* @param {Object} monitorInfo Monitor details (For Up/Down only)
|
|
||||||
* @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve)
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") {
|
|
||||||
|
|
||||||
let monitorUrl;
|
|
||||||
if (monitorInfo.type === "port") {
|
|
||||||
monitorUrl = monitorInfo.hostname;
|
|
||||||
if (monitorInfo.port) {
|
|
||||||
monitorUrl += ":" + monitorInfo.port;
|
|
||||||
}
|
|
||||||
} else if (monitorInfo.hostname != null) {
|
|
||||||
monitorUrl = monitorInfo.hostname;
|
|
||||||
} else {
|
|
||||||
monitorUrl = monitorInfo.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventAction === "recovery") {
|
|
||||||
if (notification.splunkAutoResolve === "0") {
|
|
||||||
return "No action required";
|
|
||||||
}
|
|
||||||
eventAction = notification.splunkAutoResolve;
|
|
||||||
} else {
|
|
||||||
eventAction = notification.splunkSeverity;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
method: "POST",
|
|
||||||
url: notification.splunkRestURL,
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
data: {
|
|
||||||
message_type: eventAction,
|
|
||||||
state_message: `[${title}] [${monitorUrl}] ${body}`,
|
|
||||||
entity_display_name: "Uptime Kuma Alert: " + monitorInfo.name,
|
|
||||||
routing_key: notification.pagerdutyIntegrationKey,
|
|
||||||
entity_id: "Uptime Kuma/" + monitorInfo.id,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const baseURL = await setting("primaryBaseURL");
|
|
||||||
if (baseURL && monitorInfo) {
|
|
||||||
options.client = "Uptime Kuma";
|
|
||||||
options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = await axios.request(options);
|
|
||||||
this.checkResult(result);
|
|
||||||
if (result.statusText != null) {
|
|
||||||
return "Splunk notification succeed: " + result.statusText;
|
|
||||||
}
|
|
||||||
|
|
||||||
return successMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Splunk;
|
|
@ -40,7 +40,6 @@ const Stackfield = require("./notification-providers/stackfield");
|
|||||||
const Teams = require("./notification-providers/teams");
|
const Teams = require("./notification-providers/teams");
|
||||||
const TechulusPush = require("./notification-providers/techulus-push");
|
const TechulusPush = require("./notification-providers/techulus-push");
|
||||||
const Telegram = require("./notification-providers/telegram");
|
const Telegram = require("./notification-providers/telegram");
|
||||||
const Splunk = require("./notification-providers/splunk");
|
|
||||||
const Webhook = require("./notification-providers/webhook");
|
const Webhook = require("./notification-providers/webhook");
|
||||||
const WeCom = require("./notification-providers/wecom");
|
const WeCom = require("./notification-providers/wecom");
|
||||||
const GoAlert = require("./notification-providers/goalert");
|
const GoAlert = require("./notification-providers/goalert");
|
||||||
@ -101,7 +100,6 @@ class Notification {
|
|||||||
new Teams(),
|
new Teams(),
|
||||||
new TechulusPush(),
|
new TechulusPush(),
|
||||||
new Telegram(),
|
new Telegram(),
|
||||||
new Splunk(),
|
|
||||||
new Webhook(),
|
new Webhook(),
|
||||||
new WeCom(),
|
new WeCom(),
|
||||||
new GoAlert(),
|
new GoAlert(),
|
||||||
|
@ -99,7 +99,6 @@ class Prometheus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove monitor from prometheus */
|
|
||||||
remove() {
|
remove() {
|
||||||
try {
|
try {
|
||||||
monitorCertDaysRemaining.remove(this.monitorLabelValues);
|
monitorCertDaysRemaining.remove(this.monitorLabelValues);
|
||||||
|
@ -941,21 +941,13 @@ let needSetup = false;
|
|||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
let bean = await R.findOne("tag", " id = ? ", [ tag.id ]);
|
let bean = await R.findOne("monitor", " id = ? ", [ tag.id ]);
|
||||||
if (bean == null) {
|
|
||||||
callback({
|
|
||||||
ok: false,
|
|
||||||
msg: "Tag not found",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bean.name = tag.name;
|
bean.name = tag.name;
|
||||||
bean.color = tag.color;
|
bean.color = tag.color;
|
||||||
await R.store(bean);
|
await R.store(bean);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Saved",
|
|
||||||
tag: await bean.toJSON(),
|
tag: await bean.toJSON(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ class UptimeCacheList {
|
|||||||
static list = {};
|
static list = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the uptime for a specific period
|
*
|
||||||
* @param {number} monitorID
|
* @param monitorID
|
||||||
* @param {number} duration
|
* @param duration
|
||||||
* @return {number}
|
* @return number
|
||||||
*/
|
*/
|
||||||
static getUptime(monitorID, duration) {
|
static getUptime(monitorID, duration) {
|
||||||
if (UptimeCacheList.list[monitorID] && UptimeCacheList.list[monitorID][duration]) {
|
if (UptimeCacheList.list[monitorID] && UptimeCacheList.list[monitorID][duration]) {
|
||||||
@ -20,12 +20,6 @@ class UptimeCacheList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add uptime for specified monitor
|
|
||||||
* @param {number} monitorID
|
|
||||||
* @param {number} duration
|
|
||||||
* @param {number} uptime Uptime to add
|
|
||||||
*/
|
|
||||||
static addUptime(monitorID, duration, uptime) {
|
static addUptime(monitorID, duration, uptime) {
|
||||||
log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration);
|
log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration);
|
||||||
if (!UptimeCacheList.list[monitorID]) {
|
if (!UptimeCacheList.list[monitorID]) {
|
||||||
@ -34,10 +28,6 @@ class UptimeCacheList {
|
|||||||
UptimeCacheList.list[monitorID][duration] = uptime;
|
UptimeCacheList.list[monitorID][duration] = uptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear cache for specified monitor
|
|
||||||
* @param {number} monitorID
|
|
||||||
*/
|
|
||||||
static clearCache(monitorID) {
|
static clearCache(monitorID) {
|
||||||
log.debug("UptimeCacheList", "clearCache: " + monitorID);
|
log.debug("UptimeCacheList", "clearCache: " + monitorID);
|
||||||
delete UptimeCacheList.list[monitorID];
|
delete UptimeCacheList.list[monitorID];
|
||||||
|
@ -86,7 +86,6 @@ class UptimeKumaServer {
|
|||||||
this.io = new Server(this.httpServer);
|
this.io = new Server(this.httpServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialise app after the database has been set up */
|
|
||||||
async initAfterDatabaseReady() {
|
async initAfterDatabaseReady() {
|
||||||
await CacheableDnsHttpAgent.update();
|
await CacheableDnsHttpAgent.update();
|
||||||
|
|
||||||
@ -99,11 +98,6 @@ class UptimeKumaServer {
|
|||||||
this.generateMaintenanceTimeslotsInterval = setInterval(this.generateMaintenanceTimeslots, 60 * 1000);
|
this.generateMaintenanceTimeslotsInterval = setInterval(this.generateMaintenanceTimeslots, 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send list of monitors to client
|
|
||||||
* @param {Socket} socket
|
|
||||||
* @returns {Object} List of monitors
|
|
||||||
*/
|
|
||||||
async sendMonitorList(socket) {
|
async sendMonitorList(socket) {
|
||||||
let list = await this.getMonitorJSONList(socket.userID);
|
let list = await this.getMonitorJSONList(socket.userID);
|
||||||
this.io.to(socket.userID).emit("monitorList", list);
|
this.io.to(socket.userID).emit("monitorList", list);
|
||||||
@ -140,11 +134,6 @@ class UptimeKumaServer {
|
|||||||
return await this.sendMaintenanceListByUserID(socket.userID);
|
return await this.sendMaintenanceListByUserID(socket.userID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send list of maintenances to user
|
|
||||||
* @param {number} userID
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
async sendMaintenanceListByUserID(userID) {
|
async sendMaintenanceListByUserID(userID) {
|
||||||
let list = await this.getMaintenanceJSONList(userID);
|
let list = await this.getMaintenanceJSONList(userID);
|
||||||
this.io.to(userID).emit("maintenanceList", list);
|
this.io.to(userID).emit("maintenanceList", list);
|
||||||
@ -196,11 +185,6 @@ class UptimeKumaServer {
|
|||||||
errorLogStream.end();
|
errorLogStream.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the IP of the client connected to the socket
|
|
||||||
* @param {Socket} socket
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async getClientIP(socket) {
|
async getClientIP(socket) {
|
||||||
let clientIP = socket.client.conn.remoteAddress;
|
let clientIP = socket.client.conn.remoteAddress;
|
||||||
|
|
||||||
@ -219,12 +203,6 @@ class UptimeKumaServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to get the current server timezone
|
|
||||||
* If this fails, fall back to environment variables and then make a
|
|
||||||
* guess.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async getTimezone() {
|
async getTimezone() {
|
||||||
let timezone = await Settings.get("serverTimezone");
|
let timezone = await Settings.get("serverTimezone");
|
||||||
if (timezone) {
|
if (timezone) {
|
||||||
@ -236,25 +214,16 @@ class UptimeKumaServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current offset
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
getTimezoneOffset() {
|
getTimezoneOffset() {
|
||||||
return dayjs().format("Z");
|
return dayjs().format("Z");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current server timezone and environment variables
|
|
||||||
* @param {string} timezone
|
|
||||||
*/
|
|
||||||
async setTimezone(timezone) {
|
async setTimezone(timezone) {
|
||||||
await Settings.set("serverTimezone", timezone, "general");
|
await Settings.set("serverTimezone", timezone, "general");
|
||||||
process.env.TZ = timezone;
|
process.env.TZ = timezone;
|
||||||
dayjs.tz.setDefault(timezone);
|
dayjs.tz.setDefault(timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load the timeslots for maintenance */
|
|
||||||
async generateMaintenanceTimeslots() {
|
async generateMaintenanceTimeslots() {
|
||||||
|
|
||||||
let list = await R.find("maintenance_timeslot", " generated_next = 0 AND start_date <= DATETIME('now') ");
|
let list = await R.find("maintenance_timeslot", " generated_next = 0 AND start_date <= DATETIME('now') ");
|
||||||
@ -268,7 +237,6 @@ class UptimeKumaServer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop the server */
|
|
||||||
async stop() {
|
async stop() {
|
||||||
clearTimeout(this.generateMaintenanceTimeslotsInterval);
|
clearTimeout(this.generateMaintenanceTimeslotsInterval);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const tcpp = require("tcp-ping");
|
const tcpp = require("tcp-ping");
|
||||||
const ping = require("@louislam/ping");
|
const ping = require("ping");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { log, genSecret } = require("../src/util");
|
const { log, genSecret } = require("../src/util");
|
||||||
const passwordHash = require("./password-hash");
|
const passwordHash = require("./password-hash");
|
||||||
@ -14,13 +14,11 @@ const mssql = require("mssql");
|
|||||||
const { Client } = require("pg");
|
const { Client } = require("pg");
|
||||||
const postgresConParse = require("pg-connection-string").parse;
|
const postgresConParse = require("pg-connection-string").parse;
|
||||||
const mysql = require("mysql2");
|
const mysql = require("mysql2");
|
||||||
const { MongoClient } = require("mongodb");
|
|
||||||
const { NtlmClient } = require("axios-ntlm");
|
const { NtlmClient } = require("axios-ntlm");
|
||||||
const { Settings } = require("./settings");
|
const { Settings } = require("./settings");
|
||||||
const grpc = require("@grpc/grpc-js");
|
const grpc = require("@grpc/grpc-js");
|
||||||
const protojs = require("protobufjs");
|
const protojs = require("protobufjs");
|
||||||
const radiusClient = require("node-radius-client");
|
const radiusClient = require("node-radius-client");
|
||||||
const redis = require("redis");
|
|
||||||
const {
|
const {
|
||||||
dictionaries: {
|
dictionaries: {
|
||||||
rfc2865: { file, attributes },
|
rfc2865: { file, attributes },
|
||||||
@ -105,7 +103,7 @@ exports.pingAsync = function (hostname, ipv6 = false) {
|
|||||||
ping.promise.probe(hostname, {
|
ping.promise.probe(hostname, {
|
||||||
v6: ipv6,
|
v6: ipv6,
|
||||||
min_reply: 1,
|
min_reply: 1,
|
||||||
deadline: 10,
|
timeout: 10,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// If ping failed, it will set field to unknown
|
// If ping failed, it will set field to unknown
|
||||||
if (res.alive) {
|
if (res.alive) {
|
||||||
@ -137,7 +135,7 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
|||||||
const { port, username, password, interval = 20 } = options;
|
const { port, username, password, interval = 20 } = options;
|
||||||
|
|
||||||
// Adds MQTT protocol to the hostname if not already present
|
// Adds MQTT protocol to the hostname if not already present
|
||||||
if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
|
if (!/^(?:http|mqtt)s?:\/\//.test(hostname)) {
|
||||||
hostname = "mqtt://" + hostname;
|
hostname = "mqtt://" + hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,11 +145,10 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
|||||||
reject(new Error("Timeout"));
|
reject(new Error("Timeout"));
|
||||||
}, interval * 1000 * 0.8);
|
}, interval * 1000 * 0.8);
|
||||||
|
|
||||||
const mqttUrl = `${hostname}:${port}`;
|
log.debug("mqtt", "MQTT connecting");
|
||||||
|
|
||||||
log.debug("mqtt", `MQTT connecting to ${mqttUrl}`);
|
let client = mqtt.connect(hostname, {
|
||||||
|
port,
|
||||||
let client = mqtt.connect(mqttUrl, {
|
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
@ -283,23 +280,18 @@ exports.postgresQuery = function (connectionString, query) {
|
|||||||
|
|
||||||
const client = new Client({ connectionString });
|
const client = new Client({ connectionString });
|
||||||
|
|
||||||
client.connect((err) => {
|
client.connect();
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
client.end();
|
|
||||||
} else {
|
|
||||||
// Connected here
|
|
||||||
client.query(query, (err, res) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(res);
|
|
||||||
}
|
|
||||||
client.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
return client.query(query)
|
||||||
|
.then(res => {
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
reject(err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
client.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -325,23 +317,6 @@ exports.mysqlQuery = function (connectionString, query) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to and Ping a MongoDB database
|
|
||||||
* @param {string} connectionString The database connection string
|
|
||||||
* @returns {Promise<(string[]|Object[]|Object)>}
|
|
||||||
*/
|
|
||||||
exports.mongodbPing = async function (connectionString) {
|
|
||||||
let client = await MongoClient.connect(connectionString);
|
|
||||||
let dbPing = await client.db().command({ ping: 1 });
|
|
||||||
await client.close();
|
|
||||||
|
|
||||||
if (dbPing["ok"] === 1) {
|
|
||||||
return "UP";
|
|
||||||
} else {
|
|
||||||
throw Error("failed");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query radius server
|
* Query radius server
|
||||||
* @param {string} hostname Hostname of radius server
|
* @param {string} hostname Hostname of radius server
|
||||||
@ -379,30 +354,6 @@ exports.radius = function (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis server ping
|
|
||||||
* @param {string} dsn The redis connection string
|
|
||||||
*/
|
|
||||||
exports.redisPingAsync = function (dsn) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const client = redis.createClient({
|
|
||||||
url: dsn,
|
|
||||||
});
|
|
||||||
client.on("error", (err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
client.connect().then(() => {
|
|
||||||
client.ping().then((res, err) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve value of setting based on key
|
* Retrieve value of setting based on key
|
||||||
* @param {string} key Key of setting to retrieve
|
* @param {string} key Key of setting to retrieve
|
||||||
|
@ -3,7 +3,3 @@ html[lang='fa'] {
|
|||||||
font-family: 'IRANSans', 'Iranian Sans','B Nazanin', 'Tahoma', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
font-family: 'IRANSans', 'Iranian Sans','B Nazanin', 'Tahoma', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.multiselect__content {
|
|
||||||
padding-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
@ -73,7 +73,7 @@ export default {
|
|||||||
emits: [ "added" ],
|
emits: [ "added" ],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal: null,
|
model: null,
|
||||||
processing: false,
|
processing: false,
|
||||||
id: null,
|
id: null,
|
||||||
connectionTypes: [ "socket", "tcp" ],
|
connectionTypes: [ "socket", "tcp" ],
|
||||||
@ -91,16 +91,11 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
/** Confirm deletion of docker host */
|
|
||||||
deleteConfirm() {
|
deleteConfirm() {
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Show specified docker host
|
|
||||||
* @param {number} dockerHostID
|
|
||||||
*/
|
|
||||||
show(dockerHostID) {
|
show(dockerHostID) {
|
||||||
if (dockerHostID) {
|
if (dockerHostID) {
|
||||||
let found = false;
|
let found = false;
|
||||||
@ -131,7 +126,6 @@ export default {
|
|||||||
this.modal.show();
|
this.modal.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Add docker host */
|
|
||||||
submit() {
|
submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => {
|
this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => {
|
||||||
@ -150,7 +144,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Test the docker host */
|
|
||||||
test() {
|
test() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
||||||
@ -159,7 +152,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Delete this docker host */
|
|
||||||
deleteDockerHost() {
|
deleteDockerHost() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => {
|
this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => {
|
||||||
|
@ -41,7 +41,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
displayText() {
|
displayText() {
|
||||||
if (this.item.value === "" || this.item.value === undefined) {
|
if (this.item.value === "") {
|
||||||
return this.item.name;
|
return this.item.name;
|
||||||
} else {
|
} else {
|
||||||
return `${this.item.name}: ${this.item.value}`;
|
return `${this.item.name}: ${this.item.value}`;
|
||||||
|
@ -1,376 +0,0 @@
|
|||||||
<template>
|
|
||||||
<form @submit.prevent="submit">
|
|
||||||
<div ref="modal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 id="exampleModalLabel" class="modal-title">
|
|
||||||
{{ $t("Edit Tag") }}
|
|
||||||
</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="tag-name" class="form-label">{{ $t("Name") }}</label>
|
|
||||||
<input id="tag-name" v-model="tag.name" type="text" class="form-control" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="tag-color" class="form-label">{{ $t("Color") }}</label>
|
|
||||||
<div class="d-flex">
|
|
||||||
<div class="col-8 pe-1">
|
|
||||||
<vue-multiselect
|
|
||||||
v-model="selectedColor"
|
|
||||||
:options="colorOptions"
|
|
||||||
:multiple="false"
|
|
||||||
:searchable="true"
|
|
||||||
:placeholder="$t('color')"
|
|
||||||
track-by="color"
|
|
||||||
label="name"
|
|
||||||
select-label=""
|
|
||||||
deselect-label=""
|
|
||||||
>
|
|
||||||
<template #option="{ option }">
|
|
||||||
<div
|
|
||||||
class="mx-2 py-1 px-3 rounded d-inline-flex"
|
|
||||||
style="height: 24px; color: white;"
|
|
||||||
:style="{ backgroundColor: option.color + ' !important' }"
|
|
||||||
>
|
|
||||||
<span>{{ option.name }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #singleLabel="{ option }">
|
|
||||||
<div
|
|
||||||
class="py-1 px-3 rounded d-inline-flex"
|
|
||||||
style="height: 24px; color: white;"
|
|
||||||
:style="{ backgroundColor: option.color + ' !important' }"
|
|
||||||
>
|
|
||||||
<span>{{ option.name }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</vue-multiselect>
|
|
||||||
</div>
|
|
||||||
<div class="col-4 ps-1">
|
|
||||||
<input id="tag-color-hex" v-model="tag.color" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="tag-monitors" class="form-label">{{ $tc("Monitor", selectedMonitors.length) }}</label>
|
|
||||||
<div class="tag-monitors-list">
|
|
||||||
<router-link v-for="monitor in selectedMonitors" :key="monitor.id" class="d-flex align-items-center justify-content-between text-decoration-none tag-monitors-list-row py-2 px-3" :to="monitorURL(monitor.id)" @click="modal.hide()">
|
|
||||||
<span>{{ monitor.name }}</span>
|
|
||||||
<button type="button" class="btn-rm-monitor btn btn-outline-danger ms-2 py-1" @click.stop.prevent="removeMonitor(monitor.id)">
|
|
||||||
<font-awesome-icon class="" icon="times" />
|
|
||||||
</button>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div v-if="allMonitorList.length > 0" class="pt-3 px-3">
|
|
||||||
<label class="form-label">{{ $t("Add a monitor") }}:</label>
|
|
||||||
<select v-model="selectedAddMonitor" class="form-control">
|
|
||||||
<option v-for="monitor in allMonitorList" :key="monitor.id" :value="monitor">{{ monitor.name }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button v-if="tag" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm">
|
|
||||||
{{ $t("Delete") }}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-primary" :disabled="processing">
|
|
||||||
<div v-if="processing" class="spinner-border spinner-border-sm me-1"></div>
|
|
||||||
{{ $t("Save") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteTag">
|
|
||||||
{{ $t("confirmDeleteTagMsg") }}
|
|
||||||
</Confirm>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { Modal } from "bootstrap";
|
|
||||||
import Confirm from "./Confirm.vue";
|
|
||||||
import VueMultiselect from "vue-multiselect";
|
|
||||||
import { colorOptions } from "../util-frontend";
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import { getMonitorRelativeURL } from "../util.ts";
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
VueMultiselect,
|
|
||||||
Confirm,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
updated: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
modal: null,
|
|
||||||
processing: false,
|
|
||||||
selectedColor: {
|
|
||||||
name: null,
|
|
||||||
color: null,
|
|
||||||
},
|
|
||||||
tag: {
|
|
||||||
id: null,
|
|
||||||
name: "",
|
|
||||||
color: "",
|
|
||||||
// Do not set default value here, please scroll to show()
|
|
||||||
},
|
|
||||||
monitors: [],
|
|
||||||
removingMonitor: [],
|
|
||||||
addingMonitor: [],
|
|
||||||
selectedAddMonitor: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
colorOptions() {
|
|
||||||
if (!colorOptions(this).find(option => option.color === this.tag.color)) {
|
|
||||||
return colorOptions(this).concat(
|
|
||||||
{
|
|
||||||
name: "custom",
|
|
||||||
color: this.tag.color
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return colorOptions(this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedMonitors() {
|
|
||||||
return this.monitors
|
|
||||||
.concat(Object.values(this.$root.monitorList).filter(monitor => this.addingMonitor.includes(monitor.id)))
|
|
||||||
.filter(monitor => !this.removingMonitor.includes(monitor.id));
|
|
||||||
},
|
|
||||||
allMonitorList() {
|
|
||||||
return Object.values(this.$root.monitorList).filter(monitor => !this.selectedMonitors.includes(monitor));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
// Set color option to "Custom" when a unknown color is entered
|
|
||||||
"tag.color"(to, from) {
|
|
||||||
if (colorOptions(this).find(x => x.color === to) == null) {
|
|
||||||
this.selectedColor.name = this.$t("Custom");
|
|
||||||
this.selectedColor.color = to;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedColor(to, from) {
|
|
||||||
if (to != null) {
|
|
||||||
this.tag.color = to.color;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Selected a monitor and add to the list.
|
|
||||||
*/
|
|
||||||
selectedAddMonitor(monitor) {
|
|
||||||
if (monitor) {
|
|
||||||
if (this.removingMonitor.includes(monitor.id)) {
|
|
||||||
this.removingMonitor = this.removingMonitor.filter(id => id !== monitor.id);
|
|
||||||
} else {
|
|
||||||
this.addingMonitor.push(monitor.id);
|
|
||||||
}
|
|
||||||
this.selectedAddMonitor = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.modal = new Modal(this.$refs.modal);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Show confirmation for deleting a tag
|
|
||||||
*/
|
|
||||||
deleteConfirm() {
|
|
||||||
this.$refs.confirmDelete.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load tag information for display in the edit dialog
|
|
||||||
* @param {Object} tag tag object to edit
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
show(tag) {
|
|
||||||
if (tag) {
|
|
||||||
this.selectedColor = this.colorOptions.find(x => x.color === tag.color) ?? {
|
|
||||||
name: this.$t("Custom"),
|
|
||||||
color: tag.color
|
|
||||||
};
|
|
||||||
this.tag.id = tag.id;
|
|
||||||
this.tag.name = tag.name;
|
|
||||||
this.tag.color = tag.color;
|
|
||||||
this.monitors = this.monitorsByTag(tag.id);
|
|
||||||
this.removingMonitor = [];
|
|
||||||
this.addingMonitor = [];
|
|
||||||
this.selectedAddMonitor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modal.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit tag and monitorTag changes to server
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
async submit() {
|
|
||||||
this.processing = true;
|
|
||||||
let editResult = true;
|
|
||||||
|
|
||||||
for (let addId of this.addingMonitor) {
|
|
||||||
await this.addMonitorTagAsync(this.tag.id, addId, "").then((res) => {
|
|
||||||
if (!res.ok) {
|
|
||||||
toast.error(res.msg);
|
|
||||||
editResult = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let removeId of this.removingMonitor) {
|
|
||||||
this.monitors.find(monitor => monitor.id === removeId)?.tags.forEach(async (monitorTag) => {
|
|
||||||
await this.deleteMonitorTagAsync(this.tag.id, removeId, monitorTag.value).then((res) => {
|
|
||||||
if (!res.ok) {
|
|
||||||
toast.error(res.msg);
|
|
||||||
editResult = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$root.getSocket().emit("editTag", this.tag, (res) => {
|
|
||||||
this.$root.toastRes(res);
|
|
||||||
this.processing = false;
|
|
||||||
|
|
||||||
if (res.ok && editResult) {
|
|
||||||
this.updated();
|
|
||||||
this.modal.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the editing tag from server
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
deleteTag() {
|
|
||||||
this.processing = true;
|
|
||||||
this.$root.getSocket().emit("deleteTag", this.tag.id, (res) => {
|
|
||||||
this.$root.toastRes(res);
|
|
||||||
this.processing = false;
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
this.updated();
|
|
||||||
this.modal.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a monitor from the monitors list locally
|
|
||||||
* @param {number} id id of the tag to remove
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
removeMonitor(id) {
|
|
||||||
if (this.addingMonitor.includes(id)) {
|
|
||||||
this.addingMonitor = this.addingMonitor.filter(x => x !== id);
|
|
||||||
} else {
|
|
||||||
this.removingMonitor.push(id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get monitors which has a specific tag locally
|
|
||||||
* @param {number} tagId id of the tag to filter
|
|
||||||
* @returns {Object[]} list of monitors which has a specific tag
|
|
||||||
*/
|
|
||||||
monitorsByTag(tagId) {
|
|
||||||
return Object.values(this.$root.monitorList).filter((monitor) => {
|
|
||||||
return monitor.tags.find(monitorTag => monitorTag.tag_id === tagId);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get URL of monitor
|
|
||||||
* @param {number} id ID of monitor
|
|
||||||
* @returns {string} Relative URL of monitor
|
|
||||||
*/
|
|
||||||
monitorURL(id) {
|
|
||||||
return getMonitorRelativeURL(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a tag to a monitor asynchronously
|
|
||||||
* @param {number} tagId ID of tag to add
|
|
||||||
* @param {number} monitorId ID of monitor to add tag to
|
|
||||||
* @param {string} value Value of tag
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
addMonitorTagAsync(tagId, monitorId, value) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.$root.getSocket().emit("addMonitorTag", tagId, monitorId, value, resolve);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Delete a tag from a monitor asynchronously
|
|
||||||
* @param {number} tagId ID of tag to remove
|
|
||||||
* @param {number} monitorId ID of monitor to remove tag from
|
|
||||||
* @param {string} value Value of tag
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
deleteMonitorTagAsync(tagId, monitorId, value) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.$root.getSocket().emit("deleteMonitorTag", tagId, monitorId, value, resolve);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import "../assets/vars.scss";
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
.modal-dialog .form-text, .modal-dialog p {
|
|
||||||
color: $dark-font-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-rm-monitor {
|
|
||||||
padding-left: 11px;
|
|
||||||
padding-right: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-monitors-list {
|
|
||||||
max-height: 40vh;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-monitors-list .tag-monitors-list-row {
|
|
||||||
cursor: pointer;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
|
||||||
|
|
||||||
.dark & {
|
|
||||||
border-bottom: 1px solid $dark-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $highlight-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark &:hover {
|
|
||||||
background-color: $dark-bg2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -130,7 +130,6 @@
|
|||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import VueMultiselect from "vue-multiselect";
|
import VueMultiselect from "vue-multiselect";
|
||||||
import { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
import { colorOptions } from "../util-frontend";
|
|
||||||
import Tag from "../components/Tag.vue";
|
import Tag from "../components/Tag.vue";
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
@ -177,7 +176,24 @@ export default {
|
|||||||
return this.preSelectedTags.concat(this.newTags).filter(tag => !this.deleteTags.find(monitorTag => monitorTag.id === tag.id));
|
return this.preSelectedTags.concat(this.newTags).filter(tag => !this.deleteTags.find(monitorTag => monitorTag.id === tag.id));
|
||||||
},
|
},
|
||||||
colorOptions() {
|
colorOptions() {
|
||||||
return colorOptions(this);
|
return [
|
||||||
|
{ name: this.$t("Gray"),
|
||||||
|
color: "#4B5563" },
|
||||||
|
{ name: this.$t("Red"),
|
||||||
|
color: "#DC2626" },
|
||||||
|
{ name: this.$t("Orange"),
|
||||||
|
color: "#D97706" },
|
||||||
|
{ name: this.$t("Green"),
|
||||||
|
color: "#059669" },
|
||||||
|
{ name: this.$t("Blue"),
|
||||||
|
color: "#2563EB" },
|
||||||
|
{ name: this.$t("Indigo"),
|
||||||
|
color: "#4F46E5" },
|
||||||
|
{ name: this.$t("Purple"),
|
||||||
|
color: "#7C3AED" },
|
||||||
|
{ name: this.$t("Pink"),
|
||||||
|
color: "#DB2777" },
|
||||||
|
];
|
||||||
},
|
},
|
||||||
validateDraftTag() {
|
validateDraftTag() {
|
||||||
let nameInvalid = false;
|
let nameInvalid = false;
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
/** Monitor this represents */
|
/** Monitor this represents */
|
||||||
@ -26,6 +24,7 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
uptime() {
|
uptime() {
|
||||||
|
|
||||||
if (this.type === "maintenance") {
|
if (this.type === "maintenance") {
|
||||||
return this.$t("statusMaintenance");
|
return this.$t("statusMaintenance");
|
||||||
}
|
}
|
||||||
@ -40,19 +39,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
color() {
|
color() {
|
||||||
if (this.lastHeartBeat.status === MAINTENANCE) {
|
if (this.type === "maintenance" || this.monitor.maintenance) {
|
||||||
return "maintenance";
|
return "maintenance";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === DOWN) {
|
if (this.lastHeartBeat.status === 0) {
|
||||||
return "danger";
|
return "danger";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === UP) {
|
if (this.lastHeartBeat.status === 1) {
|
||||||
return "primary";
|
return "primary";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastHeartBeat.status === PENDING) {
|
if (this.lastHeartBeat.status === 2) {
|
||||||
return "warning";
|
return "warning";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +26,6 @@
|
|||||||
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
|
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
|
||||||
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-switch">
|
|
||||||
<input id="promosms-allow-long" v-model="$parent.notification.promosmsAllowLongSMS" type="checkbox" class="form-check-input">
|
|
||||||
<label for="promosms-allow-long" class="form-label">{{ $t("promosmsAllowLongSMS") }}</label>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="splunk-rest-url" class="form-label">{{ $t("Splunk Rest URL") }}</label>
|
|
||||||
<HiddenInput id="splunk-rest-url" v-model="$parent.notification.splunkRestURL" :required="true" autocomplete="false"></HiddenInput>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="splunk-severity" class="form-label">{{ $t("Severity") }}</label>
|
|
||||||
<select id="splunk-severity" v-model="$parent.notification.splunkSeverity" class="form-select">
|
|
||||||
<option value="INFO">{{ $t("info") }}</option>
|
|
||||||
<option value="WARNING">{{ $t("warning") }}</option>
|
|
||||||
<option value="CRITICAL" selected="selected">{{ $t("critical") }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="splunk-resolve" class="form-label">{{ $t("Auto resolve or acknowledged") }}</label>
|
|
||||||
<select id="splunk-resolve" v-model="$parent.notification.splunkAutoResolve" class="form-select">
|
|
||||||
<option value="0" selected="selected">{{ $t("do nothing") }}</option>
|
|
||||||
<option value="ACKNOWLEDGEMENT">{{ $t("auto acknowledged") }}</option>
|
|
||||||
<option value="RECOVERY">{{ $t("auto resolve") }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import HiddenInput from "../HiddenInput.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
HiddenInput,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -42,11 +42,6 @@ export default {
|
|||||||
HiddenInput,
|
HiddenInput,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
|
||||||
* Get the URL for telegram updates
|
|
||||||
* @param {string} [mode=masked] Should the token be masked?
|
|
||||||
* @returns {string} formatted URL
|
|
||||||
*/
|
|
||||||
telegramGetUpdatesURL(mode = "masked") {
|
telegramGetUpdatesURL(mode = "masked") {
|
||||||
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
let token = `<${this.$t("YOUR BOT TOKEN HERE")}>`;
|
||||||
|
|
||||||
@ -60,8 +55,6 @@ export default {
|
|||||||
|
|
||||||
return `https://api.telegram.org/bot${token}/getUpdates`;
|
return `https://api.telegram.org/bot${token}/getUpdates`;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Get the telegram chat ID */
|
|
||||||
async autoGetTelegramChatID() {
|
async autoGetTelegramChatID() {
|
||||||
try {
|
try {
|
||||||
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
let res = await axios.get(this.telegramGetUpdatesURL("withToken"));
|
||||||
|
@ -44,7 +44,6 @@ import Webhook from "./Webhook.vue";
|
|||||||
import WeCom from "./WeCom.vue";
|
import WeCom from "./WeCom.vue";
|
||||||
import GoAlert from "./GoAlert.vue";
|
import GoAlert from "./GoAlert.vue";
|
||||||
import ZohoCliq from "./ZohoCliq.vue";
|
import ZohoCliq from "./ZohoCliq.vue";
|
||||||
import Splunk from "./Splunk.vue";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage all notification form.
|
* Manage all notification form.
|
||||||
@ -93,7 +92,6 @@ const NotificationFormList = {
|
|||||||
"stackfield": Stackfield,
|
"stackfield": Stackfield,
|
||||||
"teams": Teams,
|
"teams": Teams,
|
||||||
"telegram": Telegram,
|
"telegram": Telegram,
|
||||||
"Splunk": Splunk,
|
|
||||||
"webhook": Webhook,
|
"webhook": Webhook,
|
||||||
"WeCom": WeCom,
|
"WeCom": WeCom,
|
||||||
"GoAlert": GoAlert,
|
"GoAlert": GoAlert,
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
settings.keepDataPeriodDays,
|
settings.keepDataPeriodDays,
|
||||||
])
|
])
|
||||||
}}
|
}}
|
||||||
{{ $t("infiniteRetention") }}
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="keepDataPeriodDays"
|
id="keepDataPeriodDays"
|
||||||
@ -15,12 +14,9 @@
|
|||||||
type="number"
|
type="number"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
required
|
required
|
||||||
min="0"
|
min="1"
|
||||||
step="1"
|
step="1"
|
||||||
/>
|
/>
|
||||||
<div v-if="settings.keepDataPeriodDays < 0" class="form-text">
|
|
||||||
{{ $t("dataRetentionTimeError") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<button class="btn btn-primary" type="button" @click="saveSettings()">
|
<button class="btn btn-primary" type="button" @click="saveSettings()">
|
||||||
|
@ -191,7 +191,6 @@ export default {
|
|||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Show confirmation dialog for disable auth */
|
|
||||||
confirmDisableAuth() {
|
confirmDisableAuth() {
|
||||||
this.$refs.confirmDisableAuth.show();
|
this.$refs.confirmDisableAuth.show();
|
||||||
},
|
},
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="tags-list my-3">
|
|
||||||
<div v-for="(tag, index) in tagsList" :key="tag.id" class="d-flex align-items-center mx-4 py-1 tags-list-row" :disabled="processing" @click="editTag(index)">
|
|
||||||
<div class="col-5 ps-1">
|
|
||||||
<Tag :item="tag" />
|
|
||||||
</div>
|
|
||||||
<div class="col-5 px-1">
|
|
||||||
<div>{{ monitorsByTag(tag.id).length }} {{ $tc("Monitor", monitorsByTag(tag.id).length) }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 pe-3 d-flex justify-content-end">
|
|
||||||
<button type="button" class="btn ms-2 py-1">
|
|
||||||
<font-awesome-icon class="" icon="edit" />
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn-rm-tag btn btn-outline-danger ms-2 py-1" :disabled="processing" @click.stop="deleteConfirm(index)">
|
|
||||||
<font-awesome-icon class="" icon="trash" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagEditDialog ref="tagEditDialog" :updated="tagsUpdated" />
|
|
||||||
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteTag">
|
|
||||||
{{ $t("confirmDeleteTagMsg") }}
|
|
||||||
</Confirm>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import TagEditDialog from "../../components/TagEditDialog.vue";
|
|
||||||
import Tag from "../Tag.vue";
|
|
||||||
import Confirm from "../Confirm.vue";
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Confirm,
|
|
||||||
TagEditDialog,
|
|
||||||
Tag,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
processing: false,
|
|
||||||
tagsList: null,
|
|
||||||
deletingTag: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
settings() {
|
|
||||||
return this.$parent.$parent.$parent.settings;
|
|
||||||
},
|
|
||||||
saveSettings() {
|
|
||||||
return this.$parent.$parent.$parent.saveSettings;
|
|
||||||
},
|
|
||||||
settingsLoaded() {
|
|
||||||
return this.$parent.$parent.$parent.settingsLoaded;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.getExistingTags();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* Reflect tag changes in the UI by fetching data. Callback for the edit tag dialog.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
tagsUpdated() {
|
|
||||||
this.getExistingTags();
|
|
||||||
this.$root.getMonitorList();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of tags from server
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
getExistingTags() {
|
|
||||||
this.processing = true;
|
|
||||||
this.$root.getSocket().emit("getTags", (res) => {
|
|
||||||
this.processing = false;
|
|
||||||
if (res.ok) {
|
|
||||||
this.tagsList = res.tags;
|
|
||||||
} else {
|
|
||||||
toast.error(res.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show confirmation for deleting a tag
|
|
||||||
* @param {number} index index of the tag to delete in the local tagsList
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
deleteConfirm(index) {
|
|
||||||
this.deletingTag = this.tagsList[index];
|
|
||||||
this.$refs.confirmDelete.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show dialog for editing a tag
|
|
||||||
* @param {number} index index of the tag to edit in the local tagsList
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
editTag(index) {
|
|
||||||
this.$refs.tagEditDialog.show(this.tagsList[index]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the tag "deletingTag" from server
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
deleteTag() {
|
|
||||||
this.processing = true;
|
|
||||||
this.$root.getSocket().emit("deleteTag", this.deletingTag.id, (res) => {
|
|
||||||
this.$root.toastRes(res);
|
|
||||||
this.processing = false;
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
this.tagsUpdated();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get monitors which has a specific tag locally
|
|
||||||
* @param {number} tagId id of the tag to filter
|
|
||||||
* @returns {Object[]} list of monitors which has a specific tag
|
|
||||||
*/
|
|
||||||
monitorsByTag(tagId) {
|
|
||||||
return Object.values(this.$root.monitorList).filter((monitor) => {
|
|
||||||
return monitor.tags.find(monitorTag => monitorTag.tag_id === tagId);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import "../../assets/vars.scss";
|
|
||||||
|
|
||||||
.btn-rm-tag {
|
|
||||||
padding-left: 11px;
|
|
||||||
padding-right: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-list .tags-list-row {
|
|
||||||
cursor: pointer;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
|
||||||
|
|
||||||
.dark & {
|
|
||||||
border-bottom: 1px solid $dark-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $highlight-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark &:hover {
|
|
||||||
background-color: $dark-bg2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-list .tags-list-row:last-child {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -44,7 +44,6 @@ import {
|
|||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
faInfoCircle,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
@ -89,7 +88,6 @@ library.add(
|
|||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
faInfoCircle,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export { FontAwesomeIcon };
|
export { FontAwesomeIcon };
|
||||||
|
@ -591,7 +591,7 @@ export default {
|
|||||||
"You can divide numbers with": "Čísla můžete dělit pomocí",
|
"You can divide numbers with": "Čísla můžete dělit pomocí",
|
||||||
"or": "nebo",
|
"or": "nebo",
|
||||||
recurringInterval: "Interval",
|
recurringInterval: "Interval",
|
||||||
"Recurring": "Opakující se",
|
"Recurring": "Recurring",
|
||||||
strategyManual: "Aktivní/Neaktivní Ručně",
|
strategyManual: "Aktivní/Neaktivní Ručně",
|
||||||
warningTimezone: "Používá se časové pásmo serveru",
|
warningTimezone: "Používá se časové pásmo serveru",
|
||||||
weekdayShortMon: "Po",
|
weekdayShortMon: "Po",
|
||||||
|
@ -74,7 +74,6 @@ export default {
|
|||||||
Current: "Current",
|
Current: "Current",
|
||||||
Uptime: "Uptime",
|
Uptime: "Uptime",
|
||||||
"Cert Exp.": "Cert Exp.",
|
"Cert Exp.": "Cert Exp.",
|
||||||
Monitor: "Monitor | Monitors",
|
|
||||||
day: "day | days",
|
day: "day | days",
|
||||||
"-day": "-day",
|
"-day": "-day",
|
||||||
hour: "hour",
|
hour: "hour",
|
||||||
@ -191,7 +190,6 @@ export default {
|
|||||||
Indigo: "Indigo",
|
Indigo: "Indigo",
|
||||||
Purple: "Purple",
|
Purple: "Purple",
|
||||||
Pink: "Pink",
|
Pink: "Pink",
|
||||||
Custom: "Custom",
|
|
||||||
"Search...": "Search...",
|
"Search...": "Search...",
|
||||||
"Avg. Ping": "Avg. Ping",
|
"Avg. Ping": "Avg. Ping",
|
||||||
"Avg. Response": "Avg. Response",
|
"Avg. Response": "Avg. Response",
|
||||||
@ -322,7 +320,6 @@ export default {
|
|||||||
promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
|
promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
|
||||||
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
|
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
|
||||||
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||||
promosmsAllowLongSMS: "Allow long SMS",
|
|
||||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||||
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
matrixHomeserverURL: "Homeserver URL (with http(s):// and optionally port)",
|
||||||
"Internal Room Id": "Internal Room ID",
|
"Internal Room Id": "Internal Room ID",
|
||||||
@ -678,7 +675,4 @@ export default {
|
|||||||
"General Monitor Type": "General Monitor Type",
|
"General Monitor Type": "General Monitor Type",
|
||||||
"Passive Monitor Type": "Passive Monitor Type",
|
"Passive Monitor Type": "Passive Monitor Type",
|
||||||
"Specific Monitor Type": "Specific Monitor Type",
|
"Specific Monitor Type": "Specific Monitor Type",
|
||||||
dataRetentionTimeError: "Retention period must be 0 or greater",
|
|
||||||
infiniteRetention: "Set to 0 for infinite retention.",
|
|
||||||
confirmDeleteTagMsg: "Are you sure you want to delete this tag? Monitors associated with this tag will not be deleted.",
|
|
||||||
};
|
};
|
||||||
|
@ -675,6 +675,4 @@ export default {
|
|||||||
"General Monitor Type": "Type de sonde générale",
|
"General Monitor Type": "Type de sonde générale",
|
||||||
"Passive Monitor Type": "Type de sonde passive",
|
"Passive Monitor Type": "Type de sonde passive",
|
||||||
"Specific Monitor Type": "Type de sonde spécifique",
|
"Specific Monitor Type": "Type de sonde spécifique",
|
||||||
dataRetentionTimeError: "La durée de conservation doit être supérieure ou égale à 0",
|
|
||||||
infiniteRetention: "Définissez la valeur à 0 pour une durée de conservation infinie.",
|
|
||||||
};
|
};
|
||||||
|
@ -284,7 +284,6 @@ export default {
|
|||||||
promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL",
|
promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL",
|
||||||
promosmsPhoneNumber: "Numer odbiorcy",
|
promosmsPhoneNumber: "Numer odbiorcy",
|
||||||
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)",
|
||||||
promosmsAllowLongSMS: "Zezwól na długie SMSy",
|
|
||||||
"Primary Base URL": "Główny URL",
|
"Primary Base URL": "Główny URL",
|
||||||
"Push URL": "Push URL",
|
"Push URL": "Push URL",
|
||||||
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
needPushEvery: "Powinieneś wywoływać ten URL co {0} sekund",
|
||||||
|
@ -63,12 +63,6 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/louislam/uptime-kuma/wiki" class="dropdown-item" target="_blank">
|
|
||||||
<font-awesome-icon icon="info-circle" /> {{ $t("Help") }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li v-if="$root.loggedIn && $root.socket.token !== 'autoLogin'">
|
<li v-if="$root.loggedIn && $root.socket.token !== 'autoLogin'">
|
||||||
<button class="dropdown-item" @click="$root.logout">
|
<button class="dropdown-item" @click="$root.logout">
|
||||||
<font-awesome-icon icon="sign-out-alt" />
|
<font-awesome-icon icon="sign-out-alt" />
|
||||||
|
@ -12,11 +12,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
|
||||||
* Convert value to UTC
|
|
||||||
* @param {string | number | Date | dayjs.Dayjs} value
|
|
||||||
* @returns {dayjs.Dayjs}
|
|
||||||
*/
|
|
||||||
toUTC(value) {
|
toUTC(value) {
|
||||||
return dayjs.tz(value, this.timezone).utc().format();
|
return dayjs.tz(value, this.timezone).utc().format();
|
||||||
},
|
},
|
||||||
@ -39,11 +34,6 @@ export default {
|
|||||||
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
|
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get time for maintenance
|
|
||||||
* @param {string | number | Date | dayjs.Dayjs} value
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
datetimeMaintenance(value) {
|
datetimeMaintenance(value) {
|
||||||
const inputDate = new Date(value);
|
const inputDate = new Date(value);
|
||||||
const now = new Date(Date.now());
|
const now = new Date(Date.now());
|
||||||
|
@ -3,7 +3,6 @@ import { useToast } from "vue-toastification";
|
|||||||
import jwtDecode from "jwt-decode";
|
import jwtDecode from "jwt-decode";
|
||||||
import Favico from "favico.js";
|
import Favico from "favico.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts";
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
let socket;
|
let socket;
|
||||||
@ -455,10 +454,6 @@ export default {
|
|||||||
socket.emit("getMonitorList", callback);
|
socket.emit("getMonitorList", callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of maintenances
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
getMaintenanceList(callback) {
|
getMaintenanceList(callback) {
|
||||||
if (! callback) {
|
if (! callback) {
|
||||||
callback = () => { };
|
callback = () => { };
|
||||||
@ -475,49 +470,22 @@ export default {
|
|||||||
socket.emit("add", monitor, callback);
|
socket.emit("add", monitor, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a maintenace
|
|
||||||
* @param {Object} maintenance
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
addMaintenance(maintenance, callback) {
|
addMaintenance(maintenance, callback) {
|
||||||
socket.emit("addMaintenance", maintenance, callback);
|
socket.emit("addMaintenance", maintenance, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Add monitors to maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {number[]} monitors
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
addMonitorMaintenance(maintenanceID, monitors, callback) {
|
addMonitorMaintenance(maintenanceID, monitors, callback) {
|
||||||
socket.emit("addMonitorMaintenance", maintenanceID, monitors, callback);
|
socket.emit("addMonitorMaintenance", maintenanceID, monitors, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Add status page to maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {number} statusPages
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
addMaintenanceStatusPage(maintenanceID, statusPages, callback) {
|
addMaintenanceStatusPage(maintenanceID, statusPages, callback) {
|
||||||
socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback);
|
socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get monitors affected by maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
getMonitorMaintenance(maintenanceID, callback) {
|
getMonitorMaintenance(maintenanceID, callback) {
|
||||||
socket.emit("getMonitorMaintenance", maintenanceID, callback);
|
socket.emit("getMonitorMaintenance", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get status pages where maintenance is shown
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
getMaintenanceStatusPage(maintenanceID, callback) {
|
getMaintenanceStatusPage(maintenanceID, callback) {
|
||||||
socket.emit("getMaintenanceStatusPage", maintenanceID, callback);
|
socket.emit("getMaintenanceStatusPage", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
@ -531,11 +499,6 @@ export default {
|
|||||||
socket.emit("deleteMonitor", monitorID, callback);
|
socket.emit("deleteMonitor", monitorID, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete specified maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
deleteMaintenance(maintenanceID, callback) {
|
deleteMaintenance(maintenanceID, callback) {
|
||||||
socket.emit("deleteMaintenance", maintenanceID, callback);
|
socket.emit("deleteMaintenance", maintenanceID, callback);
|
||||||
},
|
},
|
||||||
@ -627,28 +590,28 @@ export default {
|
|||||||
for (let monitorID in this.lastHeartbeatList) {
|
for (let monitorID in this.lastHeartbeatList) {
|
||||||
let lastHeartBeat = this.lastHeartbeatList[monitorID];
|
let lastHeartBeat = this.lastHeartbeatList[monitorID];
|
||||||
|
|
||||||
if (! lastHeartBeat) {
|
if (this.monitorList[monitorID] && this.monitorList[monitorID].maintenance) {
|
||||||
|
result[monitorID] = {
|
||||||
|
text: this.$t("statusMaintenance"),
|
||||||
|
color: "maintenance",
|
||||||
|
};
|
||||||
|
} else if (! lastHeartBeat) {
|
||||||
result[monitorID] = unknown;
|
result[monitorID] = unknown;
|
||||||
} else if (lastHeartBeat.status === UP) {
|
} else if (lastHeartBeat.status === 1) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Up"),
|
text: this.$t("Up"),
|
||||||
color: "primary",
|
color: "primary",
|
||||||
};
|
};
|
||||||
} else if (lastHeartBeat.status === DOWN) {
|
} else if (lastHeartBeat.status === 0) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Down"),
|
text: this.$t("Down"),
|
||||||
color: "danger",
|
color: "danger",
|
||||||
};
|
};
|
||||||
} else if (lastHeartBeat.status === PENDING) {
|
} else if (lastHeartBeat.status === 2) {
|
||||||
result[monitorID] = {
|
result[monitorID] = {
|
||||||
text: this.$t("Pending"),
|
text: this.$t("Pending"),
|
||||||
color: "warning",
|
color: "warning",
|
||||||
};
|
};
|
||||||
} else if (lastHeartBeat.status === MAINTENANCE) {
|
|
||||||
result[monitorID] = {
|
|
||||||
text: this.$t("statusMaintenance"),
|
|
||||||
color: "maintenance",
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
result[monitorID] = unknown;
|
result[monitorID] = unknown;
|
||||||
}
|
}
|
||||||
@ -670,17 +633,17 @@ export default {
|
|||||||
let beat = this.$root.lastHeartbeatList[monitorID];
|
let beat = this.$root.lastHeartbeatList[monitorID];
|
||||||
let monitor = this.$root.monitorList[monitorID];
|
let monitor = this.$root.monitorList[monitorID];
|
||||||
|
|
||||||
if (monitor && ! monitor.active) {
|
if (monitor && monitor.maintenance) {
|
||||||
|
result.maintenance++;
|
||||||
|
} else if (monitor && ! monitor.active) {
|
||||||
result.pause++;
|
result.pause++;
|
||||||
} else if (beat) {
|
} else if (beat) {
|
||||||
if (beat.status === UP) {
|
if (beat.status === 1) {
|
||||||
result.up++;
|
result.up++;
|
||||||
} else if (beat.status === DOWN) {
|
} else if (beat.status === 0) {
|
||||||
result.down++;
|
result.down++;
|
||||||
} else if (beat.status === PENDING) {
|
} else if (beat.status === 2) {
|
||||||
result.up++;
|
result.up++;
|
||||||
} else if (beat.status === MAINTENANCE) {
|
|
||||||
result.maintenance++;
|
|
||||||
} else {
|
} else {
|
||||||
result.unknown++;
|
result.unknown++;
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** Initialise page */
|
|
||||||
init() {
|
init() {
|
||||||
this.affectedMonitors = [];
|
this.affectedMonitors = [];
|
||||||
this.selectedStatusPages = [];
|
this.selectedStatusPages = [];
|
||||||
@ -415,7 +414,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Create new maintenance */
|
|
||||||
async submit() {
|
async submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
@ -460,11 +458,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Add monitor to maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
async addMonitorMaintenance(maintenanceID, callback) {
|
async addMonitorMaintenance(maintenanceID, callback) {
|
||||||
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@ -477,11 +470,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Add status page to maintenance
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
* @param {socketCB} callback
|
|
||||||
*/
|
|
||||||
async addMaintenanceStatusPage(maintenanceID, callback) {
|
async addMaintenanceStatusPage(maintenanceID, callback) {
|
||||||
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
@ -57,15 +57,9 @@
|
|||||||
<option value="mysql">
|
<option value="mysql">
|
||||||
MySQL/MariaDB
|
MySQL/MariaDB
|
||||||
</option>
|
</option>
|
||||||
<option value="mongodb">
|
|
||||||
MongoDB
|
|
||||||
</option>
|
|
||||||
<option value="radius">
|
<option value="radius">
|
||||||
Radius
|
Radius
|
||||||
</option>
|
</option>
|
||||||
<option value="redis">
|
|
||||||
Redis
|
|
||||||
</option>
|
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -111,7 +105,7 @@
|
|||||||
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius only -->
|
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius only -->
|
||||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'mqtt' || monitor.type === 'radius'" class="my-3">
|
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'mqtt' || monitor.type === 'radius'" class="my-3">
|
||||||
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
||||||
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern : ipOrHostnameRegexPattern}`" required>
|
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Port -->
|
<!-- Port -->
|
||||||
@ -273,24 +267,6 @@
|
|||||||
<textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
|
<textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- Redis -->
|
|
||||||
<template v-if="monitor.type === 'redis'">
|
|
||||||
<div class="my-3">
|
|
||||||
<label for="redisConnectionString" class="form-label">{{ $t("Connection String") }}</label>
|
|
||||||
<input id="redisConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control" placeholder="redis://user:password@host:port">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- MongoDB -->
|
|
||||||
<template v-if="monitor.type === 'mongodb'">
|
|
||||||
<div class="my-3">
|
|
||||||
<label for="sqlConnectionString" class="form-label">{{ $t("Connection String") }}</label>
|
|
||||||
|
|
||||||
<template v-if="monitor.type === 'mongodb'">
|
|
||||||
<input id="sqlConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control" placeholder="mongodb://username:password@host:port/database">
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Interval -->
|
<!-- Interval -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
@ -600,7 +576,6 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
|
|||||||
import ProxyDialog from "../components/ProxyDialog.vue";
|
import ProxyDialog from "../components/ProxyDialog.vue";
|
||||||
import TagsManager from "../components/TagsManager.vue";
|
import TagsManager from "../components/TagsManager.vue";
|
||||||
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../util.ts";
|
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../util.ts";
|
||||||
import { hostNameRegexPattern } from "../util-frontend";
|
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
@ -625,8 +600,11 @@ export default {
|
|||||||
},
|
},
|
||||||
acceptedStatusCodeOptions: [],
|
acceptedStatusCodeOptions: [],
|
||||||
dnsresolvetypeOptions: [],
|
dnsresolvetypeOptions: [],
|
||||||
ipOrHostnameRegexPattern: hostNameRegexPattern(),
|
|
||||||
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true)
|
// 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*$))",
|
||||||
|
// 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_])$"
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -65,7 +65,6 @@ export default {
|
|||||||
this.init();
|
this.init();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** Initialise page */
|
|
||||||
init() {
|
init() {
|
||||||
this.$root.getSocket().emit("getMonitorMaintenance", this.$route.params.id, (res) => {
|
this.$root.getSocket().emit("getMonitorMaintenance", this.$route.params.id, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@ -84,12 +83,10 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Confirm deletion */
|
|
||||||
deleteDialog() {
|
deleteDialog() {
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Delete maintenance after showing confirmation */
|
|
||||||
deleteMaintenance() {
|
deleteMaintenance() {
|
||||||
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -133,25 +133,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get maintenance URL
|
|
||||||
* @param {number} id
|
|
||||||
* @returns {string} Relative URL
|
|
||||||
*/
|
|
||||||
maintenanceURL(id) {
|
maintenanceURL(id) {
|
||||||
return getMaintenanceRelativeURL(id);
|
return getMaintenanceRelativeURL(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Show delete confirmation
|
|
||||||
* @param {number} maintenanceID
|
|
||||||
*/
|
|
||||||
deleteDialog(maintenanceID) {
|
deleteDialog(maintenanceID) {
|
||||||
this.selectedMaintenanceID = maintenanceID;
|
this.selectedMaintenanceID = maintenanceID;
|
||||||
this.$refs.confirmDelete.show();
|
this.$refs.confirmDelete.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Delete maintenance after showing confirmation dialog */
|
|
||||||
deleteMaintenance() {
|
deleteMaintenance() {
|
||||||
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -95,9 +95,6 @@ export default {
|
|||||||
"reverse-proxy": {
|
"reverse-proxy": {
|
||||||
title: this.$t("Reverse Proxy"),
|
title: this.$t("Reverse Proxy"),
|
||||||
},
|
},
|
||||||
tags: {
|
|
||||||
title: this.$t("Tags"),
|
|
||||||
},
|
|
||||||
"monitor-history": {
|
"monitor-history": {
|
||||||
title: this.$t("Monitor History"),
|
title: this.$t("Monitor History"),
|
||||||
},
|
},
|
||||||
@ -192,36 +189,14 @@ export default {
|
|||||||
* @param {string} [currentPassword] Only need for disableAuth to true
|
* @param {string} [currentPassword] Only need for disableAuth to true
|
||||||
*/
|
*/
|
||||||
saveSettings(callback, currentPassword) {
|
saveSettings(callback, currentPassword) {
|
||||||
let valid = this.validateSettings();
|
this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => {
|
||||||
if (valid.success) {
|
this.$root.toastRes(res);
|
||||||
this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => {
|
this.loadSettings();
|
||||||
this.$root.toastRes(res);
|
|
||||||
this.loadSettings();
|
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this.$root.toastError(valid.msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure settings are valid
|
|
||||||
* @returns {Object} Contains success state and error msg
|
|
||||||
*/
|
|
||||||
validateSettings() {
|
|
||||||
if (this.settings.keepDataPeriodDays < 0) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
msg: this.$t("dataRetentionTimeError"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
msg: "",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,6 @@ import Appearance from "./components/settings/Appearance.vue";
|
|||||||
import General from "./components/settings/General.vue";
|
import General from "./components/settings/General.vue";
|
||||||
const Notifications = () => import("./components/settings/Notifications.vue");
|
const Notifications = () => import("./components/settings/Notifications.vue");
|
||||||
import ReverseProxy from "./components/settings/ReverseProxy.vue";
|
import ReverseProxy from "./components/settings/ReverseProxy.vue";
|
||||||
import Tags from "./components/settings/Tags.vue";
|
|
||||||
import MonitorHistory from "./components/settings/MonitorHistory.vue";
|
import MonitorHistory from "./components/settings/MonitorHistory.vue";
|
||||||
const Security = () => import("./components/settings/Security.vue");
|
const Security = () => import("./components/settings/Security.vue");
|
||||||
import Proxies from "./components/settings/Proxies.vue";
|
import Proxies from "./components/settings/Proxies.vue";
|
||||||
@ -96,10 +95,6 @@ const routes = [
|
|||||||
path: "reverse-proxy",
|
path: "reverse-proxy",
|
||||||
component: ReverseProxy,
|
component: ReverseProxy,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "tags",
|
|
||||||
component: Tags,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "monitor-history",
|
path: "monitor-history",
|
||||||
component: MonitorHistory,
|
component: MonitorHistory,
|
||||||
|
@ -78,45 +78,3 @@ export function getResBaseURL() {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {} mqtt wheather or not the regex should take into account the fact that it is an mqtt uri
|
|
||||||
* @returns RegExp The requested regex
|
|
||||||
*/
|
|
||||||
export function hostNameRegexPattern(mqtt = false) {
|
|
||||||
// mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect)
|
|
||||||
const mqttSchemeRegexPattern = "((mqtt|ws)s?:\\/\\/)?";
|
|
||||||
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
|
|
||||||
const ipRegexPattern = `((^\\s*${mqtt ? mqttSchemeRegexPattern : ""}((([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
|
|
||||||
const hostNameRegexPattern = `^${mqtt ? mqttSchemeRegexPattern : ""}([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\\-_]*[A-Za-z0-9_])$`;
|
|
||||||
|
|
||||||
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the tag color options
|
|
||||||
* Shared between components
|
|
||||||
* @returns {Object[]}
|
|
||||||
*/
|
|
||||||
export function colorOptions(self) {
|
|
||||||
return [
|
|
||||||
{ name: self.$t("Gray"),
|
|
||||||
color: "#4B5563" },
|
|
||||||
{ name: self.$t("Red"),
|
|
||||||
color: "#DC2626" },
|
|
||||||
{ name: self.$t("Orange"),
|
|
||||||
color: "#D97706" },
|
|
||||||
{ name: self.$t("Green"),
|
|
||||||
color: "#059669" },
|
|
||||||
{ name: self.$t("Blue"),
|
|
||||||
color: "#2563EB" },
|
|
||||||
{ name: self.$t("Indigo"),
|
|
||||||
color: "#4F46E5" },
|
|
||||||
{ name: self.$t("Purple"),
|
|
||||||
color: "#7C3AED" },
|
|
||||||
{ name: self.$t("Pink"),
|
|
||||||
color: "#DB2777" },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
16
src/util.js
16
src/util.js
@ -315,11 +315,6 @@ function getMonitorRelativeURL(id) {
|
|||||||
return "/dashboard/" + id;
|
return "/dashboard/" + id;
|
||||||
}
|
}
|
||||||
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
exports.getMonitorRelativeURL = getMonitorRelativeURL;
|
||||||
/**
|
|
||||||
* Get relative path for maintenance
|
|
||||||
* @param id ID of maintenance
|
|
||||||
* @returns Formatted relative path
|
|
||||||
*/
|
|
||||||
function getMaintenanceRelativeURL(id) {
|
function getMaintenanceRelativeURL(id) {
|
||||||
return "/maintenance/" + id;
|
return "/maintenance/" + id;
|
||||||
}
|
}
|
||||||
@ -366,11 +361,6 @@ function parseTimeFromTimeObject(obj) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
exports.parseTimeFromTimeObject = parseTimeFromTimeObject;
|
||||||
/**
|
|
||||||
* Convert ISO date to UTC
|
|
||||||
* @param input Date
|
|
||||||
* @returns ISO Date time
|
|
||||||
*/
|
|
||||||
function isoToUTCDateTime(input) {
|
function isoToUTCDateTime(input) {
|
||||||
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT);
|
||||||
}
|
}
|
||||||
@ -389,12 +379,6 @@ function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) {
|
|||||||
return dayjs.utc(input).local().format(format);
|
return dayjs.utc(input).local().format(format);
|
||||||
}
|
}
|
||||||
exports.utcToLocal = utcToLocal;
|
exports.utcToLocal = utcToLocal;
|
||||||
/**
|
|
||||||
* Convert local datetime to UTC
|
|
||||||
* @param input Local date
|
|
||||||
* @param format Format to return
|
|
||||||
* @returns Date in requested format
|
|
||||||
*/
|
|
||||||
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) {
|
||||||
return dayjs(input).utc().format(format);
|
return dayjs(input).utc().format(format);
|
||||||
}
|
}
|
||||||
|
17
src/util.ts
17
src/util.ts
@ -352,11 +352,6 @@ export function getMonitorRelativeURL(id: string) {
|
|||||||
return "/dashboard/" + id;
|
return "/dashboard/" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get relative path for maintenance
|
|
||||||
* @param id ID of maintenance
|
|
||||||
* @returns Formatted relative path
|
|
||||||
*/
|
|
||||||
export function getMaintenanceRelativeURL(id: string) {
|
export function getMaintenanceRelativeURL(id: string) {
|
||||||
return "/maintenance/" + id;
|
return "/maintenance/" + id;
|
||||||
}
|
}
|
||||||
@ -410,11 +405,7 @@ export function parseTimeFromTimeObject(obj : any) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert ISO date to UTC
|
|
||||||
* @param input Date
|
|
||||||
* @returns ISO Date time
|
|
||||||
*/
|
|
||||||
export function isoToUTCDateTime(input : string) {
|
export function isoToUTCDateTime(input : string) {
|
||||||
return dayjs(input).utc().format(SQL_DATETIME_FORMAT);
|
return dayjs(input).utc().format(SQL_DATETIME_FORMAT);
|
||||||
}
|
}
|
||||||
@ -433,12 +424,6 @@ export function utcToLocal(input : string, format = SQL_DATETIME_FORMAT) {
|
|||||||
return dayjs.utc(input).local().format(format);
|
return dayjs.utc(input).local().format(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert local datetime to UTC
|
|
||||||
* @param input Local date
|
|
||||||
* @param format Format to return
|
|
||||||
* @returns Date in requested format
|
|
||||||
*/
|
|
||||||
export function localToUTC(input : string, format = SQL_DATETIME_FORMAT) {
|
export function localToUTC(input : string, format = SQL_DATETIME_FORMAT) {
|
||||||
return dayjs(input).utc().format(format);
|
return dayjs(input).utc().format(format);
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import { currentLocale } from "../../../src/i18n";
|
|
||||||
|
|
||||||
describe("Test i18n.js", () => {
|
|
||||||
|
|
||||||
it("currentLocale()", () => {
|
|
||||||
const setLanguage = (language) => {
|
|
||||||
Object.defineProperty(window.navigator, 'language', {
|
|
||||||
value: language,
|
|
||||||
writable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setLanguage('en-EN');
|
|
||||||
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
setLanguage('zh-HK');
|
|
||||||
expect(currentLocale()).equal("zh-HK");
|
|
||||||
|
|
||||||
// Note that in Safari on iOS prior to 10.2, the country code returned is lowercase: "en-us", "fr-fr" etc.
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language
|
|
||||||
setLanguage('zh-hk');
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
setLanguage('en-US');
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
setLanguage('ja-ZZ');
|
|
||||||
expect(currentLocale()).equal("ja");
|
|
||||||
|
|
||||||
setLanguage('zz-ZZ');
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
setLanguage('zz-ZZ');
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
setLanguage('en');
|
|
||||||
localStorage.locale = "en";
|
|
||||||
expect(currentLocale()).equal("en");
|
|
||||||
|
|
||||||
localStorage.locale = "zh-HK";
|
|
||||||
expect(currentLocale()).equal("zh-HK");
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,33 +0,0 @@
|
|||||||
import { hostNameRegexPattern } from "../../../src/util-frontend";
|
|
||||||
|
|
||||||
describe("Test util-frontend.js", () => {
|
|
||||||
|
|
||||||
describe("hostNameRegexPattern()", () => {
|
|
||||||
it('should return a valid regex for non mqtt hostnames', () => {
|
|
||||||
const regex = new RegExp(hostNameRegexPattern(false));
|
|
||||||
|
|
||||||
expect(regex.test("www.test.com")).to.be.true;
|
|
||||||
expect(regex.test("127.0.0.1")).to.be.true;
|
|
||||||
expect(regex.test("192.168.1.156")).to.be.true;
|
|
||||||
|
|
||||||
["mqtt", "mqtts", "ws", "wss"].forEach(schema => {
|
|
||||||
expect(regex.test(`${schema}://www.test.com`)).to.be.false;
|
|
||||||
expect(regex.test(`${schema}://127.0.0.1`)).to.be.false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should return a valid regex for mqtt hostnames', () => {
|
|
||||||
const hostnameString = hostNameRegexPattern(false);
|
|
||||||
console.log('*********', hostnameString, '***********');
|
|
||||||
const regex = new RegExp(hostNameRegexPattern(true));
|
|
||||||
|
|
||||||
expect(regex.test("www.test.com")).to.be.true;
|
|
||||||
expect(regex.test("127.0.0.1")).to.be.true;
|
|
||||||
expect(regex.test("192.168.1.156")).to.be.true;
|
|
||||||
|
|
||||||
["mqtt", "mqtts", "ws", "wss"].forEach(schema => {
|
|
||||||
expect(regex.test(`${schema}://www.test.com`)).to.be.true;
|
|
||||||
expect(regex.test(`${schema}://127.0.0.1`)).to.be.true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user