mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-30 18:24:03 +00:00
Feat: Add http/http keyword timeout option (#2142)
* feat: Add timeoutMs field
* chore: Update Languages (incl. ko-KR)
* Revert "chore: Update Languages (incl. ko-KR)"
This reverts commit 349331a00b
.
* chore: Update ko-KR selectively
* chore: Update en selectively
* Merge manually
* Reorder and show only if http related monitors
* fix: Update Korean translation
* fix: Rename timeoutMs to timeout, rename label, make DOUBLE
* fix: Change minimum step to 0.1, matching DOUBLE type
* Put the sql patch at the end
* Update EditMonitor.vue
* Colocate timeout with retry, fix clampTimeout logic, show default on 0
* Update src/pages/EditMonitor.vue to remove a comment
Co-authored-by: Frank Elsinga <frank@elsinga.de>
* Fix merge issue
* Update the timeout value while finished editing the interval value
---------
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
parent
c6e68fac97
commit
439b6517d1
7 changed files with 56 additions and 4 deletions
6
db/patch-add-timeout-monitor.sql
Normal file
6
db/patch-add-timeout-monitor.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD timeout DOUBLE default 0 not null;
|
||||||
|
COMMIT;
|
|
@ -79,6 +79,7 @@ class Database {
|
||||||
"patch-added-kafka-producer.sql": true,
|
"patch-added-kafka-producer.sql": true,
|
||||||
"patch-add-certificate-expiry-status-page.sql": true,
|
"patch-add-certificate-expiry-status-page.sql": true,
|
||||||
"patch-monitor-oauth-cc.sql": true,
|
"patch-monitor-oauth-cc.sql": true,
|
||||||
|
"patch-add-timeout-monitor.sql": true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -102,6 +102,7 @@ class Monitor extends BeanModel {
|
||||||
active: await this.isActive(),
|
active: await this.isActive(),
|
||||||
forceInactive: !await Monitor.isParentActive(this.id),
|
forceInactive: !await Monitor.isParentActive(this.id),
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
timeout: this.timeout,
|
||||||
interval: this.interval,
|
interval: this.interval,
|
||||||
retryInterval: this.retryInterval,
|
retryInterval: this.retryInterval,
|
||||||
resendInterval: this.resendInterval,
|
resendInterval: this.resendInterval,
|
||||||
|
@ -428,7 +429,7 @@ class Monitor extends BeanModel {
|
||||||
const options = {
|
const options = {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
method: (this.method || "get").toLowerCase(),
|
method: (this.method || "get").toLowerCase(),
|
||||||
timeout: this.interval * 1000 * 0.8,
|
timeout: this.timeout,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
||||||
"User-Agent": "Uptime-Kuma/" + version,
|
"User-Agent": "Uptime-Kuma/" + version,
|
||||||
|
@ -648,7 +649,7 @@ class Monitor extends BeanModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await axios.get(steamApiUrl, {
|
let res = await axios.get(steamApiUrl, {
|
||||||
timeout: this.interval * 1000 * 0.8,
|
timeout: this.timeout,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"User-Agent": "Uptime-Kuma/" + version,
|
"User-Agent": "Uptime-Kuma/" + version,
|
||||||
|
|
|
@ -716,6 +716,7 @@ let needSetup = false;
|
||||||
bean.headers = monitor.headers;
|
bean.headers = monitor.headers;
|
||||||
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.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 = this.oauth_auth_method,
|
||||||
|
@ -1372,6 +1373,7 @@ let needSetup = false;
|
||||||
|
|
||||||
// Define default values
|
// Define default values
|
||||||
let retryInterval = 0;
|
let retryInterval = 0;
|
||||||
|
let timeout = monitorListData[i].timeout || (monitorListData[i].interval * 0.8); // fallback to old value
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Only replace the default value with the backup file data for the specific version, where it appears the first time
|
Only replace the default value with the backup file data for the specific version, where it appears the first time
|
||||||
|
@ -1397,6 +1399,7 @@ let needSetup = false;
|
||||||
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
||||||
authWorkstation: monitorListData[i].authWorkstation,
|
authWorkstation: monitorListData[i].authWorkstation,
|
||||||
authDomain: monitorListData[i].authDomain,
|
authDomain: monitorListData[i].authDomain,
|
||||||
|
timeout,
|
||||||
interval: monitorListData[i].interval,
|
interval: monitorListData[i].interval,
|
||||||
retryInterval: retryInterval,
|
retryInterval: retryInterval,
|
||||||
resendInterval: monitorListData[i].resendInterval || 0,
|
resendInterval: monitorListData[i].resendInterval || 0,
|
||||||
|
|
|
@ -59,6 +59,8 @@
|
||||||
"Hostname": "Hostname",
|
"Hostname": "Hostname",
|
||||||
"Port": "Port",
|
"Port": "Port",
|
||||||
"Heartbeat Interval": "Heartbeat Interval",
|
"Heartbeat Interval": "Heartbeat Interval",
|
||||||
|
"Request Timeout": "Request Timeout",
|
||||||
|
"timeoutAfter": "Timeout after {0} seconds",
|
||||||
"Retries": "Retries",
|
"Retries": "Retries",
|
||||||
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
|
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
|
||||||
"Resend Notification if Down X times consecutively": "Resend Notification if Down X times consecutively",
|
"Resend Notification if Down X times consecutively": "Resend Notification if Down X times consecutively",
|
||||||
|
|
|
@ -589,6 +589,8 @@
|
||||||
"RadiusCalledStationIdDescription": "접속 스테이션의 식별자",
|
"RadiusCalledStationIdDescription": "접속 스테이션의 식별자",
|
||||||
"RadiusCallingStationId": "접속 요청 스테이션의 Id",
|
"RadiusCallingStationId": "접속 요청 스테이션의 Id",
|
||||||
"RadiusCallingStationIdDescription": "접속 요청 스테이션의 식별자",
|
"RadiusCallingStationIdDescription": "접속 요청 스테이션의 식별자",
|
||||||
|
"timeoutAfter": "{0}초 후 타임아웃",
|
||||||
|
"Request Timeout": "요청 타임아웃",
|
||||||
"Query": "쿼리",
|
"Query": "쿼리",
|
||||||
"settingsCertificateExpiry": "TLS 인증서 만료",
|
"settingsCertificateExpiry": "TLS 인증서 만료",
|
||||||
"certificationExpiryDescription": "HTTPS 모니터링 TLS 인증서가 만료되면 알림을 활성화해요:",
|
"certificationExpiryDescription": "HTTPS 모니터링 TLS 인증서가 만료되면 알림을 활성화해요:",
|
||||||
|
|
|
@ -393,7 +393,7 @@
|
||||||
<!-- Interval -->
|
<!-- Interval -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
||||||
<input id="interval" v-model="monitor.interval" type="number" class="form-control" required :min="minInterval" step="1" :max="maxInterval">
|
<input id="interval" v-model="monitor.interval" type="number" class="form-control" required :min="minInterval" step="1" :max="maxInterval" @blur="finishUpdateInterval">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
|
@ -412,6 +412,12 @@
|
||||||
<input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required :min="minInterval" step="1">
|
<input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required :min="minInterval" step="1">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Timeout: HTTP / Keyword only -->
|
||||||
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword'" class="my-3">
|
||||||
|
<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">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="resend-interval" class="form-label">
|
<label for="resend-interval" class="form-label">
|
||||||
{{ $t("Resend Notification if Down X times consecutively") }}
|
{{ $t("Resend Notification if Down X times consecutively") }}
|
||||||
|
@ -840,6 +846,7 @@ const monitorDefaults = {
|
||||||
retryInterval: 60,
|
retryInterval: 60,
|
||||||
resendInterval: 0,
|
resendInterval: 0,
|
||||||
maxretries: 1,
|
maxretries: 1,
|
||||||
|
timeout: 48,
|
||||||
notificationIDList: {},
|
notificationIDList: {},
|
||||||
ignoreTls: false,
|
ignoreTls: false,
|
||||||
upsideDown: false,
|
upsideDown: false,
|
||||||
|
@ -1113,6 +1120,13 @@ message HealthCheckResponse {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"monitor.timeout"(value, oldValue) {
|
||||||
|
// keep timeout within 80% range
|
||||||
|
if (value && value !== oldValue) {
|
||||||
|
this.monitor.timeout = this.clampTimeout(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"monitor.type"() {
|
"monitor.type"() {
|
||||||
if (this.monitor.type === "push") {
|
if (this.monitor.type === "push") {
|
||||||
if (! this.monitor.pushToken) {
|
if (! this.monitor.pushToken) {
|
||||||
|
@ -1274,6 +1288,10 @@ message HealthCheckResponse {
|
||||||
if (this.monitor.retryInterval === 0) {
|
if (this.monitor.retryInterval === 0) {
|
||||||
this.monitor.retryInterval = this.monitor.interval;
|
this.monitor.retryInterval = this.monitor.interval;
|
||||||
}
|
}
|
||||||
|
// Handling for monitors that are missing/zeroed timeout
|
||||||
|
if (!this.monitor.timeout) {
|
||||||
|
this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(res.msg);
|
toast.error(res.msg);
|
||||||
}
|
}
|
||||||
|
@ -1445,8 +1463,27 @@ message HealthCheckResponse {
|
||||||
addedDraftGroup(draftGroupName) {
|
addedDraftGroup(draftGroupName) {
|
||||||
this.draftGroupName = draftGroupName;
|
this.draftGroupName = draftGroupName;
|
||||||
this.monitor.parent = -1;
|
this.monitor.parent = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clamp timeout
|
||||||
|
clampTimeout(timeout) {
|
||||||
|
// limit to 80% of interval, narrowly avoiding epsilon bug
|
||||||
|
const maxTimeout = ~~(this.monitor.interval * 8 ) / 10;
|
||||||
|
const clamped = Math.max(0, Math.min(timeout, maxTimeout));
|
||||||
|
|
||||||
|
// 0 will be treated as 80% of interval
|
||||||
|
return Number.isFinite(clamped) ? clamped : maxTimeout;
|
||||||
|
},
|
||||||
|
|
||||||
|
finishUpdateInterval() {
|
||||||
|
// Update timeout if it is greater than the clamp timeout
|
||||||
|
let clampedValue = this.clampTimeout(this.monitor.interval);
|
||||||
|
if (this.monitor.timeout > clampedValue) {
|
||||||
|
this.monitor.timeout = clampedValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue