Compare commits

...

24 Commits

Author SHA1 Message Date
Louis Lam
c3e3f27457 Update to 1.23.3 2023-10-09 20:26:18 +08:00
Louis Lam
794f1810bf Minor 2023-10-09 20:09:29 +08:00
Louis Lam
168357d93c Update dependencies 2023-10-09 20:05:50 +08:00
Louis Lam
476deb9fec Pin npm@9 2023-10-09 07:33:52 +08:00
Louis Lam
a36f2a75ca Enable auto-test for 1.23.X branch 2023-10-09 07:23:30 +08:00
Louis Lam
88afab6571 Merge pull request from GHSA-g9v2-wqcj-j99g
* Fix attempt

* Update message
2023-10-09 07:01:54 +08:00
Nelson Chan
bd9c44cccf Fix: Disable status page saving before getData 2023-10-09 06:41:07 +08:00
前端小武
1b148786a5 Fix: Update x-forwarded-host field when using reverse proxy (#3726) 2023-10-09 06:31:52 +08:00
Louis Lam
66a10b8993 Pump gamedig from 4.0.X to 4.1.X and update dependencies 2023-10-03 04:51:02 +08:00
Muhammed Hussein karimi
2ab21ccf8a 🐛 fix: kafka producer bugs (#3771)
* 🐛 fix: missing Kafka Producer SSL option in frontend object

Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev>

* ♻️  refactor: better error handling of kafka producer

Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev>

---------

Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev>
2023-09-24 03:30:15 +08:00
Marvin A. Ruder
90d0e8ccde Enable status page certificate expiry badge for all HTTP(s) monitors (#3649) 2023-09-24 03:18:18 +08:00
Louis Lam
16a396debb Similar to #3763, but for 1.23.3 2023-09-21 20:38:54 +08:00
Louis Lam
6b3d69e1d3 Ignore /extra/healthcheck-armv7 2023-09-18 03:59:56 +08:00
Louis Lam
b3b8e9f3a0 Update to 1.23.2 2023-09-18 03:04:29 +08:00
Louis Lam
e5345848a2 Update dependencies 2023-09-18 03:01:58 +08:00
FC (Fay) Stegerman
d8a8f6c08b EditMonitor: add missing config UI for json-query (#3751) 2023-09-17 20:39:07 +08:00
Louis Lam
f98a1ce077 Ignore some build files 2023-09-17 20:32:49 +08:00
Louis Lam
86fa57449e Fix #3596 2023-09-17 02:56:19 +08:00
Louis Lam
ff51704cdf Fix #3712 2023-09-17 02:40:08 +08:00
Henrik Gerdes
33804d8823 fix: respect the user defined oauth2 auth method (#3727) 2023-09-16 05:13:20 +08:00
Nelson Chan
1e12ca4786 Fix: Disable save button on submit (#3729) 2023-09-16 05:11:18 +08:00
Nelson Chan
0af4ee6c34 Fix: Missing await for isActive (#3717) 2023-09-10 01:54:03 +08:00
ZaneL1u
1f29fabe64 fix: fix the judgment condition of Tailscale (#3701) 2023-09-07 16:15:21 +08:00
Louis Lam
c4e222d1e6 Update README.md 2023-08-29 20:49:03 +08:00
16 changed files with 1572 additions and 1280 deletions

View File

@@ -34,7 +34,12 @@ tsconfig.json
/ecosystem.config.js /ecosystem.config.js
/extra/healthcheck.exe /extra/healthcheck.exe
/extra/healthcheck /extra/healthcheck
extra/exe-builder /extra/exe-builder
/extra/push-examples
/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)

View File

@@ -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:

View File

@@ -93,7 +93,7 @@ pm2 save && pm2 startup
### Windows Portable (x64) ### Windows Portable (x64)
https://github.com/louislam/uptime-kuma/files/11886108/uptime-kuma-win64-portable-1.0.1.zip https://github.com/louislam/uptime-kuma/releases/download/1.23.1/uptime-kuma-windows-x64-portable-1.23.1.zip
### Advanced Installation ### Advanced Installation

View 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;

2666
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.23.1", "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.1 && 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",
@@ -56,6 +56,7 @@
"test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .", "test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
"simple-dns-server": "node extra/simple-dns-server.js", "simple-dns-server": "node extra/simple-dns-server.js",
"simple-mqtt-server": "node extra/simple-mqtt-server.js", "simple-mqtt-server": "node extra/simple-mqtt-server.js",
"simple-mongo": "docker run --rm -p 27017:27017 mongo",
"update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix", "update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix",
"ncu-patch": "npm-check-updates -u -t patch", "ncu-patch": "npm-check-updates -u -t patch",
"release-final": "node ./extra/test-docker.js && node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js", "release-final": "node ./extra/test-docker.js && node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js",
@@ -95,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",
@@ -108,7 +109,7 @@
"kafkajs": "^2.2.4", "kafkajs": "^2.2.4",
"limiter": "~2.1.0", "limiter": "~2.1.0",
"liquidjs": "^10.7.0", "liquidjs": "^10.7.0",
"mongodb": "~4.14.0", "mongodb": "~4.17.1",
"mqtt": "~4.3.7", "mqtt": "~4.3.7",
"mssql": "~8.1.4", "mssql": "~8.1.4",
"mysql2": "~2.3.3", "mysql2": "~2.3.3",
@@ -160,7 +161,7 @@
"core-js": "~3.26.1", "core-js": "~3.26.1",
"cronstrue": "~2.24.0", "cronstrue": "~2.24.0",
"cross-env": "~7.0.3", "cross-env": "~7.0.3",
"cypress": "^12.17.0", "cypress": "^13.2.0",
"delay": "^5.0.0", "delay": "^5.0.0",
"dns2": "~2.0.1", "dns2": "~2.0.1",
"dompurify": "~2.4.3", "dompurify": "~2.4.3",

View File

@@ -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,
}; };
/** /**

View File

@@ -53,7 +53,7 @@ class Monitor extends BeanModel {
obj.tags = await this.getTags(); obj.tags = await this.getTags();
} }
if (certExpiry && this.type === "http") { 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;
@@ -1075,6 +1075,19 @@ class Monitor extends BeanModel {
} }
} }
/**
* Example: http: or https:
* @returns {(null|string)}
*/
getURLProtocol() {
const url = this.getUrl();
if (url) {
return this.getUrl().protocol;
} else {
return null;
}
}
/** /**
* Store TLS info to database * Store TLS info to database
* @param checkCertificateResult * @param checkCertificateResult

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {
@@ -726,11 +731,11 @@ let needSetup = false;
bean.basic_auth_user = monitor.basic_auth_user; bean.basic_auth_user = monitor.basic_auth_user;
bean.basic_auth_pass = monitor.basic_auth_pass; bean.basic_auth_pass = monitor.basic_auth_pass;
bean.timeout = monitor.timeout; bean.timeout = monitor.timeout;
bean.oauth_client_id = monitor.oauth_client_id, bean.oauth_client_id = monitor.oauth_client_id;
bean.oauth_client_secret = monitor.oauth_client_secret, bean.oauth_client_secret = monitor.oauth_client_secret;
bean.oauth_auth_method = this.oauth_auth_method, bean.oauth_auth_method = monitor.oauth_auth_method;
bean.oauth_token_url = monitor.oauth_token_url, bean.oauth_token_url = monitor.oauth_token_url;
bean.oauth_scopes = monitor.oauth_scopes, bean.oauth_scopes = monitor.oauth_scopes;
bean.tlsCa = monitor.tlsCa; bean.tlsCa = monitor.tlsCa;
bean.tlsCert = monitor.tlsCert; bean.tlsCert = monitor.tlsCert;
bean.tlsKey = monitor.tlsKey; bean.tlsKey = monitor.tlsKey;
@@ -796,7 +801,7 @@ let needSetup = false;
await updateMonitorNotification(bean.id, monitor.notificationIDList); await updateMonitorNotification(bean.id, monitor.notificationIDList);
if (bean.isActive()) { if (await bean.isActive()) {
await restartMonitor(socket.userID, bean.id); await restartMonitor(socket.userID, bean.id);
} }

View File

@@ -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", (_) => {
clearTimeout(timeoutID); if (!connectedToKafka) {
reject(new Error("producer.network.request_timeout")); clearTimeout(timeoutID);
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 = {

View File

@@ -11,7 +11,7 @@
/> />
</div> </div>
<div <div
v-if="size !== 'small' && beatList.length > 4 && $root.styleElapsedTime !== 'none'" v-if="!$root.isMobile && size !== 'small' && beatList.length > 4 && $root.styleElapsedTime !== 'none'"
class="d-flex justify-content-between align-items-center word" :style="timeStyle" class="d-flex justify-content-between align-items-center word" :style="timeStyle"
> >
<div>{{ timeSinceFirstBeat }} ago</div> <div>{{ timeSinceFirstBeat }} ago</div>

View File

@@ -62,7 +62,7 @@
</span> </span>
</div> </div>
<div class="extra-info"> <div class="extra-info">
<div v-if="showCertificateExpiry && monitor.element.type === 'http'"> <div v-if="showCertificateExpiry && monitor.element.certExpiryDaysRemaining">
<Tag :item="{name: $t('Cert Exp.'), value: formattedCertExpiryMessage(monitor), color: certExpiryColor(monitor)}" :size="'sm'" /> <Tag :item="{name: $t('Cert Exp.'), value: formattedCertExpiryMessage(monitor), color: certExpiryColor(monitor)}" :size="'sm'" />
</div> </div>
<div v-if="showTags"> <div v-if="showTags">

View File

@@ -82,7 +82,7 @@
<option value="redis"> <option value="redis">
Redis Redis
</option> </option>
<option v-if="$root.info.isContainer" value="tailscale-ping"> <option v-if="!$root.info.isContainer" value="tailscale-ping">
Tailscale Ping Tailscale Ping
</option> </option>
</optgroup> </optgroup>
@@ -401,7 +401,7 @@
</div> </div>
<!-- Timeout: HTTP / Keyword only --> <!-- Timeout: HTTP / Keyword only -->
<div v-if="monitor.type === 'http' || monitor.type === 'keyword'" class="my-3"> <div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query'" class="my-3">
<label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label> <label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
<input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1"> <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
</div> </div>
@@ -460,7 +460,7 @@
</div> </div>
<!-- HTTP / Keyword only --> <!-- HTTP / Keyword only -->
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'grpc-keyword' "> <template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
<div class="my-3"> <div class="my-3">
<label for="maxRedirects" class="form-label">{{ $t("Max. Redirects") }}</label> <label for="maxRedirects" class="form-label">{{ $t("Max. Redirects") }}</label>
<input id="maxRedirects" v-model="monitor.maxredirects" type="number" class="form-control" required min="0" step="1"> <input id="maxRedirects" v-model="monitor.maxredirects" type="number" class="form-control" required min="0" step="1">
@@ -880,6 +880,7 @@ const monitorDefaults = {
kafkaProducerSaslOptions: { kafkaProducerSaslOptions: {
mechanism: "None", mechanism: "None",
}, },
kafkaProducerSsl: false,
gamedigGivenPortOnly: true, gamedigGivenPortOnly: true,
}; };

View File

@@ -102,7 +102,7 @@
<!-- Sidebar Footer --> <!-- Sidebar Footer -->
<div class="sidebar-footer"> <div class="sidebar-footer">
<button class="btn btn-success me-2" @click="save"> <button class="btn btn-success me-2" :disabled="loading" @click="save">
<font-awesome-icon icon="save" /> <font-awesome-icon icon="save" />
{{ $t("Save") }} {{ $t("Save") }}
</button> </button>
@@ -438,6 +438,7 @@ export default {
lastUpdateTime: dayjs(), lastUpdateTime: dayjs(),
updateCountdown: null, updateCountdown: null,
updateCountdownText: null, updateCountdownText: null,
loading: true,
}; };
}, },
computed: { computed: {
@@ -697,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";
@@ -806,6 +809,7 @@ export default {
/** Save the status page */ /** Save the status page */
save() { save() {
this.loading = true;
let startTime = new Date(); let startTime = new Date();
this.config.slug = this.config.slug.trim().toLowerCase(); this.config.slug = this.config.slug.trim().toLowerCase();
@@ -823,10 +827,12 @@ export default {
} }
setTimeout(() => { setTimeout(() => {
this.loading = false;
location.href = "/status/" + this.config.slug; location.href = "/status/" + this.config.slug;
}, time); }, time);
} else { } else {
this.loading = false;
toast.error(res.msg); toast.error(res.msg);
} }
}); });