mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-17 17:06:56 +08:00
Compare commits
13 Commits
api-via-so
...
1.23.3
Author | SHA1 | Date | |
---|---|---|---|
|
c3e3f27457 | ||
|
794f1810bf | ||
|
168357d93c | ||
|
476deb9fec | ||
|
a36f2a75ca | ||
|
88afab6571 | ||
|
bd9c44cccf | ||
|
1b148786a5 | ||
|
66a10b8993 | ||
|
2ab21ccf8a | ||
|
90d0e8ccde | ||
|
16a396debb | ||
|
6b3d69e1d3 |
@@ -38,6 +38,10 @@ tsconfig.json
|
|||||||
/extra/push-examples
|
/extra/push-examples
|
||||||
/extra/uptime-kuma-push
|
/extra/uptime-kuma-push
|
||||||
|
|
||||||
|
# Comment the following line if you want to rebuild the healthcheck binary
|
||||||
|
/extra/healthcheck-armv7
|
||||||
|
|
||||||
|
|
||||||
### .gitignore content (commented rules are duplicated)
|
### .gitignore content (commented rules are duplicated)
|
||||||
|
|
||||||
#node_modules
|
#node_modules
|
||||||
|
8
.github/workflows/auto-test.yml
vendored
8
.github/workflows/auto-test.yml
vendored
@@ -5,11 +5,11 @@ name: Auto Test
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master, 1.23.X ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '*.md'
|
- '*.md'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master, 2.0.X ]
|
branches: [ master, 1.23.X ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '*.md'
|
- '*.md'
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- run: npm install npm@latest -g
|
- run: npm install npm@9 -g
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm test
|
- run: npm test
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- run: npm install npm@latest -g
|
- run: npm install npm@9 -g
|
||||||
- run: npm ci --production
|
- run: npm ci --production
|
||||||
|
|
||||||
check-linters:
|
check-linters:
|
||||||
|
10
db/patch-notification-config.sql
Normal file
10
db/patch-notification-config.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
-- SQLite: Change the data type of the column "config" from VARCHAR to TEXT
|
||||||
|
ALTER TABLE notification RENAME COLUMN config TO config_old;
|
||||||
|
ALTER TABLE notification ADD COLUMN config TEXT;
|
||||||
|
UPDATE notification SET config = config_old;
|
||||||
|
ALTER TABLE notification DROP COLUMN config_old;
|
||||||
|
|
||||||
|
COMMIT;
|
1462
package-lock.json
generated
1462
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uptime-kuma",
|
"name": "uptime-kuma",
|
||||||
"version": "1.23.2",
|
"version": "1.23.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -40,7 +40,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.23.2 && npm ci --production && npm run download-dist",
|
"setup": "git checkout 1.23.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",
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"express-basic-auth": "~1.2.1",
|
"express-basic-auth": "~1.2.1",
|
||||||
"express-static-gzip": "~2.1.7",
|
"express-static-gzip": "~2.1.7",
|
||||||
"form-data": "~4.0.0",
|
"form-data": "~4.0.0",
|
||||||
"gamedig": "~4.0.5",
|
"gamedig": "~4.1.0",
|
||||||
"http-graceful-shutdown": "~3.1.7",
|
"http-graceful-shutdown": "~3.1.7",
|
||||||
"http-proxy-agent": "~5.0.0",
|
"http-proxy-agent": "~5.0.0",
|
||||||
"https-proxy-agent": "~5.0.1",
|
"https-proxy-agent": "~5.0.1",
|
||||||
|
@@ -81,6 +81,7 @@ class Database {
|
|||||||
"patch-monitor-oauth-cc.sql": true,
|
"patch-monitor-oauth-cc.sql": true,
|
||||||
"patch-add-timeout-monitor.sql": true,
|
"patch-add-timeout-monitor.sql": true,
|
||||||
"patch-add-gamedig-given-port.sql": true,
|
"patch-add-gamedig-given-port.sql": true,
|
||||||
|
"patch-notification-config.sql": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -53,7 +53,7 @@ class Monitor extends BeanModel {
|
|||||||
obj.tags = await this.getTags();
|
obj.tags = await this.getTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (certExpiry && this.type === "http" && this.getURLProtocol() === "https:") {
|
if (certExpiry && (this.type === "http" || this.type === "keyword" || this.type === "json-query") && this.getURLProtocol() === "https:") {
|
||||||
const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id);
|
const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id);
|
||||||
obj.certExpiryDaysRemaining = certExpiryDaysRemaining;
|
obj.certExpiryDaysRemaining = certExpiryDaysRemaining;
|
||||||
obj.validCert = validCert;
|
obj.validCert = validCert;
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||||
const passwordHash = require("../password-hash");
|
const passwordHash = require("../password-hash");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
|
const jwt = require("jsonwebtoken");
|
||||||
|
const { shake256, SHAKE256_LENGTH } = require("../util-server");
|
||||||
|
|
||||||
class User extends BeanModel {
|
class User extends BeanModel {
|
||||||
/**
|
/**
|
||||||
@@ -27,6 +29,19 @@ class User extends BeanModel {
|
|||||||
this.password = newPassword;
|
this.password = newPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new JWT for a user
|
||||||
|
* @param {User} user
|
||||||
|
* @param {string} jwtSecret
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
static createJWT(user, jwtSecret) {
|
||||||
|
return jwt.sign({
|
||||||
|
username: user.username,
|
||||||
|
h: shake256(user.password, SHAKE256_LENGTH),
|
||||||
|
}, jwtSecret);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = User;
|
module.exports = User;
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
let express = require("express");
|
let express = require("express");
|
||||||
const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin, sendHttpError } = require("../util-server");
|
const {
|
||||||
|
setting,
|
||||||
|
allowDevAllOrigin,
|
||||||
|
allowAllOrigin,
|
||||||
|
percentageToColor,
|
||||||
|
filterAndJoin,
|
||||||
|
sendHttpError,
|
||||||
|
} = require("../util-server");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const apicache = require("../modules/apicache");
|
const apicache = require("../modules/apicache");
|
||||||
const Monitor = require("../model/monitor");
|
const Monitor = require("../model/monitor");
|
||||||
@@ -22,10 +29,14 @@ router.get("/api/entry-page", async (request, response) => {
|
|||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
|
|
||||||
let result = { };
|
let result = { };
|
||||||
|
let hostname = request.hostname;
|
||||||
|
if ((await setting("trustProxy")) && request.headers["x-forwarded-host"]) {
|
||||||
|
hostname = request.headers["x-forwarded-host"];
|
||||||
|
}
|
||||||
|
|
||||||
if (request.hostname in StatusPage.domainMappingList) {
|
if (hostname in StatusPage.domainMappingList) {
|
||||||
result.type = "statusPageMatchedDomain";
|
result.type = "statusPageMatchedDomain";
|
||||||
result.statusPageSlug = StatusPage.domainMappingList[request.hostname];
|
result.statusPageSlug = StatusPage.domainMappingList[hostname];
|
||||||
} else {
|
} else {
|
||||||
result.type = "entryPage";
|
result.type = "entryPage";
|
||||||
result.entryPage = server.entryPage;
|
result.entryPage = server.entryPage;
|
||||||
|
@@ -83,8 +83,11 @@ const app = server.app;
|
|||||||
log.info("server", "Importing this project modules");
|
log.info("server", "Importing this project modules");
|
||||||
log.debug("server", "Importing Monitor");
|
log.debug("server", "Importing Monitor");
|
||||||
const Monitor = require("./model/monitor");
|
const Monitor = require("./model/monitor");
|
||||||
|
const User = require("./model/user");
|
||||||
|
|
||||||
log.debug("server", "Importing Settings");
|
log.debug("server", "Importing Settings");
|
||||||
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests } = require("./util-server");
|
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests, shake256, SHAKE256_LENGTH
|
||||||
|
} = require("./util-server");
|
||||||
|
|
||||||
log.debug("server", "Importing Notification");
|
log.debug("server", "Importing Notification");
|
||||||
const { Notification } = require("./notification");
|
const { Notification } = require("./notification");
|
||||||
@@ -296,6 +299,11 @@ let needSetup = false;
|
|||||||
decoded.username,
|
decoded.username,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Check if the password changed
|
||||||
|
if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) {
|
||||||
|
throw new Error("The token is invalid due to password change or old token");
|
||||||
|
}
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
log.debug("auth", "afterLogin");
|
log.debug("auth", "afterLogin");
|
||||||
afterLogin(socket, user);
|
afterLogin(socket, user);
|
||||||
@@ -316,9 +324,10 @@ let needSetup = false;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
log.error("auth", `Invalid token. IP=${clientIP}`);
|
log.error("auth", `Invalid token. IP=${clientIP}`);
|
||||||
|
if (error.message) {
|
||||||
|
log.error("auth", error.message, `IP=${clientIP}`);
|
||||||
|
}
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: "Invalid token.",
|
msg: "Invalid token.",
|
||||||
@@ -357,9 +366,7 @@ let needSetup = false;
|
|||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
token: jwt.sign({
|
token: User.createJWT(user, server.jwtSecret),
|
||||||
username: data.username,
|
|
||||||
}, server.jwtSecret),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,9 +394,7 @@ let needSetup = false;
|
|||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
token: jwt.sign({
|
token: User.createJWT(user, server.jwtSecret),
|
||||||
username: data.username,
|
|
||||||
}, server.jwtSecret),
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@@ -33,6 +33,7 @@ const dayjs = require("dayjs");
|
|||||||
// SASLOptions used in JSDoc
|
// SASLOptions used in JSDoc
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { Kafka, SASLOptions } = require("kafkajs");
|
const { Kafka, SASLOptions } = require("kafkajs");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
const isWindows = process.platform === /^win/.test(process.platform);
|
const isWindows = process.platform === /^win/.test(process.platform);
|
||||||
/**
|
/**
|
||||||
@@ -286,22 +287,22 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
|
|||||||
|
|
||||||
producer.connect().then(
|
producer.connect().then(
|
||||||
() => {
|
() => {
|
||||||
try {
|
|
||||||
producer.send({
|
producer.send({
|
||||||
topic: topic,
|
topic: topic,
|
||||||
messages: [{
|
messages: [{
|
||||||
value: message,
|
value: message,
|
||||||
}],
|
}],
|
||||||
});
|
}).then((_) => {
|
||||||
connectedToKafka = true;
|
|
||||||
clearTimeout(timeoutID);
|
|
||||||
resolve("Message sent successfully");
|
resolve("Message sent successfully");
|
||||||
} catch (e) {
|
}).catch((e) => {
|
||||||
connectedToKafka = true;
|
connectedToKafka = true;
|
||||||
producer.disconnect();
|
producer.disconnect();
|
||||||
clearTimeout(timeoutID);
|
clearTimeout(timeoutID);
|
||||||
reject(new Error("Error sending message: " + e.message));
|
reject(new Error("Error sending message: " + e.message));
|
||||||
}
|
}).finally(() => {
|
||||||
|
connectedToKafka = true;
|
||||||
|
clearTimeout(timeoutID);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
).catch(
|
).catch(
|
||||||
(e) => {
|
(e) => {
|
||||||
@@ -313,8 +314,10 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
|
|||||||
);
|
);
|
||||||
|
|
||||||
producer.on("producer.network.request_timeout", (_) => {
|
producer.on("producer.network.request_timeout", (_) => {
|
||||||
|
if (!connectedToKafka) {
|
||||||
clearTimeout(timeoutID);
|
clearTimeout(timeoutID);
|
||||||
reject(new Error("producer.network.request_timeout"));
|
reject(new Error("producer.network.request_timeout"));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
producer.on("producer.disconnect", (_) => {
|
producer.on("producer.disconnect", (_) => {
|
||||||
@@ -1053,6 +1056,23 @@ module.exports.grpcQuery = async (options) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.SHAKE256_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} data
|
||||||
|
* @param {number} len
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
module.exports.shake256 = (data, len) => {
|
||||||
|
if (!data) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return crypto.createHash("shake256", { outputLength: len })
|
||||||
|
.update(data)
|
||||||
|
.digest("hex");
|
||||||
|
};
|
||||||
|
|
||||||
// For unit test, export functions
|
// For unit test, export functions
|
||||||
if (process.env.TEST_BACKEND) {
|
if (process.env.TEST_BACKEND) {
|
||||||
module.exports.__test = {
|
module.exports.__test = {
|
||||||
|
@@ -880,6 +880,7 @@ const monitorDefaults = {
|
|||||||
kafkaProducerSaslOptions: {
|
kafkaProducerSaslOptions: {
|
||||||
mechanism: "None",
|
mechanism: "None",
|
||||||
},
|
},
|
||||||
|
kafkaProducerSsl: false,
|
||||||
gamedigGivenPortOnly: true,
|
gamedigGivenPortOnly: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -438,7 +438,7 @@ export default {
|
|||||||
lastUpdateTime: dayjs(),
|
lastUpdateTime: dayjs(),
|
||||||
updateCountdown: null,
|
updateCountdown: null,
|
||||||
updateCountdownText: null,
|
updateCountdownText: null,
|
||||||
loading: false,
|
loading: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -698,6 +698,8 @@ export default {
|
|||||||
this.incident = res.data.incident;
|
this.incident = res.data.incident;
|
||||||
this.maintenanceList = res.data.maintenanceList;
|
this.maintenanceList = res.data.maintenanceList;
|
||||||
this.$root.publicGroupList = res.data.publicGroupList;
|
this.$root.publicGroupList = res.data.publicGroupList;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
}).catch( function (error) {
|
}).catch( function (error) {
|
||||||
if (error.response.status === 404) {
|
if (error.response.status === 404) {
|
||||||
location.href = "/page-not-found";
|
location.href = "/page-not-found";
|
||||||
|
Reference in New Issue
Block a user