diff --git a/db/patch-monitor-add-resend-interval.sql b/db/patch-monitor-add-resend-interval.sql new file mode 100644 index 000000000..8e28bf693 --- /dev/null +++ b/db/patch-monitor-add-resend-interval.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD resend_interval INTEGER default 0 not null; + +ALTER TABLE heartbeat + ADD down_count INTEGER default 0 not null; + +COMMIT; diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile index cde65bb64..1d74de05d 100644 --- a/docker/alpine-base.dockerfile +++ b/docker/alpine-base.dockerfile @@ -4,5 +4,5 @@ WORKDIR /app # Install apprise, iputils for non-root ping, setpriv RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ - pip3 --no-cache-dir install apprise==0.9.9 && \ + pip3 --no-cache-dir install apprise==1.0.0 && \ rm -rf /root/.cache diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index f90968a8b..20bef3dd4 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -11,7 +11,7 @@ WORKDIR /app RUN apt update && \ apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ sqlite3 iputils-ping util-linux dumb-init && \ - pip3 --no-cache-dir install apprise==0.9.9 && \ + pip3 --no-cache-dir install apprise==1.0.0 && \ rm -rf /var/lib/apt/lists/* && \ apt --yes autoremove diff --git a/package.json b/package.json index bac451d5e..981ca1912 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.17.1", + "version": "1.18.0-beta.0", "license": "MIT", "repository": { "type": "git", diff --git a/server/database.js b/server/database.js index 69bb3e5c8..fa6b68c82 100644 --- a/server/database.js +++ b/server/database.js @@ -63,6 +63,7 @@ class Database { "patch-add-sqlserver-monitor.sql": true, "patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] }, "patch-add-radius-monitor.sql": true, + "patch-monitor-add-resend-interval.sql": true, "patch-http-body-encoding.sql": true }; diff --git a/server/model/monitor.js b/server/model/monitor.js index 8f30a0c1d..509b841c2 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -79,6 +79,7 @@ class Monitor extends BeanModel { type: this.type, interval: this.interval, retryInterval: this.retryInterval, + resendInterval: this.resendInterval, keyword: this.keyword, expiryNotification: this.isEnabledExpiryNotification(), ignoreTls: this.getIgnoreTls(), @@ -215,6 +216,7 @@ class Monitor extends BeanModel { bean.monitor_id = this.id; bean.time = R.isoDateTimeMillis(dayjs.utc()); bean.status = DOWN; + bean.downCount = previousBeat?.downCount || 0; if (this.isUpsideDown()) { bean.status = flipStatus(bean.status); @@ -612,12 +614,27 @@ class Monitor extends BeanModel { log.debug("monitor", `[${this.name}] sendNotification`); await Monitor.sendNotification(isFirstBeat, this, bean); + // Reset down count + bean.downCount = 0; + // Clear Status Page Cache log.debug("monitor", `[${this.name}] apicache clear`); apicache.clear(); } else { bean.important = false; + + if (bean.status === DOWN && this.resendInterval > 0) { + ++bean.downCount; + if (bean.downCount >= this.resendInterval) { + // Send notification again, because we are still DOWN + log.debug("monitor", `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`); + await Monitor.sendNotification(isFirstBeat, this, bean); + + // Reset down count + bean.downCount = 0; + } + } } if (bean.status === UP) { @@ -628,7 +645,7 @@ class Monitor extends BeanModel { } log.warn("monitor", `Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`); } else { - log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`); + 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}`); } log.debug("monitor", `[${this.name}] Send to socket`); diff --git a/server/server.js b/server/server.js index 7ad82a24f..d7923ffee 100644 --- a/server/server.js +++ b/server/server.js @@ -669,6 +669,7 @@ let needSetup = false; bean.basic_auth_pass = monitor.basic_auth_pass; bean.interval = monitor.interval; bean.retryInterval = monitor.retryInterval; + bean.resendInterval = monitor.resendInterval; bean.hostname = monitor.hostname; bean.maxretries = monitor.maxretries; bean.port = parseInt(monitor.port); @@ -1279,6 +1280,7 @@ let needSetup = false; authDomain: monitorListData[i].authDomain, interval: monitorListData[i].interval, retryInterval: retryInterval, + resendInterval: monitorListData[i].resendInterval || 0, hostname: monitorListData[i].hostname, maxretries: monitorListData[i].maxretries, port: monitorListData[i].port, diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 560352951..a234e56f6 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -380,7 +380,7 @@ export default { deleteProxyMsg: "Сигурни ли сте, че желаете да изтриете това прокси за всички монитори?", proxyDescription: "За да функционират трябва да бъдат зададени към монитор.", enableProxyDescription: "Това прокси няма да има ефект върху заявките за мониторинг, докато не бъде активирано. Може да контролирате временното деактивиране на проксито от всички монитори чрез статуса на активиране.", - setAsDefaultProxyDescription: "Това проки ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.", + setAsDefaultProxyDescription: "Това прокси ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.", "Certificate Chain": "Верига на сертификата", Valid: "Валиден", Invalid: "Невалиден", @@ -537,4 +537,29 @@ export default { Workstation: "Работна станция", disableCloudflaredNoAuthMsg: "Тъй като сте в режим \"No Auth mode\", парола не се изисква.", wayToGetLineNotifyToken: "Може да получите токен код за достъп от {0}", + resendEveryXTimes: "Изпращай повторно на всеки {0} пъти", + resendDisabled: "Повторното изпращане е изключено", + "Resend Notification if Down X times consequently": "Повторно изпращане на известие, ако е недостъпен X пъти последователно", + "Bark Group": "Bark група", + "Bark Sound": "Bark звук", + "HTTP Headers": "HTTP хедъри", + "Trust Proxy": "Trust Proxy", + HomeAssistant: "Home Assistant", + RadiusSecret: "Radius таен код", + RadiusSecretDescription: "Споделен таен код между клиент и сървър", + RadiusCalledStationId: "Повиквана станция ID", + RadiusCalledStationIdDescription: "Идентификатор на повикваното устройство", + RadiusCallingStationId: "Повикваща станция ID", + RadiusCallingStationIdDescription: "Идентификатор на повикващото устройство", + "Setup Docker Host": "Настройка на Docker хост", + "Connection Type": "Тип свързване", + "Docker Daemon": "Docker демон", + deleteDockerHostMsg: "Сигурни ли сте, че желаете да изтриете този Docker хост за всички монитори?", + socket: "Сокет", + tcp: "TCP / HTTP", + "Docker Container": "Docker контейнер", + "Container Name / ID": "Име на контейнер / ID", + "Docker Host": "Docker хост", + "Docker Hosts": "Docker хостове", + trustProxyDescription: "Trust 'X-Forwarded-*' headers. Ако искате да получавате правилния IP адрес на клиента, а Uptime Kuma е зад системи като Nginx или Apache, трябва да разрешите тази опция.", }; diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js index 3df13b945..ef47909c7 100644 --- a/src/languages/de-DE.js +++ b/src/languages/de-DE.js @@ -165,7 +165,10 @@ export default { Pink: "Pink", "Search...": "Suchen...", "Heartbeat Retry Interval": "Überprüfungsintervall", + "Resend Notification if Down X times consequently": "Benachrichtigung erneut senden, wenn Inaktiv X mal hintereinander", retryCheckEverySecond: "Alle {0} Sekunden neu versuchen", + resendEveryXTimes: "Erneut versenden alle {0} mal", + resendDisabled: "Erneut versenden deaktiviert", "Import Backup": "Backup importieren", "Export Backup": "Backup exportieren", "Avg. Ping": "Durchschn. Ping", diff --git a/src/languages/en.js b/src/languages/en.js index 31d926287..d1b56addf 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -2,6 +2,8 @@ export default { languageName: "English", checkEverySecond: "Check every {0} seconds", retryCheckEverySecond: "Retry every {0} seconds", + resendEveryXTimes: "Resend every {0} times", + resendDisabled: "Resend disabled", retriesDescription: "Maximum retries before the service is marked as down and a notification is sent", ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites", upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.", @@ -72,6 +74,7 @@ export default { "Heartbeat Interval": "Heartbeat Interval", Retries: "Retries", "Heartbeat Retry Interval": "Heartbeat Retry Interval", + "Resend Notification if Down X times consequently": "Resend Notification if Down X times consequently", Advanced: "Advanced", "Upside Down Mode": "Upside Down Mode", "Max. Redirects": "Max. Redirects", @@ -559,5 +562,19 @@ export default { disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.", trustProxyDescription: "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind such as Nginx or Apache, you should enable this.", wayToGetLineNotifyToken: "You can get an access token from {0}", + Examples: "Examples", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Long-Lived Access Token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ", + "Notification Service": "Notification Service", + "default: notify all devices": "default: notify all devices", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.", + "Automations can optionally be triggered in Home Assistant:": "Automations can optionally be triggered in Home Assistant:", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Event data:": "Event data:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Then choose an action, for example switch the scene to where an RGB light is red.", + "Frontend Version": "Frontend Version", + "Frontend Version do not match backend version!": "Frontend Version do not match backend version!", "Body Encoding": "Body Encoding", }; diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js index 8dbe05f0f..a37d4ae4c 100644 --- a/src/languages/zh-CN.js +++ b/src/languages/zh-CN.js @@ -540,6 +540,45 @@ export default { settingsCertificateExpiry: "TLS 证书过期通知", certificationExpiryDescription: "HTTPS 监控项发现被监控目标的 TLS 证书剩余有效期少于以下天数时将发出通知:", "ntfy Topic": "ntfy 主题", - "Domain": "域名", - "Workstation": "工作站", + Domain: "域名", + Workstation: "工作站", + resendEveryXTimes: "每 {0} 次失败则重复发送一次", + resendDisabled: "为 0 时禁用重复发送", + "Resend Notification if Down X times consequently": "连续失败时重复发送通知的间隔次数", + "HTTP Headers": "HTTP 头", + "Trust Proxy": "可信的代理类字段", + HomeAssistant: "Home Assistant", + RadiusSecret: "Radius 共享机密", + RadiusSecretDescription: "客户端和服务器之间共享的密钥", + RadiusCalledStationId: "NAS 网络访问服务器号码(Called Station Id)", + RadiusCalledStationIdDescription: "所访问的服务器的标识", + RadiusCallingStationId: "呼叫方号码(Calling Station Id)", + RadiusCallingStationIdDescription: "发出请求的设备的标识", + "Setup Docker Host": "配置 Docker 宿主信息", + "Connection Type": "连接方式", + "Docker Daemon": "Docker 守护进程", + deleteDockerHostMsg: "您确定您要删除此 Docker 宿主设置吗?这会影响所有 Docker 监控项", + socket: "Socket", + tcp: "TCP / HTTP", + "Docker Container": "Docker 容器", + "Container Name / ID": "容器名称 / ID", + "Docker Host": "Docker 宿主", + "Docker Hosts": "Docker 宿主", + disableCloudflaredNoAuthMsg: "您现在正处于 No Auth 模式,无需输入密码", + trustProxyDescription: "信任 'X-Forwarded-*' 头。如果您的 Uptime Kuma 是通过 Nginx 或 Apache 等反代服务对外提供访问的话,则您应当启用本功能以获取正确的客户端 IP。", + wayToGetLineNotifyToken: "您可以在 {0} 获取 Access token", + Examples: "例如", + "Home Assistant URL": "Home Assistant 地址", + "Long-Lived Access Token": "长期访问令牌", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "长期访问令牌可通过点击左下角您的用户名,滚动到页面底部并点击 Create Token 按钮获取。", + "Notification Service": "Notification Service", + "default: notify all devices": "默认:通知所有设备", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "通知服务的列表可在 Home Assistant 中的 Developer Tools > Services 通过搜索您的设备或手机的名称来获得。", + "Automations can optionally be triggered in Home Assistant:": "可以在 Home Assistant 使用下列模板设置自动化操作的触发条件:", + "Trigger type:": "触发类型:", + "Event type:": "事件类型:", + "Event data:": "事件数据:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "然后您可以选择关联操作,例如切换到 RGB 灯发出红光的场景", + "Frontend Version": "前端版本", + "Frontend Version do not match backend version!": "前端版本与后端版本不符!", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index aa3d1a5ff..ba6676703 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -275,6 +275,15 @@ +
+ + +
+

{{ $t("Advanced") }}

@@ -676,6 +685,7 @@ export default { method: "GET", interval: 60, retryInterval: this.interval, + resendInterval: 0, maxretries: 0, notificationIDList: {}, ignoreTls: false,