diff --git a/compose.yaml b/compose.yaml index 914e8603d..e170f85ba 100644 --- a/compose.yaml +++ b/compose.yaml @@ -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 diff --git a/db/knex_migrations/2026-01-16-0000-add-down-retry-interval.js b/db/knex_migrations/2026-01-16-0000-add-down-retry-interval.js new file mode 100644 index 000000000..41bbdb836 --- /dev/null +++ b/db/knex_migrations/2026-01-16-0000-add-down-retry-interval.js @@ -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"); + }); +}; diff --git a/docker/dockerfile b/docker/dockerfile index e2a301e7b..4f831e5e9 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -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 - diff --git a/server/model/monitor.js b/server/model/monitor.js index 3b8d89dd4..0a1b47c53 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -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`); diff --git a/server/server.js b/server/server.js index 885e88340..4ffd038a9 100644 --- a/server/server.js +++ b/server/server.js @@ -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; diff --git a/src/lang/en.json b/src/lang/en.json index a2a87669d..6f049d1b7 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -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", diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 3e7ac832e..e0056b2af 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -1200,6 +1200,35 @@ +
+ + +
+ {{ $t("downRetryIntervalDescription") }} +
+
+ {{ $t("minimumIntervalWarning") }} +
+
+
0 && this.monitor.downRetryInterval < 20)) && !this.lowIntervalConfirmation.confirmed ) { // The dialog will then re-call submit