Merge branch 'master' into fix-1448-discord-service-url
This commit is contained in:
		
						commit
						288ed1e3ca
					
				
					 94 changed files with 1230 additions and 406 deletions
				
			
		|  | @ -1,4 +1,9 @@ | ||||||
| module.exports = { | module.exports = { | ||||||
|  |     ignorePatterns: [ | ||||||
|  |         "test/*", | ||||||
|  |         "server/modules/apicache/*", | ||||||
|  |         "src/util.js" | ||||||
|  |     ], | ||||||
|     root: true, |     root: true, | ||||||
|     env: { |     env: { | ||||||
|         browser: true, |         browser: true, | ||||||
|  | @ -34,7 +39,7 @@ module.exports = { | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         quotes: ["warn", "double"], |         quotes: ["warn", "double"], | ||||||
|         semi: "warn", |         semi: "error", | ||||||
|         "vue/html-indent": ["warn", 4], // default: 2
 |         "vue/html-indent": ["warn", 4], // default: 2
 | ||||||
|         "vue/max-attributes-per-line": "off", |         "vue/max-attributes-per-line": "off", | ||||||
|         "vue/singleline-html-element-content-newline": "off", |         "vue/singleline-html-element-content-newline": "off", | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								.github/workflows/auto-test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/auto-test.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -20,6 +20,7 @@ jobs: | ||||||
|         # 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: | ||||||
|  |     - run: git config --global core.autocrlf false  # Mainly for Windows | ||||||
|     - uses: actions/checkout@v2 |     - uses: actions/checkout@v2 | ||||||
| 
 | 
 | ||||||
|     - name: Use Node.js ${{ matrix.node-version }} |     - name: Use Node.js ${{ matrix.node-version }} | ||||||
|  |  | ||||||
|  | @ -1,9 +1,13 @@ | ||||||
| { | { | ||||||
|     "extends": "stylelint-config-standard", |     "extends": "stylelint-config-standard", | ||||||
|  |     "customSyntax": "postcss-html", | ||||||
|     "rules": { |     "rules": { | ||||||
|         "indentation": 4, |         "indentation": 4, | ||||||
|         "no-descending-specificity": null, |         "no-descending-specificity": null, | ||||||
|         "selector-list-comma-newline-after": null, |         "selector-list-comma-newline-after": null, | ||||||
|         "declaration-empty-line-before": null |         "declaration-empty-line-before": null, | ||||||
|  |         "alpha-value-notation": "number", | ||||||
|  |         "color-function-notation": "legacy", | ||||||
|  |         "shorthand-property-no-redundant-values": null | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -44,6 +44,8 @@ My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/ | ||||||
| 
 | 
 | ||||||
| ### Recommended Pull Request Guideline | ### Recommended Pull Request Guideline | ||||||
| 
 | 
 | ||||||
|  | Before deep into coding, disscussion first is preferred. Creating an empty pull request for disscussion would be recommended. | ||||||
|  | 
 | ||||||
| 1. Fork the project | 1. Fork the project | ||||||
| 1. Clone your fork repo to local | 1. Clone your fork repo to local | ||||||
| 1. Create a new branch | 1. Create a new branch | ||||||
|  | @ -53,6 +55,7 @@ My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/ | ||||||
| 1. Create a pull request: https://github.com/louislam/uptime-kuma/compare | 1. Create a pull request: https://github.com/louislam/uptime-kuma/compare | ||||||
| 1. Write a proper description | 1. Write a proper description | ||||||
| 1. Click "Change to draft" | 1. Click "Change to draft" | ||||||
|  | 1. Discussion | ||||||
| 
 | 
 | ||||||
| #### ❌ Won't Merge | #### ❌ Won't Merge | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,3 +11,4 @@ services: | ||||||
|       - ./uptime-kuma:/app/data |       - ./uptime-kuma:/app/data | ||||||
|     ports: |     ports: | ||||||
|       - 3001:3001 |       - 3001:3001 | ||||||
|  |     restart: always | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| module.exports = { | module.exports = { | ||||||
|   apps: [{ |     apps: [{ | ||||||
|     name: "uptime-kuma", |         name: "uptime-kuma", | ||||||
|     script: "./server/server.js", |         script: "./server/server.js", | ||||||
|   }] |     }] | ||||||
| } | }; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| const pkg = require("../../package.json"); | const pkg = require("../../package.json"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const child_process = require("child_process"); | const childProcess = require("child_process"); | ||||||
| const util = require("../../src/util"); | const util = require("../../src/util"); | ||||||
| 
 | 
 | ||||||
| util.polyfill(); | util.polyfill(); | ||||||
|  | @ -32,7 +32,7 @@ if (! exists) { | ||||||
| function commit(version) { | function commit(version) { | ||||||
|     let msg = "Update to " + version; |     let msg = "Update to " + version; | ||||||
| 
 | 
 | ||||||
|     let res = child_process.spawnSync("git", ["commit", "-m", msg, "-a"]); |     let res = childProcess.spawnSync("git", ["commit", "-m", msg, "-a"]); | ||||||
|     let stdout = res.stdout.toString().trim(); |     let stdout = res.stdout.toString().trim(); | ||||||
|     console.log(stdout); |     console.log(stdout); | ||||||
| 
 | 
 | ||||||
|  | @ -40,15 +40,15 @@ function commit(version) { | ||||||
|         throw new Error("commit error"); |         throw new Error("commit error"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     res = child_process.spawnSync("git", ["push", "origin", "master"]); |     res = childProcess.spawnSync("git", ["push", "origin", "master"]); | ||||||
|     console.log(res.stdout.toString().trim()); |     console.log(res.stdout.toString().trim()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function tag(version) { | function tag(version) { | ||||||
|     let res = child_process.spawnSync("git", ["tag", version]); |     let res = childProcess.spawnSync("git", ["tag", version]); | ||||||
|     console.log(res.stdout.toString().trim()); |     console.log(res.stdout.toString().trim()); | ||||||
| 
 | 
 | ||||||
|     res = child_process.spawnSync("git", ["push", "origin", version]); |     res = childProcess.spawnSync("git", ["push", "origin", version]); | ||||||
|     console.log(res.stdout.toString().trim()); |     console.log(res.stdout.toString().trim()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -57,7 +57,7 @@ function tagExists(version) { | ||||||
|         throw new Error("invalid version"); |         throw new Error("invalid version"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let res = child_process.spawnSync("git", ["tag", "-l", version]); |     let res = childProcess.spawnSync("git", ["tag", "-l", version]); | ||||||
| 
 | 
 | ||||||
|     return res.stdout.toString().trim() === version; |     return res.stdout.toString().trim() === version; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,12 @@ const filename = "dist.tar.gz"; | ||||||
| const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`; | const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`; | ||||||
| download(url); | download(url); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Downloads the latest version of the dist from a GitHub release. | ||||||
|  |  * @param {string} url The URL to download from. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| function download(url) { | function download(url) { | ||||||
|     console.log(url); |     console.log(url); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,21 +4,21 @@ const util = require("../src/util"); | ||||||
| 
 | 
 | ||||||
| util.polyfill(); | util.polyfill(); | ||||||
| 
 | 
 | ||||||
| const oldVersion = pkg.version | const oldVersion = pkg.version; | ||||||
| const newVersion = oldVersion + "-nightly" | const newVersion = oldVersion + "-nightly"; | ||||||
| 
 | 
 | ||||||
| console.log("Old Version: " + oldVersion) | console.log("Old Version: " + oldVersion); | ||||||
| console.log("New Version: " + newVersion) | console.log("New Version: " + newVersion); | ||||||
| 
 | 
 | ||||||
| if (newVersion) { | if (newVersion) { | ||||||
|     // Process package.json
 |     // Process package.json
 | ||||||
|     pkg.version = newVersion |     pkg.version = newVersion; | ||||||
|     pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion) |     pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion); | ||||||
|     pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion) |     pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion); | ||||||
|     fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n") |     fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n"); | ||||||
| 
 | 
 | ||||||
|     // Process README.md
 |     // Process README.md
 | ||||||
|     if (fs.existsSync("README.md")) { |     if (fs.existsSync("README.md")) { | ||||||
|         fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion)) |         fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ server.on("request", (request, send, rinfo) => { | ||||||
|                     ttl: 300, |                     ttl: 300, | ||||||
|                     address: "1.2.3.4" |                     address: "1.2.3.4" | ||||||
|                 }); |                 }); | ||||||
|             } if (question.type === Packet.TYPE.AAAA) { |             } else if (question.type === Packet.TYPE.AAAA) { | ||||||
|                 response.answers.push({ |                 response.answers.push({ | ||||||
|                     name: question.name, |                     name: question.name, | ||||||
|                     type: question.type, |                     type: question.type, | ||||||
|  |  | ||||||
|  | @ -33,6 +33,12 @@ if (! exists) { | ||||||
|     console.log("version exists"); |     console.log("version exists"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Updates the version number in package.json and commits it to git. | ||||||
|  |  * @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; | ||||||
| 
 | 
 | ||||||
|  | @ -50,6 +56,12 @@ function tag(version) { | ||||||
|     console.log(res.stdout.toString().trim()); |     console.log(res.stdout.toString().trim()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Checks if a given version is already tagged in the git repository. | ||||||
|  |  * @param {string} version - The version to check for. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| function tagExists(version) { | function tagExists(version) { | ||||||
|     if (! version) { |     if (! version) { | ||||||
|         throw new Error("invalid version"); |         throw new Error("invalid version"); | ||||||
|  |  | ||||||
							
								
								
									
										188
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										188
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							|  | @ -1,12 +1,12 @@ | ||||||
| { | { | ||||||
|     "name": "uptime-kuma", |     "name": "uptime-kuma", | ||||||
|     "version": "1.14.0-beta.0", |     "version": "1.14.0", | ||||||
|     "lockfileVersion": 2, |     "lockfileVersion": 2, | ||||||
|     "requires": true, |     "requires": true, | ||||||
|     "packages": { |     "packages": { | ||||||
|         "": { |         "": { | ||||||
|             "name": "uptime-kuma", |             "name": "uptime-kuma", | ||||||
|             "version": "1.14.0-beta.0", |             "version": "1.14.0", | ||||||
|             "license": "MIT", |             "license": "MIT", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@fortawesome/fontawesome-svg-core": "~1.2.36", |                 "@fortawesome/fontawesome-svg-core": "~1.2.36", | ||||||
|  | @ -85,6 +85,7 @@ | ||||||
|                 "jest": "~27.2.5", |                 "jest": "~27.2.5", | ||||||
|                 "jest-puppeteer": "~6.0.3", |                 "jest-puppeteer": "~6.0.3", | ||||||
|                 "npm-check-updates": "^12.5.5", |                 "npm-check-updates": "^12.5.5", | ||||||
|  |                 "postcss-html": "^1.3.1", | ||||||
|                 "puppeteer": "~13.1.3", |                 "puppeteer": "~13.1.3", | ||||||
|                 "sass": "~1.42.1", |                 "sass": "~1.42.1", | ||||||
|                 "stylelint": "~14.2.0", |                 "stylelint": "~14.2.0", | ||||||
|  | @ -5612,6 +5613,41 @@ | ||||||
|                 "node": ">=6.0.0" |                 "node": ">=6.0.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/dom-serializer": { | ||||||
|  |             "version": "1.4.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", | ||||||
|  |             "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", | ||||||
|  |             "dev": true, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "domelementtype": "^2.0.1", | ||||||
|  |                 "domhandler": "^4.2.0", | ||||||
|  |                 "entities": "^2.0.0" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/dom-serializer/node_modules/entities": { | ||||||
|  |             "version": "2.2.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", | ||||||
|  |             "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", | ||||||
|  |             "dev": true, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/fb55/entities?sponsor=1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/domelementtype": { | ||||||
|  |             "version": "2.3.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", | ||||||
|  |             "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", | ||||||
|  |             "dev": true, | ||||||
|  |             "funding": [ | ||||||
|  |                 { | ||||||
|  |                     "type": "github", | ||||||
|  |                     "url": "https://github.com/sponsors/fb55" | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         }, | ||||||
|         "node_modules/domexception": { |         "node_modules/domexception": { | ||||||
|             "version": "2.0.1", |             "version": "2.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", |             "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", | ||||||
|  | @ -5633,6 +5669,35 @@ | ||||||
|                 "node": ">=8" |                 "node": ">=8" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/domhandler": { | ||||||
|  |             "version": "4.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", | ||||||
|  |             "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "domelementtype": "^2.2.0" | ||||||
|  |             }, | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">= 4" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/fb55/domhandler?sponsor=1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/domutils": { | ||||||
|  |             "version": "2.8.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", | ||||||
|  |             "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", | ||||||
|  |             "dev": true, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "dom-serializer": "^1.0.1", | ||||||
|  |                 "domelementtype": "^2.2.0", | ||||||
|  |                 "domhandler": "^4.2.0" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/fb55/domutils?sponsor=1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/dot-prop": { |         "node_modules/dot-prop": { | ||||||
|             "version": "5.3.0", |             "version": "5.3.0", | ||||||
|             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", |             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", | ||||||
|  | @ -5817,6 +5882,18 @@ | ||||||
|                 "node": ">=8.6" |                 "node": ">=8.6" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/entities": { | ||||||
|  |             "version": "3.0.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", | ||||||
|  |             "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", | ||||||
|  |             "dev": true, | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=0.12" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/fb55/entities?sponsor=1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/env-paths": { |         "node_modules/env-paths": { | ||||||
|             "version": "2.2.1", |             "version": "2.2.1", | ||||||
|             "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", |             "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", | ||||||
|  | @ -7557,6 +7634,25 @@ | ||||||
|                 "node": ">=8" |                 "node": ">=8" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/htmlparser2": { | ||||||
|  |             "version": "7.2.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", | ||||||
|  |             "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", | ||||||
|  |             "dev": true, | ||||||
|  |             "funding": [ | ||||||
|  |                 "https://github.com/fb55/htmlparser2?sponsor=1", | ||||||
|  |                 { | ||||||
|  |                     "type": "github", | ||||||
|  |                     "url": "https://github.com/sponsors/fb55" | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |             "dependencies": { | ||||||
|  |                 "domelementtype": "^2.0.1", | ||||||
|  |                 "domhandler": "^4.2.2", | ||||||
|  |                 "domutils": "^2.8.0", | ||||||
|  |                 "entities": "^3.0.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/http-cache-semantics": { |         "node_modules/http-cache-semantics": { | ||||||
|             "version": "4.1.0", |             "version": "4.1.0", | ||||||
|             "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", |             "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", | ||||||
|  | @ -12505,6 +12601,20 @@ | ||||||
|                 "node": "^10 || ^12 || >=14" |                 "node": "^10 || ^12 || >=14" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/postcss-html": { | ||||||
|  |             "version": "1.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.3.1.tgz", | ||||||
|  |             "integrity": "sha512-SJ7iRw+IngyZv3Z9lChlZU30a9y9MZjZZcoUJmx0T/nKE9S+hetJ8fAv/MRu4bPnGDsXhVlaFs5+umpK3yaaQQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "htmlparser2": "^7.1.2", | ||||||
|  |                 "postcss": "^8.4.0", | ||||||
|  |                 "postcss-safe-parser": "^6.0.0" | ||||||
|  |             }, | ||||||
|  |             "engines": { | ||||||
|  |                 "node": "^12 || >=14" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/postcss-media-query-parser": { |         "node_modules/postcss-media-query-parser": { | ||||||
|             "version": "0.2.3", |             "version": "0.2.3", | ||||||
|             "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", |             "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", | ||||||
|  | @ -20003,6 +20113,31 @@ | ||||||
|                 "esutils": "^2.0.2" |                 "esutils": "^2.0.2" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "dom-serializer": { | ||||||
|  |             "version": "1.4.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", | ||||||
|  |             "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "domelementtype": "^2.0.1", | ||||||
|  |                 "domhandler": "^4.2.0", | ||||||
|  |                 "entities": "^2.0.0" | ||||||
|  |             }, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "entities": { | ||||||
|  |                     "version": "2.2.0", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", | ||||||
|  |                     "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", | ||||||
|  |                     "dev": true | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "domelementtype": { | ||||||
|  |             "version": "2.3.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", | ||||||
|  |             "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", | ||||||
|  |             "dev": true | ||||||
|  |         }, | ||||||
|         "domexception": { |         "domexception": { | ||||||
|             "version": "2.0.1", |             "version": "2.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", |             "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", | ||||||
|  | @ -20020,6 +20155,26 @@ | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "domhandler": { | ||||||
|  |             "version": "4.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", | ||||||
|  |             "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "domelementtype": "^2.2.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "domutils": { | ||||||
|  |             "version": "2.8.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", | ||||||
|  |             "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "dom-serializer": "^1.0.1", | ||||||
|  |                 "domelementtype": "^2.2.0", | ||||||
|  |                 "domhandler": "^4.2.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "dot-prop": { |         "dot-prop": { | ||||||
|             "version": "5.3.0", |             "version": "5.3.0", | ||||||
|             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", |             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", | ||||||
|  | @ -20157,6 +20312,12 @@ | ||||||
|                 "ansi-colors": "^4.1.1" |                 "ansi-colors": "^4.1.1" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "entities": { | ||||||
|  |             "version": "3.0.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", | ||||||
|  |             "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", | ||||||
|  |             "dev": true | ||||||
|  |         }, | ||||||
|         "env-paths": { |         "env-paths": { | ||||||
|             "version": "2.2.1", |             "version": "2.2.1", | ||||||
|             "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", |             "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", | ||||||
|  | @ -21437,6 +21598,18 @@ | ||||||
|             "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", |             "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|  |         "htmlparser2": { | ||||||
|  |             "version": "7.2.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", | ||||||
|  |             "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "domelementtype": "^2.0.1", | ||||||
|  |                 "domhandler": "^4.2.2", | ||||||
|  |                 "domutils": "^2.8.0", | ||||||
|  |                 "entities": "^3.0.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "http-cache-semantics": { |         "http-cache-semantics": { | ||||||
|             "version": "4.1.0", |             "version": "4.1.0", | ||||||
|             "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", |             "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", | ||||||
|  | @ -25173,6 +25346,17 @@ | ||||||
|                 "source-map-js": "^1.0.2" |                 "source-map-js": "^1.0.2" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "postcss-html": { | ||||||
|  |             "version": "1.3.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.3.1.tgz", | ||||||
|  |             "integrity": "sha512-SJ7iRw+IngyZv3Z9lChlZU30a9y9MZjZZcoUJmx0T/nKE9S+hetJ8fAv/MRu4bPnGDsXhVlaFs5+umpK3yaaQQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "htmlparser2": "^7.1.2", | ||||||
|  |                 "postcss": "^8.4.0", | ||||||
|  |                 "postcss-safe-parser": "^6.0.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "postcss-media-query-parser": { |         "postcss-media-query-parser": { | ||||||
|             "version": "0.2.3", |             "version": "0.2.3", | ||||||
|             "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", |             "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|     "name": "uptime-kuma", |     "name": "uptime-kuma", | ||||||
|     "version": "1.14.0-beta.2", |     "version": "1.14.0", | ||||||
|     "license": "MIT", |     "license": "MIT", | ||||||
|     "repository": { |     "repository": { | ||||||
|         "type": "git", |         "type": "git", | ||||||
|  | @ -20,7 +20,7 @@ | ||||||
|         "start-server": "node server/server.js", |         "start-server": "node server/server.js", | ||||||
|         "start-server-dev": "cross-env NODE_ENV=development node server/server.js", |         "start-server-dev": "cross-env NODE_ENV=development node server/server.js", | ||||||
|         "build": "vite build --config ./config/vite.config.js", |         "build": "vite build --config ./config/vite.config.js", | ||||||
|         "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", |         "test": "npm run lint && node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", | ||||||
|         "test-with-build": "npm run build && npm test", |         "test-with-build": "npm run build && npm test", | ||||||
|         "jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend", |         "jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend", | ||||||
|         "jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js", |         "jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js", | ||||||
|  | @ -36,7 +36,7 @@ | ||||||
|         "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", |         "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", | ||||||
|         "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", | ||||||
|         "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.13.2 && npm ci --production && npm run download-dist", |         "setup": "git checkout 1.14.0 && 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", | ||||||
|  | @ -132,6 +132,7 @@ | ||||||
|         "jest": "~27.2.5", |         "jest": "~27.2.5", | ||||||
|         "jest-puppeteer": "~6.0.3", |         "jest-puppeteer": "~6.0.3", | ||||||
|         "npm-check-updates": "^12.5.5", |         "npm-check-updates": "^12.5.5", | ||||||
|  |         "postcss-html": "^1.3.1", | ||||||
|         "puppeteer": "~13.1.3", |         "puppeteer": "~13.1.3", | ||||||
|         "sass": "~1.42.1", |         "sass": "~1.42.1", | ||||||
|         "stylelint": "~14.2.0", |         "stylelint": "~14.2.0", | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ const basicAuth = require("express-basic-auth"); | ||||||
| const passwordHash = require("./password-hash"); | const passwordHash = require("./password-hash"); | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| const { setting } = require("./util-server"); | const { setting } = require("./util-server"); | ||||||
| const { debug } = require("../src/util"); |  | ||||||
| const { loginRateLimiter } = require("./rate-limiter"); | const { loginRateLimiter } = require("./rate-limiter"); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -34,6 +33,13 @@ exports.login = async function (username, password) { | ||||||
|     return null; |     return null; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * A function that checks if a user is logged in. | ||||||
|  |  * @param {string} username The username of the user to check for. | ||||||
|  |  * @param {function} callback The callback to call when done, with an error and result parameter. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| function myAuthorizer(username, password, callback) { | function myAuthorizer(username, password, callback) { | ||||||
|     // Login Rate Limit
 |     // Login Rate Limit
 | ||||||
|     loginRateLimiter.pass(null, 0).then((pass) => { |     loginRateLimiter.pass(null, 0).then((pass) => { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,12 @@ const { io } = require("./server"); | ||||||
| const { setting } = require("./util-server"); | const { setting } = require("./util-server"); | ||||||
| const checkVersion = require("./check-version"); | const checkVersion = require("./check-version"); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Send a list of notifications to the user. | ||||||
|  |  * @param {Socket} socket The socket object that is connected to the client. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function sendNotificationList(socket) { | async function sendNotificationList(socket) { | ||||||
|     const timeLogger = new TimeLogger(); |     const timeLogger = new TimeLogger(); | ||||||
| 
 | 
 | ||||||
|  | @ -100,6 +106,12 @@ async function sendProxyList(socket) { | ||||||
|     return list; |     return list; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Emits the version information to the client. | ||||||
|  |  * @param {Socket} socket The socket object that is connected to the client. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function sendInfo(socket) { | async function sendInfo(socket) { | ||||||
|     socket.emit("info", { |     socket.emit("info", { | ||||||
|         version: checkVersion.version, |         version: checkVersion.version, | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| const { setSetting, setting } = require("./util-server"); | const { setSetting, setting } = require("./util-server"); | ||||||
| const { debug, sleep } = require("../src/util"); | const { log, sleep } = require("../src/util"); | ||||||
| const dayjs = require("dayjs"); | const dayjs = require("dayjs"); | ||||||
| const knex = require("knex"); | const knex = require("knex"); | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +80,7 @@ class Database { | ||||||
|             fs.mkdirSync(Database.uploadDir, { recursive: true }); |             fs.mkdirSync(Database.uploadDir, { recursive: true }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         console.log(`Data Dir: ${Database.dataDir}`); |         log.info("db", `Data Dir: ${Database.dataDir}`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static async connect(testMode = false, autoloadModels = true, noLog = false) { |     static async connect(testMode = false, autoloadModels = true, noLog = false) { | ||||||
|  | @ -135,10 +135,10 @@ class Database { | ||||||
|         await R.exec("PRAGMA synchronous = FULL"); |         await R.exec("PRAGMA synchronous = FULL"); | ||||||
| 
 | 
 | ||||||
|         if (!noLog) { |         if (!noLog) { | ||||||
|             console.log("SQLite config:"); |             log.info("db", "SQLite config:"); | ||||||
|             console.log(await R.getAll("PRAGMA journal_mode")); |             log.info("db", await R.getAll("PRAGMA journal_mode")); | ||||||
|             console.log(await R.getAll("PRAGMA cache_size")); |             log.info("db", await R.getAll("PRAGMA cache_size")); | ||||||
|             console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()")); |             log.info("db", "SQLite Version: " + await R.getCell("SELECT sqlite_version()")); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -149,15 +149,15 @@ class Database { | ||||||
|             version = 0; |             version = 0; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         console.info("Your database version: " + version); |         log.info("db", "Your database version: " + version); | ||||||
|         console.info("Latest database version: " + this.latestVersion); |         log.info("db", "Latest database version: " + this.latestVersion); | ||||||
| 
 | 
 | ||||||
|         if (version === this.latestVersion) { |         if (version === this.latestVersion) { | ||||||
|             console.info("Database patch not needed"); |             log.info("db", "Database patch not needed"); | ||||||
|         } else if (version > this.latestVersion) { |         } else if (version > this.latestVersion) { | ||||||
|             console.info("Warning: Database version is newer than expected"); |             log.info("db", "Warning: Database version is newer than expected"); | ||||||
|         } else { |         } else { | ||||||
|             console.info("Database patch is needed"); |             log.info("db", "Database patch is needed"); | ||||||
| 
 | 
 | ||||||
|             this.backup(version); |             this.backup(version); | ||||||
| 
 | 
 | ||||||
|  | @ -165,17 +165,17 @@ class Database { | ||||||
|             try { |             try { | ||||||
|                 for (let i = version + 1; i <= this.latestVersion; i++) { |                 for (let i = version + 1; i <= this.latestVersion; i++) { | ||||||
|                     const sqlFile = `./db/patch${i}.sql`; |                     const sqlFile = `./db/patch${i}.sql`; | ||||||
|                     console.info(`Patching ${sqlFile}`); |                     log.info("db", `Patching ${sqlFile}`); | ||||||
|                     await Database.importSQLFile(sqlFile); |                     await Database.importSQLFile(sqlFile); | ||||||
|                     console.info(`Patched ${sqlFile}`); |                     log.info("db", `Patched ${sqlFile}`); | ||||||
|                     await setSetting("database_version", i); |                     await setSetting("database_version", i); | ||||||
|                 } |                 } | ||||||
|             } catch (ex) { |             } catch (ex) { | ||||||
|                 await Database.close(); |                 await Database.close(); | ||||||
| 
 | 
 | ||||||
|                 console.error(ex); |                 log.error("db", ex); | ||||||
|                 console.error("Start Uptime-Kuma failed due to issue patching the database"); |                 log.error("db", "Start Uptime-Kuma failed due to issue patching the database"); | ||||||
|                 console.error("Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); |                 log.error("db", "Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); | ||||||
| 
 | 
 | ||||||
|                 this.restore(); |                 this.restore(); | ||||||
|                 process.exit(1); |                 process.exit(1); | ||||||
|  | @ -191,15 +191,15 @@ class Database { | ||||||
|      * @returns {Promise<void>} |      * @returns {Promise<void>} | ||||||
|      */ |      */ | ||||||
|     static async patch2() { |     static async patch2() { | ||||||
|         console.log("Database Patch 2.0 Process"); |         log.info("db", "Database Patch 2.0 Process"); | ||||||
|         let databasePatchedFiles = await setting("databasePatchedFiles"); |         let databasePatchedFiles = await setting("databasePatchedFiles"); | ||||||
| 
 | 
 | ||||||
|         if (! databasePatchedFiles) { |         if (! databasePatchedFiles) { | ||||||
|             databasePatchedFiles = {}; |             databasePatchedFiles = {}; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         debug("Patched files:"); |         log.debug("db", "Patched files:"); | ||||||
|         debug(databasePatchedFiles); |         log.debug("db", databasePatchedFiles); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             for (let sqlFilename in this.patchList) { |             for (let sqlFilename in this.patchList) { | ||||||
|  | @ -207,15 +207,15 @@ class Database { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (this.patched) { |             if (this.patched) { | ||||||
|                 console.log("Database Patched Successfully"); |                 log.info("db", "Database Patched Successfully"); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } catch (ex) { |         } catch (ex) { | ||||||
|             await Database.close(); |             await Database.close(); | ||||||
| 
 | 
 | ||||||
|             console.error(ex); |             log.error("db", ex); | ||||||
|             console.error("Start Uptime-Kuma failed due to issue patching the database"); |             log.error("db", "Start Uptime-Kuma failed due to issue patching the database"); | ||||||
|             console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); |             log.error("db", "Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); | ||||||
| 
 | 
 | ||||||
|             this.restore(); |             this.restore(); | ||||||
| 
 | 
 | ||||||
|  | @ -302,16 +302,16 @@ class Database { | ||||||
|         let value = this.patchList[sqlFilename]; |         let value = this.patchList[sqlFilename]; | ||||||
| 
 | 
 | ||||||
|         if (! value) { |         if (! value) { | ||||||
|             console.log(sqlFilename + " skip"); |             log.info("db", sqlFilename + " skip"); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Check if patched
 |         // Check if patched
 | ||||||
|         if (! databasePatchedFiles[sqlFilename]) { |         if (! databasePatchedFiles[sqlFilename]) { | ||||||
|             console.log(sqlFilename + " is not patched"); |             log.info("db", sqlFilename + " is not patched"); | ||||||
| 
 | 
 | ||||||
|             if (value.parents) { |             if (value.parents) { | ||||||
|                 console.log(sqlFilename + " need parents"); |                 log.info("db", sqlFilename + " need parents"); | ||||||
|                 for (let parentSQLFilename of value.parents) { |                 for (let parentSQLFilename of value.parents) { | ||||||
|                     await this.patch2Recursion(parentSQLFilename, databasePatchedFiles); |                     await this.patch2Recursion(parentSQLFilename, databasePatchedFiles); | ||||||
|                 } |                 } | ||||||
|  | @ -319,14 +319,14 @@ class Database { | ||||||
| 
 | 
 | ||||||
|             this.backup(dayjs().format("YYYYMMDDHHmmss")); |             this.backup(dayjs().format("YYYYMMDDHHmmss")); | ||||||
| 
 | 
 | ||||||
|             console.log(sqlFilename + " is patching"); |             log.info("db", sqlFilename + " is patching"); | ||||||
|             this.patched = true; |             this.patched = true; | ||||||
|             await this.importSQLFile("./db/" + sqlFilename); |             await this.importSQLFile("./db/" + sqlFilename); | ||||||
|             databasePatchedFiles[sqlFilename] = true; |             databasePatchedFiles[sqlFilename] = true; | ||||||
|             console.log(sqlFilename + " was patched successfully"); |             log.info("db", sqlFilename + " was patched successfully"); | ||||||
| 
 | 
 | ||||||
|         } else { |         } else { | ||||||
|             debug(sqlFilename + " is already patched, skip"); |             log.debug("db", sqlFilename + " is already patched, skip"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -378,7 +378,7 @@ class Database { | ||||||
|         }; |         }; | ||||||
|         process.addListener("unhandledRejection", listener); |         process.addListener("unhandledRejection", listener); | ||||||
| 
 | 
 | ||||||
|         console.log("Closing the database"); |         log.info("db", "Closing the database"); | ||||||
| 
 | 
 | ||||||
|         while (true) { |         while (true) { | ||||||
|             Database.noReject = true; |             Database.noReject = true; | ||||||
|  | @ -388,10 +388,10 @@ class Database { | ||||||
|             if (Database.noReject) { |             if (Database.noReject) { | ||||||
|                 break; |                 break; | ||||||
|             } else { |             } else { | ||||||
|                 console.log("Waiting to close the database"); |                 log.info("db", "Waiting to close the database"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         console.log("SQLite closed"); |         log.info("db", "SQLite closed"); | ||||||
| 
 | 
 | ||||||
|         process.removeListener("unhandledRejection", listener); |         process.removeListener("unhandledRejection", listener); | ||||||
|     } |     } | ||||||
|  | @ -403,7 +403,7 @@ class Database { | ||||||
|      */ |      */ | ||||||
|     static backup(version) { |     static backup(version) { | ||||||
|         if (! this.backupPath) { |         if (! this.backupPath) { | ||||||
|             console.info("Backing up the database"); |             log.info("db", "Backing up the database"); | ||||||
|             this.backupPath = this.dataDir + "kuma.db.bak" + version; |             this.backupPath = this.dataDir + "kuma.db.bak" + version; | ||||||
|             fs.copyFileSync(Database.path, this.backupPath); |             fs.copyFileSync(Database.path, this.backupPath); | ||||||
| 
 | 
 | ||||||
|  | @ -426,7 +426,7 @@ class Database { | ||||||
|      */ |      */ | ||||||
|     static restore() { |     static restore() { | ||||||
|         if (this.backupPath) { |         if (this.backupPath) { | ||||||
|             console.error("Patching the database failed!!! Restoring the backup"); |             log.error("db", "Patching the database failed!!! Restoring the backup"); | ||||||
| 
 | 
 | ||||||
|             const shmPath = Database.path + "-shm"; |             const shmPath = Database.path + "-shm"; | ||||||
|             const walPath = Database.path + "-wal"; |             const walPath = Database.path + "-wal"; | ||||||
|  | @ -445,7 +445,7 @@ class Database { | ||||||
|                     fs.unlinkSync(walPath); |                     fs.unlinkSync(walPath); | ||||||
|                 } |                 } | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.log("Restore failed; you may need to restore the backup manually"); |                 log.error("db", "Restore failed; you may need to restore the backup manually"); | ||||||
|                 process.exit(1); |                 process.exit(1); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -461,14 +461,14 @@ class Database { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } else { |         } else { | ||||||
|             console.log("Nothing to restore"); |             log.info("db", "Nothing to restore"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static getSize() { |     static getSize() { | ||||||
|         debug("Database.getSize()"); |         log.debug("db", "Database.getSize()"); | ||||||
|         let stats = fs.statSync(Database.path); |         let stats = fs.statSync(Database.path); | ||||||
|         debug(stats); |         log.debug("db", stats); | ||||||
|         return stats.size; |         return stats.size; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,12 +3,19 @@ | ||||||
|     Modified with 0 dependencies |     Modified with 0 dependencies | ||||||
|  */ |  */ | ||||||
| let fs = require("fs"); | let fs = require("fs"); | ||||||
|  | const { log } = require("../src/util"); | ||||||
| 
 | 
 | ||||||
| let ImageDataURI = (() => { | let ImageDataURI = (() => { | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @param {string} dataURI - A string that is a valid Data URI. | ||||||
|  |      * @returns {?Object} An object with properties "imageType" and "dataBase64". The former is the image type, e.g., "png", and the latter is a base64 encoded string of the image's binary data. If it fails to parse, returns null instead of an object. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function decode(dataURI) { |     function decode(dataURI) { | ||||||
|         if (!/data:image\//.test(dataURI)) { |         if (!/data:image\//.test(dataURI)) { | ||||||
|             console.log("ImageDataURI :: Error :: It seems that it is not an Image Data URI. Couldn't match \"data:image/\""); |             log.error("image-data-uri", "It seems that it is not an Image Data URI. Couldn't match \"data:image/\""); | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -20,9 +27,16 @@ let ImageDataURI = (() => { | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @param {Buffer} data - The image data to be encoded. | ||||||
|  |      * @param {String} mediaType - The type of the image, e.g., "image/png". | ||||||
|  |      * @returns {String|null} A string representing the base64-encoded version of the given Buffer object or null if an error occurred. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function encode(data, mediaType) { |     function encode(data, mediaType) { | ||||||
|         if (!data || !mediaType) { |         if (!data || !mediaType) { | ||||||
|             console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType "); |             log.error("image-data-uri", "Missing some of the required params: data, mediaType"); | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -33,6 +47,13 @@ let ImageDataURI = (() => { | ||||||
|         return dataImgBase64; |         return dataImgBase64; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Converts a data URI to a file path. | ||||||
|  |      * @param {string} dataURI The Data URI of the image. | ||||||
|  |      * @param {string} [filePath] The path where the image will be saved, defaults to "./". | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function outputFile(dataURI, filePath) { |     function outputFile(dataURI, filePath) { | ||||||
|         filePath = filePath || "./"; |         filePath = filePath || "./"; | ||||||
|         return new Promise((resolve, reject) => { |         return new Promise((resolve, reject) => { | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const Bree = require("bree"); | const Bree = require("bree"); | ||||||
| const { SHARE_ENV } = require("worker_threads"); | const { SHARE_ENV } = require("worker_threads"); | ||||||
|  | const { log } = require("../src/util"); | ||||||
| let bree; | let bree; | ||||||
| const jobs = [ | const jobs = [ | ||||||
|     { |     { | ||||||
|  | @ -18,7 +19,7 @@ const initBackgroundJobs = function (args) { | ||||||
|             workerData: args, |             workerData: args, | ||||||
|         }, |         }, | ||||||
|         workerMessageHandler: (message) => { |         workerMessageHandler: (message) => { | ||||||
|             console.log("[Background Job]:", message); |             log.info("jobs", message); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								server/logger.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								server/logger.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -6,7 +6,7 @@ dayjs.extend(utc); | ||||||
| dayjs.extend(timezone); | dayjs.extend(timezone); | ||||||
| const axios = require("axios"); | const axios = require("axios"); | ||||||
| const { Prometheus } = require("../prometheus"); | const { Prometheus } = require("../prometheus"); | ||||||
| const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); | const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); | ||||||
| const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog } = require("../util-server"); | const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog } = 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"); | ||||||
|  | @ -193,7 +193,7 @@ class Monitor extends BeanModel { | ||||||
|                         rejectUnauthorized: !this.getIgnoreTls(), |                         rejectUnauthorized: !this.getIgnoreTls(), | ||||||
|                     }; |                     }; | ||||||
| 
 | 
 | ||||||
|                     debug(`[${this.name}] Prepare Options for axios`); |                     log.debug("monitor", `[${this.name}] Prepare Options for axios`); | ||||||
| 
 | 
 | ||||||
|                     const options = { |                     const options = { | ||||||
|                         url: this.url, |                         url: this.url, | ||||||
|  | @ -230,8 +230,8 @@ class Monitor extends BeanModel { | ||||||
|                         options.httpsAgent = new https.Agent(httpsAgentOptions); |                         options.httpsAgent = new https.Agent(httpsAgentOptions); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     debug(`[${this.name}] Axios Options: ${JSON.stringify(options)}`); |                     log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`); | ||||||
|                     debug(`[${this.name}] Axios Request`); |                     log.debug("monitor", `[${this.name}] Axios Request`); | ||||||
| 
 | 
 | ||||||
|                     let res = await axios.request(options); |                     let res = await axios.request(options); | ||||||
|                     bean.msg = `${res.status} - ${res.statusText}`; |                     bean.msg = `${res.status} - ${res.statusText}`; | ||||||
|  | @ -240,29 +240,30 @@ class Monitor extends BeanModel { | ||||||
|                     // Check certificate if https is used
 |                     // Check certificate if https is used
 | ||||||
|                     let certInfoStartTime = dayjs().valueOf(); |                     let certInfoStartTime = dayjs().valueOf(); | ||||||
|                     if (this.getUrl()?.protocol === "https:") { |                     if (this.getUrl()?.protocol === "https:") { | ||||||
|                         debug(`[${this.name}] Check cert`); |                         log.debug("monitor", `[${this.name}] Check cert`); | ||||||
|                         try { |                         try { | ||||||
|                             let tlsInfoObject = checkCertificate(res); |                             let tlsInfoObject = checkCertificate(res); | ||||||
|                             tlsInfo = await this.updateTlsInfo(tlsInfoObject); |                             tlsInfo = await this.updateTlsInfo(tlsInfoObject); | ||||||
| 
 | 
 | ||||||
|                             if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) { |                             if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) { | ||||||
|                                 debug(`[${this.name}] call sendCertNotification`); |                                 log.debug("monitor", `[${this.name}] call sendCertNotification`); | ||||||
|                                 await this.sendCertNotification(tlsInfoObject); |                                 await this.sendCertNotification(tlsInfoObject); | ||||||
|                             } |                             } | ||||||
| 
 | 
 | ||||||
|                         } catch (e) { |                         } catch (e) { | ||||||
|                             if (e.message !== "No TLS certificate in response") { |                             if (e.message !== "No TLS certificate in response") { | ||||||
|                                 console.error(e.message); |                                 log.error("monitor", "Caught error"); | ||||||
|  |                                 log.error("monitor", e.message); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (process.env.TIMELOGGER === "1") { |                     if (process.env.TIMELOGGER === "1") { | ||||||
|                         debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); |                         log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) { |                     if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) { | ||||||
|                         console.log(res.data); |                         log.info("monitor", res.data); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (this.type === "http") { |                     if (this.type === "http") { | ||||||
|  | @ -342,7 +343,7 @@ class Monitor extends BeanModel { | ||||||
|                         time |                         time | ||||||
|                     ]); |                     ]); | ||||||
| 
 | 
 | ||||||
|                     debug("heartbeatCount" + heartbeatCount + " " + time); |                     log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time); | ||||||
| 
 | 
 | ||||||
|                     if (heartbeatCount <= 0) { |                     if (heartbeatCount <= 0) { | ||||||
|                         // Fix #922, since previous heartbeat could be inserted by api, it should get from database
 |                         // Fix #922, since previous heartbeat could be inserted by api, it should get from database
 | ||||||
|  | @ -426,7 +427,7 @@ class Monitor extends BeanModel { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             debug(`[${this.name}] Check isImportant`); |             log.debug("monitor", `[${this.name}] Check isImportant`); | ||||||
|             let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); |             let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); | ||||||
| 
 | 
 | ||||||
|             // Mark as important if status changed, ignore pending pings,
 |             // Mark as important if status changed, ignore pending pings,
 | ||||||
|  | @ -434,11 +435,11 @@ class Monitor extends BeanModel { | ||||||
|             if (isImportant) { |             if (isImportant) { | ||||||
|                 bean.important = true; |                 bean.important = true; | ||||||
| 
 | 
 | ||||||
|                 debug(`[${this.name}] sendNotification`); |                 log.debug("monitor", `[${this.name}] sendNotification`); | ||||||
|                 await Monitor.sendNotification(isFirstBeat, this, bean); |                 await Monitor.sendNotification(isFirstBeat, this, bean); | ||||||
| 
 | 
 | ||||||
|                 // Clear Status Page Cache
 |                 // Clear Status Page Cache
 | ||||||
|                 debug(`[${this.name}] apicache clear`); |                 log.debug("monitor", `[${this.name}] apicache clear`); | ||||||
|                 apicache.clear(); |                 apicache.clear(); | ||||||
| 
 | 
 | ||||||
|             } else { |             } else { | ||||||
|  | @ -446,33 +447,33 @@ class Monitor extends BeanModel { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (bean.status === UP) { |             if (bean.status === UP) { | ||||||
|                 console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`); |                 log.info("monitor", `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||||
|             } else if (bean.status === PENDING) { |             } else if (bean.status === PENDING) { | ||||||
|                 if (this.retryInterval > 0) { |                 if (this.retryInterval > 0) { | ||||||
|                     beatInterval = this.retryInterval; |                     beatInterval = this.retryInterval; | ||||||
|                 } |                 } | ||||||
|                 console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`); |                 log.warn("monitor", `Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||||
|             } else { |             } else { | ||||||
|                 console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`); |                 log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             debug(`[${this.name}] Send to socket`); |             log.debug("monitor", `[${this.name}] Send to socket`); | ||||||
|             io.to(this.user_id).emit("heartbeat", bean.toJSON()); |             io.to(this.user_id).emit("heartbeat", bean.toJSON()); | ||||||
|             Monitor.sendStats(io, this.id, this.user_id); |             Monitor.sendStats(io, this.id, this.user_id); | ||||||
| 
 | 
 | ||||||
|             debug(`[${this.name}] Store`); |             log.debug("monitor", `[${this.name}] Store`); | ||||||
|             await R.store(bean); |             await R.store(bean); | ||||||
| 
 | 
 | ||||||
|             debug(`[${this.name}] prometheus.update`); |             log.debug("monitor", `[${this.name}] prometheus.update`); | ||||||
|             prometheus.update(bean, tlsInfo); |             prometheus.update(bean, tlsInfo); | ||||||
| 
 | 
 | ||||||
|             previousBeat = bean; |             previousBeat = bean; | ||||||
| 
 | 
 | ||||||
|             if (! this.isStop) { |             if (! this.isStop) { | ||||||
|                 debug(`[${this.name}] SetTimeout for next check.`); |                 log.debug("monitor", `[${this.name}] SetTimeout for next check.`); | ||||||
|                 this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000); |                 this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000); | ||||||
|             } else { |             } else { | ||||||
|                 console.log(`[${this.name}] isStop = true, no next check.`); |                 log.info("monitor", `[${this.name}] isStop = true, no next check.`); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         }; |         }; | ||||||
|  | @ -483,10 +484,10 @@ class Monitor extends BeanModel { | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.trace(e); |                 console.trace(e); | ||||||
|                 errorLog(e, false); |                 errorLog(e, false); | ||||||
|                 console.error("Please report to https://github.com/louislam/uptime-kuma/issues"); |                 log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues"); | ||||||
| 
 | 
 | ||||||
|                 if (! this.isStop) { |                 if (! this.isStop) { | ||||||
|                     console.log("Try to restart the monitor"); |                     log.info("monitor", "Try to restart the monitor"); | ||||||
|                     this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); |                     this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -533,41 +534,41 @@ class Monitor extends BeanModel { | ||||||
|      * @returns {Promise<object>} |      * @returns {Promise<object>} | ||||||
|      */ |      */ | ||||||
|     async updateTlsInfo(checkCertificateResult) { |     async updateTlsInfo(checkCertificateResult) { | ||||||
|         let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ |         let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ | ||||||
|             this.id, |             this.id, | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         if (tls_info_bean == null) { |         if (tlsInfoBean == null) { | ||||||
|             tls_info_bean = R.dispense("monitor_tls_info"); |             tlsInfoBean = R.dispense("monitor_tls_info"); | ||||||
|             tls_info_bean.monitor_id = this.id; |             tlsInfoBean.monitor_id = this.id; | ||||||
|         } else { |         } else { | ||||||
| 
 | 
 | ||||||
|             // Clear sent history if the cert changed.
 |             // Clear sent history if the cert changed.
 | ||||||
|             try { |             try { | ||||||
|                 let oldCertInfo = JSON.parse(tls_info_bean.info_json); |                 let oldCertInfo = JSON.parse(tlsInfoBean.info_json); | ||||||
| 
 | 
 | ||||||
|                 let isValidObjects = oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo; |                 let isValidObjects = oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo; | ||||||
| 
 | 
 | ||||||
|                 if (isValidObjects) { |                 if (isValidObjects) { | ||||||
|                     if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) { |                     if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) { | ||||||
|                         debug("Resetting sent_history"); |                         log.debug("monitor", "Resetting sent_history"); | ||||||
|                         await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [ |                         await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [ | ||||||
|                             this.id |                             this.id | ||||||
|                         ]); |                         ]); | ||||||
|                     } else { |                     } else { | ||||||
|                         debug("No need to reset sent_history"); |                         log.debug("monitor", "No need to reset sent_history"); | ||||||
|                         debug(oldCertInfo.certInfo.fingerprint256); |                         log.debug("monitor", oldCertInfo.certInfo.fingerprint256); | ||||||
|                         debug(checkCertificateResult.certInfo.fingerprint256); |                         log.debug("monitor", checkCertificateResult.certInfo.fingerprint256); | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     debug("Not valid object"); |                     log.debug("monitor", "Not valid object"); | ||||||
|                 } |                 } | ||||||
|             } catch (e) { } |             } catch (e) { } | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         tls_info_bean.info_json = JSON.stringify(checkCertificateResult); |         tlsInfoBean.info_json = JSON.stringify(checkCertificateResult); | ||||||
|         await R.store(tls_info_bean); |         await R.store(tlsInfoBean); | ||||||
| 
 | 
 | ||||||
|         return checkCertificateResult; |         return checkCertificateResult; | ||||||
|     } |     } | ||||||
|  | @ -581,7 +582,7 @@ class Monitor extends BeanModel { | ||||||
|             await Monitor.sendUptime(24 * 30, io, monitorID, userID); |             await Monitor.sendUptime(24 * 30, io, monitorID, userID); | ||||||
|             await Monitor.sendCertInfo(io, monitorID, userID); |             await Monitor.sendCertInfo(io, monitorID, userID); | ||||||
|         } else { |         } else { | ||||||
|             debug("No clients in the room, no need to send stats"); |             log.debug("monitor", "No clients in the room, no need to send stats"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -728,8 +729,8 @@ class Monitor extends BeanModel { | ||||||
|                 try { |                 try { | ||||||
|                     await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON()); |                     await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON()); | ||||||
|                 } catch (e) { |                 } catch (e) { | ||||||
|                     console.error("Cannot send notification to " + notification.name); |                     log.error("monitor", "Cannot send notification to " + notification.name); | ||||||
|                     console.log(e); |                     log.error("monitor", e); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -746,7 +747,7 @@ class Monitor extends BeanModel { | ||||||
|         if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { |         if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { | ||||||
|             const notificationList = await Monitor.getNotificationList(this); |             const notificationList = await Monitor.getNotificationList(this); | ||||||
| 
 | 
 | ||||||
|             debug("call sendCertNotificationByTargetDays"); |             log.debug("monitor", "call sendCertNotificationByTargetDays"); | ||||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList); |             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList); | ||||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList); |             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList); | ||||||
|             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList); |             await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList); | ||||||
|  | @ -756,7 +757,7 @@ class Monitor extends BeanModel { | ||||||
|     async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) { |     async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) { | ||||||
| 
 | 
 | ||||||
|         if (daysRemaining > targetDays) { |         if (daysRemaining > targetDays) { | ||||||
|             debug(`No need to send cert notification. ${daysRemaining} > ${targetDays}`); |             log.debug("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -770,21 +771,21 @@ class Monitor extends BeanModel { | ||||||
| 
 | 
 | ||||||
|             // Sent already, no need to send again
 |             // Sent already, no need to send again
 | ||||||
|             if (row) { |             if (row) { | ||||||
|                 debug("Sent already, no need to send again"); |                 log.debug("monitor", "Sent already, no need to send again"); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             let sent = false; |             let sent = false; | ||||||
|             debug("Send certificate notification"); |             log.debug("monitor", "Send certificate notification"); | ||||||
| 
 | 
 | ||||||
|             for (let notification of notificationList) { |             for (let notification of notificationList) { | ||||||
|                 try { |                 try { | ||||||
|                     debug("Sending to " + notification.name); |                     log.debug("monitor", "Sending to " + notification.name); | ||||||
|                     await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`); |                     await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`); | ||||||
|                     sent = true; |                     sent = true; | ||||||
|                 } catch (e) { |                 } catch (e) { | ||||||
|                     console.error("Cannot send cert notification to " + notification.name); |                     log.error("monitor", "Cannot send cert notification to " + notification.name); | ||||||
|                     console.error(e); |                     log.error("monitor", e); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -796,7 +797,7 @@ class Monitor extends BeanModel { | ||||||
|                 ]); |                 ]); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             debug("No notification, no need to send cert notification"); |             log.debug("monitor", "No notification, no need to send cert notification"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -68,6 +68,15 @@ function ApiCache() { | ||||||
|     instances.push(this); |     instances.push(this); | ||||||
|     this.id = instances.length; |     this.id = instances.length; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Logs a message to the console if the `DEBUG` environment variable is set. | ||||||
|  |      * @param {string} a - The first argument to log. | ||||||
|  |      * @param {string} b - The second argument to log. | ||||||
|  |      * @param {string} c - The third argument to log. | ||||||
|  |      * @param {string} d - The fourth argument to log, and so on... (optional) | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function debug(a, b, c, d) { |     function debug(a, b, c, d) { | ||||||
|         let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) { |         let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) { | ||||||
|             return arg !== undefined; |             return arg !== undefined; | ||||||
|  | @ -77,6 +86,13 @@ function ApiCache() { | ||||||
|         return (globalOptions.debug || debugEnv) && console.log.apply(null, arr); |         return (globalOptions.debug || debugEnv) && console.log.apply(null, arr); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns true if the given request and response should be logged. | ||||||
|  |      * @param {Object} request The HTTP request object. | ||||||
|  |      * @param {Object} response The HTTP response object. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function shouldCacheResponse(request, response, toggle) { |     function shouldCacheResponse(request, response, toggle) { | ||||||
|         let opt = globalOptions; |         let opt = globalOptions; | ||||||
|         let codes = opt.statusCodes; |         let codes = opt.statusCodes; | ||||||
|  | @ -99,6 +115,12 @@ function ApiCache() { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Adds a key to the index. | ||||||
|  |      * @param {string} key The key to add. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function addIndexEntries(key, req) { |     function addIndexEntries(key, req) { | ||||||
|         let groupName = req.apicacheGroup; |         let groupName = req.apicacheGroup; | ||||||
| 
 | 
 | ||||||
|  | @ -111,6 +133,13 @@ function ApiCache() { | ||||||
|         index.all.unshift(key); |         index.all.unshift(key); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns a new object containing only the whitelisted headers. | ||||||
|  |      * @param {Object} headers The original object of header names and values. | ||||||
|  |      * @param {Array.<string>} globalOptions.headerWhitelist An array of strings representing the whitelisted header names to keep in the output object. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function filterBlacklistedHeaders(headers) { |     function filterBlacklistedHeaders(headers) { | ||||||
|         return Object.keys(headers) |         return Object.keys(headers) | ||||||
|             .filter(function (key) { |             .filter(function (key) { | ||||||
|  | @ -122,6 +151,12 @@ function ApiCache() { | ||||||
|             }, {}); |             }, {}); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @param {Object} headers The response headers to filter. | ||||||
|  |      * @returns {Object} A new object containing only the whitelisted response headers. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function createCacheObject(status, headers, data, encoding) { |     function createCacheObject(status, headers, data, encoding) { | ||||||
|         return { |         return { | ||||||
|             status: status, |             status: status, | ||||||
|  | @ -132,6 +167,14 @@ function ApiCache() { | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Sets a cache value for the given key. | ||||||
|  |      * @param {string} key The cache key to set. | ||||||
|  |      * @param {*} value The cache value to set. | ||||||
|  |      * @param {number} duration How long in milliseconds the cached response should be valid for (defaults to 1 hour). | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function cacheResponse(key, value, duration) { |     function cacheResponse(key, value, duration) { | ||||||
|         let redis = globalOptions.redisClient; |         let redis = globalOptions.redisClient; | ||||||
|         let expireCallback = globalOptions.events.expire; |         let expireCallback = globalOptions.events.expire; | ||||||
|  | @ -154,6 +197,12 @@ function ApiCache() { | ||||||
|         }, Math.min(duration, 2147483647)); |         }, Math.min(duration, 2147483647)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Appends content to the response. | ||||||
|  |      * @param {string|Buffer} content The content to append. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function accumulateContent(res, content) { |     function accumulateContent(res, content) { | ||||||
|         if (content) { |         if (content) { | ||||||
|             if (typeof content == "string") { |             if (typeof content == "string") { | ||||||
|  | @ -179,6 +228,13 @@ function ApiCache() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Monkeypatches the response object to add cache control headers and create a cache object. | ||||||
|  |      * @param {Object} req - The request object. | ||||||
|  |      * @param {Object} res - The response object. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { |     function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { | ||||||
|     // monkeypatch res.end to create cache object
 |     // monkeypatch res.end to create cache object
 | ||||||
|         res._apicache = { |         res._apicache = { | ||||||
|  | @ -245,6 +301,13 @@ function ApiCache() { | ||||||
|         next(); |         next(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @param {Request} request | ||||||
|  |      * @param {Response} response | ||||||
|  |      * @returns {boolean|undefined} true if the request should be cached, false otherwise. If undefined, defaults to true. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function sendCachedResponse(request, response, cacheObject, toggle, next, duration) { |     function sendCachedResponse(request, response, cacheObject, toggle, next, duration) { | ||||||
|         if (toggle && !toggle(request, response)) { |         if (toggle && !toggle(request, response)) { | ||||||
|             return next(); |             return next(); | ||||||
|  | @ -365,6 +428,13 @@ function ApiCache() { | ||||||
|         return this.getIndex(); |         return this.getIndex(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Converts a duration string to an integer number of milliseconds. | ||||||
|  |      * @param {string} duration - The string to convert. | ||||||
|  |      * @returns {number} The converted value in milliseconds, or the defaultDuration if it can't be parsed. | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function parseDuration(duration, defaultDuration) { |     function parseDuration(duration, defaultDuration) { | ||||||
|         if (typeof duration === "number") { |         if (typeof duration === "number") { | ||||||
|             return duration; |             return duration; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ class Apprise extends NotificationProvider { | ||||||
|     name = "apprise"; |     name = "apprise"; | ||||||
| 
 | 
 | ||||||
|     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { |     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { | ||||||
|         let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL]) |         let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL]); | ||||||
| 
 | 
 | ||||||
|         let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; |         let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; | ||||||
| 
 | 
 | ||||||
|  | @ -16,7 +16,7 @@ class Apprise extends NotificationProvider { | ||||||
|                 return "Sent Successfully"; |                 return "Sent Successfully"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             throw new Error(output) |             throw new Error(output); | ||||||
|         } else { |         } else { | ||||||
|             return "No output from apprise"; |             return "No output from apprise"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -21,31 +21,26 @@ class Bark extends NotificationProvider { | ||||||
|     name = "Bark"; |     name = "Bark"; | ||||||
| 
 | 
 | ||||||
|     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { |     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { | ||||||
|         try { |         let barkEndpoint = notification.barkEndpoint; | ||||||
|             var barkEndpoint = notification.barkEndpoint; |  | ||||||
| 
 | 
 | ||||||
|             // check if the endpoint has a "/" suffix, if so, delete it first
 |         // check if the endpoint has a "/" suffix, if so, delete it first
 | ||||||
|             if (barkEndpoint.endsWith("/")) { |         if (barkEndpoint.endsWith("/")) { | ||||||
|                 barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1); |             barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1); | ||||||
|             } |         } | ||||||
| 
 | 
 | ||||||
|             if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) { |         if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) { | ||||||
|                 let title = "UptimeKuma Monitor Up"; |             let title = "UptimeKuma Monitor Up"; | ||||||
|                 return await this.postNotification(title, msg, barkEndpoint); |             return await this.postNotification(title, msg, barkEndpoint); | ||||||
|             } |         } | ||||||
| 
 | 
 | ||||||
|             if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) { |         if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) { | ||||||
|                 let title = "UptimeKuma Monitor Down"; |             let title = "UptimeKuma Monitor Down"; | ||||||
|                 return await this.postNotification(title, msg, barkEndpoint); |             return await this.postNotification(title, msg, barkEndpoint); | ||||||
|             } |         } | ||||||
| 
 | 
 | ||||||
|             if (msg != null) { |         if (msg != null) { | ||||||
|                 let title = "UptimeKuma Message"; |             let title = "UptimeKuma Message"; | ||||||
|                 return await this.postNotification(title, msg, barkEndpoint); |             return await this.postNotification(title, msg, barkEndpoint); | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } catch (error) { |  | ||||||
|             throw error; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ class ClickSendSMS extends NotificationProvider { | ||||||
|             let config = { |             let config = { | ||||||
|                 headers: { |                 headers: { | ||||||
|                     "Content-Type": "application/json", |                     "Content-Type": "application/json", | ||||||
|                     "Authorization": "Basic " + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString('base64'), |                     "Authorization": "Basic " + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString("base64"), | ||||||
|                     "Accept": "text/json", |                     "Accept": "text/json", | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|  | @ -17,8 +17,8 @@ class Discord extends NotificationProvider { | ||||||
|                 let discordtestdata = { |                 let discordtestdata = { | ||||||
|                     username: discordDisplayName, |                     username: discordDisplayName, | ||||||
|                     content: msg, |                     content: msg, | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(notification.discordWebhookUrl, discordtestdata) |                 await axios.post(notification.discordWebhookUrl, discordtestdata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -66,13 +66,13 @@ class Discord extends NotificationProvider { | ||||||
|                             }, |                             }, | ||||||
|                         ], |                         ], | ||||||
|                     }], |                     }], | ||||||
|                 } |                 }; | ||||||
| 
 | 
 | ||||||
|                 if (notification.discordPrefixMessage) { |                 if (notification.discordPrefixMessage) { | ||||||
|                     discorddowndata.content = notification.discordPrefixMessage; |                     discorddowndata.content = notification.discordPrefixMessage; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 await axios.post(notification.discordWebhookUrl, discorddowndata) |                 await axios.post(notification.discordWebhookUrl, discorddowndata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
| 
 | 
 | ||||||
|             } else if (heartbeatJSON["status"] == UP) { |             } else if (heartbeatJSON["status"] == UP) { | ||||||
|  | @ -101,17 +101,17 @@ class Discord extends NotificationProvider { | ||||||
|                             }, |                             }, | ||||||
|                         ], |                         ], | ||||||
|                     }], |                     }], | ||||||
|                 } |                 }; | ||||||
| 
 | 
 | ||||||
|                 if (notification.discordPrefixMessage) { |                 if (notification.discordPrefixMessage) { | ||||||
|                     discordupdata.content = notification.discordPrefixMessage; |                     discordupdata.content = notification.discordPrefixMessage; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 await axios.post(notification.discordWebhookUrl, discordupdata) |                 await axios.post(notification.discordWebhookUrl, discordupdata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
|             } |             } | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,11 +13,11 @@ class GoogleChat extends NotificationProvider { | ||||||
|         try { |         try { | ||||||
|             // Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic
 |             // Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic
 | ||||||
| 
 | 
 | ||||||
|             let textMsg = '' |             let textMsg = ""; | ||||||
|             if (heartbeatJSON && heartbeatJSON.status === UP) { |             if (heartbeatJSON && heartbeatJSON.status === UP) { | ||||||
|                 textMsg = `✅ Application is back online\n`; |                 textMsg = "✅ Application is back online\n"; | ||||||
|             } else if (heartbeatJSON && heartbeatJSON.status === DOWN) { |             } else if (heartbeatJSON && heartbeatJSON.status === DOWN) { | ||||||
|                 textMsg = `🔴 Application went down\n`; |                 textMsg = "🔴 Application went down\n"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (monitorJSON && monitorJSON.name) { |             if (monitorJSON && monitorJSON.name) { | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ class Gotify extends NotificationProvider { | ||||||
|                 "message": msg, |                 "message": msg, | ||||||
|                 "priority": notification.gotifyPriority || 8, |                 "priority": notification.gotifyPriority || 8, | ||||||
|                 "title": "Uptime-Kuma", |                 "title": "Uptime-Kuma", | ||||||
|             }) |             }); | ||||||
| 
 | 
 | ||||||
|             return okMsg; |             return okMsg; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,8 +25,8 @@ class Line extends NotificationProvider { | ||||||
|                             "text": "Test Successful!" |                             "text": "Test Successful!" | ||||||
|                         } |                         } | ||||||
|                     ] |                     ] | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lineAPIUrl, testMessage, config) |                 await axios.post(lineAPIUrl, testMessage, config); | ||||||
|             } else if (heartbeatJSON["status"] == DOWN) { |             } else if (heartbeatJSON["status"] == DOWN) { | ||||||
|                 let downMessage = { |                 let downMessage = { | ||||||
|                     "to": notification.lineUserID, |                     "to": notification.lineUserID, | ||||||
|  | @ -36,8 +36,8 @@ class Line extends NotificationProvider { | ||||||
|                             "text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"] |                             "text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"] | ||||||
|                         } |                         } | ||||||
|                     ] |                     ] | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lineAPIUrl, downMessage, config) |                 await axios.post(lineAPIUrl, downMessage, config); | ||||||
|             } else if (heartbeatJSON["status"] == UP) { |             } else if (heartbeatJSON["status"] == UP) { | ||||||
|                 let upMessage = { |                 let upMessage = { | ||||||
|                     "to": notification.lineUserID, |                     "to": notification.lineUserID, | ||||||
|  | @ -47,12 +47,12 @@ class Line extends NotificationProvider { | ||||||
|                             "text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"] |                             "text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"] | ||||||
|                         } |                         } | ||||||
|                     ] |                     ] | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lineAPIUrl, upMessage, config) |                 await axios.post(lineAPIUrl, upMessage, config); | ||||||
|             } |             } | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,15 +8,15 @@ class LunaSea 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."; | ||||||
|         let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice |         let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             if (heartbeatJSON == null) { |             if (heartbeatJSON == null) { | ||||||
|                 let testdata = { |                 let testdata = { | ||||||
|                     "title": "Uptime Kuma Alert", |                     "title": "Uptime Kuma Alert", | ||||||
|                     "body": "Testing Successful.", |                     "body": "Testing Successful.", | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lunaseadevice, testdata) |                 await axios.post(lunaseadevice, testdata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -24,8 +24,8 @@ class LunaSea extends NotificationProvider { | ||||||
|                 let downdata = { |                 let downdata = { | ||||||
|                     "title": "UptimeKuma Alert: " + monitorJSON["name"], |                     "title": "UptimeKuma Alert: " + monitorJSON["name"], | ||||||
|                     "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], |                     "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lunaseadevice, downdata) |                 await axios.post(lunaseadevice, downdata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -33,13 +33,13 @@ class LunaSea extends NotificationProvider { | ||||||
|                 let updata = { |                 let updata = { | ||||||
|                     "title": "UptimeKuma Alert: " + monitorJSON["name"], |                     "title": "UptimeKuma Alert: " + monitorJSON["name"], | ||||||
|                     "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], |                     "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(lunaseadevice, updata) |                 await axios.post(lunaseadevice, updata); | ||||||
|                 return okMsg; |                 return okMsg; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| const NotificationProvider = require("./notification-provider"); | const NotificationProvider = require("./notification-provider"); | ||||||
| const axios = require("axios"); | const axios = require("axios"); | ||||||
| const Crypto = require("crypto"); | const Crypto = require("crypto"); | ||||||
| const { debug } = require("../../src/util"); | const { log } = require("../../src/util"); | ||||||
| 
 | 
 | ||||||
| class Matrix extends NotificationProvider { | class Matrix extends NotificationProvider { | ||||||
|     name = "matrix"; |     name = "matrix"; | ||||||
|  | @ -17,11 +17,11 @@ class Matrix extends NotificationProvider { | ||||||
|                 .slice(0, size) |                 .slice(0, size) | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         debug("Random String: " + randomString); |         log.debug("notification", "Random String: " + randomString); | ||||||
| 
 | 
 | ||||||
|         const roomId = encodeURIComponent(notification.internalRoomId); |         const roomId = encodeURIComponent(notification.internalRoomId); | ||||||
| 
 | 
 | ||||||
|         debug("Matrix Room ID: " + roomId); |         log.debug("notification", "Matrix Room ID: " + roomId); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             let config = { |             let config = { | ||||||
|  |  | ||||||
|  | @ -25,11 +25,11 @@ class NotificationProvider { | ||||||
|             if (typeof error.response.data === "string") { |             if (typeof error.response.data === "string") { | ||||||
|                 msg += error.response.data; |                 msg += error.response.data; | ||||||
|             } else { |             } else { | ||||||
|                 msg += JSON.stringify(error.response.data) |                 msg += JSON.stringify(error.response.data); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         throw new Error(msg) |         throw new Error(msg); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -30,7 +30,7 @@ class Octopush extends NotificationProvider { | ||||||
|                     "purpose": "alert", |                     "purpose": "alert", | ||||||
|                     "sender": notification.octopushSenderName |                     "sender": notification.octopushSenderName | ||||||
|                 }; |                 }; | ||||||
|                 await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config) |                 await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config); | ||||||
|             } else if (notification.octopushVersion == 1) { |             } else if (notification.octopushVersion == 1) { | ||||||
|                 let data = { |                 let data = { | ||||||
|                     "user_login": notification.octopushDMLogin, |                     "user_login": notification.octopushDMLogin, | ||||||
|  | @ -49,7 +49,7 @@ class Octopush extends NotificationProvider { | ||||||
|                     }, |                     }, | ||||||
|                     params: data |                     params: data | ||||||
|                 }; |                 }; | ||||||
|                 await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config) |                 await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config); | ||||||
|             } else { |             } else { | ||||||
|                 throw new Error("Unknown Octopush version!"); |                 throw new Error("Unknown Octopush version!"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								server/notification-providers/onebot.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								server/notification-providers/onebot.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | const NotificationProvider = require("./notification-provider"); | ||||||
|  | const axios = require("axios"); | ||||||
|  | 
 | ||||||
|  | class OneBot extends NotificationProvider { | ||||||
|  | 
 | ||||||
|  |     name = "OneBot"; | ||||||
|  | 
 | ||||||
|  |     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { | ||||||
|  |         let okMsg = "Sent Successfully."; | ||||||
|  |         try { | ||||||
|  |             let httpAddr = notification.httpAddr; | ||||||
|  |             if (!httpAddr.startsWith("http")) { | ||||||
|  |                 httpAddr = "http://" + httpAddr; | ||||||
|  |             } | ||||||
|  |             if (!httpAddr.endsWith("/")) { | ||||||
|  |                 httpAddr += "/"; | ||||||
|  |             } | ||||||
|  |             let onebotAPIUrl = httpAddr + "send_msg"; | ||||||
|  |             let config = { | ||||||
|  |                 headers: { | ||||||
|  |                     "Content-Type": "application/json", | ||||||
|  |                     "Authorization": "Bearer " + notification.accessToken, | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |             let pushText = "UptimeKuma Alert: " + msg; | ||||||
|  |             let data = { | ||||||
|  |                 "auto_escape": true, | ||||||
|  |                 "message": pushText, | ||||||
|  |             }; | ||||||
|  |             if (notification.msgType == "group") { | ||||||
|  |                 data["message_type"] = "group"; | ||||||
|  |                 data["group_id"] = notification.recieverId; | ||||||
|  |             } else { | ||||||
|  |                 data["message_type"] = "private"; | ||||||
|  |                 data["user_id"] = notification.recieverId; | ||||||
|  |             } | ||||||
|  |             await axios.post(onebotAPIUrl, data, config); | ||||||
|  |             return okMsg; | ||||||
|  |         } catch (error) { | ||||||
|  |             this.throwGeneralAxiosError(error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = OneBot; | ||||||
|  | @ -12,7 +12,7 @@ class PromoSMS extends NotificationProvider { | ||||||
|             let config = { |             let config = { | ||||||
|                 headers: { |                 headers: { | ||||||
|                     "Content-Type": "application/json", |                     "Content-Type": "application/json", | ||||||
|                     "Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString('base64'), |                     "Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString("base64"), | ||||||
|                     "Accept": "text/json", |                     "Accept": "text/json", | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  | @ -30,7 +30,7 @@ class PromoSMS extends NotificationProvider { | ||||||
|                 let error = "Something gone wrong. Api returned " + resp.data.response.status + "."; |                 let error = "Something gone wrong. Api returned " + resp.data.response.status + "."; | ||||||
|                 this.throwGeneralAxiosError(error); |                 this.throwGeneralAxiosError(error); | ||||||
|             } |             } | ||||||
|              | 
 | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error); |             this.throwGeneralAxiosError(error); | ||||||
|  |  | ||||||
|  | @ -23,26 +23,26 @@ class Pushbullet extends NotificationProvider { | ||||||
|                     "type": "note", |                     "type": "note", | ||||||
|                     "title": "Uptime Kuma Alert", |                     "title": "Uptime Kuma Alert", | ||||||
|                     "body": "Testing Successful.", |                     "body": "Testing Successful.", | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(pushbulletUrl, testdata, config) |                 await axios.post(pushbulletUrl, testdata, config); | ||||||
|             } else if (heartbeatJSON["status"] == DOWN) { |             } else if (heartbeatJSON["status"] == DOWN) { | ||||||
|                 let downdata = { |                 let downdata = { | ||||||
|                     "type": "note", |                     "type": "note", | ||||||
|                     "title": "UptimeKuma Alert: " + monitorJSON["name"], |                     "title": "UptimeKuma Alert: " + monitorJSON["name"], | ||||||
|                     "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], |                     "body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(pushbulletUrl, downdata, config) |                 await axios.post(pushbulletUrl, downdata, config); | ||||||
|             } else if (heartbeatJSON["status"] == UP) { |             } else if (heartbeatJSON["status"] == UP) { | ||||||
|                 let updata = { |                 let updata = { | ||||||
|                     "type": "note", |                     "type": "note", | ||||||
|                     "title": "UptimeKuma Alert: " + monitorJSON["name"], |                     "title": "UptimeKuma Alert: " + monitorJSON["name"], | ||||||
|                     "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], |                     "body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"], | ||||||
|                 } |                 }; | ||||||
|                 await axios.post(pushbulletUrl, updata, config) |                 await axios.post(pushbulletUrl, updata, config); | ||||||
|             } |             } | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										52
									
								
								server/notification-providers/pushdeer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								server/notification-providers/pushdeer.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | const NotificationProvider = require("./notification-provider"); | ||||||
|  | const axios = require("axios"); | ||||||
|  | const { DOWN, UP } = require("../../src/util"); | ||||||
|  | 
 | ||||||
|  | class PushDeer extends NotificationProvider { | ||||||
|  | 
 | ||||||
|  |     name = "PushDeer"; | ||||||
|  | 
 | ||||||
|  |     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { | ||||||
|  |         let okMsg = "Sent Successfully."; | ||||||
|  |         let pushdeerlink = "https://api2.pushdeer.com/message/push"; | ||||||
|  | 
 | ||||||
|  |         let valid = msg != null && monitorJSON != null && heartbeatJSON != null; | ||||||
|  | 
 | ||||||
|  |         let title; | ||||||
|  |         if (valid && heartbeatJSON.status == UP) { | ||||||
|  |             title = "## Uptime Kuma: " + monitorJSON.name + " up"; | ||||||
|  |         } else if (valid && heartbeatJSON.status == DOWN) { | ||||||
|  |             title = "## Uptime Kuma: " + monitorJSON.name + " down"; | ||||||
|  |         } else { | ||||||
|  |             title = "## Uptime Kuma Message"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let data = { | ||||||
|  |             "pushkey": notification.pushdeerKey, | ||||||
|  |             "text": title, | ||||||
|  |             "desp": msg.replace(/\n/g, "\n\n"), | ||||||
|  |             "type": "markdown", | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             let res = await axios.post(pushdeerlink, data); | ||||||
|  | 
 | ||||||
|  |             if ("error" in res.data) { | ||||||
|  |                 let error = res.data.error; | ||||||
|  |                 this.throwGeneralAxiosError(error); | ||||||
|  |             } | ||||||
|  |             if (res.data.content.result.length === 0) { | ||||||
|  |                 let error = "Invalid PushDeer key"; | ||||||
|  |                 this.throwGeneralAxiosError(error); | ||||||
|  |             } else if (JSON.parse(res.data.content.result[0]).success != "ok") { | ||||||
|  |                 let error = "Unknown error"; | ||||||
|  |                 this.throwGeneralAxiosError(error); | ||||||
|  |             } | ||||||
|  |             return okMsg; | ||||||
|  |         } catch (error) { | ||||||
|  |             this.throwGeneralAxiosError(error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = PushDeer; | ||||||
|  | @ -19,10 +19,10 @@ class Pushy extends NotificationProvider { | ||||||
|                     "badge": 1, |                     "badge": 1, | ||||||
|                     "sound": "ping.aiff" |                     "sound": "ping.aiff" | ||||||
|                 } |                 } | ||||||
|             }) |             }); | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ const NotificationProvider = require("./notification-provider"); | ||||||
| const axios = require("axios"); | const axios = require("axios"); | ||||||
| const Slack = require("./slack"); | const Slack = require("./slack"); | ||||||
| const { setting } = require("../util-server"); | const { setting } = require("../util-server"); | ||||||
| const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util"); | const { getMonitorRelativeURL, DOWN } = require("../../src/util"); | ||||||
| 
 | 
 | ||||||
| class RocketChat extends NotificationProvider { | class RocketChat extends NotificationProvider { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,10 +16,10 @@ class Signal extends NotificationProvider { | ||||||
|             }; |             }; | ||||||
|             let config = {}; |             let config = {}; | ||||||
| 
 | 
 | ||||||
|             await axios.post(notification.signalURL, data, config) |             await axios.post(notification.signalURL, data, config); | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,10 +12,10 @@ class TechulusPush extends NotificationProvider { | ||||||
|             await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, { |             await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, { | ||||||
|                 "title": "Uptime-Kuma", |                 "title": "Uptime-Kuma", | ||||||
|                 "body": msg, |                 "body": msg, | ||||||
|             }) |             }); | ||||||
|             return okMsg; |             return okMsg; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,12 +14,12 @@ class Telegram extends NotificationProvider { | ||||||
|                     chat_id: notification.telegramChatID, |                     chat_id: notification.telegramChatID, | ||||||
|                     text: msg, |                     text: msg, | ||||||
|                 }, |                 }, | ||||||
|             }) |             }); | ||||||
|             return okMsg; |             return okMsg; | ||||||
| 
 | 
 | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             let msg = (error.response.data.description) ? error.response.data.description : "Error without description" |             let msg = (error.response.data.description) ? error.response.data.description : "Error without description"; | ||||||
|             throw new Error(msg) |             throw new Error(msg); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,17 +24,17 @@ class Webhook extends NotificationProvider { | ||||||
| 
 | 
 | ||||||
|                 config = { |                 config = { | ||||||
|                     headers: finalData.getHeaders(), |                     headers: finalData.getHeaders(), | ||||||
|                 } |                 }; | ||||||
| 
 | 
 | ||||||
|             } else { |             } else { | ||||||
|                 finalData = data; |                 finalData = data; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             await axios.post(notification.webhookURL, finalData, config) |             await axios.post(notification.webhookURL, finalData, config); | ||||||
|             return okMsg; |             return okMsg; | ||||||
| 
 | 
 | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.throwGeneralAxiosError(error) |             this.throwGeneralAxiosError(error); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ class WeCom extends NotificationProvider { | ||||||
| 
 | 
 | ||||||
|     composeMessage(heartbeatJSON, msg) { |     composeMessage(heartbeatJSON, msg) { | ||||||
|         let title; |         let title; | ||||||
|         if (msg != null && heartbeatJSON != null && heartbeatJSON['status'] == UP) { |         if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == UP) { | ||||||
|             title = "UptimeKuma Monitor Up"; |             title = "UptimeKuma Monitor Up"; | ||||||
|         } |         } | ||||||
|         if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) { |         if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) { | ||||||
|  |  | ||||||
|  | @ -24,19 +24,22 @@ const Feishu = require("./notification-providers/feishu"); | ||||||
| const AliyunSms = require("./notification-providers/aliyun-sms"); | const AliyunSms = require("./notification-providers/aliyun-sms"); | ||||||
| const DingDing = require("./notification-providers/dingding"); | const DingDing = require("./notification-providers/dingding"); | ||||||
| const Bark = require("./notification-providers/bark"); | const Bark = require("./notification-providers/bark"); | ||||||
|  | const { log } = require("../src/util"); | ||||||
| const SerwerSMS = require("./notification-providers/serwersms"); | const SerwerSMS = require("./notification-providers/serwersms"); | ||||||
| const Stackfield = require("./notification-providers/stackfield"); | const Stackfield = require("./notification-providers/stackfield"); | ||||||
| const WeCom = require("./notification-providers/wecom"); | const WeCom = require("./notification-providers/wecom"); | ||||||
| const GoogleChat = require("./notification-providers/google-chat"); | const GoogleChat = require("./notification-providers/google-chat"); | ||||||
| const Gorush = require("./notification-providers/gorush"); | const Gorush = require("./notification-providers/gorush"); | ||||||
| const Alerta = require("./notification-providers/alerta"); | const Alerta = require("./notification-providers/alerta"); | ||||||
|  | const OneBot = require("./notification-providers/onebot"); | ||||||
|  | const PushDeer = require("./notification-providers/pushdeer"); | ||||||
| 
 | 
 | ||||||
| class Notification { | class Notification { | ||||||
| 
 | 
 | ||||||
|     providerList = {}; |     providerList = {}; | ||||||
| 
 | 
 | ||||||
|     static init() { |     static init() { | ||||||
|         console.log("Prepare Notification Providers"); |         log.info("notification", "Prepare Notification Providers"); | ||||||
| 
 | 
 | ||||||
|         this.providerList = {}; |         this.providerList = {}; | ||||||
| 
 | 
 | ||||||
|  | @ -72,6 +75,8 @@ class Notification { | ||||||
|             new GoogleChat(), |             new GoogleChat(), | ||||||
|             new Gorush(), |             new Gorush(), | ||||||
|             new Alerta(), |             new Alerta(), | ||||||
|  |             new OneBot(), | ||||||
|  |             new PushDeer(), | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         for (let item of list) { |         for (let item of list) { | ||||||
|  | @ -104,27 +109,27 @@ class Notification { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static async save(notification, notificationID, userID) { |     static async save(notification, notificationID, userID) { | ||||||
|         let bean |         let bean; | ||||||
| 
 | 
 | ||||||
|         if (notificationID) { |         if (notificationID) { | ||||||
|             bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ |             bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ | ||||||
|                 notificationID, |                 notificationID, | ||||||
|                 userID, |                 userID, | ||||||
|             ]) |             ]); | ||||||
| 
 | 
 | ||||||
|             if (! bean) { |             if (! bean) { | ||||||
|                 throw new Error("notification not found") |                 throw new Error("notification not found"); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } else { |         } else { | ||||||
|             bean = R.dispense("notification") |             bean = R.dispense("notification"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         bean.name = notification.name; |         bean.name = notification.name; | ||||||
|         bean.user_id = userID; |         bean.user_id = userID; | ||||||
|         bean.config = JSON.stringify(notification); |         bean.config = JSON.stringify(notification); | ||||||
|         bean.is_default = notification.isDefault || false; |         bean.is_default = notification.isDefault || false; | ||||||
|         await R.store(bean) |         await R.store(bean); | ||||||
| 
 | 
 | ||||||
|         if (notification.applyExisting) { |         if (notification.applyExisting) { | ||||||
|             await applyNotificationEveryMonitor(bean.id, userID); |             await applyNotificationEveryMonitor(bean.id, userID); | ||||||
|  | @ -137,13 +142,13 @@ class Notification { | ||||||
|         let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ |         let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ | ||||||
|             notificationID, |             notificationID, | ||||||
|             userID, |             userID, | ||||||
|         ]) |         ]); | ||||||
| 
 | 
 | ||||||
|         if (! bean) { |         if (! bean) { | ||||||
|             throw new Error("notification not found") |             throw new Error("notification not found"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         await R.trash(bean) |         await R.trash(bean); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static checkApprise() { |     static checkApprise() { | ||||||
|  | @ -154,6 +159,13 @@ class Notification { | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Adds a new monitor to the database. | ||||||
|  |  * @param {number} userID The ID of the user that owns this monitor. | ||||||
|  |  * @param {string} name The name of this monitor. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function applyNotificationEveryMonitor(notificationID, userID) { | async function applyNotificationEveryMonitor(notificationID, userID) { | ||||||
|     let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ |     let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ | ||||||
|         userID |         userID | ||||||
|  | @ -163,17 +175,17 @@ async function applyNotificationEveryMonitor(notificationID, userID) { | ||||||
|         let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ |         let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ | ||||||
|             monitors[i].id, |             monitors[i].id, | ||||||
|             notificationID, |             notificationID, | ||||||
|         ]) |         ]); | ||||||
| 
 | 
 | ||||||
|         if (! checkNotification) { |         if (! checkNotification) { | ||||||
|             let relation = R.dispense("monitor_notification"); |             let relation = R.dispense("monitor_notification"); | ||||||
|             relation.monitor_id = monitors[i].id; |             relation.monitor_id = monitors[i].id; | ||||||
|             relation.notification_id = notificationID; |             relation.notification_id = notificationID; | ||||||
|             await R.store(relation) |             await R.store(relation); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
|     Notification, |     Notification, | ||||||
| } | }; | ||||||
|  |  | ||||||
|  | @ -4,20 +4,20 @@ const saltRounds = 10; | ||||||
| 
 | 
 | ||||||
| exports.generate = function (password) { | exports.generate = function (password) { | ||||||
|     return bcrypt.hashSync(password, saltRounds); |     return bcrypt.hashSync(password, saltRounds); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| exports.verify = function (password, hash) { | exports.verify = function (password, hash) { | ||||||
|     if (isSHA1(hash)) { |     if (isSHA1(hash)) { | ||||||
|         return passwordHashOld.verify(password, hash) |         return passwordHashOld.verify(password, hash); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return bcrypt.compareSync(password, hash); |     return bcrypt.compareSync(password, hash); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function isSHA1(hash) { | function isSHA1(hash) { | ||||||
|     return (typeof hash === "string" && hash.startsWith("sha1")) |     return (typeof hash === "string" && hash.startsWith("sha1")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| exports.needRehash = function (hash) { | exports.needRehash = function (hash) { | ||||||
|     return isSHA1(hash); |     return isSHA1(hash); | ||||||
| } | }; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,13 @@ const util = require("./util-server"); | ||||||
| 
 | 
 | ||||||
| module.exports = Ping; | module.exports = Ping; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * @param {string} host - The host to ping | ||||||
|  |  * @param {object} [options] - Options for the ping command | ||||||
|  |  * @param {array|string} [options.args] - Arguments to pass to the ping command | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| function Ping(host, options) { | function Ping(host, options) { | ||||||
|     if (!host) { |     if (!host) { | ||||||
|         throw new Error("You must specify a host to ping!"); |         throw new Error("You must specify a host to ping!"); | ||||||
|  | @ -125,6 +132,11 @@ Ping.prototype.send = function (callback) { | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @param {Function} callback | ||||||
|  |      * | ||||||
|  |      * Generated by Trelent | ||||||
|  |      */ | ||||||
|     function onEnd() { |     function onEnd() { | ||||||
|         let stdout = this.stdout._stdout; |         let stdout = this.stdout._stdout; | ||||||
|         let stderr = this.stderr._stderr; |         let stderr = this.stderr._stderr; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| const PrometheusClient = require("prom-client"); | const PrometheusClient = require("prom-client"); | ||||||
|  | const { log } = require("../src/util"); | ||||||
| 
 | 
 | ||||||
| const commonLabels = [ | const commonLabels = [ | ||||||
|     "monitor_name", |     "monitor_name", | ||||||
|  | @ -48,15 +49,16 @@ class Prometheus { | ||||||
| 
 | 
 | ||||||
|         if (typeof tlsInfo !== "undefined") { |         if (typeof tlsInfo !== "undefined") { | ||||||
|             try { |             try { | ||||||
|                 let is_valid = 0; |                 let isValid = 0; | ||||||
|                 if (tlsInfo.valid == true) { |                 if (tlsInfo.valid == true) { | ||||||
|                     is_valid = 1; |                     isValid = 1; | ||||||
|                 } else { |                 } else { | ||||||
|                     is_valid = 0; |                     isValid = 0; | ||||||
|                 } |                 } | ||||||
|                 monitor_cert_is_valid.set(this.monitorLabelValues, is_valid); |                 monitor_cert_is_valid.set(this.monitorLabelValues, isValid); | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error(e); |                 log.error("prometheus", "Caught error"); | ||||||
|  |                 log.error("prometheus", e); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|  | @ -64,14 +66,16 @@ class Prometheus { | ||||||
|                     monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining); |                     monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining); | ||||||
|                 } |                 } | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error(e); |                 log.error("prometheus", "Caught error"); | ||||||
|  |                 log.error("prometheus", e); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             monitor_status.set(this.monitorLabelValues, heartbeat.status); |             monitor_status.set(this.monitorLabelValues, heartbeat.status); | ||||||
|         } catch (e) { |         } catch (e) { | ||||||
|             console.error(e); |             log.error("prometheus", "Caught error"); | ||||||
|  |             log.error("prometheus", e); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|  | @ -82,7 +86,8 @@ class Prometheus { | ||||||
|                 monitor_response_time.set(this.monitorLabelValues, -1); |                 monitor_response_time.set(this.monitorLabelValues, -1); | ||||||
|             } |             } | ||||||
|         } catch (e) { |         } catch (e) { | ||||||
|             console.error(e); |             log.error("prometheus", "Caught error"); | ||||||
|  |             log.error("prometheus", e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| const { RateLimiter } = require("limiter"); | const { RateLimiter } = require("limiter"); | ||||||
| const { debug } = require("../src/util"); | const { log } = require("../src/util"); | ||||||
| 
 | 
 | ||||||
| class KumaRateLimiter { | class KumaRateLimiter { | ||||||
|     constructor(config) { |     constructor(config) { | ||||||
|  | @ -9,7 +9,7 @@ class KumaRateLimiter { | ||||||
| 
 | 
 | ||||||
|     async pass(callback, num = 1) { |     async pass(callback, num = 1) { | ||||||
|         const remainingRequests = await this.removeTokens(num); |         const remainingRequests = await this.removeTokens(num); | ||||||
|         debug("Rate Limit (remainingRequests):" + remainingRequests); |         log.info("rate-limit", "remaining requests: " + remainingRequests); | ||||||
|         if (remainingRequests < 0) { |         if (remainingRequests < 0) { | ||||||
|             if (callback) { |             if (callback) { | ||||||
|                 callback({ |                 callback({ | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ const server = require("../server"); | ||||||
| const apicache = require("../modules/apicache"); | const apicache = require("../modules/apicache"); | ||||||
| const Monitor = require("../model/monitor"); | const Monitor = require("../model/monitor"); | ||||||
| const dayjs = require("dayjs"); | const dayjs = require("dayjs"); | ||||||
| const { UP, flipStatus, debug } = require("../../src/util"); | const { UP, flipStatus, log } = require("../../src/util"); | ||||||
| const StatusPage = require("../model/status_page"); | const StatusPage = require("../model/status_page"); | ||||||
| let router = express.Router(); | let router = express.Router(); | ||||||
| 
 | 
 | ||||||
|  | @ -62,8 +62,8 @@ router.get("/api/push/:pushToken", async (request, response) => { | ||||||
|             duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); |             duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         debug("PreviousStatus: " + previousStatus); |         log.debug("router", "PreviousStatus: " + previousStatus); | ||||||
|         debug("Current Status: " + status); |         log.debug("router", "Current Status: " + status); | ||||||
| 
 | 
 | ||||||
|         bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); |         bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); | ||||||
|         bean.monitor_id = monitor.id; |         bean.monitor_id = monitor.id; | ||||||
|  | @ -124,7 +124,7 @@ router.get("/api/status-page/:slug", cache("5 minutes"), async (request, respons | ||||||
|         // Public Group List
 |         // Public Group List
 | ||||||
|         const publicGroupList = []; |         const publicGroupList = []; | ||||||
|         const showTags = !!statusPage.show_tags; |         const showTags = !!statusPage.show_tags; | ||||||
|         debug("Show Tags???" + showTags); | 
 | ||||||
|         const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ |         const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ | ||||||
|             statusPage.id |             statusPage.id | ||||||
|         ]); |         ]); | ||||||
|  |  | ||||||
							
								
								
									
										240
									
								
								server/server.js
									
									
									
									
									
								
							
							
						
						
									
										240
									
								
								server/server.js
									
									
									
									
									
								
							|  | @ -11,40 +11,42 @@ if (nodeVersion < requiredVersion) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const args = require("args-parser")(process.argv); | const args = require("args-parser")(process.argv); | ||||||
| const { sleep, debug, getRandomInt, genSecret } = require("../src/util"); | const { sleep, log, getRandomInt, genSecret, debug } = require("../src/util"); | ||||||
| const config = require("./config"); | const config = require("./config"); | ||||||
| 
 | 
 | ||||||
| debug(args); | log.info("server", "Welcome to Uptime Kuma"); | ||||||
|  | log.debug("server", "Arguments"); | ||||||
|  | log.debug("server", args); | ||||||
| 
 | 
 | ||||||
| if (! process.env.NODE_ENV) { | if (! process.env.NODE_ENV) { | ||||||
|     process.env.NODE_ENV = "production"; |     process.env.NODE_ENV = "production"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| console.log("Node Env: " + process.env.NODE_ENV); | log.info("server", "Node Env: " + process.env.NODE_ENV); | ||||||
| 
 | 
 | ||||||
| console.log("Importing Node libraries"); | log.info("server", "Importing Node libraries"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const http = require("http"); | const http = require("http"); | ||||||
| const https = require("https"); | const https = require("https"); | ||||||
| 
 | 
 | ||||||
| console.log("Importing 3rd-party libraries"); | log.info("server", "Importing 3rd-party libraries"); | ||||||
| debug("Importing express"); | log.debug("server", "Importing express"); | ||||||
| const express = require("express"); | const express = require("express"); | ||||||
| debug("Importing socket.io"); | log.debug("server", "Importing socket.io"); | ||||||
| const { Server } = require("socket.io"); | const { Server } = require("socket.io"); | ||||||
| debug("Importing redbean-node"); | log.debug("server", "Importing redbean-node"); | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| debug("Importing jsonwebtoken"); | log.debug("server", "Importing jsonwebtoken"); | ||||||
| const jwt = require("jsonwebtoken"); | const jwt = require("jsonwebtoken"); | ||||||
| debug("Importing http-graceful-shutdown"); | log.debug("server", "Importing http-graceful-shutdown"); | ||||||
| const gracefulShutdown = require("http-graceful-shutdown"); | const gracefulShutdown = require("http-graceful-shutdown"); | ||||||
| debug("Importing prometheus-api-metrics"); | log.debug("server", "Importing prometheus-api-metrics"); | ||||||
| const prometheusAPIMetrics = require("prometheus-api-metrics"); | const prometheusAPIMetrics = require("prometheus-api-metrics"); | ||||||
| debug("Importing compare-versions"); | log.debug("server", "Importing compare-versions"); | ||||||
| const compareVersions = require("compare-versions"); | const compareVersions = require("compare-versions"); | ||||||
| const { passwordStrength } = require("check-password-strength"); | const { passwordStrength } = require("check-password-strength"); | ||||||
| 
 | 
 | ||||||
| debug("Importing 2FA Modules"); | log.debug("server", "Importing 2FA Modules"); | ||||||
| const notp = require("notp"); | const notp = require("notp"); | ||||||
| const base32 = require("thirty-two"); | const base32 = require("thirty-two"); | ||||||
| 
 | 
 | ||||||
|  | @ -69,23 +71,23 @@ class UptimeKumaServer { | ||||||
| 
 | 
 | ||||||
| const server = module.exports = new UptimeKumaServer(); | const server = module.exports = new UptimeKumaServer(); | ||||||
| 
 | 
 | ||||||
| console.log("Importing this project modules"); | log.info("server", "Importing this project modules"); | ||||||
| debug("Importing Monitor"); | log.debug("server", "Importing Monitor"); | ||||||
| const Monitor = require("./model/monitor"); | const Monitor = require("./model/monitor"); | ||||||
| debug("Importing Settings"); | log.debug("server", "Importing Settings"); | ||||||
| const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog, doubleCheckPassword } = require("./util-server"); | const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog, doubleCheckPassword } = require("./util-server"); | ||||||
| 
 | 
 | ||||||
| debug("Importing Notification"); | log.debug("server", "Importing Notification"); | ||||||
| const { Notification } = require("./notification"); | const { Notification } = require("./notification"); | ||||||
| Notification.init(); | Notification.init(); | ||||||
| 
 | 
 | ||||||
| debug("Importing Proxy"); | log.debug("server", "Importing Proxy"); | ||||||
| const { Proxy } = require("./proxy"); | const { Proxy } = require("./proxy"); | ||||||
| 
 | 
 | ||||||
| debug("Importing Database"); | log.debug("server", "Importing Database"); | ||||||
| const Database = require("./database"); | const Database = require("./database"); | ||||||
| 
 | 
 | ||||||
| debug("Importing Background Jobs"); | log.debug("server", "Importing Background Jobs"); | ||||||
| const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs"); | const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs"); | ||||||
| const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter"); | const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter"); | ||||||
| 
 | 
 | ||||||
|  | @ -94,27 +96,26 @@ const { login } = require("./auth"); | ||||||
| const passwordHash = require("./password-hash"); | const passwordHash = require("./password-hash"); | ||||||
| 
 | 
 | ||||||
| const checkVersion = require("./check-version"); | const checkVersion = require("./check-version"); | ||||||
| console.info("Version: " + checkVersion.version); | log.info("server", "Version: " + checkVersion.version); | ||||||
| 
 | 
 | ||||||
| // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
 | // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
 | ||||||
| // Dual-stack support for (::)
 | // Dual-stack support for (::)
 | ||||||
| let hostname = process.env.UPTIME_KUMA_HOST || args.host; |  | ||||||
| 
 |  | ||||||
| // Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
 | // Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
 | ||||||
| if (!hostname && !FBSD) { | let hostEnv = FBSD ? null : process.env.HOST; | ||||||
|     hostname = process.env.HOST; | let hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv; | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| if (hostname) { | if (hostname) { | ||||||
|     console.log("Custom hostname: " + hostname); |     log.info("server", "Custom hostname: " + hostname); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001); | const port = [args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001] | ||||||
|  |     .map(portValue => parseInt(portValue)) | ||||||
|  |     .find(portValue => !isNaN(portValue)); | ||||||
| 
 | 
 | ||||||
| // SSL
 | // SSL
 | ||||||
| const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined; | const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined; | ||||||
| const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined; | const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined; | ||||||
| const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false; | const disableFrameSameOrigin = args["disable-frame-sameorigin"] || !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || false; | ||||||
| const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined; | const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined; | ||||||
| 
 | 
 | ||||||
| // 2FA / notp verification defaults
 | // 2FA / notp verification defaults
 | ||||||
|  | @ -130,22 +131,22 @@ const twofa_verification_opts = { | ||||||
| const testMode = !!args["test"] || false; | const testMode = !!args["test"] || false; | ||||||
| 
 | 
 | ||||||
| if (config.demoMode) { | if (config.demoMode) { | ||||||
|     console.log("==== Demo Mode ===="); |     log.info("server", "==== Demo Mode ===="); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| console.log("Creating express and socket.io instance"); | log.info("server", "Creating express and socket.io instance"); | ||||||
| const app = express(); | const app = express(); | ||||||
| 
 | 
 | ||||||
| let httpServer; | let httpServer; | ||||||
| 
 | 
 | ||||||
| if (sslKey && sslCert) { | if (sslKey && sslCert) { | ||||||
|     console.log("Server Type: HTTPS"); |     log.info("server", "Server Type: HTTPS"); | ||||||
|     httpServer = https.createServer({ |     httpServer = https.createServer({ | ||||||
|         key: fs.readFileSync(sslKey), |         key: fs.readFileSync(sslKey), | ||||||
|         cert: fs.readFileSync(sslCert) |         cert: fs.readFileSync(sslCert) | ||||||
|     }, app); |     }, app); | ||||||
| } else { | } else { | ||||||
|     console.log("Server Type: HTTP"); |     log.info("server", "Server Type: HTTP"); | ||||||
|     httpServer = http.createServer(app); |     httpServer = http.createServer(app); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -201,7 +202,7 @@ try { | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     // "dist/index.html" is not necessary for development
 |     // "dist/index.html" is not necessary for development
 | ||||||
|     if (process.env.NODE_ENV !== "development") { |     if (process.env.NODE_ENV !== "development") { | ||||||
|         console.error("Error: Cannot find 'dist/index.html', did you install correctly?"); |         log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?"); | ||||||
|         process.exit(1); |         process.exit(1); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -213,7 +214,7 @@ try { | ||||||
|     exports.entryPage = await setting("entryPage"); |     exports.entryPage = await setting("entryPage"); | ||||||
|     await StatusPage.loadDomainMappingList(); |     await StatusPage.loadDomainMappingList(); | ||||||
| 
 | 
 | ||||||
|     console.log("Adding route"); |     log.info("server", "Adding route"); | ||||||
| 
 | 
 | ||||||
|     // ***************************
 |     // ***************************
 | ||||||
|     // Normal Router here
 |     // Normal Router here
 | ||||||
|  | @ -271,7 +272,7 @@ try { | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     console.log("Adding socket handler"); |     log.info("server", "Adding socket handler"); | ||||||
|     io.on("connection", async (socket) => { |     io.on("connection", async (socket) => { | ||||||
| 
 | 
 | ||||||
|         sendInfo(socket); |         sendInfo(socket); | ||||||
|  | @ -279,7 +280,7 @@ try { | ||||||
|         totalClient++; |         totalClient++; | ||||||
| 
 | 
 | ||||||
|         if (needSetup) { |         if (needSetup) { | ||||||
|             console.log("Redirect to setup page"); |             log.info("server", "Redirect to setup page"); | ||||||
|             socket.emit("setup"); |             socket.emit("setup"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -292,33 +293,40 @@ try { | ||||||
|         // ***************************
 |         // ***************************
 | ||||||
| 
 | 
 | ||||||
|         socket.on("loginByToken", async (token, callback) => { |         socket.on("loginByToken", async (token, callback) => { | ||||||
|  |             log.info("auth", `Login by token. IP=${getClientIp(socket)}`); | ||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|                 let decoded = jwt.verify(token, jwtSecret); |                 let decoded = jwt.verify(token, jwtSecret); | ||||||
| 
 | 
 | ||||||
|                 console.log("Username from JWT: " + decoded.username); |                 log.info("auth", "Username from JWT: " + decoded.username); | ||||||
| 
 | 
 | ||||||
|                 let user = await R.findOne("user", " username = ? AND active = 1 ", [ |                 let user = await R.findOne("user", " username = ? AND active = 1 ", [ | ||||||
|                     decoded.username, |                     decoded.username, | ||||||
|                 ]); |                 ]); | ||||||
| 
 | 
 | ||||||
|                 if (user) { |                 if (user) { | ||||||
|                     debug("afterLogin"); |                     log.debug("auth", "afterLogin"); | ||||||
| 
 |  | ||||||
|                     afterLogin(socket, user); |                     afterLogin(socket, user); | ||||||
|  |                     log.debug("auth", "afterLogin ok"); | ||||||
| 
 | 
 | ||||||
|                     debug("afterLogin ok"); |                     log.info("auth", `Successfully logged in user ${decoded.username}. IP=${getClientIp(socket)}`); | ||||||
| 
 | 
 | ||||||
|                     callback({ |                     callback({ | ||||||
|                         ok: true, |                         ok: true, | ||||||
|                     }); |                     }); | ||||||
|                 } else { |                 } else { | ||||||
|  | 
 | ||||||
|  |                     log.info("auth", `Inactive or deleted user ${decoded.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                     callback({ |                     callback({ | ||||||
|                         ok: false, |                         ok: false, | ||||||
|                         msg: "The user is inactive or deleted.", |                         msg: "The user is inactive or deleted.", | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|             } catch (error) { |             } catch (error) { | ||||||
|  | 
 | ||||||
|  |                 log.error("auth", `Invalid token. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: "Invalid token.", |                     msg: "Invalid token.", | ||||||
|  | @ -328,7 +336,7 @@ try { | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         socket.on("login", async (data, callback) => { |         socket.on("login", async (data, callback) => { | ||||||
|             console.log("Login"); |             log.info("auth", `Login by username + password. IP=${getClientIp(socket)}`); | ||||||
| 
 | 
 | ||||||
|             // Checking
 |             // Checking
 | ||||||
|             if (typeof callback !== "function") { |             if (typeof callback !== "function") { | ||||||
|  | @ -341,6 +349,7 @@ try { | ||||||
| 
 | 
 | ||||||
|             // Login Rate Limit
 |             // Login Rate Limit
 | ||||||
|             if (! await loginRateLimiter.pass(callback)) { |             if (! await loginRateLimiter.pass(callback)) { | ||||||
|  |                 log.info("auth", `Too many failed requests for user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -349,6 +358,9 @@ try { | ||||||
|             if (user) { |             if (user) { | ||||||
|                 if (user.twofa_status == 0) { |                 if (user.twofa_status == 0) { | ||||||
|                     afterLogin(socket, user); |                     afterLogin(socket, user); | ||||||
|  | 
 | ||||||
|  |                     log.info("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                     callback({ |                     callback({ | ||||||
|                         ok: true, |                         ok: true, | ||||||
|                         token: jwt.sign({ |                         token: jwt.sign({ | ||||||
|  | @ -358,6 +370,9 @@ try { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (user.twofa_status == 1 && !data.token) { |                 if (user.twofa_status == 1 && !data.token) { | ||||||
|  | 
 | ||||||
|  |                     log.info("auth", `2FA token required for user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                     callback({ |                     callback({ | ||||||
|                         tokenRequired: true, |                         tokenRequired: true, | ||||||
|                     }); |                     }); | ||||||
|  | @ -374,6 +389,8 @@ try { | ||||||
|                             socket.userID, |                             socket.userID, | ||||||
|                         ]); |                         ]); | ||||||
| 
 | 
 | ||||||
|  |                         log.info("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                         callback({ |                         callback({ | ||||||
|                             ok: true, |                             ok: true, | ||||||
|                             token: jwt.sign({ |                             token: jwt.sign({ | ||||||
|  | @ -381,6 +398,9 @@ try { | ||||||
|                             }, jwtSecret), |                             }, jwtSecret), | ||||||
|                         }); |                         }); | ||||||
|                     } else { |                     } else { | ||||||
|  | 
 | ||||||
|  |                         log.warn("auth", `Invalid token provided for user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                         callback({ |                         callback({ | ||||||
|                             ok: false, |                             ok: false, | ||||||
|                             msg: "Invalid Token!", |                             msg: "Invalid Token!", | ||||||
|  | @ -388,6 +408,9 @@ try { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|  | 
 | ||||||
|  |                 log.warn("auth", `Incorrect username or password for user ${data.username}. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: "Incorrect username or password.", |                     msg: "Incorrect username or password.", | ||||||
|  | @ -470,11 +493,16 @@ try { | ||||||
|                     socket.userID, |                     socket.userID, | ||||||
|                 ]); |                 ]); | ||||||
| 
 | 
 | ||||||
|  |                 log.info("auth", `Saved 2FA token. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: true, |                     ok: true, | ||||||
|                     msg: "2FA Enabled.", |                     msg: "2FA Enabled.", | ||||||
|                 }); |                 }); | ||||||
|             } catch (error) { |             } catch (error) { | ||||||
|  | 
 | ||||||
|  |                 log.error("auth", `Error changing 2FA token. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: error.message, |                     msg: error.message, | ||||||
|  | @ -492,11 +520,16 @@ try { | ||||||
|                 await doubleCheckPassword(socket, currentPassword); |                 await doubleCheckPassword(socket, currentPassword); | ||||||
|                 await TwoFA.disable2FA(socket.userID); |                 await TwoFA.disable2FA(socket.userID); | ||||||
| 
 | 
 | ||||||
|  |                 log.info("auth", `Disabled 2FA token. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: true, |                     ok: true, | ||||||
|                     msg: "2FA Disabled.", |                     msg: "2FA Disabled.", | ||||||
|                 }); |                 }); | ||||||
|             } catch (error) { |             } catch (error) { | ||||||
|  | 
 | ||||||
|  |                 log.error("auth", `Error disabling 2FA token. IP=${getClientIp(socket)}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: error.message, |                     msg: error.message, | ||||||
|  | @ -623,6 +656,8 @@ try { | ||||||
|                 await server.sendMonitorList(socket); |                 await server.sendMonitorList(socket); | ||||||
|                 await startMonitor(socket.userID, bean.id); |                 await startMonitor(socket.userID, bean.id); | ||||||
| 
 | 
 | ||||||
|  |                 log.info("monitor", `Added Monitor: ${monitor.id} User ID: ${socket.userID}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: true, |                     ok: true, | ||||||
|                     msg: "Added Successfully.", |                     msg: "Added Successfully.", | ||||||
|  | @ -630,6 +665,9 @@ try { | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|  | 
 | ||||||
|  |                 log.error("monitor", `Error adding Monitor: ${monitor.id} User ID: ${socket.userID}`); | ||||||
|  | 
 | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: e.message, |                     msg: e.message, | ||||||
|  | @ -692,7 +730,7 @@ try { | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error(e); |                 log.error("monitor", e); | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: e.message, |                     msg: e.message, | ||||||
|  | @ -708,7 +746,7 @@ try { | ||||||
|                     ok: true, |                     ok: true, | ||||||
|                 }); |                 }); | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error(e); |                 log.error("monitor", e); | ||||||
|                 callback({ |                 callback({ | ||||||
|                     ok: false, |                     ok: false, | ||||||
|                     msg: e.message, |                     msg: e.message, | ||||||
|  | @ -720,7 +758,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Get Monitor: ${monitorID} User ID: ${socket.userID}`); |                 log.info("monitor", `Get Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [ |                 let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [ | ||||||
|                     monitorID, |                     monitorID, | ||||||
|  | @ -744,7 +782,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); |                 log.info("monitor", `Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 if (period == null) { |                 if (period == null) { | ||||||
|                     throw new Error("Invalid period."); |                     throw new Error("Invalid period."); | ||||||
|  | @ -815,7 +853,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`); |                 log.info("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 if (monitorID in server.monitorList) { |                 if (monitorID in server.monitorList) { | ||||||
|                     server.monitorList[monitorID].stop(); |                     server.monitorList[monitorID].stop(); | ||||||
|  | @ -1147,7 +1185,7 @@ try { | ||||||
| 
 | 
 | ||||||
|                 let backupData = JSON.parse(uploadedJSON); |                 let backupData = JSON.parse(uploadedJSON); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`); |                 log.info("manage", `Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`); | ||||||
| 
 | 
 | ||||||
|                 let notificationListData = backupData.notificationList; |                 let notificationListData = backupData.notificationList; | ||||||
|                 let proxyListData = backupData.proxyList; |                 let proxyListData = backupData.proxyList; | ||||||
|  | @ -1190,7 +1228,7 @@ try { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // Only starts importing if the backup file contains at least one proxy
 |                 // Only starts importing if the backup file contains at least one proxy
 | ||||||
|                 if (proxyListData.length >= 1) { |                 if (proxyListData && proxyListData.length >= 1) { | ||||||
|                     const proxies = await R.findAll("proxy"); |                     const proxies = await R.findAll("proxy"); | ||||||
| 
 | 
 | ||||||
|                     // Loop over proxy list and save proxies
 |                     // Loop over proxy list and save proxies
 | ||||||
|  | @ -1342,7 +1380,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`); |                 log.info("manage", `Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", [ |                 await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", [ | ||||||
|                     "", |                     "", | ||||||
|  | @ -1368,7 +1406,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); |                 log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [ |                 await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [ | ||||||
|                     monitorID |                     monitorID | ||||||
|  | @ -1392,7 +1430,7 @@ try { | ||||||
|             try { |             try { | ||||||
|                 checkLogin(socket); |                 checkLogin(socket); | ||||||
| 
 | 
 | ||||||
|                 console.log(`Clear Statistics User ID: ${socket.userID}`); |                 log.info("manage", `Clear Statistics User ID: ${socket.userID}`); | ||||||
| 
 | 
 | ||||||
|                 await R.exec("DELETE FROM heartbeat"); |                 await R.exec("DELETE FROM heartbeat"); | ||||||
| 
 | 
 | ||||||
|  | @ -1414,24 +1452,24 @@ try { | ||||||
|         databaseSocketHandler(socket); |         databaseSocketHandler(socket); | ||||||
|         proxySocketHandler(socket); |         proxySocketHandler(socket); | ||||||
| 
 | 
 | ||||||
|         debug("added all socket handlers"); |         log.debug("server", "added all socket handlers"); | ||||||
| 
 | 
 | ||||||
|         // ***************************
 |         // ***************************
 | ||||||
|         // Better do anything after added all socket handlers here
 |         // Better do anything after added all socket handlers here
 | ||||||
|         // ***************************
 |         // ***************************
 | ||||||
| 
 | 
 | ||||||
|         debug("check auto login"); |         log.debug("auth", "check auto login"); | ||||||
|         if (await setting("disableAuth")) { |         if (await setting("disableAuth")) { | ||||||
|             console.log("Disabled Auth: auto login to admin"); |             log.info("auth", "Disabled Auth: auto login to admin"); | ||||||
|             afterLogin(socket, await R.findOne("user")); |             afterLogin(socket, await R.findOne("user")); | ||||||
|             socket.emit("autoLogin"); |             socket.emit("autoLogin"); | ||||||
|         } else { |         } else { | ||||||
|             debug("need auth"); |             log.debug("auth", "need auth"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     console.log("Init the server"); |     log.info("server", "Init the server"); | ||||||
| 
 | 
 | ||||||
|     httpServer.once("error", async (err) => { |     httpServer.once("error", async (err) => { | ||||||
|         console.error("Cannot listen: " + err.message); |         console.error("Cannot listen: " + err.message); | ||||||
|  | @ -1440,9 +1478,9 @@ try { | ||||||
| 
 | 
 | ||||||
|     httpServer.listen(port, hostname, () => { |     httpServer.listen(port, hostname, () => { | ||||||
|         if (hostname) { |         if (hostname) { | ||||||
|             console.log(`Listening on ${hostname}:${port}`); |             log.info("server", `Listening on ${hostname}:${port}`); | ||||||
|         } else { |         } else { | ||||||
|             console.log(`Listening on ${port}`); |             log.info("server", `Listening on ${port}`); | ||||||
|         } |         } | ||||||
|         startMonitors(); |         startMonitors(); | ||||||
|         checkVersion.startInterval(); |         checkVersion.startInterval(); | ||||||
|  | @ -1459,6 +1497,13 @@ try { | ||||||
| 
 | 
 | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Adds or removes notifications from a monitor. | ||||||
|  |  * @param {number} monitorID The ID of the monitor to add/remove notifications from. | ||||||
|  |  * @param {Array.<number>} notificationIDList An array of IDs for the notifications to add/remove. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function updateMonitorNotification(monitorID, notificationIDList) { | async function updateMonitorNotification(monitorID, notificationIDList) { | ||||||
|     await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ |     await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ | ||||||
|         monitorID, |         monitorID, | ||||||
|  | @ -1474,6 +1519,13 @@ async function updateMonitorNotification(monitorID, notificationIDList) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * This function checks if the user owns a monitor with the given ID. | ||||||
|  |  * @param {number} monitorID - The ID of the monitor to check ownership for. | ||||||
|  |  * @param {number} userID - The ID of the user who is trying to access this data. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function checkOwner(userID, monitorID) { | async function checkOwner(userID, monitorID) { | ||||||
|     let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [ |     let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [ | ||||||
|         monitorID, |         monitorID, | ||||||
|  | @ -1485,6 +1537,10 @@ async function checkOwner(userID, monitorID) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * This function is used to send the heartbeat list of a monitor. | ||||||
|  |  * @param {Socket} socket - The socket object that will be used to send the data. | ||||||
|  |  */ | ||||||
| async function afterLogin(socket, user) { | async function afterLogin(socket, user) { | ||||||
|     socket.userID = user.id; |     socket.userID = user.id; | ||||||
|     socket.join(user.id); |     socket.join(user.id); | ||||||
|  | @ -1510,6 +1566,13 @@ async function afterLogin(socket, user) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Get a list of monitors for the given user. | ||||||
|  |  * @param {string} userID - The ID of the user to get monitors for. | ||||||
|  |  * @returns {Promise<Object>} A promise that resolves to an object with monitor IDs as keys and monitor objects as values. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function getMonitorJSONList(userID) { | async function getMonitorJSONList(userID) { | ||||||
|     let result = {}; |     let result = {}; | ||||||
| 
 | 
 | ||||||
|  | @ -1524,15 +1587,20 @@ async function getMonitorJSONList(userID) { | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Connect to the database and patch it if necessary. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function initDatabase(testMode = false) { | async function initDatabase(testMode = false) { | ||||||
|     if (! fs.existsSync(Database.path)) { |     if (! fs.existsSync(Database.path)) { | ||||||
|         console.log("Copying Database"); |         log.info("server", "Copying Database"); | ||||||
|         fs.copyFileSync(Database.templatePath, Database.path); |         fs.copyFileSync(Database.templatePath, Database.path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     console.log("Connecting to the Database"); |     log.info("server", "Connecting to the Database"); | ||||||
|     await Database.connect(testMode); |     await Database.connect(testMode); | ||||||
|     console.log("Connected"); |     log.info("server", "Connected"); | ||||||
| 
 | 
 | ||||||
|     // Patch the database
 |     // Patch the database
 | ||||||
|     await Database.patch(); |     await Database.patch(); | ||||||
|  | @ -1542,26 +1610,33 @@ async function initDatabase(testMode = false) { | ||||||
|     ]); |     ]); | ||||||
| 
 | 
 | ||||||
|     if (! jwtSecretBean) { |     if (! jwtSecretBean) { | ||||||
|         console.log("JWT secret is not found, generate one."); |         log.info("server", "JWT secret is not found, generate one."); | ||||||
|         jwtSecretBean = await initJWTSecret(); |         jwtSecretBean = await initJWTSecret(); | ||||||
|         console.log("Stored JWT secret into database"); |         log.info("server", "Stored JWT secret into database"); | ||||||
|     } else { |     } else { | ||||||
|         console.log("Load JWT secret from database."); |         log.info("server", "Load JWT secret from database."); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // If there is no record in user table, it is a new Uptime Kuma instance, need to setup
 |     // If there is no record in user table, it is a new Uptime Kuma instance, need to setup
 | ||||||
|     if ((await R.count("user")) === 0) { |     if ((await R.count("user")) === 0) { | ||||||
|         console.log("No user, need setup"); |         log.info("server", "No user, need setup"); | ||||||
|         needSetup = true; |         needSetup = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     jwtSecret = jwtSecretBean.value; |     jwtSecret = jwtSecretBean.value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Resume a monitor. | ||||||
|  |  * @param {string} userID - The ID of the user who owns the monitor. | ||||||
|  |  * @param {string} monitorID - The ID of the monitor to resume. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function startMonitor(userID, monitorID) { | async function startMonitor(userID, monitorID) { | ||||||
|     await checkOwner(userID, monitorID); |     await checkOwner(userID, monitorID); | ||||||
| 
 | 
 | ||||||
|     console.log(`Resume Monitor: ${monitorID} User ID: ${userID}`); |     log.info("manage", `Resume Monitor: ${monitorID} User ID: ${userID}`); | ||||||
| 
 | 
 | ||||||
|     await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [ |     await R.exec("UPDATE monitor SET active = 1 WHERE id = ? AND user_id = ? ", [ | ||||||
|         monitorID, |         monitorID, | ||||||
|  | @ -1584,10 +1659,17 @@ async function restartMonitor(userID, monitorID) { | ||||||
|     return await startMonitor(userID, monitorID); |     return await startMonitor(userID, monitorID); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Pause a monitor. | ||||||
|  |  * @param {string} userID - The ID of the user who owns the monitor. | ||||||
|  |  * @param {string} monitorID - The ID of the monitor to pause. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function pauseMonitor(userID, monitorID) { | async function pauseMonitor(userID, monitorID) { | ||||||
|     await checkOwner(userID, monitorID); |     await checkOwner(userID, monitorID); | ||||||
| 
 | 
 | ||||||
|     console.log(`Pause Monitor: ${monitorID} User ID: ${userID}`); |     log.info("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`); | ||||||
| 
 | 
 | ||||||
|     await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [ |     await R.exec("UPDATE monitor SET active = 0 WHERE id = ? AND user_id = ? ", [ | ||||||
|         monitorID, |         monitorID, | ||||||
|  | @ -1616,11 +1698,17 @@ async function startMonitors() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Stops all monitors and closes the database connection. | ||||||
|  |  * @param {string} signal The signal that triggered this function to be called. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| async function shutdownFunction(signal) { | async function shutdownFunction(signal) { | ||||||
|     console.log("Shutdown requested"); |     log.info("server", "Shutdown requested"); | ||||||
|     console.log("Called signal: " + signal); |     log.info("server", "Called signal: " + signal); | ||||||
| 
 | 
 | ||||||
|     console.log("Stopping all monitors"); |     log.info("server", "Stopping all monitors"); | ||||||
|     for (let id in server.monitorList) { |     for (let id in server.monitorList) { | ||||||
|         let monitor = server.monitorList[id]; |         let monitor = server.monitorList[id]; | ||||||
|         monitor.stop(); |         monitor.stop(); | ||||||
|  | @ -1632,8 +1720,12 @@ async function shutdownFunction(signal) { | ||||||
|     await cloudflaredStop(); |     await cloudflaredStop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function getClientIp(socket) { | ||||||
|  |     return socket.client.conn.remoteAddress.replace(/^.*:/, ""); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function finalFunction() { | function finalFunction() { | ||||||
|     console.log("Graceful shutdown successful!"); |     log.info("server", "Graceful shutdown successful!"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| gracefulShutdown(httpServer, { | gracefulShutdown(httpServer, { | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| const { checkLogin, setSettings, setSetting } = require("../util-server"); | const { checkLogin, setSetting } = require("../util-server"); | ||||||
| const dayjs = require("dayjs"); | const dayjs = require("dayjs"); | ||||||
| const { debug } = require("../../src/util"); | const { log } = require("../../src/util"); | ||||||
| const ImageDataURI = require("../image-data-uri"); | const ImageDataURI = require("../image-data-uri"); | ||||||
| const Database = require("../database"); | const Database = require("../database"); | ||||||
| const apicache = require("../modules/apicache"); | const apicache = require("../modules/apicache"); | ||||||
|  | @ -202,8 +202,8 @@ module.exports.statusPageSocketHandler = (socket) => { | ||||||
|                 group.id = groupBean.id; |                 group.id = groupBean.id; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Delete groups that not in the list
 |             // Delete groups that are not in the list
 | ||||||
|             debug("Delete groups that not in the list"); |             log.debug("socket", "Delete groups that are not in the list"); | ||||||
|             const slots = groupIDList.map(() => "?").join(","); |             const slots = groupIDList.map(() => "?").join(","); | ||||||
| 
 | 
 | ||||||
|             const data = [ |             const data = [ | ||||||
|  | @ -226,7 +226,7 @@ module.exports.statusPageSocketHandler = (socket) => { | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             console.error(error); |             log.error("socket", error); | ||||||
| 
 | 
 | ||||||
|             callback({ |             callback({ | ||||||
|                 ok: false, |                 ok: false, | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| const tcpp = require("tcp-ping"); | const tcpp = require("tcp-ping"); | ||||||
| const Ping = require("./ping-lite"); | const Ping = require("./ping-lite"); | ||||||
| const { R } = require("redbean-node"); | const { R } = require("redbean-node"); | ||||||
| const { debug, genSecret } = require("../src/util"); | const { log, genSecret } = require("../src/util"); | ||||||
| const passwordHash = require("./password-hash"); | const passwordHash = require("./password-hash"); | ||||||
| const { Resolver } = require("dns"); | const { Resolver } = require("dns"); | ||||||
| const child_process = require("child_process"); | const childProcess = require("child_process"); | ||||||
| const iconv = require("iconv-lite"); | const iconv = require("iconv-lite"); | ||||||
| const chardet = require("chardet"); | const chardet = require("chardet"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
|  | @ -119,7 +119,7 @@ exports.setting = async function (key) { | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         const v = JSON.parse(value); |         const v = JSON.parse(value); | ||||||
|         debug(`Get Setting: ${key}: ${v}`); |         log.debug("util", `Get Setting: ${key}: ${v}`); | ||||||
|         return v; |         return v; | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|         return value; |         return value; | ||||||
|  | @ -206,7 +206,7 @@ const parseCertificateInfo = function (info) { | ||||||
|     const existingList = {}; |     const existingList = {}; | ||||||
| 
 | 
 | ||||||
|     while (link) { |     while (link) { | ||||||
|         debug(`[${i}] ${link.fingerprint}`); |         log.debug("util", `[${i}] ${link.fingerprint}`); | ||||||
| 
 | 
 | ||||||
|         if (!link.valid_from || !link.valid_to) { |         if (!link.valid_from || !link.valid_to) { | ||||||
|             break; |             break; | ||||||
|  | @ -221,7 +221,7 @@ const parseCertificateInfo = function (info) { | ||||||
|         if (link.issuerCertificate == null) { |         if (link.issuerCertificate == null) { | ||||||
|             break; |             break; | ||||||
|         } else if (link.issuerCertificate.fingerprint in existingList) { |         } else if (link.issuerCertificate.fingerprint in existingList) { | ||||||
|             debug(`[Last] ${link.issuerCertificate.fingerprint}`); |             log.debug("util", `[Last] ${link.issuerCertificate.fingerprint}`); | ||||||
|             link.issuerCertificate = null; |             link.issuerCertificate = null; | ||||||
|             break; |             break; | ||||||
|         } else { |         } else { | ||||||
|  | @ -242,7 +242,7 @@ exports.checkCertificate = function (res) { | ||||||
|     const info = res.request.res.socket.getPeerCertificate(true); |     const info = res.request.res.socket.getPeerCertificate(true); | ||||||
|     const valid = res.request.res.socket.authorized || false; |     const valid = res.request.res.socket.authorized || false; | ||||||
| 
 | 
 | ||||||
|     debug("Parsing Certificate Info"); |     log.debug("util", "Parsing Certificate Info"); | ||||||
|     const parsedInfo = parseCertificateInfo(info); |     const parsedInfo = parseCertificateInfo(info); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|  | @ -345,7 +345,7 @@ exports.doubleCheckPassword = async (socket, currentPassword) => { | ||||||
| exports.startUnitTest = async () => { | exports.startUnitTest = async () => { | ||||||
|     console.log("Starting unit test..."); |     console.log("Starting unit test..."); | ||||||
|     const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; |     const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; | ||||||
|     const child = child_process.spawn(npm, ["run", "jest"]); |     const child = childProcess.spawn(npm, ["run", "jest"]); | ||||||
| 
 | 
 | ||||||
|     child.stdout.on("data", (data) => { |     child.stdout.on("data", (data) => { | ||||||
|         console.log(data.toString()); |         console.log(data.toString()); | ||||||
|  | @ -367,7 +367,6 @@ exports.startUnitTest = async () => { | ||||||
|  */ |  */ | ||||||
| exports.convertToUTF8 = (body) => { | exports.convertToUTF8 = (body) => { | ||||||
|     const guessEncoding = chardet.detect(body); |     const guessEncoding = chardet.detect(body); | ||||||
|     //debug("Guess Encoding: " + guessEncoding);
 |  | ||||||
|     const str = iconv.decode(body, guessEncoding); |     const str = iconv.decode(body, guessEncoding); | ||||||
|     return str.toString(); |     return str.toString(); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| <template> | <template> | ||||||
|   <router-view /> |     <router-view /> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| import { setPageLocale } from "./util-frontend"; | import { setPageLocale } from "./util-frontend"; | ||||||
| export default { | export default { | ||||||
|   created() { |     created() { | ||||||
|     setPageLocale(); |         setPageLocale(); | ||||||
|   }, |     }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| import { Modal } from "bootstrap" | import { Modal } from "bootstrap"; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|     props: { |     props: { | ||||||
|  | @ -46,15 +46,15 @@ export default { | ||||||
|         modal: null, |         modal: null, | ||||||
|     }), |     }), | ||||||
|     mounted() { |     mounted() { | ||||||
|         this.modal = new Modal(this.$refs.modal) |         this.modal = new Modal(this.$refs.modal); | ||||||
|     }, |     }, | ||||||
|     methods: { |     methods: { | ||||||
|         show() { |         show() { | ||||||
|             this.modal.show() |             this.modal.show(); | ||||||
|         }, |         }, | ||||||
|         yes() { |         yes() { | ||||||
|             this.$emit("yes"); |             this.$emit("yes"); | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 
 | 
 | ||||||
| import { sleep } from "../util.ts" | import { sleep } from "../util.ts"; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
| 
 | 
 | ||||||
|  | @ -25,12 +25,12 @@ export default { | ||||||
|         return { |         return { | ||||||
|             output: "", |             output: "", | ||||||
|             frameDuration: 30, |             frameDuration: 30, | ||||||
|         } |         }; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     computed: { |     computed: { | ||||||
|         isNum() { |         isNum() { | ||||||
|             return typeof this.value === "number" |             return typeof this.value === "number"; | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  | @ -45,7 +45,7 @@ export default { | ||||||
|             } else { |             } else { | ||||||
|                 for (let i = 1; i < frames; i++) { |                 for (let i = 1; i < frames; i++) { | ||||||
|                     this.output += step; |                     this.output += step; | ||||||
|                     await sleep(15) |                     await sleep(15); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -59,5 +59,5 @@ export default { | ||||||
| 
 | 
 | ||||||
|     methods: {}, |     methods: {}, | ||||||
| 
 | 
 | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -4,12 +4,12 @@ | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||||
| import relativeTime from "dayjs/plugin/relativeTime" | import relativeTime from "dayjs/plugin/relativeTime"; | ||||||
| import utc from "dayjs/plugin/utc" | import utc from "dayjs/plugin/utc"; | ||||||
| import timezone from "dayjs/plugin/timezone" // dependent on utc plugin | import timezone from "dayjs/plugin/timezone"; // dependent on utc plugin | ||||||
| dayjs.extend(utc) | dayjs.extend(utc); | ||||||
| dayjs.extend(timezone) | dayjs.extend(timezone); | ||||||
| dayjs.extend(relativeTime) | dayjs.extend(relativeTime); | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|     props: { |     props: { | ||||||
|  | @ -29,5 +29,5 @@ export default { | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ export default { | ||||||
|             beatMargin: 4, |             beatMargin: 4, | ||||||
|             move: false, |             move: false, | ||||||
|             maxBeat: -1, |             maxBeat: -1, | ||||||
|         } |         }; | ||||||
|     }, |     }, | ||||||
|     computed: { |     computed: { | ||||||
| 
 | 
 | ||||||
|  | @ -69,12 +69,12 @@ export default { | ||||||
|             if (start < 0) { |             if (start < 0) { | ||||||
|                 // Add empty placeholder |                 // Add empty placeholder | ||||||
|                 for (let i = start; i < 0; i++) { |                 for (let i = start; i < 0; i++) { | ||||||
|                     placeholders.push(0) |                     placeholders.push(0); | ||||||
|                 } |                 } | ||||||
|                 start = 0; |                 start = 0; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return placeholders.concat(this.beatList.slice(start)) |             return placeholders.concat(this.beatList.slice(start)); | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         wrapStyle() { |         wrapStyle() { | ||||||
|  | @ -84,7 +84,7 @@ export default { | ||||||
|             return { |             return { | ||||||
|                 padding: `${topBottom}px ${leftRight}px`, |                 padding: `${topBottom}px ${leftRight}px`, | ||||||
|                 width: "100%", |                 width: "100%", | ||||||
|             } |             }; | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         barStyle() { |         barStyle() { | ||||||
|  | @ -94,12 +94,12 @@ export default { | ||||||
|                 return { |                 return { | ||||||
|                     transition: "all ease-in-out 0.25s", |                     transition: "all ease-in-out 0.25s", | ||||||
|                     transform: `translateX(${width}px)`, |                     transform: `translateX(${width}px)`, | ||||||
|                 } |                 }; | ||||||
| 
 | 
 | ||||||
|             } |             } | ||||||
|             return { |             return { | ||||||
|                 transform: "translateX(0)", |                 transform: "translateX(0)", | ||||||
|             } |             }; | ||||||
| 
 | 
 | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|  | @ -109,7 +109,7 @@ export default { | ||||||
|                 height: this.beatHeight + "px", |                 height: this.beatHeight + "px", | ||||||
|                 margin: this.beatMargin + "px", |                 margin: this.beatMargin + "px", | ||||||
|                 "--hover-scale": this.hoverScale, |                 "--hover-scale": this.hoverScale, | ||||||
|             } |             }; | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|     }, |     }, | ||||||
|  | @ -120,7 +120,7 @@ export default { | ||||||
| 
 | 
 | ||||||
|                 setTimeout(() => { |                 setTimeout(() => { | ||||||
|                     this.move = false; |                     this.move = false; | ||||||
|                 }, 300) |                 }, 300); | ||||||
|             }, |             }, | ||||||
|             deep: true, |             deep: true, | ||||||
|         }, |         }, | ||||||
|  | @ -162,15 +162,15 @@ export default { | ||||||
|     methods: { |     methods: { | ||||||
|         resize() { |         resize() { | ||||||
|             if (this.$refs.wrap) { |             if (this.$refs.wrap) { | ||||||
|                 this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)) |                 this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         getBeatTitle(beat) { |         getBeatTitle(beat) { | ||||||
|             return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``); |             return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  |  | ||||||
|  | @ -51,15 +51,15 @@ export default { | ||||||
|     data() { |     data() { | ||||||
|         return { |         return { | ||||||
|             visibility: "password", |             visibility: "password", | ||||||
|         } |         }; | ||||||
|     }, |     }, | ||||||
|     computed: { |     computed: { | ||||||
|         model: { |         model: { | ||||||
|             get() { |             get() { | ||||||
|                 return this.modelValue |                 return this.modelValue; | ||||||
|             }, |             }, | ||||||
|             set(value) { |             set(value) { | ||||||
|                 this.$emit("update:modelValue", value) |                 this.$emit("update:modelValue", value); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|  | @ -74,5 +74,5 @@ export default { | ||||||
|             this.visibility = "password"; |             this.visibility = "password"; | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
| 
 | 
 | ||||||
|             <router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> |             <router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> | ||||||
|                 <div class="row"> |                 <div class="row"> | ||||||
|                     <div class="col-9 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> |                     <div class="col-9 col-md-8 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> | ||||||
|                         <div class="info"> |                         <div class="info"> | ||||||
|                             <Uptime :monitor="item" type="24" :pill="true" /> |                             <Uptime :monitor="item" type="24" :pill="true" /> | ||||||
|                             {{ item.name }} |                             {{ item.name }} | ||||||
|  | @ -36,7 +36,7 @@ | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|                 <div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> |                 <div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> | ||||||
|                     <div class="col-12"> |                     <div class="col-12 bottom-style"> | ||||||
|                         <HeartbeatBar size="small" :monitor-id="item.id" /> |                         <HeartbeatBar size="small" :monitor-id="item.id" /> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|  | @ -172,7 +172,7 @@ export default { | ||||||
| 
 | 
 | ||||||
| .dark { | .dark { | ||||||
|     .footer { |     .footer { | ||||||
|       //  background-color: $dark-bg; |         //  background-color: $dark-bg; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -198,14 +198,21 @@ export default { | ||||||
|     max-width: 15em; |     max-width: 15em; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .monitorItem { | .monitor-item { | ||||||
|     width: 100%; |     width: 100%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .tags { | .tags { | ||||||
|     padding-left: 62px; |     margin-top: 4px; | ||||||
|  |     padding-left: 67px; | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-wrap: wrap; |     flex-wrap: wrap; | ||||||
|     gap: 0; |     gap: 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .bottom-style { | ||||||
|  |     padding-left: 67px; | ||||||
|  |     margin-top: 5px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -69,7 +69,6 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import { Modal } from "bootstrap"; | import { Modal } from "bootstrap"; | ||||||
| import { ucfirst } from "../util.ts"; |  | ||||||
| 
 | 
 | ||||||
| import Confirm from "./Confirm.vue"; | import Confirm from "./Confirm.vue"; | ||||||
| import NotificationFormList from "./notifications"; | import NotificationFormList from "./notifications"; | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ import timezone from "dayjs/plugin/timezone"; | ||||||
| import "chartjs-adapter-dayjs"; | import "chartjs-adapter-dayjs"; | ||||||
| import { LineChart } from "vue-chart-3"; | import { LineChart } from "vue-chart-3"; | ||||||
| import { useToast } from "vue-toastification"; | import { useToast } from "vue-toastification"; | ||||||
| import { UP, DOWN, PENDING } from "../util.ts"; | import { DOWN } from "../util.ts"; | ||||||
| 
 | 
 | ||||||
| dayjs.extend(utc); | dayjs.extend(utc); | ||||||
| dayjs.extend(timezone); | dayjs.extend(timezone); | ||||||
|  | @ -278,7 +278,7 @@ export default { | ||||||
| 
 | 
 | ||||||
|         .dropdown-item { |         .dropdown-item { | ||||||
|             border-radius: 0.3rem; |             border-radius: 0.3rem; | ||||||
|             padding: 2px 16px 4px 16px; |             padding: 2px 16px 4px; | ||||||
| 
 | 
 | ||||||
|             .dark & { |             .dark & { | ||||||
|                 background: $dark-bg; |                 background: $dark-bg; | ||||||
|  | @ -286,6 +286,7 @@ export default { | ||||||
| 
 | 
 | ||||||
|             .dark &:hover { |             .dark &:hover { | ||||||
|                 background: $dark-font-color; |                 background: $dark-font-color; | ||||||
|  |                 color: $dark-font-color2; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
|                             <label for="proxy-host" class="form-label">{{ $t("Proxy Server") }}</label> |                             <label for="proxy-host" class="form-label">{{ $t("Proxy Server") }}</label> | ||||||
|                             <div class="d-flex"> |                             <div class="d-flex"> | ||||||
|                                 <input id="proxy-host" v-model="proxy.host" type="text" class="form-control" required :placeholder="$t('Server Address')"> |                                 <input id="proxy-host" v-model="proxy.host" type="text" class="form-control" required :placeholder="$t('Server Address')"> | ||||||
|                                 <input v-model="proxy.port" type="number" class="form-control ms-2" style="width: 100px" required min="1" max="65535" :placeholder="$t('Port')"> |                                 <input v-model="proxy.port" type="number" class="form-control ms-2" style="width: 100px;" required min="1" max="65535" :placeholder="$t('Port')"> | ||||||
|                             </div> |                             </div> | ||||||
|                         </div> |                         </div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ export default { | ||||||
| 
 | 
 | ||||||
| .mobile { | .mobile { | ||||||
|     .item { |     .item { | ||||||
|         padding: 13px 0 10px 0; |         padding: 13px 0 10px; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ export default { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ export default { | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "../assets/vars.scss"; | @import "../assets/vars.scss"; | ||||||
| 
 | 
 | ||||||
| h5:after { | h5::after { | ||||||
|     content: ""; |     content: ""; | ||||||
|     display: block; |     display: block; | ||||||
|     width: 50%; |     width: 50%; | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ | ||||||
|                                     <input v-model="token" type="text" maxlength="6" class="form-control"> |                                     <input v-model="token" type="text" maxlength="6" class="form-control"> | ||||||
|                                     <button class="btn btn-outline-primary" type="button" @click="verifyToken()">{{ $t("Verify Token") }}</button> |                                     <button class="btn btn-outline-primary" type="button" @click="verifyToken()">{{ $t("Verify Token") }}</button> | ||||||
|                                 </div> |                                 </div> | ||||||
|                                 <p v-show="tokenValid" class="mt-2" style="color: green">{{ $t("tokenValidSettingsMsg") }}</p> |                                 <p v-show="tokenValid" class="mt-2" style="color: green;">{{ $t("tokenValidSettingsMsg") }}</p> | ||||||
|                             </div> |                             </div> | ||||||
|                         </div> |                         </div> | ||||||
|                     </div> |                     </div> | ||||||
|  |  | ||||||
|  | @ -22,33 +22,33 @@ export default { | ||||||
|                 return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%"; |                 return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return this.$t("notAvailableShort") |             return this.$t("notAvailableShort"); | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         color() { |         color() { | ||||||
|             if (this.lastHeartBeat.status === 0) { |             if (this.lastHeartBeat.status === 0) { | ||||||
|                 return "danger" |                 return "danger"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (this.lastHeartBeat.status === 1) { |             if (this.lastHeartBeat.status === 1) { | ||||||
|                 return "primary" |                 return "primary"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (this.lastHeartBeat.status === 2) { |             if (this.lastHeartBeat.status === 2) { | ||||||
|                 return "warning" |                 return "warning"; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return "secondary" |             return "secondary"; | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         lastHeartBeat() { |         lastHeartBeat() { | ||||||
|             if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) { |             if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) { | ||||||
|                 return this.$root.lastHeartbeatList[this.monitor.id] |                 return this.$root.lastHeartbeatList[this.monitor.id]; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return { |             return { | ||||||
|                 status: -1, |                 status: -1, | ||||||
|             } |             }; | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         className() { |         className() { | ||||||
|  | @ -59,7 +59,7 @@ export default { | ||||||
|             return ""; |             return ""; | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
|  |  | ||||||
|  | @ -28,5 +28,5 @@ export default { | ||||||
|             this.$parent.notification.gotifyPriority = 8; |             this.$parent.notification.gotifyPriority = 8; | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
|     <div class="mb-3"> |     <div class="mb-3"> | ||||||
|         <label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color:red;"><sup>*</sup></span></label> |         <label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label> | ||||||
|         <input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required> |         <input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required> | ||||||
|         <label for="mattermost-username" class="form-label">{{ $t("Username") }}</label> |         <label for="mattermost-username" class="form-label">{{ $t("Username") }}</label> | ||||||
|         <input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control"> |         <input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control"> | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
|         <label for="mattermost-channel" class="form-label">{{ $t("Channel Name") }}</label> |         <label for="mattermost-channel" class="form-label">{{ $t("Channel Name") }}</label> | ||||||
|         <input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control"> |         <input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control"> | ||||||
|         <div class="form-text"> |         <div class="form-text"> | ||||||
|             <span style="color:red;"><sup>*</sup></span>{{ $t("Required") }} |             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }} | ||||||
|             <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;"> |             <i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;"> | ||||||
|                 <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a> |                 <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a> | ||||||
|             </i18n-t> |             </i18n-t> | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								src/components/notifications/OneBot.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/components/notifications/OneBot.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | <template> | ||||||
|  |     <div class="mb-3"> | ||||||
|  |         <div class="mb-3"> | ||||||
|  |             <label for="onebot-http-addr" class="form-label">{{ $t("onebotHttpAddress") }}<span style="color: red;"><sup>*</sup></span></label> | ||||||
|  |             <input id="HttpUrl" v-model="$parent.notification.httpAddr" type="text" class="form-control" required> | ||||||
|  |         </div> | ||||||
|  |         <div class="mb-3"> | ||||||
|  |             <label for="onebot-access-token" class="form-label">AccessToken<span style="color: red;"><sup>*</sup></span></label> | ||||||
|  |             <input id="HttpUrl" v-model="$parent.notification.accessToken" type="text" class="form-control" required> | ||||||
|  |             <div class="form-text"> | ||||||
|  |                 <p>{{ $t("onebotSafetyTips") }}</p> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="mb-3"> | ||||||
|  |             <label for="onebot-msg-type" class="form-label">{{ $t("onebotMessageType") }}</label> | ||||||
|  |             <select id="onebot-msg-type" v-model="$parent.notification.msgType" class="form-select"> | ||||||
|  |                 <option value="group">{{ $t("onebotGroupMessage") }}</option> | ||||||
|  |                 <option value="private">{{ $t("onebotPrivateMessage") }}</option> | ||||||
|  |             </select> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="mb-3"> | ||||||
|  |             <label for="onebot-reciever-id" class="form-label">{{ $t("onebotUserOrGroupId") }}<span style="color: red;"><sup>*</sup></span></label> | ||||||
|  |             <input id="secretKey" v-model="$parent.notification.recieverId" type="text" class="form-control" required> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="form-text"> | ||||||
|  |             <i18n-t tag="p" keypath="Read more:"> | ||||||
|  |                 <a href="https://github.com/botuniverse/onebot-11" target="_blank">https://github.com/botuniverse/onebot-11</a> | ||||||
|  |             </i18n-t> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </template> | ||||||
							
								
								
									
										19
									
								
								src/components/notifications/PushDeer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/notifications/PushDeer.vue
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | <template> | ||||||
|  |     <div class="mb-3"> | ||||||
|  |         <label for="pushdeer-key" class="form-label">{{ $t("PushDeer Key") }}</label> | ||||||
|  |         <HiddenInput id="pushdeer-key" v-model="$parent.notification.pushdeerKey" :required="true" autocomplete="one-time-code" placeholder="PDUxxxx"></HiddenInput> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;"> | ||||||
|  |         <a href="http://www.pushdeer.com/" rel="noopener noreferrer" target="_blank">http://www.pushdeer.com/</a> | ||||||
|  |     </i18n-t> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import HiddenInput from "../HiddenInput.vue"; | ||||||
|  | export default { | ||||||
|  |     components: { | ||||||
|  |         HiddenInput, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | @ -63,5 +63,5 @@ export default { | ||||||
|     components: { |     components: { | ||||||
|         HiddenInput, |         HiddenInput, | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -24,11 +24,13 @@ import AliyunSMS from "./AliyunSms.vue"; | ||||||
| import DingDing from "./DingDing.vue"; | import DingDing from "./DingDing.vue"; | ||||||
| import Bark from "./Bark.vue"; | import Bark from "./Bark.vue"; | ||||||
| import SerwerSMS from "./SerwerSMS.vue"; | import SerwerSMS from "./SerwerSMS.vue"; | ||||||
| import Stackfield from './Stackfield.vue'; | import Stackfield from "./Stackfield.vue"; | ||||||
| import WeCom from "./WeCom.vue"; | import WeCom from "./WeCom.vue"; | ||||||
| import GoogleChat from "./GoogleChat.vue"; | import GoogleChat from "./GoogleChat.vue"; | ||||||
| import Gorush from "./Gorush.vue"; | import Gorush from "./Gorush.vue"; | ||||||
| import Alerta from "./Alerta.vue"; | import Alerta from "./Alerta.vue"; | ||||||
|  | import OneBot from "./OneBot.vue"; | ||||||
|  | import PushDeer from "./PushDeer.vue"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Manage all notification form. |  * Manage all notification form. | ||||||
|  | @ -67,6 +69,8 @@ const NotificationFormList = { | ||||||
|     "GoogleChat": GoogleChat, |     "GoogleChat": GoogleChat, | ||||||
|     "gorush": Gorush, |     "gorush": Gorush, | ||||||
|     "alerta": Alerta, |     "alerta": Alerta, | ||||||
|  |     "OneBot": OneBot, | ||||||
|  |     "PushDeer": PushDeer, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default NotificationFormList; | export default NotificationFormList; | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ export default { | ||||||
| .logo { | .logo { | ||||||
|     margin: 4em 1em; |     margin: 4em 1em; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| .update-link { | .update-link { | ||||||
|     font-size: 0.9em; |     font-size: 0.9em; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -69,7 +69,7 @@ | ||||||
| 
 | 
 | ||||||
|             <div class="mb-2"> |             <div class="mb-2"> | ||||||
|                 <input |                 <input | ||||||
|                     id="importBackup" |                     id="import-backend" | ||||||
|                     type="file" |                     type="file" | ||||||
|                     class="form-control" |                     class="form-control" | ||||||
|                     accept="application/json" |                     accept="application/json" | ||||||
|  | @ -94,7 +94,7 @@ | ||||||
|             <div |             <div | ||||||
|                 v-if="importAlert" |                 v-if="importAlert" | ||||||
|                 class="alert alert-danger mt-3" |                 class="alert alert-danger mt-3" | ||||||
|                 style="padding: 6px 16px" |                 style="padding: 6px 16px;" | ||||||
|             > |             > | ||||||
|                 {{ importAlert }} |                 {{ importAlert }} | ||||||
|             </div> |             </div> | ||||||
|  | @ -159,7 +159,7 @@ export default { | ||||||
| 
 | 
 | ||||||
|         importBackup() { |         importBackup() { | ||||||
|             this.processing = true; |             this.processing = true; | ||||||
|             let uploadItem = document.getElementById("importBackup").files; |             let uploadItem = document.getElementById("import-backend").files; | ||||||
| 
 | 
 | ||||||
|             if (uploadItem.length <= 0) { |             if (uploadItem.length <= 0) { | ||||||
|                 this.processing = false; |                 this.processing = false; | ||||||
|  | @ -198,7 +198,7 @@ export default { | ||||||
| @import "../../assets/vars.scss"; | @import "../../assets/vars.scss"; | ||||||
| 
 | 
 | ||||||
| .dark { | .dark { | ||||||
|     #importBackup { |     #import-backend { | ||||||
|         &::file-selector-button { |         &::file-selector-button { | ||||||
|             color: $primary; |             color: $primary; | ||||||
|             background-color: $dark-bg; |             background-color: $dark-bg; | ||||||
|  |  | ||||||
|  | @ -189,4 +189,3 @@ export default { | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style></style> |  | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| import Confirm from "../../components/Confirm.vue"; | import Confirm from "../../components/Confirm.vue"; | ||||||
| import { debug } from "../../util.ts"; | import { log } from "../../util.ts"; | ||||||
| import { useToast } from "vue-toastification"; | import { useToast } from "vue-toastification"; | ||||||
| 
 | 
 | ||||||
| const toast = useToast(); | const toast = useToast(); | ||||||
|  | @ -91,13 +91,13 @@ export default { | ||||||
| 
 | 
 | ||||||
|     methods: { |     methods: { | ||||||
|         loadDatabaseSize() { |         loadDatabaseSize() { | ||||||
|             debug("load database size"); |             log.debug("monitorhistory", "load database size"); | ||||||
|             this.$root.getSocket().emit("getDatabaseSize", (res) => { |             this.$root.getSocket().emit("getDatabaseSize", (res) => { | ||||||
|                 if (res.ok) { |                 if (res.ok) { | ||||||
|                     this.databaseSize = res.size; |                     this.databaseSize = res.size; | ||||||
|                     debug("database size: " + res.size); |                     log.debug("monitorhistory", "database size: " + res.size); | ||||||
|                 } else { |                 } else { | ||||||
|                     debug(res); |                     log.debug("monitorhistory", res); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }, |         }, | ||||||
|  | @ -108,7 +108,7 @@ export default { | ||||||
|                     this.loadDatabaseSize(); |                     this.loadDatabaseSize(); | ||||||
|                     toast.success("Done"); |                     toast.success("Done"); | ||||||
|                 } else { |                 } else { | ||||||
|                     debug(res); |                     log.debug("monitorhistory", res); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }, |         }, | ||||||
|  | @ -129,5 +129,3 @@ export default { | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
| 
 |  | ||||||
| <style></style> |  | ||||||
|  |  | ||||||
|  | @ -355,7 +355,7 @@ export default { | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "../../assets/vars.scss"; | @import "../../assets/vars.scss"; | ||||||
| 
 | 
 | ||||||
| h5:after { | h5::after { | ||||||
|     content: ""; |     content: ""; | ||||||
|     display: block; |     display: block; | ||||||
|     width: 50%; |     width: 50%; | ||||||
|  |  | ||||||
|  | @ -343,7 +343,7 @@ export default { | ||||||
|     "No Monitors": "Няма монитори", |     "No Monitors": "Няма монитори", | ||||||
|     "Untitled Group": "Група без заглавие", |     "Untitled Group": "Група без заглавие", | ||||||
|     Services: "Услуги", |     Services: "Услуги", | ||||||
|     Discard: "Премахни", |     Discard: "Отмени", | ||||||
|     Cancel: "Отмени", |     Cancel: "Отмени", | ||||||
|     "Powered by": "Създадено чрез", |     "Powered by": "Създадено чрез", | ||||||
|     serwersms: "SerwerSMS.pl", |     serwersms: "SerwerSMS.pl", | ||||||
|  |  | ||||||
|  | @ -350,7 +350,7 @@ export default { | ||||||
|     serwersmsAPIPassword: "API Passwort", |     serwersmsAPIPassword: "API Passwort", | ||||||
|     serwersmsPhoneNumber: "Telefonnummer", |     serwersmsPhoneNumber: "Telefonnummer", | ||||||
|     serwersmsSenderName: "Name des SMS-Absenders (über Kundenportal registriert)", |     serwersmsSenderName: "Name des SMS-Absenders (über Kundenportal registriert)", | ||||||
|     "stackfield": "Stackfield", |     stackfield: "Stackfield", | ||||||
|     clicksendsms: "ClickSend SMS", |     clicksendsms: "ClickSend SMS", | ||||||
|     apiCredentials: "API Zugangsdaten", |     apiCredentials: "API Zugangsdaten", | ||||||
|     smtpDkimSettings: "DKIM Einstellungen", |     smtpDkimSettings: "DKIM Einstellungen", | ||||||
|  | @ -362,4 +362,84 @@ export default { | ||||||
|     smtpDkimHashAlgo: "Hash-Algorithmus (Optional)", |     smtpDkimHashAlgo: "Hash-Algorithmus (Optional)", | ||||||
|     smtpDkimheaderFieldNames: "Zu validierende Header-Schlüssel (optional)", |     smtpDkimheaderFieldNames: "Zu validierende Header-Schlüssel (optional)", | ||||||
|     smtpDkimskipFields: "Zu ignorierende Header Schlüssel (optional)", |     smtpDkimskipFields: "Zu ignorierende Header Schlüssel (optional)", | ||||||
|  |     PushByTechulus: "Push by Techulus", | ||||||
|  |     gorush: "Gorush", | ||||||
|  |     alerta: "Alerta", | ||||||
|  |     alertaApiEndpoint: "API Endpunkt", | ||||||
|  |     alertaEnvironment: "Umgebung", | ||||||
|  |     alertaApiKey: "API Schlüssel", | ||||||
|  |     alertaAlertState: "Alarmstatus", | ||||||
|  |     alertaRecoverState: "Wiederherstellungsstatus", | ||||||
|  |     deleteStatusPageMsg: "Bist du sicher, dass du diese Status-Seite löschen willst?", | ||||||
|  |     Proxies: "Proxies", | ||||||
|  |     default: "Standard", | ||||||
|  |     enabled: "Aktiviert", | ||||||
|  |     setAsDefault: "Als Standard setzen", | ||||||
|  |     deleteProxyMsg: "Bist du sicher, dass du diesen Proxy für alle Monitore löschen willst?", | ||||||
|  |     proxyDescription: "Proxies müssen einem Monitor zugewiesen werden, um zu funktionieren.", | ||||||
|  |     enableProxyDescription: "Dieser Proxy wird keinen Effekt auf Monitor-Anfragen haben, bis er aktiviert ist. Du kannst ihn temporär von allen Monitoren nach Aktivierungsstatus deaktivieren.", | ||||||
|  |     setAsDefaultProxyDescription: "Dieser Proxy wird standardmäßig für alle neuen Monitore aktiviert sein. Du kannst den Proxy immernoch für jeden Monitor einzeln deaktivieren.", | ||||||
|  |     "Certificate Chain": "Zertifikatskette", | ||||||
|  |     Valid: "Gültig", | ||||||
|  |     Invalid: "Ungültig", | ||||||
|  |     AccessKeyId: "AccessKey ID", | ||||||
|  |     SecretAccessKey: "AccessKey Secret", | ||||||
|  |     PhoneNumbers: "Telefonnummern", | ||||||
|  |     TemplateCode: "Vorlagencode", | ||||||
|  |     SignName: "Signaturname", | ||||||
|  |     "Sms template must contain parameters: ": "SMS Vorlage muss folgende Parameter enthalten: ", | ||||||
|  |     "Bark Endpoint": "Bark Endpunkt", | ||||||
|  |     WebHookUrl: "Webhook URL", | ||||||
|  |     SecretKey: "Geheimer Schlüssel", | ||||||
|  |     "For safety, must use secret key": "Zur Sicherheit muss ein geheimer Schlüssel verwendet werden", | ||||||
|  |     "Device Token": "Gerätetoken", | ||||||
|  |     Platform: "Platform", | ||||||
|  |     iOS: "iOS", | ||||||
|  |     Android: "Android", | ||||||
|  |     Huawei: "Huawei", | ||||||
|  |     High: "Hoch", | ||||||
|  |     Retry: "Wiederholungen", | ||||||
|  |     Topic: "Thema", | ||||||
|  |     "WeCom Bot Key": "WeCom Bot Schlüssel", | ||||||
|  |     "Setup Proxy": "Proxy einrichten", | ||||||
|  |     "Proxy Protocol": "Proxy Protokoll", | ||||||
|  |     "Proxy Server": "Proxy Server", | ||||||
|  |     "Proxy server has authentication": "Proxy server hat Authentifizierung", | ||||||
|  |     User: "Benutzer", | ||||||
|  |     Installed: "Installiert", | ||||||
|  |     "Not installed": "Nicht installiert", | ||||||
|  |     Running: "Läuft", | ||||||
|  |     "Not running": "Gestoppt", | ||||||
|  |     "Remove Token": "Token entfernen", | ||||||
|  |     Start: "Start", | ||||||
|  |     Stop: "Stop", | ||||||
|  |     "Uptime Kuma": "Uptime Kuma", | ||||||
|  |     "Add New Status Page": "Neue Status-Seite hinzufügen", | ||||||
|  |     Slug: "Slug", | ||||||
|  |     "Accept characters:": "Akzeptierte Zeichen:", | ||||||
|  |     startOrEndWithOnly: "Nur mit {0} anfangen und enden", | ||||||
|  |     "No consecutive dashes": "Keine aufeinanderfolgenden Bindestriche", | ||||||
|  |     Next: "Weiter", | ||||||
|  |     "The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.", | ||||||
|  |     "No Proxy": "Kein Proxy", | ||||||
|  |     "HTTP Basic Auth": "HTTP Basisauthentifizierung", | ||||||
|  |     "New Status Page": "Neue Status-Seite", | ||||||
|  |     "Page Not Found": "Seite nicht gefunden", | ||||||
|  |     "Reverse Proxy": "Reverse Proxy", | ||||||
|  |     Backup: "Sicherung", | ||||||
|  |     About: "Über", | ||||||
|  |     wayToGetCloudflaredURL: "(Lade cloudflared von {0} herunter)", | ||||||
|  |     cloudflareWebsite: "Cloudflare Website", | ||||||
|  |     "Message:": "Nachricht:", | ||||||
|  |     "Don't know how to get the token? Please read the guide:": "Du weißt nicht, wie man den Token bekommt? Lies die Anleitung dazu:", | ||||||
|  |     "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Die aktuelle Verbindung kann unterbrochen werden, wenn du aktuell über Cloudflare Tunnel verbunden bist. Bist du sicher, dass du es stoppen willst? Gib zur Bestätigung dein aktuelles Passwort ein.", | ||||||
|  |     "Other Software": "Andere Software", | ||||||
|  |     "For example: nginx, Apache and Traefik.": "Zum Beispiel: nginx, Apache und Traefik.", | ||||||
|  |     "Please read": "Bitte lesen", | ||||||
|  |     "Subject:": "Betreff:", | ||||||
|  |     "Valid To:": "Gültig bis:", | ||||||
|  |     "Days Remaining:": "Tage verbleibend:", | ||||||
|  |     "Issuer:": "Aussteller:", | ||||||
|  |     "Fingerprint:": "Fingerabdruck:", | ||||||
|  |     "No status pages": "Keine Status-Seiten", | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -442,4 +442,14 @@ export default { | ||||||
|     "Issuer:": "Issuer:", |     "Issuer:": "Issuer:", | ||||||
|     "Fingerprint:": "Fingerprint:", |     "Fingerprint:": "Fingerprint:", | ||||||
|     "No status pages": "No status pages", |     "No status pages": "No status pages", | ||||||
|  |     "Domain Name Expiry Notification": "Domain Name Expiry Notification", | ||||||
|  |     "Proxy": "Proxy", | ||||||
|  |     "Date Created": "Date Created", | ||||||
|  |     onebotHttpAddress: "OneBot HTTP Address", | ||||||
|  |     onebotMessageType: "OneBot Message Type", | ||||||
|  |     onebotGroupMessage: "Group", | ||||||
|  |     onebotPrivateMessage: "Private", | ||||||
|  |     onebotUserOrGroupId: "Group/User ID", | ||||||
|  |     onebotSafetyTips: "For safety, must set access token", | ||||||
|  |     "PushDeer Key": "PushDeer Key", | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -374,8 +374,8 @@ export default { | ||||||
|     serwersmsSenderName: "SMS Имя Отправителя (регистрированный через пользовательский портал)", |     serwersmsSenderName: "SMS Имя Отправителя (регистрированный через пользовательский портал)", | ||||||
|     stackfield: "Stackfield", |     stackfield: "Stackfield", | ||||||
|     smtpDkimSettings: "DKIM Настройки", |     smtpDkimSettings: "DKIM Настройки", | ||||||
|     smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.", |     smtpDkimDesc: "Пожалуйста ознакомьтесь с {0} Nodemailer DKIM для использования.", | ||||||
|     documentation: "документация", |     documentation: "документацией", | ||||||
|     smtpDkimDomain: "Имя Домена", |     smtpDkimDomain: "Имя Домена", | ||||||
|     smtpDkimKeySelector: "Ключ", |     smtpDkimKeySelector: "Ключ", | ||||||
|     smtpDkimPrivateKey: "Приватный ключ", |     smtpDkimPrivateKey: "Приватный ключ", | ||||||
|  | @ -389,4 +389,12 @@ export default { | ||||||
|     alertaApiKey: "Ключ API", |     alertaApiKey: "Ключ API", | ||||||
|     alertaAlertState: "Состояние алерта", |     alertaAlertState: "Состояние алерта", | ||||||
|     alertaRecoverState: "Состояние восстановления", |     alertaRecoverState: "Состояние восстановления", | ||||||
|  |     Proxies: "Прокси", | ||||||
|  |     default: "По умолчанию", | ||||||
|  |     enabled: "Включено", | ||||||
|  |     setAsDefault: "Установлено по умолчанию", | ||||||
|  |     deleteProxyMsg: "Вы действительно хотите удалить этот прокси для всех мониторов?", | ||||||
|  |     proxyDescription: "Прокси должны быть привязаны к монитору, чтобы работать.", | ||||||
|  |     enableProxyDescription: "Этот прокси не будет влиять на запросы монитора, пока не будет активирован. Вы можете контролировать временное отключение прокси для всех мониторов через статус активации.", | ||||||
|  |     setAsDefaultProxyDescription: "Этот прокси будет по умолчанию включен для новых мониторов. Вы всё ещё можете отдельно отключать прокси в каждом мониторе.", | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -449,4 +449,13 @@ export default { | ||||||
|     "Issuer:": "颁发者:", |     "Issuer:": "颁发者:", | ||||||
|     "Fingerprint:": "指纹:", |     "Fingerprint:": "指纹:", | ||||||
|     "No status pages": "无状态页", |     "No status pages": "无状态页", | ||||||
|  |     "Domain Name Expiry Notification": "域名到期时通知", | ||||||
|  |     "Proxy": "代理", | ||||||
|  |     "Date Created": "创建于", | ||||||
|  |     onebotHttpAddress: "OneBot HTTP 地址", | ||||||
|  |     onebotMessageType: "OneBot 消息类型", | ||||||
|  |     onebotGroupMessage: "群聊", | ||||||
|  |     onebotPrivateMessage: "私聊", | ||||||
|  |     onebotUserOrGroupId: "群组/用户ID", | ||||||
|  |     onebotSafetyTips: "出于安全原因,请务必设置AccessToken", | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -3,5 +3,6 @@ | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| export default {} | export default {}; | ||||||
| </script> | </script> | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -25,9 +25,9 @@ export default { | ||||||
|         MonitorList, |         MonitorList, | ||||||
|     }, |     }, | ||||||
|     data() { |     data() { | ||||||
|         return {} |         return {}; | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  |  | ||||||
|  | @ -118,6 +118,7 @@ export default { | ||||||
|                 return 0; |                 return 0; | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  |             // eslint-disable-next-line vue/no-side-effects-in-computed-properties | ||||||
|             this.heartBeatList = result; |             this.heartBeatList = result; | ||||||
| 
 | 
 | ||||||
|             return result; |             return result; | ||||||
|  |  | ||||||
|  | @ -11,6 +11,6 @@ export default { | ||||||
|     components: { |     components: { | ||||||
|         MonitorList, |         MonitorList, | ||||||
|     }, |     }, | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -92,7 +92,6 @@ export default { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         .info { |         .info { | ||||||
| 
 |  | ||||||
|             .title { |             .title { | ||||||
|                 font-weight: bold; |                 font-weight: bold; | ||||||
|                 font-size: 20px; |                 font-size: 20px; | ||||||
|  |  | ||||||
|  | @ -836,7 +836,7 @@ footer { | ||||||
| 
 | 
 | ||||||
| .incident { | .incident { | ||||||
|     .content { |     .content { | ||||||
|         &[contenteditable=true] { |         &[contenteditable="true"] { | ||||||
|             min-height: 60px; |             min-height: 60px; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -7,6 +7,12 @@ import { localeDirection, currentLocale } from "./i18n"; | ||||||
| dayjs.extend(utc); | dayjs.extend(utc); | ||||||
| dayjs.extend(timezone); | dayjs.extend(timezone); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Returns the offset from UTC in hours for the current locale. | ||||||
|  |  * @returns {number} The offset from UTC in hours. | ||||||
|  |  * | ||||||
|  |  * Generated by Trelent | ||||||
|  |  */ | ||||||
| function getTimezoneOffset(timeZone) { | function getTimezoneOffset(timeZone) { | ||||||
|     const now = new Date(); |     const now = new Date(); | ||||||
|     const tzString = now.toLocaleString("en-US", { |     const tzString = now.toLocaleString("en-US", { | ||||||
|  | @ -18,6 +24,13 @@ function getTimezoneOffset(timeZone) { | ||||||
|     return -offset; |     return -offset; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  | * Returns a list of timezones sorted by their offset from UTC. | ||||||
|  | * @param {Array} timezones - An array of timezone objects. | ||||||
|  | * @returns {Array} A list of the given timezones sorted by their offset from UTC. | ||||||
|  | * | ||||||
|  | * Generated by Trelent | ||||||
|  | */ | ||||||
| export function timezoneList() { | export function timezoneList() { | ||||||
|     let result = []; |     let result = []; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										56
									
								
								src/util.js
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								src/util.js
									
									
									
									
									
								
							|  | @ -7,7 +7,7 @@ | ||||||
| // Backend uses the compiled file util.js
 | // Backend uses the compiled file util.js
 | ||||||
| // Frontend uses util.ts
 | // Frontend uses util.ts
 | ||||||
| Object.defineProperty(exports, "__esModule", { value: true }); | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
| exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; | exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; | ||||||
| const _dayjs = require("dayjs"); | const _dayjs = require("dayjs"); | ||||||
| const dayjs = _dayjs; | const dayjs = _dayjs; | ||||||
| exports.isDev = process.env.NODE_ENV === "development"; | exports.isDev = process.env.NODE_ENV === "development"; | ||||||
|  | @ -44,12 +44,60 @@ function ucfirst(str) { | ||||||
|     return firstLetter.toUpperCase() + str.substr(1); |     return firstLetter.toUpperCase() + str.substr(1); | ||||||
| } | } | ||||||
| exports.ucfirst = ucfirst; | exports.ucfirst = ucfirst; | ||||||
|  | /** | ||||||
|  |  * @deprecated Use log.debug | ||||||
|  |  * @since https://github.com/louislam/uptime-kuma/pull/910
 | ||||||
|  |  * @param msg | ||||||
|  |  */ | ||||||
| function debug(msg) { | function debug(msg) { | ||||||
|     if (exports.isDev) { |     exports.log.log("", msg, "debug"); | ||||||
|         console.log(msg); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| exports.debug = debug; | exports.debug = debug; | ||||||
|  | class Logger { | ||||||
|  |     log(module, msg, level) { | ||||||
|  |         module = module.toUpperCase(); | ||||||
|  |         level = level.toUpperCase(); | ||||||
|  |         const now = new Date().toISOString(); | ||||||
|  |         const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg; | ||||||
|  |         if (level === "INFO") { | ||||||
|  |             console.info(formattedMessage); | ||||||
|  |         } | ||||||
|  |         else if (level === "WARN") { | ||||||
|  |             console.warn(formattedMessage); | ||||||
|  |         } | ||||||
|  |         else if (level === "ERROR") { | ||||||
|  |             console.error(formattedMessage); | ||||||
|  |         } | ||||||
|  |         else if (level === "DEBUG") { | ||||||
|  |             if (exports.isDev) { | ||||||
|  |                 console.debug(formattedMessage); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             console.log(formattedMessage); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     info(module, msg) { | ||||||
|  |         this.log(module, msg, "info"); | ||||||
|  |     } | ||||||
|  |     warn(module, msg) { | ||||||
|  |         this.log(module, msg, "warn"); | ||||||
|  |     } | ||||||
|  |     error(module, msg) { | ||||||
|  |         this.log(module, msg, "error"); | ||||||
|  |     } | ||||||
|  |     debug(module, msg) { | ||||||
|  |         this.log(module, msg, "debug"); | ||||||
|  |     } | ||||||
|  |     exception(module, exception, msg) { | ||||||
|  |         let finalMessage = exception; | ||||||
|  |         if (msg) { | ||||||
|  |             finalMessage = `${msg}: ${exception}`; | ||||||
|  |         } | ||||||
|  |         this.log(module, finalMessage, "error"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | exports.log = new Logger(); | ||||||
| function polyfill() { | function polyfill() { | ||||||
|     /** |     /** | ||||||
|      * String.prototype.replaceAll() polyfill |      * String.prototype.replaceAll() polyfill | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								src/util.ts
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								src/util.ts
									
									
									
									
									
								
							|  | @ -49,12 +49,66 @@ export function ucfirst(str: string) { | ||||||
|     return firstLetter.toUpperCase() + str.substr(1); |     return firstLetter.toUpperCase() + str.substr(1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * @deprecated Use log.debug | ||||||
|  |  * @since https://github.com/louislam/uptime-kuma/pull/910
 | ||||||
|  |  * @param msg | ||||||
|  |  */ | ||||||
| export function debug(msg: any) { | export function debug(msg: any) { | ||||||
|     if (isDev) { |     log.log("", msg, "debug"); | ||||||
|         console.log(msg); | } | ||||||
|  | 
 | ||||||
|  | class Logger { | ||||||
|  |     log(module: string, msg: any, level: string) { | ||||||
|  |         module = module.toUpperCase(); | ||||||
|  |         level = level.toUpperCase(); | ||||||
|  | 
 | ||||||
|  |         const now = new Date().toISOString(); | ||||||
|  |         const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg; | ||||||
|  | 
 | ||||||
|  |         if (level === "INFO") { | ||||||
|  |             console.info(formattedMessage); | ||||||
|  |         } else if (level === "WARN") { | ||||||
|  |             console.warn(formattedMessage); | ||||||
|  |         } else if (level === "ERROR") { | ||||||
|  |             console.error(formattedMessage); | ||||||
|  |         } else if (level === "DEBUG") { | ||||||
|  |             if (isDev) { | ||||||
|  |                 console.debug(formattedMessage); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             console.log(formattedMessage); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     info(module: string, msg: any) { | ||||||
|  |         this.log(module, msg, "info"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     warn(module: string, msg: any) { | ||||||
|  |         this.log(module, msg, "warn"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |    error(module: string, msg: any) { | ||||||
|  |        this.log(module, msg, "error"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |    debug(module: string, msg: any) { | ||||||
|  |        this.log(module, msg, "debug"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     exception(module: string, exception: any, msg: any) { | ||||||
|  |         let finalMessage = exception | ||||||
|  | 
 | ||||||
|  |         if (msg) { | ||||||
|  |             finalMessage = `${msg}: ${exception}` | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.log(module, finalMessage , "error"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const log = new Logger(); | ||||||
| 
 | 
 | ||||||
| declare global { interface String { replaceAll(str: string, newStr: string): string; } } | declare global { interface String { replaceAll(str: string, newStr: string): string; } } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| const { genSecret, sleep, DOWN } = require("../src/util"); | const { genSecret, DOWN } = require("../src/util"); | ||||||
| const utilServerRewire = require("../server/util-server"); | const utilServerRewire = require("../server/util-server"); | ||||||
| const Discord = require("../server/notification-providers/discord"); | const Discord = require("../server/notification-providers/discord"); | ||||||
| const axios = require("axios"); | const axios = require("axios"); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| // eslint-disable-next-line no-unused-vars
 | // eslint-disable-next-line no-unused-vars
 | ||||||
| const { Page, Browser } = require("puppeteer"); | const { Page, Browser } = require("puppeteer"); | ||||||
| const { sleep } = require("../src/util"); | const { sleep } = require("../src/util"); | ||||||
| const axios = require("axios"); |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Set back the correct data type for page object |  * Set back the correct data type for page object | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue