This commit is contained in:
oleksandr.shostak@consultic.com 2026-01-20 06:03:21 +00:00 committed by GitHub
commit c933a1b778
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 6 deletions

View File

@ -1,6 +1,9 @@
services:
uptime-kuma:
image: louislam/uptime-kuma:2
image: uptime-kuma:consultic-2.1.0-beta.1
build:
context: .
dockerfile: docker/dockerfile
restart: unless-stopped
volumes:
- ./data:/app/data

View File

@ -0,0 +1,11 @@
exports.up = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.integer("down_retry_interval").notNullable().defaultTo(0);
});
};
exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("down_retry_interval");
});
};

View File

@ -15,12 +15,15 @@ USER node
WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
COPY --chown=node:node .npmrc .npmrc
COPY --chown=node:node package.json package.json
COPY --chown=node:node package-lock.json package-lock.json
RUN npm ci --omit=dev
COPY . .
RUN npm ci
COPY --chown=node:node . .
COPY --chown=node:node --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck
RUN npm run build
RUN npm prune --omit=dev
RUN mkdir ./data
############################################
@ -115,4 +118,3 @@ RUN chmod +x /app/extra/upload-github-release-asset.sh
# Dist only
RUN cd /app && tar -zcvf $DIST dist
RUN /app/extra/upload-github-release-asset.sh github_api_token=$GITHUB_TOKEN owner=louislam repo=uptime-kuma tag=$VERSION filename=/app/$DIST

View File

@ -150,6 +150,7 @@ class Monitor extends BeanModel {
interval: this.interval,
retryInterval: this.retryInterval,
retryOnlyOnStatusCodeFailure: Boolean(this.retry_only_on_status_code_failure),
downRetryInterval: this.downRetryInterval,
resendInterval: this.resendInterval,
keyword: this.keyword,
invertKeyword: this.isInvertKeyword(),
@ -1087,9 +1088,13 @@ class Monitor extends BeanModel {
} else if (bean.status === MAINTENANCE) {
log.warn("monitor", `Monitor #${this.id} '${this.name}': Under Maintenance | Type: ${this.type}`);
} else {
if (this.downRetryInterval > 0) {
beatInterval = this.downRetryInterval;
}
const intervalNote = this.downRetryInterval > 0 ? " (recovery)" : "";
log.warn(
"monitor",
`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`
`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds${intervalNote} | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`
);
}
@ -1736,6 +1741,16 @@ class Monitor extends BeanModel {
throw new Error(`Retry interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
}
const downRetryInterval = Number(this.downRetryInterval);
if (downRetryInterval !== 0) {
if (downRetryInterval > MAX_INTERVAL_SECOND) {
throw new Error(`Down retry interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`);
}
if (downRetryInterval < MIN_INTERVAL_SECOND) {
throw new Error(`Down retry interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
}
}
if (this.response_max_length !== undefined) {
if (this.response_max_length < 0) {
throw new Error(`Response max length cannot be less than 0`);

View File

@ -844,6 +844,7 @@ let needSetup = false;
bean.tlsKey = monitor.tlsKey;
bean.interval = monitor.interval;
bean.retryInterval = monitor.retryInterval;
bean.downRetryInterval = monitor.downRetryInterval;
bean.resendInterval = monitor.resendInterval;
bean.hostname = monitor.hostname;
bean.game = monitor.game;

View File

@ -86,10 +86,14 @@
"timeoutAfter": "Timeout after {0} seconds",
"Retries": "Retries",
"Heartbeat Retry Interval": "Heartbeat Retry Interval",
"Recovery Check Interval": "Recovery Check Interval",
"Resend Notification if Down X times consecutively": "Resend Notification if Down X times consecutively",
"Advanced": "Advanced",
"checkEverySecond": "Check every {0} seconds",
"retryCheckEverySecond": "Retry every {0} seconds",
"downCheckEverySecond": "Check every {0} seconds while down",
"downRetryIntervalDisabled": "Use heartbeat interval when down",
"downRetryIntervalDescription": "Set to 0 to use the heartbeat interval while the monitor is down.",
"resendEveryXTimes": "Resend every {0} times",
"resendDisabled": "Resend disabled",
"retriesDescription": "Maximum retries before the service is marked as down and a notification is sent",

View File

@ -1200,6 +1200,35 @@
</div>
</div>
<div class="my-3">
<label for="down-retry-interval" class="form-label">
{{ $t("Recovery Check Interval") }}
<span v-if="monitor.downRetryInterval > 0">
({{ $t("downCheckEverySecond", [monitor.downRetryInterval]) }})
</span>
<span v-else>({{ $t("downRetryIntervalDisabled") }})</span>
</label>
<input
id="down-retry-interval"
v-model="monitor.downRetryInterval"
type="number"
class="form-control"
required
min="0"
step="1"
@focus="lowIntervalConfirmation.editedValue = true"
/>
<div class="form-text">
{{ $t("downRetryIntervalDescription") }}
</div>
<div
v-if="monitor.downRetryInterval > 0 && monitor.downRetryInterval < 20"
class="form-text"
>
{{ $t("minimumIntervalWarning") }}
</div>
</div>
<!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP / Websocket Upgrade only -->
<div
v-if="
@ -2302,6 +2331,7 @@ const monitorDefaults = {
interval: 60,
humanReadableInterval: timeDurationFormatter.secondsToHumanReadableFormat(60),
retryInterval: 60,
downRetryInterval: 0,
resendInterval: 0,
maxretries: 0,
retryOnlyOnStatusCodeFailure: false,
@ -2707,6 +2737,9 @@ message HealthCheckResponse {
if (this.monitor.retryInterval === oldValue) {
this.monitor.retryInterval = value;
}
if (this.monitor.downRetryInterval === oldValue) {
this.monitor.downRetryInterval = value;
}
// Converting monitor.interval to human readable format.
this.monitor.humanReadableInterval = timeDurationFormatter.secondsToHumanReadableFormat(value);
},
@ -2949,6 +2982,9 @@ message HealthCheckResponse {
if (this.monitor.retryInterval === 0) {
this.monitor.retryInterval = this.monitor.interval;
}
if (this.monitor.downRetryInterval === undefined || this.monitor.downRetryInterval === null) {
this.monitor.downRetryInterval = 0;
}
// Handling for monitors that are missing/zeroed timeout
if (!this.monitor.timeout) {
if (this.monitor.type === "ping") {
@ -3127,7 +3163,9 @@ message HealthCheckResponse {
// do this if the interval value has changed since last save.
if (
this.lowIntervalConfirmation.editedValue &&
(this.monitor.interval < 20 || this.monitor.retryInterval < 20) &&
(this.monitor.interval < 20 ||
this.monitor.retryInterval < 20 ||
(this.monitor.downRetryInterval > 0 && this.monitor.downRetryInterval < 20)) &&
!this.lowIntervalConfirmation.confirmed
) {
// The dialog will then re-call submit