This is an automated email from the ASF dual-hosted git repository.

tomsun28 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new e553f2b0e4 [alerter]feature: add WPUSH as a new alert notification 
channel (#4365)
e553f2b0e4 is described below

commit e553f2b0e46de022eab5c85e4cb33c56b506a819
Author: Alone88 <[email protected]>
AuthorDate: Mon Oct 5 09:29:03 2026 +0800

    [alerter]feature: add WPUSH as a new alert notification channel (#4365)
    
    Co-authored-by: Tomsun28 <[email protected]>
---
 .../apache/hertzbeat/alert/AlerterProperties.java  |   5 +
 .../notice/impl/WpushAlertNotifyHandlerImpl.java   | 109 +++++++++++++
 .../alert/util/NoticeReceiverMaskUtil.java         |   3 +-
 .../impl/WpushAlertNotifyHandlerImplTest.java      | 173 +++++++++++++++++++++
 .../alert/util/NoticeReceiverMaskUtilTest.java     |   3 +
 .../common/entity/alerter/NoticeReceiver.java      |  21 +++
 .../main/resources/templates/16-WpushTemplate.txt  |  47 ++++++
 web-app/src/app/pojo/NoticeReceiver.ts             |   5 +-
 .../alert-notice-receiver.component.html           |  26 ++++
 .../alert-notice-template.component.html           |   8 +
 web-app/src/assets/i18n/en-US.json                 |   4 +
 web-app/src/assets/i18n/ja-JP.json                 |   4 +
 web-app/src/assets/i18n/ko-KR.json                 |   4 +
 web-app/src/assets/i18n/pt-BR.json                 |   4 +
 web-app/src/assets/i18n/zh-CN.json                 |   4 +
 web-app/src/assets/i18n/zh-TW.json                 |   4 +
 16 files changed, 422 insertions(+), 2 deletions(-)

diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
index b855c647bb..022c75b4e1 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
@@ -79,6 +79,11 @@ public class AlerterProperties {
      */
     private String ntfyDefaultServerUrl = "https://ntfy.sh";;
 
+    /**
+     * WPUSH Notify url
+     */
+    private String wpushWebhookUrl = "https://api.wpush.cn/api/v1/send";;
+
     /**
      * Data entry configuration properties
      */
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImpl.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImpl.java
new file mode 100644
index 0000000000..330289df42
--- /dev/null
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.notice.impl;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.alert.notice.AlertNoticeException;
+import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
+import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * Send alert notification through WPUSH (https://wpush.cn).
+ * Success requires HTTP 200 and response JSON {@code code == 0}.
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class WpushAlertNotifyHandlerImpl extends 
AbstractAlertNotifyHandlerImpl {
+
+    @Override
+    public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, 
GroupAlert alert)
+            throws AlertNoticeException {
+        try {
+            if (StringUtils.isBlank(receiver.getWpushToken())) {
+                throw new AlertNoticeException("WPUSH apikey (wpushToken) is 
required");
+            }
+            WpushNotifyDto notifyDto = new WpushNotifyDto();
+            notifyDto.setApikey(receiver.getWpushToken());
+            notifyDto.setTitle(bundle.getString("alerter.notify.title"));
+            notifyDto.setContent(renderContent(noticeTemplate, alert));
+            if (StringUtils.isNotBlank(receiver.getWpushChannel())) {
+                notifyDto.setChannel(receiver.getWpushChannel());
+            }
+            if (StringUtils.isNotBlank(receiver.getWpushTopicCode())) {
+                notifyDto.setTopicCode(receiver.getWpushTopicCode());
+            }
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            HttpEntity<WpushNotifyDto> httpEntity = new 
HttpEntity<>(notifyDto, headers);
+            String webHookUrl = alerterProperties.getWpushWebhookUrl();
+            ResponseEntity<CommonRobotNotifyResp> responseEntity =
+                    restTemplate.postForEntity(webHookUrl, httpEntity, 
CommonRobotNotifyResp.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                CommonRobotNotifyResp body = responseEntity.getBody();
+                // WPUSH success is defined by JSON code == 0, not HTTP 2xx 
alone
+                if (body != null && body.getCode() != null && body.getCode() 
== 0) {
+                    log.debug("Send WPUSH notification Success");
+                } else {
+                    String msg = body == null ? "empty response body"
+                            : (body.getMsg() != null ? body.getMsg() : "code=" 
+ body.getCode());
+                    log.warn("Send WPUSH notification Failed: {}", msg);
+                    throw new AlertNoticeException("WPUSH response code not 0: 
" + msg);
+                }
+            } else {
+                log.warn("Send WPUSH notification Failed: Http StatusCode {}", 
responseEntity.getStatusCode());
+                throw new AlertNoticeException("Http StatusCode " + 
responseEntity.getStatusCode());
+            }
+        } catch (AlertNoticeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new AlertNoticeException("[WPUSH Notify Error] " + 
e.getMessage());
+        }
+    }
+
+    @Override
+    public byte type() {
+        return 16;
+    }
+
+    @Data
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    protected static class WpushNotifyDto {
+        /**
+         * WPUSH API key — never log this field in cleartext
+         */
+        private String apikey;
+        private String title;
+        private String content;
+        private String channel;
+        @JsonProperty("topic_code")
+        private String topicCode;
+    }
+}
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtil.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtil.java
index 51da9d0f52..868f82bbce 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtil.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtil.java
@@ -65,7 +65,8 @@ public final class NoticeReceiverMaskUtil {
             new SecretField(NoticeReceiver::getSmnSk, 
NoticeReceiver::setSmnSk),
             new SecretField(NoticeReceiver::getServerChanToken, 
NoticeReceiver::setServerChanToken),
             new SecretField(NoticeReceiver::getGotifyToken, 
NoticeReceiver::setGotifyToken),
-            new SecretField(NoticeReceiver::getNtfyToken, 
NoticeReceiver::setNtfyToken));
+            new SecretField(NoticeReceiver::getNtfyToken, 
NoticeReceiver::setNtfyToken),
+            new SecretField(NoticeReceiver::getWpushToken, 
NoticeReceiver::setWpushToken));
 
     private NoticeReceiverMaskUtil() {
     }
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImplTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImplTest.java
new file mode 100644
index 0000000000..50ea34c096
--- /dev/null
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/notice/impl/WpushAlertNotifyHandlerImplTest.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.notice.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ResourceBundle;
+import org.apache.hertzbeat.alert.AlerterProperties;
+import org.apache.hertzbeat.alert.notice.AlertNoticeException;
+import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
+import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Test case for {@link WpushAlertNotifyHandlerImpl}
+ */
+@ExtendWith(MockitoExtension.class)
+class WpushAlertNotifyHandlerImplTest {
+
+    @Mock
+    private RestTemplate restTemplate;
+
+    @Mock
+    private AlerterProperties alerterProperties;
+
+    @Mock
+    private ResourceBundle bundle;
+
+    @InjectMocks
+    private WpushAlertNotifyHandlerImpl wpushAlertNotifyHandler;
+
+    private NoticeReceiver receiver;
+    private GroupAlert groupAlert;
+    private NoticeTemplate template;
+
+    @BeforeEach
+    public void setUp() {
+        receiver = new NoticeReceiver();
+        receiver.setId(1L);
+        receiver.setName("test-receiver");
+        receiver.setWpushToken("wpush-test-apikey-xxxxx");
+        receiver.setWpushChannel("wechat");
+        receiver.setWpushTopicCode("topic-demo");
+
+        groupAlert = new GroupAlert();
+        SingleAlert singleAlert = new SingleAlert();
+        singleAlert.setLabels(new HashMap<>());
+        singleAlert.getLabels().put("severity", "critical");
+        singleAlert.getLabels().put("alertname", "Test Alert");
+
+        List<SingleAlert> alerts = new ArrayList<>();
+        alerts.add(singleAlert);
+        groupAlert.setAlerts(alerts);
+
+        template = new NoticeTemplate();
+        template.setId(1L);
+        template.setName("test-template");
+        template.setContent("test content");
+
+        lenient().when(alerterProperties.getWpushWebhookUrl())
+                .thenReturn("https://api.wpush.cn/api/v1/send";);
+        
lenient().when(bundle.getString("alerter.notify.title")).thenReturn("Alert 
Notification");
+    }
+
+    @Test
+    public void testType() {
+        assertEquals(16, wpushAlertNotifyHandler.type());
+    }
+
+    @Test
+    public void testNotifyAlertSuccessWhenCodeIsZero() {
+        CommonRobotNotifyResp successResp = new CommonRobotNotifyResp();
+        successResp.setCode(0);
+        successResp.setMsg("success");
+        ResponseEntity<CommonRobotNotifyResp> responseEntity =
+                new ResponseEntity<>(successResp, HttpStatus.OK);
+
+        when(restTemplate.postForEntity(
+                any(String.class),
+                any(),
+                eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
+
+        wpushAlertNotifyHandler.send(receiver, template, groupAlert);
+
+        ArgumentCaptor<HttpEntity> entityCaptor = 
ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate).postForEntity(
+                eq("https://api.wpush.cn/api/v1/send";),
+                entityCaptor.capture(),
+                eq(CommonRobotNotifyResp.class));
+        WpushAlertNotifyHandlerImpl.WpushNotifyDto body =
+                (WpushAlertNotifyHandlerImpl.WpushNotifyDto) 
entityCaptor.getValue().getBody();
+        assertEquals("wpush-test-apikey-xxxxx", body.getApikey());
+        assertEquals("Alert Notification", body.getTitle());
+        assertEquals("wechat", body.getChannel());
+        assertEquals("topic-demo", body.getTopicCode());
+    }
+
+    @Test
+    public void testNotifyAlertFailureWhenCodeNonZero() {
+        CommonRobotNotifyResp failResp = new CommonRobotNotifyResp();
+        failResp.setCode(1);
+        failResp.setMsg("invalid apikey");
+        ResponseEntity<CommonRobotNotifyResp> responseEntity =
+                new ResponseEntity<>(failResp, HttpStatus.OK);
+
+        when(restTemplate.postForEntity(
+                any(String.class),
+                any(),
+                eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
+
+        assertThrows(AlertNoticeException.class,
+                () -> wpushAlertNotifyHandler.send(receiver, template, 
groupAlert));
+    }
+
+    @Test
+    public void testNotifyAlertFailureWhenHttpNotOk() {
+        CommonRobotNotifyResp failResp = new CommonRobotNotifyResp();
+        failResp.setCode(0);
+        ResponseEntity<CommonRobotNotifyResp> responseEntity =
+                new ResponseEntity<>(failResp, HttpStatus.BAD_REQUEST);
+
+        when(restTemplate.postForEntity(
+                any(String.class),
+                any(),
+                eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
+
+        assertThrows(AlertNoticeException.class,
+                () -> wpushAlertNotifyHandler.send(receiver, template, 
groupAlert));
+    }
+
+    @Test
+    public void testNotifyAlertFailureWhenMissingToken() {
+        receiver.setWpushToken(null);
+        assertThrows(AlertNoticeException.class,
+                () -> wpushAlertNotifyHandler.send(receiver, template, 
groupAlert));
+    }
+}
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtilTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtilTest.java
index cdac126c46..4133b44fc7 100644
--- 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtilTest.java
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/util/NoticeReceiverMaskUtilTest.java
@@ -47,6 +47,7 @@ class NoticeReceiverMaskUtilTest {
         receiver.setServerChanToken("SCT193569TSNm6xIabdjqeZPtOGOWcvU1e");
         receiver.setGotifyToken("A845h__ZMqDxZlO");
         receiver.setNtfyToken("tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2");
+        receiver.setWpushToken("wpush-test-apikey-xxxx");
         return receiver;
     }
 
@@ -66,6 +67,7 @@ class NoticeReceiverMaskUtilTest {
         assertEquals(SECRET_MASK + "vU1e", masked.getServerChanToken());
         assertEquals(SECRET_MASK + "xZlO", masked.getGotifyToken());
         assertEquals(SECRET_MASK + "NIz2", masked.getNtfyToken());
+        assertEquals(SECRET_MASK + "xxxx", masked.getWpushToken());
 
         assertEquals(receiver.getId(), masked.getId());
         assertEquals(receiver.getName(), masked.getName());
@@ -119,6 +121,7 @@ class NoticeReceiverMaskUtilTest {
         assertEquals("NCVBODJOEYHSW3VNSMAK", incoming.getSmnAk());
         assertEquals("nmSNhUJN9MlpPl8lfCsgdA0KvHCL9JSMSK", 
incoming.getSmnSk());
         assertEquals("SCT193569TSNm6xIabdjqeZPtOGOWcvU1e", 
incoming.getServerChanToken());
+        assertEquals("wpush-test-apikey-xxxx", incoming.getWpushToken());
     }
 
     @Test
diff --git 
a/hertzbeat-common-spring/src/main/java/org/apache/hertzbeat/common/entity/alerter/NoticeReceiver.java
 
b/hertzbeat-common-spring/src/main/java/org/apache/hertzbeat/common/entity/alerter/NoticeReceiver.java
index e31a1ac29c..edb8b84875 100644
--- 
a/hertzbeat-common-spring/src/main/java/org/apache/hertzbeat/common/entity/alerter/NoticeReceiver.java
+++ 
b/hertzbeat-common-spring/src/main/java/org/apache/hertzbeat/common/entity/alerter/NoticeReceiver.java
@@ -279,6 +279,27 @@ public class NoticeReceiver {
     @Column(length = 300)
     private String ntfyToken;
 
+    @Schema(title = "WPUSH API key (apikey) : The notification method is valid 
for WPUSH",
+            description = "WPUSH API key (apikey) : The notification method is 
valid for WPUSH",
+            example = "wpush-xxxxxxxxxxxxxxxx", accessMode = READ_WRITE)
+    @Size(max = 300)
+    @Column(length = 300)
+    private String wpushToken;
+
+    @Schema(title = "WPUSH channel : Optional channel, default wechat",
+            description = "WPUSH channel : Optional channel such as wechat, 
default wechat",
+            example = "wechat", accessMode = READ_WRITE)
+    @Size(max = 64)
+    @Column(length = 64)
+    private String wpushChannel;
+
+    @Schema(title = "WPUSH topic_code : Optional topic code for channel 
subscription",
+            description = "WPUSH topic_code : Optional topic code for channel 
subscription",
+            example = "topic_demo", accessMode = READ_WRITE)
+    @Size(max = 300)
+    @Column(length = 300)
+    private String wpushTopicCode;
+
     @Schema(title = "The creator of this record", example = "tom",
             accessMode = READ_ONLY)
     @CreatedBy
diff --git 
a/hertzbeat-manager/src/main/resources/templates/16-WpushTemplate.txt 
b/hertzbeat-manager/src/main/resources/templates/16-WpushTemplate.txt
new file mode 100644
index 0000000000..4f9e8deed1
--- /dev/null
+++ b/hertzbeat-manager/src/main/resources/templates/16-WpushTemplate.txt
@@ -0,0 +1,47 @@
+## 🔔 HertzBeat Alert Notification
+
+### Alert Summary
+> - Status: ${status}
+<#if commonLabels??>
+<#if commonLabels.severity??>
+> - Severity: ${commonLabels.severity?switch("critical", "❤️ Critical", 
"warning", "💛 Warning", "info", "💚 Info", "Unknown")}
+</#if>
+<#if commonLabels.alertname??>
+> - Alert Name: ${commonLabels.alertname}
+</#if>
+</#if>
+
+### Alert Details
+<#list alerts as alert>
+#### Alert ${alert?index + 1}
+<#if alert.labels??>
+<#list alert.labels?keys as key>
+> - ${key}: ${alert.labels[key]}
+</#list>
+</#if>
+<#if alert.content?? && alert.content != "">
+> - Content: ${alert.content}
+</#if>
+> - Trigger Count: ${alert.triggerTimes}
+> - Start Time: ${(alert.startAt?number_to_datetime)?string('yyyy-MM-dd 
HH:mm:ss')}
+<#if alert.activeAt??>
+> - Active Time: ${(alert.activeAt?number_to_datetime)?string('yyyy-MM-dd 
HH:mm:ss')}
+</#if>
+<#if alert.endAt??>
+> - End Time: ${(alert.endAt?number_to_datetime)?string('yyyy-MM-dd HH:mm:ss')}
+</#if>
+
+<#if alert.annotations?? && alert.annotations?size gt 0>
+##### Additional Information
+<#list alert.annotations?keys as key>
+> - ${key}: ${alert.annotations[key]}
+</#list>
+</#if>
+</#list>
+
+<#if commonAnnotations?? && commonAnnotations?size gt 0>
+### Common Information
+<#list commonAnnotations?keys as key>
+> - ${key}: ${commonAnnotations[key]}
+</#list>
+</#if>
diff --git a/web-app/src/app/pojo/NoticeReceiver.ts 
b/web-app/src/app/pojo/NoticeReceiver.ts
index d9bf032d32..8c9c7c75f7 100644
--- a/web-app/src/app/pojo/NoticeReceiver.ts
+++ b/web-app/src/app/pojo/NoticeReceiver.ts
@@ -22,7 +22,7 @@ export class NoticeReceiver {
   name!: string;
   // notification mode: 0-sms 1-email 2-webhook 3-wechat public account 4-work 
wechat robot 5-Dingding robot 6-Feishu robot
   // 7-Telegram robot 8-SlackWebHook 9-Discord robot 10-work wechat app 
message 11-Huawei cloud SMN 12-ServerChan 13-Gotify
-  // 14-FeiShu app message 15-Ntfy
+  // 14-FeiShu app message 15-Ntfy 16-WPUSH
   type: number = 1;
   phone!: string;
   email!: string;
@@ -53,6 +53,9 @@ export class NoticeReceiver {
   ntfyServerUrl!: string;
   ntfyTopic!: string;
   ntfyToken!: string;
+  wpushToken!: string;
+  wpushChannel!: string;
+  wpushTopicCode!: string;
   creator!: string;
   modifier!: string;
   gmtCreate!: number;
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.html
 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.html
index 1469982ce0..785da7f07e 100644
--- 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.html
+++ 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.html
@@ -135,6 +135,10 @@
           <i nz-icon nzTheme="outline" nzType="notification"></i>
           <span>{{ 'alert.notice.type.ntfy' | i18n }}</span>
         </nz-tag>
+        <nz-tag *ngIf="data.type == 16" nzColor="orange">
+          <i nz-icon nzTheme="outline" nzType="notification"></i>
+          <span>{{ 'alert.notice.type.wpush' | i18n }}</span>
+        </nz-tag>
       </td>
       <td nzAlign="center">
         <span *ngIf="data.type == 0">{{ data.phone }}</span>
@@ -153,6 +157,7 @@
         <span *ngIf="data.type == 13">{{ data.gotifyToken }}</span>
         <span *ngIf="data.type == 14">{{ data.appId }}</span>
         <span *ngIf="data.type == 15">{{ data.ntfyServerUrl }}/{{ 
data.ntfyTopic }}</span>
+        <span *ngIf="data.type == 16">{{ data.wpushChannel || 'wechat' 
}}</span>
       </td>
       <td nzAlign="center">{{ (data.gmtUpdate ? data.gmtUpdate : 
data.gmtCreate) | date : 'YYYY-MM-dd HH:mm:ss' }}</td>
       <td nzAlign="center" nzRight>
@@ -236,6 +241,7 @@
             <nz-option [nzLabel]="'alert.notice.type.gotify' | i18n" 
[nzValue]="13"></nz-option>
             <nz-option [nzLabel]="'alert.notice.type.lark-app' | i18n" 
[nzValue]="14"></nz-option>
             <nz-option [nzLabel]="'alert.notice.type.ntfy' | i18n" 
[nzValue]="15"></nz-option>
+            <nz-option [nzLabel]="'alert.notice.type.wpush' | i18n" 
[nzValue]="16"></nz-option>
           </nz-select>
         </nz-form-control>
       </nz-form-item>
@@ -554,6 +560,26 @@
           <input [(ngModel)]="receiver.ntfyToken" name="ntfyToken" nz-input 
type="password" placeholder="" />
         </nz-form-control>
       </nz-form-item>
+      <nz-form-item *ngIf="receiver.type === 16">
+        <nz-form-label [nzRequired]="receiver.type === 16" [nzSpan]="7" 
nzFor="wpushToken"
+          >{{ 'alert.notice.type.wpush-token' | i18n }}
+        </nz-form-label>
+        <nz-form-control [nzErrorTip]="'validation.required' | i18n" 
[nzSpan]="12">
+          <input [(ngModel)]="receiver.wpushToken" [required]="receiver.type 
=== 16" name="wpushToken" nz-input type="password" />
+        </nz-form-control>
+      </nz-form-item>
+      <nz-form-item *ngIf="receiver.type === 16">
+        <nz-form-label [nzSpan]="7" nzFor="wpushChannel">{{ 
'alert.notice.type.wpush-channel' | i18n }}</nz-form-label>
+        <nz-form-control [nzSpan]="12">
+          <input [(ngModel)]="receiver.wpushChannel" name="wpushChannel" 
nz-input type="text" placeholder="wechat" />
+        </nz-form-control>
+      </nz-form-item>
+      <nz-form-item *ngIf="receiver.type === 16">
+        <nz-form-label [nzSpan]="7" nzFor="wpushTopicCode">{{ 
'alert.notice.type.wpush-topic-code' | i18n }}</nz-form-label>
+        <nz-form-control [nzSpan]="12">
+          <input [(ngModel)]="receiver.wpushTopicCode" name="wpushTopicCode" 
nz-input type="text" />
+        </nz-form-control>
+      </nz-form-item>
       <nz-form-item *ngIf="receiver.type === 14">
         <nz-form-label [nzRequired]="receiver.type === 14" [nzSpan]="7" 
nzFor="appId">
           {{ 'alert.notice.type.lark-app-appId' | i18n }}
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.html
 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.html
index c5ca96413c..c29935aac4 100644
--- 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.html
+++ 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.html
@@ -120,6 +120,12 @@
           <nz-tag *ngIf="data.type == 14" nzColor="orange">
             <span>{{ 'alert.notice.type.lark-app' | i18n }}</span>
           </nz-tag>
+          <nz-tag *ngIf="data.type == 15" nzColor="orange">
+            <span>{{ 'alert.notice.type.ntfy' | i18n }}</span>
+          </nz-tag>
+          <nz-tag *ngIf="data.type == 16" nzColor="orange">
+            <span>{{ 'alert.notice.type.wpush' | i18n }}</span>
+          </nz-tag>
         </span>
       </td>
       <td nzAlign="center">
@@ -216,6 +222,8 @@
             <nz-option [nzLabel]="'alert.notice.type.serverchan' | i18n" 
[nzValue]="12"></nz-option>
             <nz-option [nzLabel]="'alert.notice.type.gotify' | i18n" 
[nzValue]="13"></nz-option>
             <nz-option [nzLabel]="'alert.notice.type.lark-app' | i18n" 
[nzValue]="14"></nz-option>
+            <nz-option [nzLabel]="'alert.notice.type.ntfy' | i18n" 
[nzValue]="15"></nz-option>
+            <nz-option [nzLabel]="'alert.notice.type.wpush' | i18n" 
[nzValue]="16"></nz-option>
           </nz-select>
         </nz-form-control>
       </nz-form-item>
diff --git a/web-app/src/assets/i18n/en-US.json 
b/web-app/src/assets/i18n/en-US.json
index 3b0607042b..9e22fd842a 100644
--- a/web-app/src/assets/i18n/en-US.json
+++ b/web-app/src/assets/i18n/en-US.json
@@ -219,6 +219,10 @@
   "alert.notice.type.ntfy-server-url": "Ntfy Server URL",
   "alert.notice.type.ntfy-topic": "Ntfy Topic",
   "alert.notice.type.ntfy-token": "Ntfy Access Token",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "WPUSH API Key",
+  "alert.notice.type.wpush-channel": "WPUSH Channel",
+  "alert.notice.type.wpush-topic-code": "WPUSH Topic Code",
   "alert.notice.type.phone": "Phone",
   "alert.notice.type.serverchan": "ServerChan",
   "alert.notice.type.serverchan-token": "ServerChanToken",
diff --git a/web-app/src/assets/i18n/ja-JP.json 
b/web-app/src/assets/i18n/ja-JP.json
index e523c9d9ef..b7af980c88 100644
--- a/web-app/src/assets/i18n/ja-JP.json
+++ b/web-app/src/assets/i18n/ja-JP.json
@@ -206,6 +206,10 @@
   "alert.notice.type.ntfy-server-url": "Ntfy サーバーURL",
   "alert.notice.type.ntfy-topic": "Ntfy トピック",
   "alert.notice.type.ntfy-token": "Ntfy アクセストークン",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "WPUSH API Key",
+  "alert.notice.type.wpush-channel": "WPUSH チャネル",
+  "alert.notice.type.wpush-topic-code": "WPUSH Topic Code",
   "alert.notice.type.phone": "電話",
   "alert.notice.type.serverchan": "ServerChan",
   "alert.notice.type.serverchan-token": "ServerChanトークン",
diff --git a/web-app/src/assets/i18n/ko-KR.json 
b/web-app/src/assets/i18n/ko-KR.json
index 8f4641fbd2..46bc31dd69 100644
--- a/web-app/src/assets/i18n/ko-KR.json
+++ b/web-app/src/assets/i18n/ko-KR.json
@@ -219,6 +219,10 @@
   "alert.notice.type.ntfy-server-url": "Ntfy 서버 URL",
   "alert.notice.type.ntfy-topic": "Ntfy Topic",
   "alert.notice.type.ntfy-token": "Ntfy 액세스 토큰",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "WPUSH API Key",
+  "alert.notice.type.wpush-channel": "WPUSH 채널",
+  "alert.notice.type.wpush-topic-code": "WPUSH Topic Code",
   "alert.notice.type.phone": "전화",
   "alert.notice.type.serverchan": "ServerChan",
   "alert.notice.type.serverchan-token": "ServerChanToken",
diff --git a/web-app/src/assets/i18n/pt-BR.json 
b/web-app/src/assets/i18n/pt-BR.json
index bec7bae3f7..f0cf435aed 100644
--- a/web-app/src/assets/i18n/pt-BR.json
+++ b/web-app/src/assets/i18n/pt-BR.json
@@ -205,6 +205,10 @@
   "alert.notice.type.ntfy-server-url": "URL do Servidor Ntfy",
   "alert.notice.type.ntfy-topic": "Tópico Ntfy",
   "alert.notice.type.ntfy-token": "Token de Acesso Ntfy",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "Chave API do WPUSH",
+  "alert.notice.type.wpush-channel": "Canal WPUSH",
+  "alert.notice.type.wpush-topic-code": "Código do Tópico WPUSH",
   "alert.notice.rule": "Política de Notificação",
   "alert.notice.rule.new": "Nova Política de Notificação",
   "alert.notice.rule.edit": "Editar Política de Notificação",
diff --git a/web-app/src/assets/i18n/zh-CN.json 
b/web-app/src/assets/i18n/zh-CN.json
index 54fc54453b..723dc68118 100644
--- a/web-app/src/assets/i18n/zh-CN.json
+++ b/web-app/src/assets/i18n/zh-CN.json
@@ -219,6 +219,10 @@
   "alert.notice.type.ntfy-server-url": "Ntfy 服务器地址",
   "alert.notice.type.ntfy-topic": "Ntfy 主题",
   "alert.notice.type.ntfy-token": "Ntfy 访问令牌",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "WPUSH API Key",
+  "alert.notice.type.wpush-channel": "WPUSH 渠道",
+  "alert.notice.type.wpush-topic-code": "WPUSH Topic Code",
   "alert.notice.type.phone": "手机号",
   "alert.notice.type.serverchan": "Server酱(ServerChan)",
   "alert.notice.type.serverchan-token": "Server酱Token",
diff --git a/web-app/src/assets/i18n/zh-TW.json 
b/web-app/src/assets/i18n/zh-TW.json
index 145f01493b..276a5f8444 100644
--- a/web-app/src/assets/i18n/zh-TW.json
+++ b/web-app/src/assets/i18n/zh-TW.json
@@ -205,6 +205,10 @@
   "alert.notice.type.ntfy-server-url": "Ntfy 伺服器地址",
   "alert.notice.type.ntfy-topic": "Ntfy 主題",
   "alert.notice.type.ntfy-token": "Ntfy 存取權杖",
+  "alert.notice.type.wpush": "WPUSH",
+  "alert.notice.type.wpush-token": "WPUSH API Key",
+  "alert.notice.type.wpush-channel": "WPUSH 渠道",
+  "alert.notice.type.wpush-topic-code": "WPUSH Topic Code",
   "alert.notice.type.phone": "手機號",
   "alert.notice.type.serverchan": "ServerChan",
   "alert.notice.type.serverchan-token": "ServerChanToken",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to