Merge remote-tracking branch 'origin/master' into exe

This commit is contained in:
Louis Lam 2022-10-07 20:43:53 +08:00
commit e741e7582d
10 changed files with 172 additions and 22 deletions

41
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.18.2", "version": "1.18.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.18.2", "version": "1.18.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@louislam/sqlite3": "~15.0.6", "@louislam/sqlite3": "~15.0.6",
@ -33,6 +33,7 @@
"http-proxy-agent": "^5.0.0", "http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"jsesc": "^3.0.2",
"jsonwebtoken": "~8.5.1", "jsonwebtoken": "~8.5.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"limiter": "^2.1.0", "limiter": "^2.1.0",
@ -474,6 +475,18 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/generator/node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true,
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/helper-annotate-as-pure": { "node_modules/@babel/helper-annotate-as-pure": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
@ -11229,15 +11242,14 @@
"dev": true "dev": true
}, },
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "2.5.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
"dev": true,
"bin": { "bin": {
"jsesc": "bin/jsesc" "jsesc": "bin/jsesc"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=6"
} }
}, },
"node_modules/json-parse-even-better-errors": { "node_modules/json-parse-even-better-errors": {
@ -16957,6 +16969,14 @@
"@babel/types": "^7.18.13", "@babel/types": "^7.18.13",
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
"jsesc": "^2.5.1" "jsesc": "^2.5.1"
},
"dependencies": {
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
}
} }
}, },
"@babel/helper-annotate-as-pure": { "@babel/helper-annotate-as-pure": {
@ -25010,10 +25030,9 @@
} }
}, },
"jsesc": { "jsesc": {
"version": "2.5.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="
"dev": true
}, },
"json-parse-even-better-errors": { "json-parse-even-better-errors": {
"version": "2.3.1", "version": "2.3.1",

View file

@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.18.2", "version": "1.18.3",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@ -23,9 +23,9 @@
"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": "node test/prepare-test-server.js && npm run jest-backend",
"test-with-build": "npm run build && npm test", "test-with-build": "npm run build && npm test",
"jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js", "jest-backend": "cross-env TEST_BACKEND=1 jest --runInBand --detectOpenHandles --forceExit --config=./config/jest-backend.config.js",
"tsc": "tsc", "tsc": "tsc",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
"build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine", "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",
@ -38,7 +38,7 @@
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.18.2 && npm ci --production && npm run download-dist", "setup": "git checkout 1.18.3 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",
@ -88,6 +88,7 @@
"http-proxy-agent": "^5.0.0", "http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"jsesc": "^3.0.2",
"jsonwebtoken": "~8.5.1", "jsonwebtoken": "~8.5.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"limiter": "^2.1.0", "limiter": "^2.1.0",

View file

@ -2,6 +2,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const cheerio = require("cheerio"); const cheerio = require("cheerio");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const jsesc = require("jsesc");
class StatusPage extends BeanModel { class StatusPage extends BeanModel {
@ -56,13 +57,19 @@ class StatusPage extends BeanModel {
head.append(`<meta property="og:description" content="${description155}" />`); head.append(`<meta property="og:description" content="${description155}" />`);
// Preload data // Preload data
const json = JSON.stringify(await StatusPage.getStatusPageData(statusPage)); // Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
head.append(` const escapedJSONObject = jsesc(JSON.stringify(await StatusPage.getStatusPageData(statusPage)), {
<script> "isScriptContext": true
window.preloadData = ${json} });
const script = $(`
<script id="preload-data" data-json="{}">
window.preloadData = ${escapedJSONObject};
</script> </script>
`); `);
head.append(script);
// manifest.json // manifest.json
$("link[rel=manifest]").attr("href", `/api/status-page/${statusPage.slug}/manifest.json`); $("link[rel=manifest]").attr("href", `/api/status-page/${statusPage.slug}/manifest.json`);

View file

@ -0,0 +1,24 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class FreeMobile extends NotificationProvider {
name = "FreeMobile";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
await axios.post(`https://smsapi.free-mobile.fr/sendmsg?msg=${encodeURIComponent(msg.replace("🔴", "⛔️"))}`, {
"user": notification.freemobileUser,
"pass": notification.freemobilePass,
});
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = FreeMobile;

View file

@ -9,7 +9,7 @@ class Ntfy extends NotificationProvider {
let okMsg = "Sent Successfully."; let okMsg = "Sent Successfully.";
try { try {
let headers = {}; let headers = {};
if (notification.ntfyusername.length > 0) { if (notification.ntfyusername) {
headers = { headers = {
"Authorization": "Basic " + Buffer.from(notification.ntfyusername + ":" + notification.ntfypassword).toString("base64"), "Authorization": "Basic " + Buffer.from(notification.ntfyusername + ":" + notification.ntfypassword).toString("base64"),
}; };

View file

@ -9,6 +9,7 @@ const ClickSendSMS = require("./notification-providers/clicksendsms");
const DingDing = require("./notification-providers/dingding"); const DingDing = require("./notification-providers/dingding");
const Discord = require("./notification-providers/discord"); const Discord = require("./notification-providers/discord");
const Feishu = require("./notification-providers/feishu"); const Feishu = require("./notification-providers/feishu");
const FreeMobile = require("./notification-providers/freemobile");
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 Gotify = require("./notification-providers/gotify"); const Gotify = require("./notification-providers/gotify");
@ -63,6 +64,7 @@ class Notification {
new DingDing(), new DingDing(),
new Discord(), new Discord(),
new Feishu(), new Feishu(),
new FreeMobile(),
new GoogleChat(), new GoogleChat(),
new Gorush(), new Gorush(),
new Gotify(), new Gotify(),

View file

@ -138,7 +138,9 @@ class UptimeKumaServer {
} }
if (await Settings.get("trustProxy")) { if (await Settings.get("trustProxy")) {
return socket.client.conn.request.headers["x-forwarded-for"] const forwardedFor = socket.client.conn.request.headers["x-forwarded-for"];
return (typeof forwardedFor === "string" ? forwardedFor.split(",")[0].trim() : null)
|| socket.client.conn.request.headers["x-real-ip"] || socket.client.conn.request.headers["x-real-ip"]
|| clientIP.replace(/^.*:/, ""); || clientIP.replace(/^.*:/, "");
} else { } else {

View file

@ -0,0 +1,12 @@
<template>
<div class="mb-3">
<label for="freemobileUser" class="form-label">{{ $t("Free Mobile User Identifier") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="freemobileUser" v-model="$parent.notification.freemobileUser" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label for="freemobilePass" class="form-label">{{ $t("Free Mobile API Key") }}<span style="color: red;"><sup>*</sup></span></label>
<input id="freemobilePass" v-model="$parent.notification.freemobilePass" type="text" class="form-control" required>
</div>
</template>

View file

@ -7,6 +7,7 @@ import ClickSendSMS from "./ClickSendSMS.vue";
import DingDing from "./DingDing.vue"; import DingDing from "./DingDing.vue";
import Discord from "./Discord.vue"; import Discord from "./Discord.vue";
import Feishu from "./Feishu.vue"; import Feishu from "./Feishu.vue";
import FreeMobile from "./FreeMobile.vue";
import GoogleChat from "./GoogleChat.vue"; import GoogleChat from "./GoogleChat.vue";
import Gorush from "./Gorush.vue"; import Gorush from "./Gorush.vue";
import Gotify from "./Gotify.vue"; import Gotify from "./Gotify.vue";
@ -56,6 +57,7 @@ const NotificationFormList = {
"DingDing": DingDing, "DingDing": DingDing,
"discord": Discord, "discord": Discord,
"Feishu": Feishu, "Feishu": Feishu,
"FreeMobile": FreeMobile,
"GoogleChat": GoogleChat, "GoogleChat": GoogleChat,
"gorush": Gorush, "gorush": Gorush,
"gotify": Gotify, "gotify": Gotify,

View file

@ -1,7 +1,11 @@
const { genSecret, DOWN } = require("../src/util"); const { genSecret, DOWN, log} = 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");
const { UptimeKumaServer } = require("../server/uptime-kuma-server");
const Database = require("../server/database");
const {Settings} = require("../server/settings");
const fs = require("fs");
jest.mock("axios"); jest.mock("axios");
@ -225,3 +229,80 @@ describe("The function filterAndJoin", () => {
expect(result).toBe(""); expect(result).toBe("");
}); });
}); });
describe("Test uptimeKumaServer.getClientIP()", () => {
it("should able to get a correct client IP", async () => {
Database.init({
"data-dir": "./data/test"
});
if (! fs.existsSync(Database.path)) {
log.info("server", "Copying Database");
fs.copyFileSync(Database.templatePath, Database.path);
}
await Database.connect(true);
await Database.patch();
const fakeSocket = {
client: {
conn: {
remoteAddress: "192.168.10.10",
request: {
headers: {
}
}
}
}
}
const server = Object.create(UptimeKumaServer.prototype);
let ip = await server.getClientIP(fakeSocket);
await Settings.set("trustProxy", false);
expect(await Settings.get("trustProxy")).toBe(false);
expect(ip).toBe("192.168.10.10");
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "10.10.10.10";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("192.168.10.10");
fakeSocket.client.conn.request.headers["x-real-ip"] = "20.20.20.20";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("192.168.10.10");
await Settings.set("trustProxy", true);
expect(await Settings.get("trustProxy")).toBe(true);
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "10.10.10.10";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("10.10.10.10");
// x-real-ip
delete fakeSocket.client.conn.request.headers["x-forwarded-for"];
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("20.20.20.20");
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("2001:db8:85a3:8d3:1319:8a2e:370:7348");
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("203.0.113.195");
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("203.0.113.195");
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("203.0.113.195");
// Elements are comma-separated, with optional whitespace surrounding the commas.
fakeSocket.client.conn.request.headers["x-forwarded-for"] = "203.0.113.195 , 2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178";
ip = await server.getClientIP(fakeSocket);
expect(ip).toBe("203.0.113.195");
await Database.close();
}, 120000);
});