diff --git a/.dockerignore b/.dockerignore index 3d92084d..539e9328 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,7 +19,6 @@ README.md .eslint* .stylelint* /.github -package-lock.json yarn.lock app.json CODE_OF_CONDUCT.md diff --git a/README.md b/README.md index f0fa92d9..4ad1c555 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Uptime Kuma - + +
diff --git a/db/patch-monitor-push_token.sql b/db/patch-monitor-push_token.sql new file mode 100644 index 00000000..8c2e7a42 --- /dev/null +++ b/db/patch-monitor-push_token.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD push_token VARCHAR(20) DEFAULT NULL; + +COMMIT; diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile new file mode 100644 index 00000000..922fd252 --- /dev/null +++ b/docker/alpine-base.dockerfile @@ -0,0 +1,8 @@ +# DON'T UPDATE TO alpine3.13, 1.14, see #41. +FROM node:14-alpine3.12 +WORKDIR /app + +# Install apprise, iputils for non-root ping, setpriv +RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ + pip3 --no-cache-dir install apprise && \ + rm -rf /root/.cache diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile new file mode 100644 index 00000000..a4d70167 --- /dev/null +++ b/docker/debian-base.dockerfile @@ -0,0 +1,12 @@ +# DON'T UPDATE TO node:14-bullseye-slim, see #372. +# If the image changed, the second stage image should be changed too +FROM node:14-buster-slim +WORKDIR /app + +# Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv +# Stupid python3 and python3-pip actually install a lot of useless things into Debian, specific --no-install-recommends to skip them, make the base even smaller than alpine! +RUN apt update && \ + apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ + sqlite3 iputils-ping util-linux dumb-init && \ + pip3 --no-cache-dir install apprise && \ + rm -rf /var/lib/apt/lists/* diff --git a/dockerfile b/dockerfile index e0616ba2..969d5ff3 100644 --- a/dockerfile +++ b/dockerfile @@ -1,6 +1,4 @@ -# DON'T UPDATE TO node:14-bullseye-slim, see #372. -# If the image changed, the second stage image should be changed too -FROM node:14-buster-slim AS build +FROM louislam/uptime-kuma:base-debian AS build WORKDIR /app COPY . . @@ -10,16 +8,9 @@ RUN npm install --legacy-peer-deps && \ chmod +x /app/extra/entrypoint.sh -FROM node:14-buster-slim AS release +FROM louislam/uptime-kuma:base-debian AS release WORKDIR /app -# Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv -RUN apt update && \ - apt --yes install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ - sqlite3 iputils-ping util-linux dumb-init && \ - pip3 --no-cache-dir install apprise && \ - rm -rf /var/lib/apt/lists/* - # Copy app files from build layer COPY --from=build /app /app @@ -33,7 +24,7 @@ FROM release AS nightly RUN npm run mark-as-nightly # Upload the artifact to Github -FROM node:14-buster-slim AS upload-artifact +FROM louislam/uptime-kuma:base-debian AS upload-artifact WORKDIR / RUN apt update && \ apt --yes install curl file @@ -41,17 +32,18 @@ RUN apt update && \ ARG GITHUB_TOKEN ARG TARGETARCH ARG PLATFORM=debian -ARG VERSION=1.5.0 - +ARG VERSION +ARG FILE=$PLATFORM-$TARGETARCH-$VERSION.tar.gz +ARG DIST=dist.tar.gz COPY --from=build /app /app +RUN chmod +x /app/extra/upload-github-release-asset.sh -RUN FILE=uptime-kuma.tar.gz -RUN tar -czf $FILE app +# Full Build +# RUN tar -zcvf $FILE app +# RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=$FILE -RUN curl \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: $(file -b --mime-type $FILE)" \ - --data-binary @$FILE \ - "https://uploads.github.com/repos/louislam/uptime-kuma/releases/$VERSION/assets?name=$(basename $FILE)" +# Dist only +RUN cd /app && tar -zcvf $DIST dist +RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=$DIST diff --git a/dockerfile-alpine b/dockerfile-alpine index 07ae3079..178afcab 100644 --- a/dockerfile-alpine +++ b/dockerfile-alpine @@ -1,5 +1,4 @@ -# DON'T UPDATE TO alpine3.13, 1.14, see #41. -FROM node:14-alpine3.12 AS build +FROM louislam/uptime-kuma:base-alpine AS build WORKDIR /app COPY . . @@ -9,14 +8,9 @@ RUN npm install --legacy-peer-deps && \ chmod +x /app/extra/entrypoint.sh -FROM node:14-alpine3.12 AS release +FROM louislam/uptime-kuma:base-alpine AS release WORKDIR /app -# Install apprise, iputils for non-root ping, setpriv -RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ - pip3 --no-cache-dir install apprise && \ - rm -rf /root/.cache - # Copy app files from build layer COPY --from=build /app /app diff --git a/extra/upload-github-release-asset.sh b/extra/upload-github-release-asset.sh new file mode 100644 index 00000000..206e3cd6 --- /dev/null +++ b/extra/upload-github-release-asset.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# +# Author: Stefan Buck +# License: MIT +# https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 +# +# +# This script accepts the following parameters: +# +# * owner +# * repo +# * tag +# * filename +# * github_api_token +# +# Script to upload a release asset using the GitHub API v3. +# +# Example: +# +# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip +# + +# Check dependencies. +set -e +xargs=$(which gxargs || which xargs) + +# Validate settings. +[ "$TRACE" ] && set -x + +CONFIG=$@ + +for line in $CONFIG; do + eval "$line" +done + +# Define variables. +GH_API="https://api.github.com" +GH_REPO="$GH_API/repos/$owner/$repo" +GH_TAGS="$GH_REPO/releases/tags/$tag" +AUTH="Authorization: token $github_api_token" +WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie" +CURL_ARGS="-LJO#" + +if [[ "$tag" == 'LATEST' ]]; then + GH_TAGS="$GH_REPO/releases/latest" +fi + +# Validate token. +curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } + +# Read asset tags. +response=$(curl -sH "$AUTH" $GH_TAGS) + +# Get ID of the asset based on given filename. +eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=') +[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; } + +# Upload asset +echo "Uploading asset... " + +# Construct url +GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)" + +curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET diff --git a/package-lock.json b/package-lock.json index 88053f53..34615b9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,75 +1,77 @@ { "name": "uptime-kuma", - "version": "1.7.2", + "version": "1.7.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.7.2", + "version": "1.7.3", "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-regular-svg-icons": "^5.15.4", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/vue-fontawesome": "^3.0.0-4", - "@louislam/sqlite3": "^5.0.6", - "@popperjs/core": "^2.10.1", - "args-parser": "^1.3.0", - "axios": "^0.21.4", - "bcryptjs": "^2.4.3", - "bootstrap": "^5.1.1", - "chart.js": "^3.5.1", - "chartjs-adapter-dayjs": "^1.0.0", - "command-exists": "^1.2.9", - "compare-versions": "^3.6.0", - "dayjs": "^1.10.7", - "express": "^4.17.1", - "express-basic-auth": "^1.2.0", - "form-data": "^4.0.0", - "http-graceful-shutdown": "^3.1.4", - "jsonwebtoken": "^8.5.1", - "nodemailer": "^6.6.5", - "notp": "^2.0.3", - "password-hash": "^1.2.2", - "prom-client": "^13.2.0", - "prometheus-api-metrics": "^3.2.0", - "qrcode": "^1.4.4", + "@fortawesome/fontawesome-svg-core": "~1.2.36", + "@fortawesome/free-regular-svg-icons": "~5.15.4", + "@fortawesome/free-solid-svg-icons": "~5.15.4", + "@fortawesome/vue-fontawesome": "~3.0.0-4", + "@louislam/sqlite3": "~5.0.6", + "@popperjs/core": "~2.10.1", + "args-parser": "~1.3.0", + "axios": "~0.21.4", + "bcryptjs": "~2.4.3", + "bootstrap": "~5.1.1", + "chart.js": "~3.5.1", + "chartjs-adapter-dayjs": "~1.0.0", + "command-exists": "~1.2.9", + "compare-versions": "~3.6.0", + "dayjs": "~1.10.7", + "express": "~4.17.1", + "express-basic-auth": "~1.2.0", + "form-data": "~4.0.0", + "http-graceful-shutdown": "~3.1.4", + "jsonwebtoken": "~8.5.1", + "nodemailer": "~6.6.5", + "notp": "~2.0.3", + "password-hash": "~1.2.2", + "postcss-rtlcss": "~3.4.1", + "postcss-scss": "~4.0.0", + "prom-client": "~13.2.0", + "prometheus-api-metrics": "~3.2.0", + "qrcode": "~1.4.4", "redbean-node": "0.1.2", - "socket.io": "^4.2.0", - "socket.io-client": "^4.2.0", - "tcp-ping": "^0.1.1", - "thirty-two": "^1.0.2", - "timezones-list": "^3.0.1", - "v-pagination-3": "^0.1.6", + "socket.io": "~4.2.0", + "socket.io-client": "~4.2.0", + "tcp-ping": "~0.1.1", + "thirty-two": "~1.0.2", + "timezones-list": "~3.0.1", + "v-pagination-3": "~0.1.6", "vue": "next", - "vue-chart-3": "^0.5.8", - "vue-confirm-dialog": "^1.0.2", - "vue-contenteditable": "^3.0.4", - "vue-i18n": "^9.1.7", - "vue-image-crop-upload": "^3.0.3", - "vue-multiselect": "^3.0.0-alpha.2", - "vue-qrcode": "^1.0.0", - "vue-router": "^4.0.11", - "vue-toastification": "^2.0.0-rc.1", - "vuedraggable": "^4.1.0" + "vue-chart-3": "~0.5.8", + "vue-confirm-dialog": "~1.0.2", + "vue-contenteditable": "~3.0.4", + "vue-i18n": "~9.1.7", + "vue-image-crop-upload": "~3.0.3", + "vue-multiselect": "~3.0.0-alpha.2", + "vue-qrcode": "~1.0.0", + "vue-router": "~4.0.11", + "vue-toastification": "~2.0.0-rc.1", + "vuedraggable": "~4.1.0" }, "devDependencies": { - "@babel/eslint-parser": "^7.15.7", - "@types/bootstrap": "^5.1.6", - "@vitejs/plugin-legacy": "^1.5.3", - "@vitejs/plugin-vue": "^1.9.1", - "@vue/compiler-sfc": "^3.2.16", - "core-js": "^3.18.0", - "cross-env": "^7.0.3", - "dns2": "^2.0.1", - "eslint": "^7.32.0", - "eslint-plugin-vue": "^7.18.0", - "sass": "^1.42.1", - "stylelint": "^13.13.1", - "stylelint-config-standard": "^22.0.0", - "typescript": "^4.4.3", - "vite": "2.5.*" + "@babel/eslint-parser": "~7.15.7", + "@types/bootstrap": "~5.1.6", + "@vitejs/plugin-legacy": "~1.5.3", + "@vitejs/plugin-vue": "~1.9.1", + "@vue/compiler-sfc": "~3.2.16", + "core-js": "~3.18.0", + "cross-env": "~7.0.3", + "dns2": "~2.0.1", + "eslint": "~7.32.0", + "eslint-plugin-vue": "~7.18.0", + "sass": "~1.42.1", + "stylelint": "~13.13.1", + "stylelint-config-standard": "~22.0.0", + "typescript": "~4.4.3", + "vite": "~2.5.10" }, "engines": { "node": "14.*" @@ -1056,16 +1058,16 @@ "dev": true }, "node_modules/@vitejs/plugin-legacy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.0.tgz", - "integrity": "sha512-MrOT7DWJyln10Eobh38TL9Pg0yDjRec5ZlK0Opi+jZA/qniXgofvGJskOyvfbSKKmUkjLO2dUDLz2rIm2oIYtw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz", + "integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==", "dev": true, "dependencies": { - "@babel/standalone": "^7.15.7", - "core-js": "^3.18.1", + "@babel/standalone": "^7.14.9", + "core-js": "^3.16.0", "magic-string": "^0.25.7", "regenerator-runtime": "^0.13.9", - "systemjs": "^6.10.3" + "systemjs": "^6.10.2" }, "engines": { "node": ">=12.0.0" @@ -1280,7 +1282,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1783,7 +1784,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1942,7 +1942,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1953,8 +1952,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/colorette": { "version": "1.2.1", @@ -3345,7 +3343,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -4779,7 +4776,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -4930,6 +4926,20 @@ "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", "dev": true }, + "node_modules/postcss-rtlcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.4.1.tgz", + "integrity": "sha512-4SOkC34IJ086dYjmqGCeIOqQe4JTDk+jwETvq1M/57+bQA6CXEWAjGtqifjcSH75nd0vfW7+hve0Ec4ZYHmMtA==", + "dependencies": { + "rtlcss": "^3.3.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, "node_modules/postcss-safe-parser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", @@ -4987,32 +4997,18 @@ } }, "node_modules/postcss-scss": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", - "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.6" - }, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.1.tgz", + "integrity": "sha512-7QghUu2l07OyVFT5LyvU/QJ1f2s8IL0mfToN69Yu533PgMZm2B1S6hYd4bao8tFq70r3P5MmAbKhVrZ4wOADxg==", "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-scss/node_modules/postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", - "dev": true, - "dependencies": { - "nanocolors": "^0.2.2", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" + "node": ">=12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" } }, "node_modules/postcss-selector-parser": { @@ -5562,6 +5558,81 @@ "fsevents": "~2.3.2" } }, + "node_modules/rtlcss": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.3.0.tgz", + "integrity": "sha512-XZ2KEatH2nU5yPlts1Wu8SGIuZ3ndN025HQX5MqtUCUiOn5WkCDbcpJ2VJWjpuFmM2cUTQ1xtH21fhMCSseI5A==", + "dependencies": { + "chalk": "^4.1.0", + "find-up": "^5.0.0", + "mkdirp": "^1.0.4", + "postcss": "^8.2.4", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + }, + "peerDependencies": { + "postcss": "^8.2.4" + } + }, + "node_modules/rtlcss/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5972,7 +6043,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -6120,6 +6190,18 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/stylelint/node_modules/postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -6173,7 +6255,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7222,6 +7303,17 @@ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", @@ -8021,16 +8113,16 @@ "dev": true }, "@vitejs/plugin-legacy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.6.0.tgz", - "integrity": "sha512-MrOT7DWJyln10Eobh38TL9Pg0yDjRec5ZlK0Opi+jZA/qniXgofvGJskOyvfbSKKmUkjLO2dUDLz2rIm2oIYtw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.5.3.tgz", + "integrity": "sha512-/b2x6dU+BbdW7C7KWxh9kMrVzv1JlUi1ucPQpSzWUUUVJjihbG+GRlpqcvfQ0p/TnAKl2d/VecbTLByVJJHORg==", "dev": true, "requires": { - "@babel/standalone": "^7.15.7", - "core-js": "^3.18.1", + "@babel/standalone": "^7.14.9", + "core-js": "^3.16.0", "magic-string": "^0.25.7", "regenerator-runtime": "^0.13.9", - "systemjs": "^6.10.3" + "systemjs": "^6.10.2" } }, "@vitejs/plugin-vue": { @@ -8207,7 +8299,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -8593,7 +8684,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8707,7 +8797,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -8715,8 +8804,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "colorette": { "version": "1.2.1", @@ -9804,8 +9892,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-unicode": { "version": "2.0.1", @@ -10853,8 +10940,7 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", @@ -10962,6 +11048,14 @@ "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", "dev": true }, + "postcss-rtlcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.4.1.tgz", + "integrity": "sha512-4SOkC34IJ086dYjmqGCeIOqQe4JTDk+jwETvq1M/57+bQA6CXEWAjGtqifjcSH75nd0vfW7+hve0Ec4ZYHmMtA==", + "requires": { + "rtlcss": "^3.3.0" + } + }, "postcss-safe-parser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", @@ -11006,25 +11100,10 @@ } }, "postcss-scss": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", - "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", - "dev": true, - "requires": { - "postcss": "^7.0.6" - }, - "dependencies": { - "postcss": { - "version": "7.0.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.38.tgz", - "integrity": "sha512-wNrSHWjHDQJR/IZL5IKGxRtFgrYNaAA/UrkW2WqbtZO6uxSLMxMN+s2iqUMwnAWm3fMROlDYZB41dr0Mt7vBwQ==", - "dev": true, - "requires": { - "nanocolors": "^0.2.2", - "source-map": "^0.6.1" - } - } - } + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.1.tgz", + "integrity": "sha512-7QghUu2l07OyVFT5LyvU/QJ1f2s8IL0mfToN69Yu533PgMZm2B1S6hYd4bao8tFq70r3P5MmAbKhVrZ4wOADxg==", + "requires": {} }, "postcss-selector-parser": { "version": "6.0.6", @@ -11444,6 +11523,53 @@ "fsevents": "~2.3.2" } }, + "rtlcss": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.3.0.tgz", + "integrity": "sha512-XZ2KEatH2nU5yPlts1Wu8SGIuZ3ndN025HQX5MqtUCUiOn5WkCDbcpJ2VJWjpuFmM2cUTQ1xtH21fhMCSseI5A==", + "requires": { + "chalk": "^4.1.0", + "find-up": "^5.0.0", + "mkdirp": "^1.0.4", + "postcss": "^8.2.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11770,8 +11896,7 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "style-search": { "version": "0.1.0", @@ -11869,6 +11994,15 @@ "source-map": "^0.6.1" } }, + "postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "requires": { + "postcss": "^7.0.6" + } + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -11929,7 +12063,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -12706,6 +12839,11 @@ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index 87b30565..17bfe975 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "tsc": "tsc", "vite-preview-dist": "vite preview --host", "build-docker": "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-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-alpine": "docker buildx build -f 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:1.7.3-alpine --target release . --push", "build-docker-debian": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.7.3 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.7.3-debian --target release . --push", "build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", @@ -44,66 +46,68 @@ "update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix" }, "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-regular-svg-icons": "^5.15.4", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/vue-fontawesome": "^3.0.0-4", - "@louislam/sqlite3": "^5.0.6", - "@popperjs/core": "^2.10.1", - "args-parser": "^1.3.0", - "axios": "^0.21.4", - "bcryptjs": "^2.4.3", - "bootstrap": "^5.1.1", - "chart.js": "^3.5.1", - "chartjs-adapter-dayjs": "^1.0.0", - "command-exists": "^1.2.9", - "compare-versions": "^3.6.0", - "dayjs": "^1.10.7", - "express": "^4.17.1", - "express-basic-auth": "^1.2.0", - "form-data": "^4.0.0", - "http-graceful-shutdown": "^3.1.4", - "jsonwebtoken": "^8.5.1", - "nodemailer": "^6.6.5", - "notp": "^2.0.3", - "password-hash": "^1.2.2", - "prom-client": "^13.2.0", - "prometheus-api-metrics": "^3.2.0", - "qrcode": "^1.4.4", + "@fortawesome/fontawesome-svg-core": "~1.2.36", + "@fortawesome/free-regular-svg-icons": "~5.15.4", + "@fortawesome/free-solid-svg-icons": "~5.15.4", + "@fortawesome/vue-fontawesome": "~3.0.0-4", + "@louislam/sqlite3": "~5.0.6", + "@popperjs/core": "~2.10.1", + "args-parser": "~1.3.0", + "axios": "~0.21.4", + "bcryptjs": "~2.4.3", + "bootstrap": "~5.1.1", + "chart.js": "~3.5.1", + "chartjs-adapter-dayjs": "~1.0.0", + "command-exists": "~1.2.9", + "compare-versions": "~3.6.0", + "dayjs": "~1.10.7", + "express": "~4.17.1", + "express-basic-auth": "~1.2.0", + "form-data": "~4.0.0", + "http-graceful-shutdown": "~3.1.4", + "jsonwebtoken": "~8.5.1", + "nodemailer": "~6.6.5", + "notp": "~2.0.3", + "password-hash": "~1.2.2", + "postcss-rtlcss": "~3.4.1", + "postcss-scss": "~4.0.0", + "prom-client": "~13.2.0", + "prometheus-api-metrics": "~3.2.0", + "qrcode": "~1.4.4", "redbean-node": "0.1.2", - "socket.io": "^4.2.0", - "socket.io-client": "^4.2.0", - "tcp-ping": "^0.1.1", - "thirty-two": "^1.0.2", - "timezones-list": "^3.0.1", - "v-pagination-3": "^0.1.6", + "socket.io": "~4.2.0", + "socket.io-client": "~4.2.0", + "tcp-ping": "~0.1.1", + "thirty-two": "~1.0.2", + "timezones-list": "~3.0.1", + "v-pagination-3": "~0.1.6", "vue": "next", - "vue-chart-3": "^0.5.8", - "vue-confirm-dialog": "^1.0.2", - "vue-contenteditable": "^3.0.4", - "vue-i18n": "^9.1.7", - "vue-image-crop-upload": "^3.0.3", - "vue-multiselect": "^3.0.0-alpha.2", - "vue-qrcode": "^1.0.0", - "vue-router": "^4.0.11", - "vue-toastification": "^2.0.0-rc.1", - "vuedraggable": "^4.1.0" + "vue-chart-3": "~0.5.8", + "vue-confirm-dialog": "~1.0.2", + "vue-contenteditable": "~3.0.4", + "vue-i18n": "~9.1.7", + "vue-image-crop-upload": "~3.0.3", + "vue-multiselect": "~3.0.0-alpha.2", + "vue-qrcode": "~1.0.0", + "vue-router": "~4.0.11", + "vue-toastification": "~2.0.0-rc.1", + "vuedraggable": "~4.1.0" }, "devDependencies": { - "@babel/eslint-parser": "^7.15.7", - "@types/bootstrap": "^5.1.6", - "@vitejs/plugin-legacy": "^1.5.3", - "@vitejs/plugin-vue": "^1.9.1", - "@vue/compiler-sfc": "^3.2.16", - "core-js": "^3.18.0", - "cross-env": "^7.0.3", - "dns2": "^2.0.1", - "eslint": "^7.32.0", - "eslint-plugin-vue": "^7.18.0", - "sass": "^1.42.1", - "stylelint": "^13.13.1", - "stylelint-config-standard": "^22.0.0", - "typescript": "^4.4.3", - "vite": "2.5.*" + "@babel/eslint-parser": "~7.15.7", + "@types/bootstrap": "~5.1.6", + "@vitejs/plugin-legacy": "~1.5.3", + "@vitejs/plugin-vue": "~1.9.1", + "@vue/compiler-sfc": "~3.2.16", + "core-js": "~3.18.0", + "cross-env": "~7.0.3", + "dns2": "~2.0.1", + "eslint": "~7.32.0", + "eslint-plugin-vue": "~7.18.0", + "sass": "~1.42.1", + "stylelint": "~13.13.1", + "stylelint-config-standard": "~22.0.0", + "typescript": "~4.4.3", + "vite": "~2.5.10" } } diff --git a/server/database.js b/server/database.js index 4cf1e393..47eca283 100644 --- a/server/database.js +++ b/server/database.js @@ -48,6 +48,7 @@ class Database { "patch-add-retry-interval-monitor.sql": true, "patch-incident-table.sql": true, "patch-group-table.sql": true, + "patch-monitor-push_token.sql": true, } /** diff --git a/server/model/monitor.js b/server/model/monitor.js index a50baccf..c551fa7d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -69,6 +69,7 @@ class Monitor extends BeanModel { dns_resolve_type: this.dns_resolve_type, dns_resolve_server: this.dns_resolve_server, dns_last_result: this.dns_last_result, + pushToken: this.pushToken, notificationIDList, tags: tags, }; @@ -236,6 +237,28 @@ class Monitor extends BeanModel { bean.msg = dnsMessage; bean.status = UP; + } else if (this.type === "push") { // Type: Push + const time = R.isoDateTime(dayjs.utc().subtract(this.interval, "second")); + + let heartbeatCount = await R.count("heartbeat", " monitor_id = ? AND time > ? ", [ + this.id, + time + ]); + + debug("heartbeatCount" + heartbeatCount + " " + time); + + if (heartbeatCount <= 0) { + throw new Error("No heartbeat in the time window"); + } else { + // No need to insert successful heartbeat for push type, so end here + retries = 0; + this.heartbeatInterval = setTimeout(beat, this.interval * 1000); + return; + } + + } else { + bean.msg = "Unknown Monitor Type"; + bean.status = PENDING; } if (this.isUpsideDown()) { @@ -263,6 +286,8 @@ class Monitor extends BeanModel { } } + let beatInterval = this.interval; + // * ? -> ANY STATUS = important [isFirstBeat] // UP -> PENDING = not important // * UP -> DOWN = important @@ -312,8 +337,6 @@ class Monitor extends BeanModel { bean.important = false; } - let beatInterval = this.interval; - if (bean.status === UP) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`); } else if (bean.status === PENDING) { @@ -339,7 +362,14 @@ class Monitor extends BeanModel { }; - beat(); + // Delay Push Type + if (this.type === "push") { + setTimeout(() => { + beat(); + }, this.interval * 1000); + } else { + beat(); + } } stop() { diff --git a/server/routers/api-router.js b/server/routers/api-router.js index b56efcb2..0da1fd70 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -4,15 +4,55 @@ const { R } = require("redbean-node"); const server = require("../server"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); +const dayjs = require("dayjs"); +const { UP } = require("../../src/util"); let router = express.Router(); let cache = apicache.middleware; +let io = server.io; router.get("/api/entry-page", async (_, response) => { allowDevAllOrigin(response); response.json(server.entryPage); }); +router.get("/api/push/:pushToken", async (request, response) => { + try { + let pushToken = request.params.pushToken; + let msg = request.query.msg || "OK"; + let ping = request.query.ping; + + let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ + pushToken + ]); + + if (! monitor) { + throw new Error("Monitor not found or not active."); + } + + let bean = R.dispense("heartbeat"); + bean.monitor_id = monitor.id; + bean.time = R.isoDateTime(dayjs.utc()); + bean.status = UP; + bean.msg = msg; + bean.ping = ping; + + await R.store(bean); + + io.to(monitor.user_id).emit("heartbeat", bean.toJSON()); + Monitor.sendStats(io, monitor.id, monitor.user_id); + + response.json({ + ok: true, + }); + } catch (e) { + response.json({ + ok: false, + msg: e.message + }); + } +}); + // Status Page Config router.get("/api/status-page/config", async (_request, response) => { allowDevAllOrigin(response); diff --git a/server/server.js b/server/server.js index ddd68695..0fbe8325 100644 --- a/server/server.js +++ b/server/server.js @@ -6,7 +6,7 @@ if (! process.env.NODE_ENV) { console.log("Node Env: " + process.env.NODE_ENV); -const { sleep, debug, TimeLogger, getRandomInt } = require("../src/util"); +const { sleep, debug, getRandomInt, genSecret } = require("../src/util"); console.log("Importing Node libraries"); const fs = require("fs"); @@ -37,7 +37,7 @@ console.log("Importing this project modules"); debug("Importing Monitor"); const Monitor = require("./model/monitor"); debug("Importing Settings"); -const { getSettings, setSettings, setting, initJWTSecret, genSecret, allowDevAllOrigin, checkLogin } = require("./util-server"); +const { getSettings, setSettings, setting, initJWTSecret, checkLogin } = require("./util-server"); debug("Importing Notification"); const { Notification } = require("./notification"); @@ -71,7 +71,7 @@ if (demoMode) { console.log("==== Demo Mode ===="); } -console.log("Creating express and socket.io instance") +console.log("Creating express and socket.io instance"); const app = express(); let server; @@ -303,6 +303,12 @@ exports.entryPage = "dashboard"; if (user.twofa_status == 0) { let newSecret = await genSecret(); let encodedSecret = base32.encode(newSecret); + + // Google authenticator doesn't like equal signs + // The fix is found at https://github.com/guyht/notp + // Related issue: https://github.com/louislam/uptime-kuma/issues/486 + encodedSecret = encodedSecret.toString().replace(/=/g, ""); + let uri = `otpauth://totp/Uptime%20Kuma:${user.username}?secret=${encodedSecret}`; await R.exec("UPDATE `user` SET twofa_secret = ? WHERE id = ? ", [ @@ -511,6 +517,7 @@ exports.entryPage = "dashboard"; bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes); bean.dns_resolve_type = monitor.dns_resolve_type; bean.dns_resolve_server = monitor.dns_resolve_server; + bean.pushToken = monitor.pushToken; await R.store(bean); diff --git a/server/util-server.js b/server/util-server.js index 4d2b6cbe..29e4b11f 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -272,16 +272,6 @@ exports.getTotalClientInRoom = (io, roomName) => { } }; -exports.genSecret = () => { - let secret = ""; - let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let charsLength = chars.length; - for ( let i = 0; i < 64; i++ ) { - secret += chars.charAt(Math.floor(Math.random() * charsLength)); - } - return secret; -}; - exports.allowDevAllOrigin = (res) => { if (process.env.NODE_ENV === "development") { exports.allowAllOrigin(res); diff --git a/src/App.vue b/src/App.vue index a16d4208..099450d4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,7 +1,12 @@ diff --git a/src/assets/app.scss b/src/assets/app.scss index f4707df9..34a4560c 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -180,6 +180,11 @@ h2 { border-color: $dark-border-color; } + .form-control:disabled, .form-control[readonly] { + background-color: #232f3b; + opacity: 1; + } + .table-hover > tbody > tr:hover { --bs-table-accent-bg: #070a10; color: $dark-font-color; @@ -405,3 +410,7 @@ h2 { .vue-image-crop-upload .vicp-wrap { border-radius: 10px !important; } + +// Localization + +@import "localization.scss"; \ No newline at end of file diff --git a/src/assets/localization.scss b/src/assets/localization.scss new file mode 100644 index 00000000..f9a28d8a --- /dev/null +++ b/src/assets/localization.scss @@ -0,0 +1,5 @@ +html[lang='fa'] { + #app { + 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; + } +} \ No newline at end of file diff --git a/src/components/CopyableInput.vue b/src/components/CopyableInput.vue new file mode 100644 index 00000000..1fe89802 --- /dev/null +++ b/src/components/CopyableInput.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/components/Login.vue b/src/components/Login.vue index ca36fdb9..8a20fbdb 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -52,7 +52,7 @@ export default { token: "", res: null, tokenRequired: false, - } + }; }, methods: { submit() { @@ -60,21 +60,20 @@ export default { this.$root.login(this.username, this.password, this.token, (res) => { this.processing = false; - console.log(res) + console.log(res); if (res.tokenRequired) { this.tokenRequired = true; } else { this.res = res; } - }) + }); }, }, -} +}; -