mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
696432b59c | ||
|
|
01be0c7a1d | ||
|
|
0fdd76c0de | ||
|
|
299f41b769 | ||
|
|
0776480256 |
@@ -54,4 +54,8 @@ public class SmsConfig {
|
||||
*/
|
||||
private UniSmsProperties unisms;
|
||||
|
||||
/**
|
||||
* Smslocal SMS configuration
|
||||
*/
|
||||
private SmslocalSmsProperties smslocal;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Smslocal SMS Properties
|
||||
*/
|
||||
@Data
|
||||
public class SmslocalSmsProperties {
|
||||
/**
|
||||
* SmsLocal account api key
|
||||
*/
|
||||
private String apiKey;
|
||||
|
||||
}
|
||||
+6
-3
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.alert.service;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.config.SmsConfig;
|
||||
import org.apache.hertzbeat.alert.service.impl.SmsLocalSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.TencentSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.UniSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlibabaSmsClientImpl;
|
||||
@@ -33,6 +34,7 @@ import org.springframework.stereotype.Component;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.ALIBABA;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.TENCENT;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.UNISMS;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.SMSLOCAL;
|
||||
|
||||
/**
|
||||
* SMS client factory
|
||||
@@ -49,9 +51,7 @@ public class SmsClientFactory {
|
||||
|
||||
private volatile SmsClient currentSmsClient;
|
||||
|
||||
public SmsClientFactory(GeneralConfigDao generalConfigDao,
|
||||
ObjectMapper objectMapper,
|
||||
SmsConfig yamlSmsConfig) {
|
||||
public SmsClientFactory(GeneralConfigDao generalConfigDao, ObjectMapper objectMapper, SmsConfig yamlSmsConfig) {
|
||||
this.generalConfigDao = generalConfigDao;
|
||||
this.objectMapper = objectMapper;
|
||||
this.yamlSmsConfig = yamlSmsConfig;
|
||||
@@ -133,6 +133,9 @@ public class SmsClientFactory {
|
||||
case ALIBABA:
|
||||
currentSmsClient = new AlibabaSmsClientImpl(smsConfig.getAlibaba());
|
||||
break;
|
||||
case SMSLOCAL:
|
||||
currentSmsClient = new SmsLocalSmsClientImpl(smsConfig.getSmslocal());
|
||||
break;
|
||||
default:
|
||||
log.warn("[SmsClientFactory] Unsupported SMS provider type: {}", smsConfig.getType());
|
||||
break;
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.config.SmslocalSmsProperties;
|
||||
import org.apache.hertzbeat.alert.service.SmsClient;
|
||||
import org.apache.hertzbeat.common.constants.SmsConstants;
|
||||
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.support.exception.SendMessageException;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Smslocal SMS Client Implement
|
||||
*/
|
||||
@Slf4j
|
||||
public class SmsLocalSmsClientImpl implements SmsClient {
|
||||
private static final String HOST = "secure.smslocal.com";
|
||||
|
||||
private static final String PATH = "/api/service/enterprise-service/external/sms";
|
||||
|
||||
private static final String FROM = "Hertzbeat";
|
||||
|
||||
private static final String SUCCESS_CODE = "200";
|
||||
|
||||
private final SmslocalSmsProperties config;
|
||||
|
||||
public SmsLocalSmsClientImpl(SmslocalSmsProperties smslocalSmsProperties) {
|
||||
this.config = smslocalSmsProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
if (Objects.isNull(receiver) || Objects.isNull(alert)) {
|
||||
log.warn("receiver and alert can not be null! receiver: {}, alert:{}", receiver, alert);
|
||||
return;
|
||||
}
|
||||
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
String content = alert.getCommonAnnotations().get("summary");
|
||||
if (Objects.isNull(content) || Objects.isNull(alert.getCommonAnnotations().get("description"))) {
|
||||
content = alert.getAlerts().get(0).getContent();
|
||||
}
|
||||
SmsMessage smsMessage = new SmsMessage(FROM, receiver.getPhone(), content);
|
||||
|
||||
String payload = JsonUtil.toJson(smsMessage);
|
||||
|
||||
HttpPost httpPost = new HttpPost("https://" + HOST + PATH);
|
||||
httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
httpPost.setHeader("Token", config.getApiKey());
|
||||
httpPost.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));
|
||||
|
||||
log.debug("Sending SMS request to {}, payload: {}", httpPost.getURI(), payload);
|
||||
|
||||
// send http request and handle response
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String responseBody = EntityUtils.toString(response.getEntity());
|
||||
|
||||
log.debug("SMS response status: {}, body: {}", statusCode, responseBody);
|
||||
|
||||
if (statusCode != 200) {
|
||||
throw new SendMessageException("HTTP request failed with status code: " + statusCode);
|
||||
}
|
||||
|
||||
JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
|
||||
JsonNode jsonNode = jsonResponse.get(0);
|
||||
if (Objects.isNull(jsonNode)) {
|
||||
log.warn("jsonResponse parse errorCode failed: {}", jsonResponse);
|
||||
return;
|
||||
}
|
||||
String errorCode = jsonNode.get("errorCode").asText();
|
||||
if (!SUCCESS_CODE.equals(errorCode)) {
|
||||
String msgid = jsonNode.get("id").asText();
|
||||
throw new SendMessageException(errorCode + ":" + msgid);
|
||||
}
|
||||
|
||||
log.info("Successfully sent SMS to phone: {}", receiver.getPhone());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to send SMS: {}", e.getMessage());
|
||||
throw new SendMessageException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return SmsConstants.SMSLOCAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConfig() {
|
||||
if (Objects.isNull(config) || Objects.isNull(config.getApiKey()) || config.getApiKey().isBlank()) {
|
||||
log.warn("smslocal properties can not be null: {}", config);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private static class SmsMessage {
|
||||
String from;
|
||||
String to;
|
||||
String content;
|
||||
final int datacoding = 0;
|
||||
final String direction = "mt";
|
||||
|
||||
public SmsMessage(String from, String to, String content) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common.constants;
|
||||
|
||||
/**
|
||||
* Import/Export task constants
|
||||
*/
|
||||
|
||||
public interface ImExportTaskConstant {
|
||||
|
||||
/**
|
||||
* If the number of tasks exceeds 100, progress information will broadcast
|
||||
*/
|
||||
Integer IMPORT_TASK_PROCESS_THRESHOLD = 100;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.common.constants;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Import/Export task status
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public enum ImportTaskStatusEnum {
|
||||
|
||||
/**
|
||||
* In progress
|
||||
*/
|
||||
IN_PROGRESS("IN_PROGRESS"),
|
||||
|
||||
/**
|
||||
* Completed
|
||||
*/
|
||||
COMPLETED("COMPLETED"),
|
||||
|
||||
/**
|
||||
* Failed
|
||||
*/
|
||||
FAILED("FAILED");
|
||||
|
||||
private final String value;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.common.constants;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Manager Event Type Enum
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public enum ManagerEventTypeEnum {
|
||||
|
||||
/**
|
||||
* IMPORT_TASK_EVENT
|
||||
*/
|
||||
IMPORT_TASK_EVENT("IMPORT_TASK_EVENT");
|
||||
|
||||
private final String value;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.common.constants;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Notify Level Enum
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public enum NotifyLevelEnum {
|
||||
|
||||
SUCCESS("SUCCESS"),
|
||||
ERROR("ERROR"),
|
||||
INFO("INFO"),
|
||||
WARNING("WARNING"),
|
||||
BLANK("BLANK");
|
||||
|
||||
private final String value;
|
||||
}
|
||||
+5
-2
@@ -23,10 +23,13 @@ package org.apache.hertzbeat.common.constants;
|
||||
public interface SmsConstants {
|
||||
// Tencent cloud SMS
|
||||
String TENCENT = "tencent";
|
||||
|
||||
|
||||
// Alibaba Cloud SMS
|
||||
String ALIBABA = "alibaba";
|
||||
|
||||
|
||||
// UniSMS
|
||||
String UNISMS = "unisms";
|
||||
|
||||
// Smslocal SMS
|
||||
String SMSLOCAL = "smslocal";
|
||||
}
|
||||
|
||||
+1
@@ -63,6 +63,7 @@ public class GroupAlert {
|
||||
private Long id;
|
||||
|
||||
@Schema(title = "Group Key", example = "HighCPUUsage{alertname=\"HighCPUUsage\", instance=\"server1\"}")
|
||||
@Column(length = 2048)
|
||||
private String groupKey;
|
||||
|
||||
@Schema(title = "Status", example = "resolved")
|
||||
|
||||
+4
-2
@@ -59,8 +59,9 @@ public class SingleAlert {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(title = "Threshold Id", example = "87584674384", accessMode = READ_ONLY)
|
||||
private Long id;
|
||||
|
||||
@Schema(title = "Fingerprint", example = "dxsdfdsf")
|
||||
|
||||
@Schema(title = "Fingerprint", example = "alertname:demo")
|
||||
@Column(length = 2048)
|
||||
private String fingerprint;
|
||||
|
||||
@Schema(title = "Labels", example = "{\"alertname\": \"HighCPUUsage\", \"priority\": \"critical\", \"instance\": \"343483943\"}")
|
||||
@@ -74,6 +75,7 @@ public class SingleAlert {
|
||||
private Map<String, String> annotations;
|
||||
|
||||
@Schema(title = "Content", example = "CPU usage is above 80% for the last 5 minutes on instance server1.example.com.")
|
||||
@Column(length = 4096)
|
||||
private String content;
|
||||
|
||||
@Schema(title = "Status", example = "firing|resolved")
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.common.entity.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.hertzbeat.common.constants.ImportTaskStatusEnum;
|
||||
import org.apache.hertzbeat.common.constants.ManagerEventTypeEnum;
|
||||
import org.apache.hertzbeat.common.constants.NotifyLevelEnum;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Import task message
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ImportTaskMessage extends ManagerMessage {
|
||||
|
||||
/**
|
||||
* Task name
|
||||
*/
|
||||
@NotNull
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* Progress, expressed as a percentage
|
||||
*/
|
||||
@Nullable
|
||||
private Integer progress;
|
||||
|
||||
/**
|
||||
* Task Status,
|
||||
* @see ImportTaskStatusEnum
|
||||
*/
|
||||
@NotNull
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* If Fail, the error message
|
||||
*/
|
||||
@Nullable
|
||||
private String errMsg;
|
||||
|
||||
public ImportTaskMessage(String notifyLevel, String managerEventType, String taskName, @Nullable Integer progress, String status, @Nullable String errMsg){
|
||||
super(notifyLevel, managerEventType);
|
||||
this.taskName = taskName;
|
||||
this.progress = progress;
|
||||
this.status = status;
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
public static ManagerMessage createInProgressMessage(String taskName, Integer process){
|
||||
return new ImportTaskMessage(NotifyLevelEnum.INFO.getValue(), ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), taskName, process, ImportTaskStatusEnum.IN_PROGRESS.getValue(), null);
|
||||
}
|
||||
|
||||
public static ManagerMessage createCompletedMessage(String taskName){
|
||||
return new ImportTaskMessage(NotifyLevelEnum.SUCCESS.getValue(), ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), taskName, null, ImportTaskStatusEnum.COMPLETED.getValue(), null);
|
||||
}
|
||||
|
||||
public static ManagerMessage createFailedMessage(String taskName, String errMsg){
|
||||
return new ImportTaskMessage(NotifyLevelEnum.ERROR.getValue(), ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), taskName, null, ImportTaskStatusEnum.FAILED.getValue(), errMsg);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.common.entity.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Manager Message Entity
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ManagerMessage {
|
||||
/**
|
||||
* Notify Level
|
||||
* @see org.apache.hertzbeat.common.constants.NotifyLevelEnum
|
||||
*/
|
||||
private String notifyLevel;
|
||||
|
||||
/**
|
||||
* Manager Event Type
|
||||
* @see org.apache.hertzbeat.common.constants.ManagerEventTypeEnum
|
||||
*/
|
||||
private String managerEventType;
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.manager.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.ManagerEventTypeEnum;
|
||||
import org.apache.hertzbeat.common.entity.dto.ImportTaskMessage;
|
||||
import org.apache.hertzbeat.common.entity.dto.ManagerMessage;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manager SSE
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ManagerSseManager {
|
||||
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
|
||||
|
||||
public SseEmitter createEmitter(Long clientId) {
|
||||
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
|
||||
emitter.onCompletion(() -> removeEmitter(clientId));
|
||||
emitter.onTimeout(() -> removeEmitter(clientId));
|
||||
emitters.put(clientId, emitter);
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void broadcast(String eventName, String data) {
|
||||
emitters.forEach((clientId, emitter) -> {
|
||||
try {
|
||||
emitter.send(SseEmitter.event()
|
||||
.id(String.valueOf(System.currentTimeMillis()))
|
||||
.name(eventName)
|
||||
.data(data));
|
||||
} catch (IOException | IllegalStateException e) {
|
||||
emitter.complete();
|
||||
removeEmitter(clientId);
|
||||
} catch (Exception exception) {
|
||||
log.error("Failed to broadcast manager message data to client: {}", exception.getMessage());
|
||||
emitter.complete();
|
||||
removeEmitter(clientId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void broadcastImportTaskInProgress(String taskName, Integer progress){
|
||||
ManagerMessage managerMessage = ImportTaskMessage.createInProgressMessage(taskName, progress);
|
||||
broadcast(ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), JsonUtil.toJson(managerMessage));
|
||||
}
|
||||
|
||||
public void broadcastImportTaskSuccess(String taskName){
|
||||
ManagerMessage managerMessage = ImportTaskMessage.createCompletedMessage(taskName);
|
||||
broadcast(ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), JsonUtil.toJson(managerMessage));
|
||||
}
|
||||
|
||||
public void broadcastImportTaskFail(String taskName, String errMsg){
|
||||
ManagerMessage managerMessage = ImportTaskMessage.createFailedMessage(taskName, errMsg);
|
||||
broadcast(ManagerEventTypeEnum.IMPORT_TASK_EVENT.getValue(), JsonUtil.toJson(managerMessage));
|
||||
}
|
||||
|
||||
private void removeEmitter(Long clientId) {
|
||||
emitters.remove(clientId);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.manager.controller;
|
||||
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.apache.hertzbeat.manager.config.ManagerSseManager;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
* SSE controller for manager
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/manager/sse", produces = {TEXT_EVENT_STREAM_VALUE})
|
||||
public class ManagerSseController {
|
||||
|
||||
private final ManagerSseManager emitterManager;
|
||||
|
||||
public ManagerSseController(ManagerSseManager emitterManager) {
|
||||
this.emitterManager = emitterManager;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/subscribe")
|
||||
public SseEmitter subscribe() {
|
||||
Long clientId = SnowFlakeIdGenerator.generateId();
|
||||
return emitterManager.createEmitter(clientId);
|
||||
}
|
||||
}
|
||||
+2
@@ -39,5 +39,7 @@ public class SmsNoticeSender {
|
||||
|
||||
private SmsUniSmsConfig unisms;
|
||||
|
||||
private SmslocalConfig smslocal;
|
||||
|
||||
private boolean enable = true;
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.manager.pojo.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
/**
|
||||
* Smslocal SMS configuration
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmslocalConfig {
|
||||
/**
|
||||
* Smslocal api key
|
||||
*/
|
||||
private String apiKey;
|
||||
}
|
||||
+2
-1
@@ -29,9 +29,10 @@ public interface ImExportService {
|
||||
|
||||
/**
|
||||
* Import Configuration
|
||||
* @param taskName task name
|
||||
* @param is input stream
|
||||
*/
|
||||
void importConfig(InputStream is);
|
||||
void importConfig(String taskName, InputStream is);
|
||||
|
||||
/**
|
||||
* Export Configuration
|
||||
|
||||
+40
-39
@@ -22,17 +22,12 @@ import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.ImExportTaskConstant;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.manager.config.ManagerSseManager;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.MonitorDto;
|
||||
import org.apache.hertzbeat.manager.service.ImExportService;
|
||||
import org.apache.hertzbeat.manager.service.MonitorService;
|
||||
@@ -41,6 +36,14 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* class AbstractImExportServiceImpl
|
||||
*/
|
||||
@@ -54,27 +57,30 @@ public abstract class AbstractImExportServiceImpl implements ImExportService {
|
||||
@Resource
|
||||
private TagService tagService;
|
||||
|
||||
@Resource
|
||||
private ManagerSseManager managerSseManager;
|
||||
|
||||
@Override
|
||||
public void importConfig(InputStream is) {
|
||||
var formList = parseImport(is)
|
||||
.stream()
|
||||
.map(this::convert)
|
||||
.toList();
|
||||
public void importConfig(String taskName, InputStream is) {
|
||||
var formList = parseImport(is).stream().map(this::convert).toList();
|
||||
if (!CollectionUtils.isEmpty(formList)) {
|
||||
formList.forEach(monitorDto -> {
|
||||
int totalElements = formList.size();
|
||||
int progressInterval = Math.max(1, totalElements / 10);
|
||||
for (int i = 0; i < totalElements; i++) {
|
||||
MonitorDto monitorDto = formList.get(i);
|
||||
monitorService.validate(monitorDto, false);
|
||||
monitorService.addMonitor(monitorDto.getMonitor(), monitorDto.getParams(), monitorDto.getCollector(), monitorDto.getGrafanaDashboard());
|
||||
});
|
||||
if (totalElements >= ImExportTaskConstant.IMPORT_TASK_PROCESS_THRESHOLD && ((i + 1) % progressInterval == 0) && (i + 1 < totalElements)) {
|
||||
managerSseManager.broadcastImportTaskInProgress(taskName, (int) ((i + 1) * 100.0 / totalElements));
|
||||
}
|
||||
}
|
||||
managerSseManager.broadcastImportTaskSuccess(taskName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportConfig(OutputStream os, List<Long> configList) {
|
||||
var monitorList = configList.stream()
|
||||
.map(it -> monitorService.getMonitorDto(it))
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::convert)
|
||||
.toList();
|
||||
var monitorList = configList.stream().map(it -> monitorService.getMonitorDto(it)).filter(Objects::nonNull).map(this::convert).toList();
|
||||
writeOs(monitorList, os);
|
||||
}
|
||||
|
||||
@@ -99,15 +105,13 @@ public abstract class AbstractImExportServiceImpl implements ImExportService {
|
||||
var monitor = new MonitorDTO();
|
||||
BeanUtils.copyProperties(dto.getMonitor(), monitor);
|
||||
exportMonitor.setMonitor(monitor);
|
||||
exportMonitor.setParams(dto.getParams().stream()
|
||||
.map(it -> {
|
||||
var param = new ParamDTO();
|
||||
param.setField(it.getField());
|
||||
param.setType(it.getType());
|
||||
param.setValue(it.getParamValue());
|
||||
return param;
|
||||
})
|
||||
.toList());
|
||||
exportMonitor.setParams(dto.getParams().stream().map(it -> {
|
||||
var param = new ParamDTO();
|
||||
param.setField(it.getField());
|
||||
param.setType(it.getType());
|
||||
param.setValue(it.getParamValue());
|
||||
return param;
|
||||
}).toList());
|
||||
exportMonitor.getMonitor().setCollector(dto.getCollector());
|
||||
return exportMonitor;
|
||||
}
|
||||
@@ -120,7 +124,7 @@ public abstract class AbstractImExportServiceImpl implements ImExportService {
|
||||
var monitorDto = new MonitorDto();
|
||||
var monitor = new Monitor();
|
||||
log.debug("exportMonitor.monitor{}", exportMonitor.monitor);
|
||||
if (exportMonitor.monitor != null) {
|
||||
if (exportMonitor.monitor != null) {
|
||||
// Add one more null check
|
||||
BeanUtils.copyProperties(exportMonitor.monitor, monitor);
|
||||
}
|
||||
@@ -129,15 +133,13 @@ public abstract class AbstractImExportServiceImpl implements ImExportService {
|
||||
monitorDto.setCollector(exportMonitor.getMonitor().getCollector());
|
||||
}
|
||||
if (exportMonitor.params != null) {
|
||||
monitorDto.setParams(exportMonitor.params.stream()
|
||||
.map(it -> {
|
||||
var param = new Param();
|
||||
param.setField(it.field);
|
||||
param.setType(it.type);
|
||||
param.setParamValue(it.value);
|
||||
return param;
|
||||
})
|
||||
.toList());
|
||||
monitorDto.setParams(exportMonitor.params.stream().map(it -> {
|
||||
var param = new Param();
|
||||
param.setField(it.field);
|
||||
param.setType(it.type);
|
||||
param.setParamValue(it.value);
|
||||
return param;
|
||||
}).toList());
|
||||
} else {
|
||||
monitorDto.setParams(Collections.emptyList());
|
||||
}
|
||||
@@ -203,5 +205,4 @@ public abstract class AbstractImExportServiceImpl implements ImExportService {
|
||||
@Excel(name = "Value")
|
||||
private String value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-6
@@ -54,6 +54,7 @@ import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.util.SdMonitorOperator;
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.apache.hertzbeat.grafana.service.DashboardService;
|
||||
import org.apache.hertzbeat.manager.config.ManagerSseManager;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorDao;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
|
||||
@@ -138,6 +139,8 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
private WarehouseService warehouseService;
|
||||
@Autowired
|
||||
private DashboardService dashboardService;
|
||||
@Autowired
|
||||
private ManagerSseManager managerSseManager;
|
||||
|
||||
public MonitorServiceImpl(List<ImExportService> imExportServiceList) {
|
||||
imExportServiceList.forEach(it -> imExportServiceMap.put(it.type(), it));
|
||||
@@ -183,17 +186,21 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
|
||||
@Override
|
||||
public void importConfig(MultipartFile file) throws Exception {
|
||||
|
||||
var fileName = FileUtil.getFileName(file);
|
||||
var type = FileUtil.getFileType(file);
|
||||
if (!imExportServiceMap.containsKey(type)) {
|
||||
throw new RuntimeException(ExportFileConstants.FILE + " " + fileName + " is not supported.");
|
||||
try {
|
||||
if (!imExportServiceMap.containsKey(type)) {
|
||||
String errMsg = ExportFileConstants.FILE + " " + fileName + " is not supported.";
|
||||
throw new RuntimeException(errMsg);
|
||||
}
|
||||
var imExportService = imExportServiceMap.get(type);
|
||||
imExportService.importConfig(fileName, file.getInputStream());
|
||||
} catch (Exception e){
|
||||
managerSseManager.broadcastImportTaskFail(fileName, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
var imExportService = imExportServiceMap.get(type);
|
||||
imExportService.importConfig(file.getInputStream());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgumentException {
|
||||
|
||||
@@ -225,6 +225,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
@@ -247,4 +249,4 @@ ai:
|
||||
# api key
|
||||
api-key:
|
||||
#At present, only IFLYTEK large model needs to be filled in
|
||||
api-secret:
|
||||
api-secret:
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
+7
-4
@@ -73,10 +73,13 @@ public class DataStorageDispatch {
|
||||
if (metricsData == null) {
|
||||
continue;
|
||||
}
|
||||
calculateMonitorStatus(metricsData);
|
||||
historyDataWriter.ifPresent(dataWriter -> dataWriter.saveData(metricsData));
|
||||
pluginRunner.pluginExecute(PostCollectPlugin.class, ((postCollectPlugin, pluginContext) -> postCollectPlugin.execute(metricsData, pluginContext)));
|
||||
realTimeDataWriter.saveData(metricsData);
|
||||
try {
|
||||
calculateMonitorStatus(metricsData);
|
||||
historyDataWriter.ifPresent(dataWriter -> dataWriter.saveData(metricsData));
|
||||
pluginRunner.pluginExecute(PostCollectPlugin.class, ((postCollectPlugin, pluginContext) -> postCollectPlugin.execute(metricsData, pluginContext)));
|
||||
} finally {
|
||||
realTimeDataWriter.saveData(metricsData);
|
||||
}
|
||||
} catch (InterruptedException interruptedException) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ public class MemoryDataStorage extends AbstractRealTimeDataStorage {
|
||||
Long monitorId = metricsData.getId();
|
||||
String metrics = metricsData.getMetrics();
|
||||
if (metricsData.getCode() != CollectRep.Code.SUCCESS) {
|
||||
metricsData.close();
|
||||
return;
|
||||
}
|
||||
Map<String, CollectRep.MetricsData> metricsDataMap =
|
||||
|
||||
+1
@@ -63,6 +63,7 @@ public class RedisDataStorage extends AbstractRealTimeDataStorage {
|
||||
String hashKey = metricsData.getMetrics();
|
||||
if (metricsData.getCode() == CollectRep.Code.SUCCESS) {
|
||||
redisCommandDelegate.operate().hset(key, hashKey, metricsData, future -> future.thenAccept(response -> {
|
||||
metricsData.close();
|
||||
if (response) {
|
||||
log.debug("[warehouse] redis add new data {}:{}.", key, hashKey);
|
||||
} else {
|
||||
|
||||
@@ -156,6 +156,33 @@ alerter:
|
||||
|
||||
Now you can configure this information in your hertzbeat application.
|
||||
|
||||
### Smslocal SMS Configuration
|
||||
|
||||
SMSLocal is an all-in-one SMS service for businesses, with features like multi-way sending, strong security, and 24/7 support. You can refer to smslocal's [Developer Documentation](https://www.smslocal.com/developer/) for configuration.
|
||||
|
||||
Add/Fill in the following Smslocal configuration to `application.yml` (replace parameters with your own SMS server configuration):
|
||||
|
||||
```yaml
|
||||
alerter:
|
||||
sms:
|
||||
enable: true # Whether to enable
|
||||
type: smslocal # SMS provider type, set to smslocal
|
||||
smslocal: # Smslocal configuration
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
```
|
||||
|
||||
1. Register smslocal account
|
||||
- Visit [Smslocal Website](https://www.smslocal.com/)
|
||||
|
||||
2. Obtain `api-key`
|
||||
- Log in to [Smslocal Api Access](https://secure.smslocal.com/cpaas/pages/profile/settings/api-reference)
|
||||
- Go to "API Access" page
|
||||
- Click the eye button
|
||||
- Copy the displayed access key
|
||||
- Then you can configure the `application.yml` file
|
||||
|
||||
Now you can configure this information in your hertzbeat application.
|
||||
|
||||
## Operation steps
|
||||
|
||||
1. **【Alarm notification】->【Add new recipient】 ->【Select SMS notification method】**
|
||||
|
||||
@@ -155,6 +155,33 @@ alerter:
|
||||
|
||||
现在您可以把这些信息配置到您的hertzbeat应用中。
|
||||
|
||||
### smslocal短信配置
|
||||
|
||||
smslocal是一款面向企业的一体化短信服务平台,具备诸如多种发送方式、强大的安全性以及全天候支持等特性。你可以参考 smslocal 的[开发者文档](https://www.smslocal.com/developer/)来进行配置。
|
||||
|
||||
在 `application.yml` 中添加/填写以下 smslocal 配置内容(请用你自己的短信服务器配置参数替换相关参数):
|
||||
|
||||
```yaml
|
||||
alerter:
|
||||
sms:
|
||||
enable: true # 是否启用
|
||||
type: smslocal # 短信服务提供商类型,设置为smslocal
|
||||
smslocal: # smslocal配置
|
||||
api-key: 在此处填入你的API密钥
|
||||
```
|
||||
|
||||
1. 注册 smslocal 账号
|
||||
- 访问 [smslocal官网](https://www.smslocal.com/)
|
||||
|
||||
2. 获取 `api-key`
|
||||
- 登录 [smslocal API accessKey访问页面](https://secure.smslocal.com/cpaas/pages/profile/settings/api-reference)
|
||||
- 进入 “API 访问” 页面
|
||||
- 点击眼睛图标按钮
|
||||
- 复制显示的访问密钥
|
||||
- 然后你就可以配置 `application.yml` 文件了
|
||||
|
||||
现在你可以在你的 Hertzbeat 应用程序中配置这些信息。
|
||||
|
||||
## 操作步骤
|
||||
|
||||
1. **【告警通知】->【新增接收人】 ->【选择短信通知方式】**
|
||||
|
||||
@@ -224,6 +224,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
|
||||
@@ -191,6 +191,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
@@ -187,6 +187,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
@@ -185,6 +185,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
@@ -184,6 +184,8 @@ alerter:
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
signature: YOUR_SMS_SIGNATURE
|
||||
template-id: YOUR_TEMPLATE_ID
|
||||
smslocal:
|
||||
api-key: YOUR_API_KEY_HERE
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
@@ -78,6 +78,7 @@ excludedResource:
|
||||
- /api/apps/hierarchy===get
|
||||
- /api/push/**===*
|
||||
- /api/status/page/public/**===*
|
||||
- /api/manager/sse/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
|
||||
@@ -156,7 +156,8 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
);
|
||||
this.loadData();
|
||||
this.initSSEConnection();
|
||||
this.initAlertSSEConnection();
|
||||
this.initManagerSSEConnection();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -271,7 +272,7 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private initSSEConnection(): void {
|
||||
private initAlertSSEConnection(): void {
|
||||
const sseUrl = '/api/alert/sse/subscribe';
|
||||
|
||||
this.eventSource = new EventSource(sseUrl);
|
||||
@@ -302,4 +303,34 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
this.eventSource.close();
|
||||
};
|
||||
}
|
||||
|
||||
private initManagerSSEConnection(): void {
|
||||
const sseUrl = '/api/manager/sse/subscribe';
|
||||
this.eventSource = new EventSource(sseUrl);
|
||||
this.eventSource.addEventListener('IMPORT_TASK_EVENT', (evt: MessageEvent) => {
|
||||
let msg = JSON.parse(evt.data);
|
||||
if (msg.notifyLevel === 'SUCCESS') {
|
||||
this.notifySvc.success(
|
||||
this.i18nSvc.fanyi('common.notice'),
|
||||
this.i18nSvc.fanyi('common.notify.import-success-detail', { taskName: msg.taskName })
|
||||
);
|
||||
} else if (msg.notifyLevel === 'ERROR') {
|
||||
this.notifySvc.error(
|
||||
this.i18nSvc.fanyi('common.notice'),
|
||||
this.i18nSvc.fanyi('common.notify.import-fail-detail', { taskName: msg.taskName, errMsg: msg.errMsg })
|
||||
);
|
||||
} else if (msg.notifyLevel === 'INFO') {
|
||||
this.notifySvc.info(
|
||||
this.i18nSvc.fanyi('common.notice'),
|
||||
this.i18nSvc.fanyi('common.notify.import-progress', { taskName: msg.taskName, progress: msg.progress })
|
||||
);
|
||||
} else {
|
||||
console.error('Parse message error, msg:', evt.data);
|
||||
}
|
||||
});
|
||||
this.eventSource.onerror = error => {
|
||||
console.error('Manager SSE connection error:', error);
|
||||
this.eventSource.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
import { AlibabaSmsConfig } from './AlibabaSmsConfig';
|
||||
import { SmslocalSmsConfig } from './SmslocalSmsConfig';
|
||||
import { TencentSmsConfig } from './TencentSmsConfig';
|
||||
import { UniSmsConfig } from './UniSmsConfig';
|
||||
import { SmsType } from './enums/sms-type.enum';
|
||||
@@ -28,6 +29,7 @@ export class SmsNoticeSender {
|
||||
tencent: TencentSmsConfig = new TencentSmsConfig();
|
||||
alibaba: AlibabaSmsConfig = new AlibabaSmsConfig();
|
||||
unisms: UniSmsConfig = new UniSmsConfig();
|
||||
smslocal: SmslocalSmsConfig = new SmslocalSmsConfig();
|
||||
enable: boolean = false;
|
||||
creator!: string;
|
||||
modifier!: string;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export class SmslocalSmsConfig {
|
||||
apiKey!: string;
|
||||
}
|
||||
@@ -20,7 +20,8 @@
|
||||
export enum SmsType {
|
||||
TENCENT = 'tencent',
|
||||
ALIBABA = 'alibaba',
|
||||
UNISMS = 'unisms'
|
||||
UNISMS = 'unisms',
|
||||
SMSLOCAL = 'smslocal'
|
||||
}
|
||||
|
||||
export enum UniSmsAuthMode {
|
||||
|
||||
@@ -265,15 +265,20 @@ export class MonitorListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
onImportMonitors(info: NzUploadChangeParam): void {
|
||||
if (info.file.response) {
|
||||
console.log(info.type);
|
||||
if (info.type === 'start') {
|
||||
this.notifySvc.info(
|
||||
this.i18nSvc.fanyi('common.notice'),
|
||||
this.i18nSvc.fanyi('common.notify.import-submitted', { taskName: info.file.name })
|
||||
);
|
||||
}
|
||||
if (info.type === 'success' && info.file.response) {
|
||||
this.tableLoading = true;
|
||||
const message = info.file.response;
|
||||
if (message.code === 0) {
|
||||
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.import-success'), '');
|
||||
this.loadMonitorTable();
|
||||
} else {
|
||||
this.tableLoading = false;
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.import-fail'), message.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-1
@@ -139,7 +139,7 @@
|
||||
</div>
|
||||
</nz-modal>
|
||||
|
||||
<!-- sms server modal -->
|
||||
<!-- sms server model -->
|
||||
<nz-modal
|
||||
[(nzVisible)]="isSmsServerModalVisible"
|
||||
[nzTitle]="'settings.server.sms.setting' | i18n"
|
||||
@@ -158,9 +158,12 @@
|
||||
<nz-option [nzValue]="SmsType.TENCENT" nzLabel="{{ 'alert.notice.sender.sms.type.tencent' | i18n }}"></nz-option>
|
||||
<nz-option [nzValue]="SmsType.ALIBABA" nzLabel="{{ 'alert.notice.sender.sms.type.alibaba' | i18n }}"></nz-option>
|
||||
<nz-option [nzValue]="SmsType.UNISMS" nzLabel="{{ 'alert.notice.sender.sms.type.unisms' | i18n }}"></nz-option>
|
||||
<nz-option [nzValue]="SmsType.SMSLOCAL" nzLabel="{{ 'alert.notice.sender.sms.type.smslocal' | i18n }}"></nz-option>
|
||||
</nz-select>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
|
||||
<!-- Tencent SMS -->
|
||||
<ng-container *ngIf="smsType === SmsType.TENCENT">
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="7" nzFor="secretId" nzRequired="true">
|
||||
@@ -201,6 +204,8 @@
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Alibaba SMS -->
|
||||
<ng-container *ngIf="smsType === SmsType.ALIBABA">
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="7" nzFor="accessKeyId" nzRequired="true">
|
||||
@@ -256,6 +261,8 @@
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- UniSMS -->
|
||||
<ng-container *ngIf="smsType === SmsType.UNISMS">
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="7" nzFor="accessKeyId" nzRequired="true">
|
||||
@@ -317,6 +324,18 @@
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Smslocal SMS -->
|
||||
<ng-container *ngIf="smsType === SmsType.SMSLOCAL">
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="7" nzFor="apiKey" nzRequired="true">
|
||||
{{ 'alert.notice.sender.sms.smslocal.apiKey' | i18n }}
|
||||
</nz-form-label>
|
||||
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
|
||||
<input [(ngModel)]="smsNoticeSender.smslocal.apiKey" nz-input required name="apiKey" type="password" id="apiKey" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</ng-container>
|
||||
<nz-form-item>
|
||||
<nz-form-label nzSpan="7" nzFor="smsEnable" nzRequired="true">{{ 'common.enable' | i18n }}</nz-form-label>
|
||||
<nz-form-control nzSpan="12">
|
||||
|
||||
@@ -31,6 +31,7 @@ import { UniSmsConfig } from 'src/app/pojo/UniSmsConfig';
|
||||
import { SmsType, UniSmsAuthMode } from 'src/app/pojo/enums/sms-type.enum';
|
||||
|
||||
import { EmailNoticeSender } from '../../../../pojo/EmailNoticeSender';
|
||||
import { SmslocalSmsConfig } from '../../../../pojo/SmslocalSmsConfig';
|
||||
import { GeneralConfigService } from '../../../../service/general-config.service';
|
||||
|
||||
@Component({
|
||||
@@ -142,6 +143,7 @@ export class MessageServerComponent implements OnInit {
|
||||
this.smsNoticeSender.tencent = { ...new TencentSmsConfig(), ...message.data.tencent };
|
||||
this.smsNoticeSender.alibaba = { ...new AlibabaSmsConfig(), ...message.data.alibaba };
|
||||
this.smsNoticeSender.unisms = { ...new UniSmsConfig(), ...message.data.unisms };
|
||||
this.smsNoticeSender.smslocal = { ...new SmslocalSmsConfig(), ...message.data.smslocal };
|
||||
this.smsType = message.data.type || 'tencent';
|
||||
} else {
|
||||
this.smsNoticeSender = new SmsNoticeSender();
|
||||
@@ -165,7 +167,8 @@ export class MessageServerComponent implements OnInit {
|
||||
...this.smsNoticeSender,
|
||||
tencent: { ...this.smsNoticeSender.tencent },
|
||||
alibaba: { ...this.smsNoticeSender.alibaba },
|
||||
unisms: { ...this.smsNoticeSender.unisms }
|
||||
unisms: { ...this.smsNoticeSender.unisms },
|
||||
smslocal: { ...this.smsNoticeSender.smslocal }
|
||||
};
|
||||
this.isSmsServerModalVisible = true;
|
||||
}
|
||||
@@ -176,7 +179,8 @@ export class MessageServerComponent implements OnInit {
|
||||
...this.tempSmsNoticeSender,
|
||||
tencent: { ...this.tempSmsNoticeSender.tencent },
|
||||
alibaba: { ...this.tempSmsNoticeSender.alibaba },
|
||||
unisms: { ...this.tempSmsNoticeSender.unisms }
|
||||
unisms: { ...this.tempSmsNoticeSender.unisms },
|
||||
smslocal: { ...this.tempSmsNoticeSender.smslocal }
|
||||
};
|
||||
this.isSmsServerModalVisible = false;
|
||||
}
|
||||
|
||||
@@ -153,10 +153,12 @@
|
||||
"alert.notice.sender.sms.unisms.signature": "UniSMS Signature",
|
||||
"alert.notice.sender.sms.unisms.templateId": "UniSMS TemplateId",
|
||||
"alert.notice.sender.sms.unisms.authMode": "UniSMS Authentication Mode",
|
||||
"alert.notice.sender.sms.smslocal.apiKey": "Smslocal ApiKey",
|
||||
"alert.notice.sender.sms.type": "Sms Type",
|
||||
"alert.notice.sender.sms.type.alibaba": "Alibaba Sms",
|
||||
"alert.notice.sender.sms.type.tencent": "Tencent Sms",
|
||||
"alert.notice.sender.sms.type.unisms": "UniSMS",
|
||||
"alert.notice.sender.sms.type.smslocal": "Smslocal Sms",
|
||||
"alert.notice.template": "Notice Template",
|
||||
"alert.notice.template.content": "Template Content",
|
||||
"alert.notice.template.delete": "Delete Template",
|
||||
@@ -460,6 +462,10 @@
|
||||
"common.notify.export-success": "Export Success!",
|
||||
"common.notify.import-fail": "Import Failed!",
|
||||
"common.notify.import-success": "Import Success!",
|
||||
"common.notify.import-fail-detail": "Import [{{taskName}}] Failed: [{{errMsg}}]",
|
||||
"common.notify.import-success-detail": "Import [{{taskName}}] Success!",
|
||||
"common.notify.import-submitted": "Import [{{taskName}}] Submitted!",
|
||||
"common.notify.import-progress": "Importing [{{taskName}}], progress: {{progress}}%",
|
||||
"common.notify.mark-fail": "Mark Failed!",
|
||||
"common.notify.mark-success": "Mark Success!",
|
||||
"common.notify.new-fail": "Add Failed!",
|
||||
|
||||
@@ -153,10 +153,12 @@
|
||||
"alert.notice.sender.sms.unisms.signature": "UniSMS Signature",
|
||||
"alert.notice.sender.sms.unisms.templateId": "UniSMS TemplateId",
|
||||
"alert.notice.sender.sms.unisms.authMode": "UniSMS認証モード",
|
||||
"alert.notice.sender.sms.smslocal.apiKey": "Smslocal ApiKey",
|
||||
"alert.notice.sender.sms.type": "SMSタイプ",
|
||||
"alert.notice.sender.sms.type.alibaba": "Alibaba Sms",
|
||||
"alert.notice.sender.sms.type.tencent": "Tencent Sms",
|
||||
"alert.notice.sender.sms.type.unisms": "UniSMS",
|
||||
"alert.notice.sender.sms.type.smslocal": "Smslocal Sms",
|
||||
"alert.notice.template": "通知テンプレート",
|
||||
"alert.notice.template.content": "テンプレート内容",
|
||||
"alert.notice.template.delete": "テンプレートを削除",
|
||||
@@ -460,6 +462,10 @@
|
||||
"common.notify.export-success": "エクスポートに成功しました!",
|
||||
"common.notify.import-fail": "インポートに失敗しました!",
|
||||
"common.notify.import-success": "インポートに成功しました!",
|
||||
"common.notify.import-fail-detail": "[{{taskName}}]のインポートに失敗しました: [{{errMsg}}]",
|
||||
"common.notify.import-success-detail": "[{{taskName}}]のインポートが成功しました!",
|
||||
"common.notify.import-submitted": "インポートタスク[{{taskName}}]が送信されました!",
|
||||
"common.notify.import-progress": "[{{taskName}}]のインポート中、進捗状況: {{progress}}%",
|
||||
"common.notify.mark-fail": "マークに失敗しました!",
|
||||
"common.notify.mark-success": "マークに成功しました!",
|
||||
"common.notify.new-fail": "追加に失敗しました!",
|
||||
|
||||
@@ -153,10 +153,12 @@
|
||||
"alert.notice.sender.sms.unisms.signature": "合一短信Signature",
|
||||
"alert.notice.sender.sms.unisms.templateId": "合一短信TemplateId",
|
||||
"alert.notice.sender.sms.unisms.authMode": "合一短信鉴权方式",
|
||||
"alert.notice.sender.sms.smslocal.apiKey": "Smslocal短信鉴权方式",
|
||||
"alert.notice.sender.sms.type": "短信类型",
|
||||
"alert.notice.sender.sms.type.alibaba": "阿里短信",
|
||||
"alert.notice.sender.sms.type.tencent": "腾讯短信",
|
||||
"alert.notice.sender.sms.type.unisms": "合一短信(UniSMS)",
|
||||
"alert.notice.sender.sms.type.smslocal": "当地短信(Smslocal)",
|
||||
"alert.notice.template": "通知模板",
|
||||
"alert.notice.template.content": "模板内容",
|
||||
"alert.notice.template.delete": "删除通知模板",
|
||||
@@ -460,6 +462,10 @@
|
||||
"common.notify.export-success": "导出成功!",
|
||||
"common.notify.import-fail": "导入失败!",
|
||||
"common.notify.import-success": "导入成功!",
|
||||
"common.notify.import-fail-detail": "导入 [{{taskName}}] 失败: [{{errMsg}}]",
|
||||
"common.notify.import-success-detail": "导入 [{{taskName}}] 成功!",
|
||||
"common.notify.import-submitted": "导入任务 [{{taskName}}] 已提交!",
|
||||
"common.notify.import-progress": "正在导入 [{{taskName}}], 进度: {{progress}}%",
|
||||
"common.notify.mark-fail": "标记失败!",
|
||||
"common.notify.mark-success": "标记成功!",
|
||||
"common.notify.new-fail": "新增失败!",
|
||||
|
||||
@@ -153,10 +153,12 @@
|
||||
"alert.notice.sender.sms.unisms.signature": "合一簡訊Signature",
|
||||
"alert.notice.sender.sms.unisms.templateId": "合一簡訊TemplateId",
|
||||
"alert.notice.sender.sms.unisms.authMode": "合一簡訊驗證方式",
|
||||
"alert.notice.sender.sms.smslocal.authMode": "Smslocal短訊ApiKey",
|
||||
"alert.notice.sender.sms.type": "騰訊類型",
|
||||
"alert.notice.sender.sms.type.alibaba": "阿裏短訊",
|
||||
"alert.notice.sender.sms.type.tencent": "騰訊短訊",
|
||||
"alert.notice.sender.sms.type.unisms": "合一簡訊(UniSMS)",
|
||||
"alert.notice.sender.sms.type.smslocal": "当地短訊(Smslocal)",
|
||||
"alert.notice.template": "通知模板",
|
||||
"alert.notice.template.content": "模板内容",
|
||||
"alert.notice.template.delete": "刪除通知模板",
|
||||
@@ -460,6 +462,10 @@
|
||||
"common.notify.export-success": "導出成功!",
|
||||
"common.notify.import-fail": "導入失敗!",
|
||||
"common.notify.import-success": "導入成功!",
|
||||
"common.notify.import-fail-detail": "導入 [{{taskName}}] 失敗: [{{errMsg}}]",
|
||||
"common.notify.import-success-detail": "導入 [{{taskName}}] 成功!",
|
||||
"common.notify.import-submitted": "導入任務 [{{taskName}}] 已提交!",
|
||||
"common.notify.import-progress": "正在導入 [{{taskName}}], 進度: {{progress}}%",
|
||||
"common.notify.mark-fail": "標記失敗!",
|
||||
"common.notify.mark-success": "標記成功!",
|
||||
"common.notify.new-fail": "新增失敗!",
|
||||
|
||||
Reference in New Issue
Block a user