diff --git a/.dockerignore b/.dockerignore index b68a8a694..d439b2db5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,11 @@ /node_modules /data/kuma.db /.do +**/.dockerignore +**/.git +**/.gitignore +**/docker-compose* +**/Dockerfile* +LICENSE +README.md +.editorconfig diff --git a/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md b/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md new file mode 100644 index 000000000..eb8623709 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md @@ -0,0 +1,10 @@ +--- +name: ⚠ Please go to "Discussions" Tab if you want to ask or share something +about: BUG REPORT ONLY HERE +title: '' +labels: '' +assignees: '' + +--- + +BUG REPORT ONLY HERE diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..cea1fc16e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Uptime Kuma Version: + - Using Docker?: Yes/No + - OS: + - Browser: + + +**Additional context** +Add any other context about the problem here. diff --git a/README.md b/README.md index ed971a14a..7777cb2c5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ It is a self-hosted monitoring tool like "Uptime Robot". docker volume create uptime-kuma # Start the container -docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma +docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1 ``` Browse to http://localhost:3001 after started. @@ -35,7 +35,7 @@ Browse to http://localhost:3001 after started. Change Port and Volume ```bash -docker run -d --restart=always -p :3001 -v :/app/data --name uptime-kuma louislam/uptime-kuma +docker run -d --restart=always -p :3001 -v :/app/data --name uptime-kuma louislam/uptime-kuma:1 ``` ### Without Docker @@ -80,12 +80,17 @@ PS: For every new release, it takes some time to build the docker image, please ```bash git fetch --all -git checkout 1.0.4 --force +git checkout 1.0.6 --force npm install npm run build pm2 restart uptime-kuma ``` +# What's Next? + +I will mark requests/issues to the next milestone. +https://github.com/louislam/uptime-kuma/milestones + # More Screenshots Settings Page: @@ -109,3 +114,11 @@ Telegram Notification Sample: If you love this project, please consider giving me a ⭐. + +# Contribute + +If you want to report a bug or request a new feature. Free feel to open a new issue. + +If you want to modify Uptime Kuma, this guideline maybe useful for you: https://github.com/louislam/uptime-kuma/wiki/%5BDev%5D-Setup-Development-Environment + +English proofreading is needed too, because my grammar is not that great sadly. Feel free to correct my grammar in this Readme, source code or wiki. diff --git a/dockerfile b/dockerfile index d21c22bee..1ebf00999 100644 --- a/dockerfile +++ b/dockerfile @@ -1,5 +1,5 @@ # DON'T UPDATE TO alpine3.13, 1.14, see #41. -FROM node:14-alpine3.12 +FROM node:14-alpine3.12 AS release WORKDIR /app # split the sqlite install here, so that it can caches the arm prebuilt @@ -17,11 +17,14 @@ RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev && \ # Compilation Fail 3 => Google Search "alpine opensslv.h" => Add openssl-dev # Compilation Fail 4 => Google Search "alpine opensslv.h" again => Change to libressl-dev musl-dev # Compilation Fail 5 => Google Search "ERROR: libressl3.3-libtls-3.3.3-r0: trying to overwrite usr/lib/libtls.so.20 owned by libretls-3.3.3-r0." again => Change back to openssl-dev with musl-dev +# Runtime Error => ModuleNotFoundError: No module named 'six' => pip3 install six +# Runtime Error 2 => ModuleNotFoundError: No module named 'six' => apk add py3-six ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1 -RUN apk add --no-cache python3 -RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev cargo py3-pip python3-dev && \ +RUN apk add --no-cache python3 py3-pip py3-six cargo +RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev python3-dev && \ pip3 install apprise && \ apk del .build-deps +RUN apprise --version # New things add here @@ -31,5 +34,8 @@ RUN npm run build EXPOSE 3001 VOLUME ["/app/data"] +HEALTHCHECK --interval=60s --timeout=30s --start-period=300s CMD node extra/healthcheck.js CMD ["npm", "run", "start-server"] +FROM release AS nightly +RUN npm run mark-as-nightly diff --git a/extra/healthcheck.js b/extra/healthcheck.js new file mode 100644 index 000000000..b547fbcba --- /dev/null +++ b/extra/healthcheck.js @@ -0,0 +1,19 @@ +var http = require("http"); +var options = { + host: "localhost", + port: "3001", + timeout: 2000, +}; +var request = http.request(options, (res) => { + console.log(`STATUS: ${res.statusCode}`); + if (res.statusCode == 200) { + process.exit(0); + } else { + process.exit(1); + } +}); +request.on("error", function (err) { + console.log("ERROR"); + process.exit(1); +}); +request.end(); diff --git a/extra/mark-as-nightly.js b/extra/mark-as-nightly.js new file mode 100644 index 000000000..28496511b --- /dev/null +++ b/extra/mark-as-nightly.js @@ -0,0 +1,40 @@ +/** + * String.prototype.replaceAll() polyfill + * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ + * @author Chris Ferdinandi + * @license MIT + */ +if (!String.prototype.replaceAll) { + String.prototype.replaceAll = function(str, newStr){ + + // If a regex pattern + if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') { + return this.replace(str, newStr); + } + + // If a string + return this.replace(new RegExp(str, 'g'), newStr); + + }; +} + +const pkg = require('../package.json'); +const fs = require("fs"); +const oldVersion = pkg.version +const newVersion = oldVersion + "-nightly" + +console.log("Old Version: " + oldVersion) +console.log("New Version: " + newVersion) + +if (newVersion) { + // Process package.json + pkg.version = newVersion + pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion) + pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion) + fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n") + + // Process README.md + if (fs.existsSync("README.md")) { + fs.writeFileSync("README.md", fs.readFileSync("README.md", 'utf8').replaceAll(oldVersion, newVersion)) + } +} diff --git a/index.html b/index.html index 3dd55d3f1..66d58c1e6 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,8 @@ + + Uptime Kuma diff --git a/package-lock.json b/package-lock.json index e7486fc81..92c91a935 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.0.4", + "version": "1.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -40,37 +40,43 @@ "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" }, "@types/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, "@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==" + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/estree": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.48.tgz", + "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", + "dev": true }, "@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz", + "integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ==" }, "@vitejs/plugin-legacy": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.3.tgz", - "integrity": "sha512-lxZUJaMWYMQuqvZM1wPzDP6KABQgA/drVL5fnaygEPcz9adc2OHhfFNN/SvvHQ1V0rP8gybIc7uA+iI1gAdkVQ==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.4.tgz", + "integrity": "sha512-pVYeQUDPG5InWwrTu7acy187WWjGonJnL/GMqMLmeKCFiwkZ6UcsoUjojiKmCUI0nAJTrrKH5lhjTqkccY9Iow==", "dev": true, "requires": { "@babel/standalone": "^7.14.7", - "core-js": "^3.15.1", + "core-js": "^3.15.2", "magic-string": "^0.25.7", "regenerator-runtime": "^0.13.7", - "systemjs": "^6.10.1" + "systemjs": "^6.10.2" } }, "@vitejs/plugin-vue": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.3.tgz", - "integrity": "sha512-LlnLpObkGKZ+b7dcpL4T24l13nPSHLjo+6Oc7MbZiKz5PMAUzADfNJ3EKfYIQ0l0969nxf2jp/9vsfnuJ7h6fw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.2.5.tgz", + "integrity": "sha512-GIR31mdXTEfvElmBUaRhDc5v7lfdkEdawWQqJRiaRL/5qKsH+xusukglkvJz5y7+c6dEpxgmvcATv2BbB7+fzQ==", "dev": true }, "@vue/compiler-core": { @@ -95,17 +101,18 @@ } }, "@vue/compiler-sfc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz", - "integrity": "sha512-lSgMsZaYHF+bAgryq5aUqpvyfhu52GJI2/4LoiJCE5uaxc6FCZfxfgqgw/d9ltiZghv+HiISFtmQVAVvlsk+/w==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.5.tgz", + "integrity": "sha512-mtMY6xMvZeSRx9MTa1+NgJWndrkzVTdJ1pQAmAKQuxyb5LsHVvrgP7kcQFvxPHVpLVTORbTJWHaiqoKrJvi1iA==", "dev": true, "requires": { "@babel/parser": "^7.13.9", "@babel/types": "^7.13.0", - "@vue/compiler-core": "3.1.1", - "@vue/compiler-dom": "3.1.1", - "@vue/compiler-ssr": "3.1.1", - "@vue/shared": "3.1.1", + "@types/estree": "^0.0.48", + "@vue/compiler-core": "3.1.5", + "@vue/compiler-dom": "3.1.5", + "@vue/compiler-ssr": "3.1.5", + "@vue/shared": "3.1.5", "consolidate": "^0.16.0", "estree-walker": "^2.0.1", "hash-sum": "^2.0.0", @@ -116,16 +123,78 @@ "postcss-modules": "^4.0.0", "postcss-selector-parser": "^6.0.4", "source-map": "^0.6.1" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.5.tgz", + "integrity": "sha512-TXBhFinoBaXKDykJzY26UEuQU1K07FOp/0Ie+OXySqqk0bS0ZO7Xvl7UmiTUPYcLrWbxWBR7Bs/y55AI0MNc2Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.1.5", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.5.tgz", + "integrity": "sha512-ZsL3jqJ52OjGU/YiT/9XiuZAmWClKInZM2aFJh9gnsAPqOrj2JIELMbkIFpVKR/CrVO/f2VxfPiiQdQTr65jcQ==", + "dev": true, + "requires": { + "@vue/compiler-core": "3.1.5", + "@vue/shared": "3.1.5" + } + }, + "@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true + } } }, "@vue/compiler-ssr": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.1.tgz", - "integrity": "sha512-7H6krZtVt3h/YzfNp7eYK41hMDz8ZskiBy+Wby+EDRINX6BD9JQ5C8zyy2xAa7T6Iz2VrQzsaJ/Bb52lTPSS5A==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.1.5.tgz", + "integrity": "sha512-CU5N7Di/a4lyJ18LGJxJYZS2a8PlLdWpWHX9p/XcsjT2TngMpj3QvHVRkuik2u8QrIDZ8OpYmTyj1WDNsOV+Dg==", "dev": true, "requires": { - "@vue/compiler-dom": "3.1.1", - "@vue/shared": "3.1.1" + "@vue/compiler-dom": "3.1.5", + "@vue/shared": "3.1.5" + }, + "dependencies": { + "@vue/compiler-core": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.1.5.tgz", + "integrity": "sha512-TXBhFinoBaXKDykJzY26UEuQU1K07FOp/0Ie+OXySqqk0bS0ZO7Xvl7UmiTUPYcLrWbxWBR7Bs/y55AI0MNc2Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "@vue/shared": "3.1.5", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.5.tgz", + "integrity": "sha512-ZsL3jqJ52OjGU/YiT/9XiuZAmWClKInZM2aFJh9gnsAPqOrj2JIELMbkIFpVKR/CrVO/f2VxfPiiQdQTr65jcQ==", + "dev": true, + "requires": { + "@vue/compiler-core": "3.1.5", + "@vue/shared": "3.1.5" + } + }, + "@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true + } } }, "@vue/devtools-api": { @@ -531,9 +600,9 @@ } }, "bootstrap": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.1.tgz", - "integrity": "sha512-Fl79+wsLOZKoiU345KeEaWD0ik8WKRI5zm0YSPj2oF1Qr+BO7z0fco6GbUtqjoG1h4VI89PeKJnMsMMVQdKKTw==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.2.tgz", + "integrity": "sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q==" }, "brace-expansion": { "version": "1.1.11", @@ -655,6 +724,11 @@ "delayed-stream": "~1.0.0" } }, + "command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, "commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -753,9 +827,9 @@ } }, "dayjs": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz", - "integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==" + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz", + "integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==" }, "debug": { "version": "4.3.1", @@ -898,9 +972,9 @@ } }, "engine.io-client": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz", - "integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.2.tgz", + "integrity": "sha512-blRrgXIE0A/eurWXRzvfCLG7uUFJqfTGFsyJzXSK71srMMGJ2VraBLg8Mdw28uUxSpVicepBN9X7asqpD1mZcQ==", "requires": { "base64-arraybuffer": "0.1.4", "component-emitter": "~1.3.0", @@ -922,9 +996,9 @@ } }, "esbuild": { - "version": "0.12.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.9.tgz", - "integrity": "sha512-MWRhAbMOJ9RJygCrt778rz/qNYgA4ZVj6aXnNPxFjs7PmIpb0fuB9Gmg5uWrr6n++XKwwm/RmSz6RR5JL2Ocsw==", + "version": "0.12.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz", + "integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==", "dev": true }, "escape-html": { @@ -2420,9 +2494,9 @@ } }, "nodemailer": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz", - "integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q==" + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz", + "integrity": "sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew==" }, "nopt": { "version": "3.0.6", @@ -2975,9 +3049,9 @@ } }, "rollup": { - "version": "2.52.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.2.tgz", - "integrity": "sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA==", + "version": "2.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz", + "integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -3002,9 +3076,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.35.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", - "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", + "version": "1.35.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.2.tgz", + "integrity": "sha512-jhO5KAR+AMxCEwIH3v+4zbB2WB0z67V1X0jbapfVwQQdjHZUGUyukpnoM6+iCMfsIUC016w9OPKQ5jrNOS9uXw==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" @@ -3225,19 +3299,19 @@ } }, "socket.io": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.2.tgz", - "integrity": "sha512-xK0SD1C7hFrh9+bYoYCdVt+ncixkSLKtNLCax5aEy1o3r5PaO5yQhVb97exIe67cE7lAK+EpyMytXWTWmyZY8w==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz", + "integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==", "requires": { "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.8", + "@types/cors": "^2.8.10", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.1", - "engine.io": "~5.1.0", - "socket.io-adapter": "~2.3.0", - "socket.io-parser": "~4.0.3" + "engine.io": "~5.1.1", + "socket.io-adapter": "~2.3.1", + "socket.io-parser": "~4.0.4" } }, "socket.io-adapter": { @@ -3246,15 +3320,15 @@ "integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==" }, "socket.io-client": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.2.tgz", - "integrity": "sha512-RDpWJP4DQT1XeexmeDyDkm0vrFc0+bUsHDKiVGaNISJvJonhQQOMqV9Vwfg0ZpPJ27LCdan7iqTI92FRSOkFWQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.3.tgz", + "integrity": "sha512-hISFn6PDpgDifVUiNklLHVPTMv1LAk8poHArfIUdXa+gKgbr0MZbAlquDFqCqsF30yBqa+jg42wgos2FK50BHA==", "requires": { "@types/component-emitter": "^1.2.10", "backo2": "~1.0.2", "component-emitter": "~1.3.0", "debug": "~4.3.1", - "engine.io-client": "~5.1.1", + "engine.io-client": "~5.1.2", "parseuri": "0.0.6", "socket.io-parser": "~4.0.4" } @@ -3649,14 +3723,14 @@ } }, "vite": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.3.8.tgz", - "integrity": "sha512-QiEx+iqNnJntSgSF2fWRQvRey9pORIrtNJzNyBJXwc+BdzWs83FQolX84cTBo393cfhObrtWa6180dAa4NLDiQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.4.2.tgz", + "integrity": "sha512-2MifxD2I9fjyDmmEzbULOo3kOUoqX90A58cT6mECxoVQlMYFuijZsPQBuA14mqSwvV3ydUsqnq+BRWXyO9Qa+w==", "dev": true, "requires": { "esbuild": "^0.12.8", "fsevents": "~2.3.2", - "postcss": "^8.3.4", + "postcss": "^8.3.5", "resolve": "^1.20.0", "rollup": "^2.38.5" } diff --git a/package.json b/package.json index af0840183..02095fd8b 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,42 @@ { "name": "uptime-kuma", - "version": "1.0.4", + "version": "1.0.6", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/louislam/uptime-kuma.git" + }, "scripts": { "dev": "vite --host", "start-server": "node server/server.js", "update": "", "build": "vite build", "vite-preview-dist": "vite preview --host", - "build-docker": "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.0.4 . --push", - "build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly . --push", - "setup": "git checkout 1.0.4 && npm install && npm run build", - "version-global-replace": "node extra/version-global-replace.js" + "build-docker": "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.0.6 --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", + "build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push", + "setup": "git checkout 1.0.6 && npm install && npm run build", + "version-global-replace": "node extra/version-global-replace.js", + "mark-as-nightly": "node extra/mark-as-nightly.js" }, "dependencies": { "@popperjs/core": "^2.9.2", "args-parser": "^1.3.0", "axios": "^0.21.1", "bcrypt": "^5.0.1", - "bootstrap": "^5.0.0", - "dayjs": "^1.10.4", + "bootstrap": "^5.0.2", + "command-exists": "^1.2.9", + "dayjs": "^1.10.6", "express": "^4.17.1", "form-data": "^4.0.0", "http-graceful-shutdown": "^3.1.2", "jsonwebtoken": "^8.5.1", - "nodemailer": "^6.6.2", + "nodemailer": "^6.6.3", "password-hash": "^1.2.2", "redbean-node": "0.0.20", - "socket.io": "^4.0.2", - "socket.io-client": "^4.1.2", - "sqlite3": "^5.0.0", + "socket.io": "^4.1.3", + "socket.io-client": "^4.1.3", + "sqlite3": "^5.0.2", "tcp-ping": "^0.1.1", "vue": "^3.0.5", "vue-confirm-dialog": "^1.0.2", @@ -36,11 +44,11 @@ "vue-toastification": "^2.0.0-rc.1" }, "devDependencies": { - "@vitejs/plugin-legacy": "^1.4.3", - "@vitejs/plugin-vue": "^1.2.3", - "@vue/compiler-sfc": "^3.0.5", + "@vitejs/plugin-legacy": "^1.4.4", + "@vitejs/plugin-vue": "^1.2.5", + "@vue/compiler-sfc": "^3.1.5", "core-js": "^3.15.2", - "sass": "^1.35.1", - "vite": "^2.3.7" + "sass": "^1.35.2", + "vite": "^2.4.2" } } diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index db02af7ba..3bb82e4c9 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 000000000..e9e57dc4d --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/server/model/monitor.js b/server/model/monitor.js index fa17262e3..3c087640e 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -48,8 +48,6 @@ class Monitor extends BeanModel { let previousBeat = null; const beat = async () => { - console.log(`Monitor ${this.id}: Heartbeat`) - if (! previousBeat) { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ this.id @@ -178,6 +176,12 @@ class Monitor extends BeanModel { bean.important = false; } + if (bean.status === 1) { + console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) + } else { + console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) + } + io.to(this.user_id).emit("heartbeat", bean.toJSON()); await R.store(bean) diff --git a/server/notification.js b/server/notification.js index 99cc1568c..79c01f6db 100644 --- a/server/notification.js +++ b/server/notification.js @@ -2,9 +2,22 @@ const axios = require("axios"); const {R} = require("redbean-node"); const FormData = require('form-data'); const nodemailer = require("nodemailer"); +const child_process = require("child_process"); class Notification { + + /** + * + * @param notification + * @param msg + * @param monitorJSON + * @param heartbeatJSON + * @returns {Promise} Successful msg + * Throw Error with fail msg + */ static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let okMsg = "Sent Successfully. "; + if (notification.type === "telegram") { try { await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, { @@ -13,15 +26,16 @@ class Notification { text: msg, } }) - return true; + return okMsg; + } catch (error) { - console.log(error) - return false; + let msg = (error.response.data.description) ? error.response.data.description : "Error without description" + throw new Error(msg) } } else if (notification.type === "gotify") { try { - if (notification.gotifyserverurl.endsWith("/")) { + if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) { notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1); } await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, { @@ -29,15 +43,15 @@ class Notification { "priority": notification.gotifyPriority || 8, "title": "Uptime-Kuma" }) - return true; + + return okMsg; + } catch (error) { - console.log(error) - return false; + throwGeneralAxiosError(error) } } else if (notification.type === "webhook") { try { - let data = { heartbeat: heartbeatJSON, monitor: monitorJSON, @@ -59,10 +73,10 @@ class Notification { } let res = await axios.post(notification.webhookURL, finalData, config) - return true; + return okMsg; + } catch (error) { - console.log(error) - return false; + throwGeneralAxiosError(error) } } else if (notification.type === "smtp") { @@ -77,7 +91,7 @@ class Notification { content: msg } let res = await axios.post(notification.discordWebhookUrl, data) - return true; + return okMsg; } // If heartbeatJSON is not null, we go into the normal alerting loop. if(heartbeatJSON['status'] == 0) { @@ -103,10 +117,9 @@ class Notification { }] } let res = await axios.post(notification.discordWebhookUrl, data) - return true; + return okMsg; } catch(error) { - console.log(error) - return false; + throwGeneralAxiosError(error) } } else if (notification.type === "signal") { @@ -119,22 +132,25 @@ class Notification { let config = {}; let res = await axios.post(notification.signalURL, data, config) - return true; + return okMsg; } catch (error) { - console.log(error) - return false; + throwGeneralAxiosError(error) } - + } else if (notification.type === "slack") { try { if (heartbeatJSON == null) { - let data = {'text': "Uptime Kuma Slack testing successful."} + let data = {'text': "Uptime Kuma Slack testing successful.", 'channel': notification.slackchannel, 'username': notification.slackusername, 'icon_emoji': notification.slackiconemo} let res = await axios.post(notification.slackwebhookURL, data) - return true; + return okMsg; } const time = heartbeatJSON["time"]; let data = { + "text": "Uptime Kuma Alert", + "channel":notification.slackchannel, + "username": notification.slackusername, + "icon_emoji": notification.slackiconemo, "blocks": [{ "type": "header", "text": { @@ -155,28 +171,59 @@ class Notification { ] }, { - "type": "actions", - "elements": [ - { - "type": "button", - "text": { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { "type": "plain_text", "text": "Visit Uptime Kuma", }, - "value": "Uptime-Kuma", - "url": notification.slackbutton - } + "value": "Uptime-Kuma", + "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma" + } ] } ] } let res = await axios.post(notification.slackwebhookURL, data) - return true; + return okMsg; } catch (error) { - console.log(error) - return false; + throwGeneralAxiosError(error) } + } else if (notification.type === "pushover") { + var pushoverlink = 'https://api.pushover.net/1/messages.json' + try { + if (heartbeatJSON == null) { + let data = {'message': "Uptime Kuma Pushover testing successful.", + 'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds, + 'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1} + let res = await axios.post(pushoverlink, data) + return okMsg; + } + + let data = { + "message": "Uptime Kuma Alert\n\nMessage:" +msg + '\nTime (UTC):' +time, + "user":notification.pushoveruserkey, + "token": notification.pushoverapptoken, + "sound": notification.pushoversounds, + "priority": notification.pushoverpriority, + "title": notification.pushovertitle, + "retry": "30", + "expire": "3600", + "html": 1 + } + let res = await axios.post(pushoverlink, data) + return okMsg; + } catch (error) { + throwGeneralAxiosError(error) + } + + } else if (notification.type === "apprise") { + + return Notification.apprise(notification, msg) + } else { throw new Error("Notification type is not supported") } @@ -238,20 +285,47 @@ class Notification { text: msg, }); - return true; + return "Sent Successfully."; } - static async discord(notification, msg) { - const client = new Discord.Client(); - await client.login(notification.discordToken) + static async apprise(notification, msg) { + let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL]) - const channel = await client.channels.fetch(notification.discordChannelID); - await channel.send(msg); - client.destroy() + let output = (s.stdout) ? s.stdout.toString() : 'ERROR: maybe apprise not found'; - return true; + if (output) { + + if (! output.includes("ERROR")) { + return "Sent Successfully"; + } else { + throw new Error(output) + } + } else { + return "" + } } + + static checkApprise() { + let commandExistsSync = require('command-exists').sync; + let exists = commandExistsSync('apprise'); + return exists; + } + +} + +function throwGeneralAxiosError(error) { + let msg = "Error: " + error + " "; + + if (error.response && error.response.data) { + if (typeof error.response.data === "string") { + msg += error.response.data; + } else { + msg += JSON.stringify(error.response.data) + } + } + + throw new Error(msg) } module.exports = { diff --git a/server/server.js b/server/server.js index 93e43cd6e..0d0ce51f1 100644 --- a/server/server.js +++ b/server/server.js @@ -19,7 +19,7 @@ const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 -console.log("Version: " + version) +console.info("Version: " + version) console.log("Creating express and socket.io instance") const app = express(); @@ -35,19 +35,21 @@ let needSetup = false; (async () => { await initDatabase(); + console.log("Adding route") app.use('/', express.static("dist")); app.get('*', function(request, response, next) { response.sendFile(process.cwd() + '/dist/index.html'); }); + + console.log("Adding socket handler") io.on('connection', async (socket) => { socket.emit("info", { version, }) - console.log('a user connected'); totalClient++; if (needSetup) { @@ -56,7 +58,6 @@ let needSetup = false; } socket.on('disconnect', () => { - console.log('user disconnected'); totalClient--; }); @@ -236,7 +237,7 @@ let needSetup = false; }); } catch (e) { - console.log(e) + console.error(e) callback({ ok: false, msg: e.message @@ -437,25 +438,36 @@ let needSetup = false; try { checkLogin(socket) - await Notification.send(notification, notification.name + " Testing") + let msg = await Notification.send(notification, notification.name + " Testing") callback({ ok: true, - msg: "Sent Successfully" + msg }); } catch (e) { + console.error(e) + callback({ ok: false, msg: e.message }); } }); + + socket.on("checkApprise", async (callback) => { + try { + checkLogin(socket) + callback(Notification.checkApprise()); + } catch (e) { + callback(false); + } + }); }); + console.log("Init") server.listen(port, hostname, () => { console.log(`Listening on ${hostname}:${port}`); - startMonitors(); }); @@ -551,10 +563,11 @@ async function initDatabase() { } console.log("Connecting to Database") - R.setup('sqlite', { filename: path }); + console.log("Connected") + R.freeze(true) await R.autoloadModels("./server/model"); @@ -569,6 +582,7 @@ async function initDatabase() { jwtSecretBean.value = passwordHash.generate(dayjs() + "") await R.store(jwtSecretBean) + console.log("Stored JWT secret into database") } else { console.log("Load JWT secret from database.") } diff --git a/server/util.js b/server/util.js index d1c9266a8..0a8877b80 100644 --- a/server/util.js +++ b/server/util.js @@ -1,9 +1,5 @@ -/* - * Common functions - can be used in frontend or backend - */ - - - +// Common JS cannot be used in frontend sadly +// sleep, ucfirst is duplicated in ../src/util-frontend.js exports.sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue index b90f430e4..33904b6a9 100644 --- a/src/components/CountUp.vue +++ b/src/components/CountUp.vue @@ -5,7 +5,7 @@