mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
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
|
||||||
|
|
|
@ -3,4 +3,4 @@ module.exports = {
|
||||||
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,8 +21,7 @@ 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("/")) {
|
||||||
|
@ -43,10 +42,6 @@ class Bark extends NotificationProvider {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add additional parameter for better on device styles (iOS 15 optimized)
|
// add additional parameter for better on device styles (iOS 15 optimized)
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
|
56
src/util.ts
56
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) {
|
||||||
|
log.log("", msg, "debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
if (isDev) {
|
||||||
console.log(msg);
|
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