Compare commits

...

5 Commits

Author SHA1 Message Date
tomsun28
301558ca59 [monitor]feature:api monitor support http headers 2022-04-03 22:08:50 +08:00
tomsun28
636303021f [monitor]feature:param yml support key-value map (#57) 2022-04-03 21:35:56 +08:00
tomsun28
0cf66f32ff [manager,collector]feature 修改默认超时时间3000毫秒为6000毫秒 (#55) 2022-04-02 23:02:37 +08:00
tomsun28
fbf7ebd834 feature 检测网站SSL证书是否过期 (#50)
* [collector]feature 检测网站SSL证书是否过期

* [collector]fix cannot find symbol class BASE64Decoder
2022-04-02 21:22:16 +08:00
tomsun28
327f527082 [manager,collector]feature linux监控支持设置超时时间 (#49) 2022-04-02 17:57:25 +08:00
41 changed files with 355 additions and 58 deletions

View File

@@ -17,7 +17,9 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -75,7 +77,18 @@ public class CommonHttpClient {
@Override @Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }
@Override @Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
// 判断服务器证书有效期时间
Date now = new Date();
if (x509Certificates != null && x509Certificates.length > 0) {
for (X509Certificate certificate : x509Certificates) {
Date deadline = certificate.getNotAfter();
if (deadline != null && now.after(deadline)) {
throw new CertificateExpiredException();
}
}
}
}
@Override @Override
public X509Certificate[] getAcceptedIssuers() { return null; } public X509Certificate[] getAcceptedIssuers() { return null; }
}; };

View File

@@ -2,6 +2,9 @@ package com.usthe.collector.collect.common.ssh;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.sshd.client.SshClient; import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.core.CoreModuleProperties;
/** /**
* ssh公共client * ssh公共client
@@ -16,6 +19,14 @@ public class CommonSshClient {
static { static {
sshClient = SshClient.setUpDefaultClient(); sshClient = SshClient.setUpDefaultClient();
// 接受所有服务端公钥校验会打印warn日志 Server at {} presented unverified {} key: {}
AcceptAllServerKeyVerifier verifier = AcceptAllServerKeyVerifier.INSTANCE;
sshClient.setServerKeyVerifier(verifier);
// 设置链接保活心跳10000毫秒一次, 客户端等待保活心跳超时响应时间3000毫秒
PropertyResolverUtils.updateProperty(
sshClient, CoreModuleProperties.HEARTBEAT_INTERVAL.getName(), 10000);
PropertyResolverUtils.updateProperty(
sshClient, CoreModuleProperties.HEARTBEAT_REPLY_WAIT.getName(), 3000);
sshClient.start(); sshClient.start();
} }

View File

@@ -52,8 +52,8 @@ public class JdbcCommonCollect extends AbstractCollect {
} }
JdbcProtocol jdbcProtocol = metrics.getJdbc(); JdbcProtocol jdbcProtocol = metrics.getJdbc();
String databaseUrl = constructDatabaseUrl(jdbcProtocol); String databaseUrl = constructDatabaseUrl(jdbcProtocol);
// 查询超时时间默认3000毫秒 // 查询超时时间默认6000毫秒
int timeout = 3000; int timeout = 6000;
try { try {
// 获取查询语句超时时间 // 获取查询语句超时时间
if (jdbcProtocol.getTimeout() != null) { if (jdbcProtocol.getTimeout() != null) {

View File

@@ -37,8 +37,8 @@ public class IcmpCollectImpl extends AbstractCollect {
return; return;
} }
IcmpProtocol icmp = metrics.getIcmp(); IcmpProtocol icmp = metrics.getIcmp();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 300; int timeout = 6000;
try { try {
timeout = Integer.parseInt(icmp.getTimeout()); timeout = Integer.parseInt(icmp.getTimeout());
} catch (Exception e) { } catch (Exception e) {

View File

@@ -5,6 +5,7 @@ import com.usthe.collector.collect.common.cache.CacheIdentifier;
import com.usthe.collector.collect.common.cache.CommonCache; import com.usthe.collector.collect.common.cache.CommonCache;
import com.usthe.collector.collect.common.ssh.CommonSshClient; import com.usthe.collector.collect.common.ssh.CommonSshClient;
import com.usthe.collector.util.CollectorConstants; import com.usthe.collector.util.CollectorConstants;
import com.usthe.collector.util.KeyPairUtil;
import com.usthe.common.entity.job.Metrics; import com.usthe.common.entity.job.Metrics;
import com.usthe.common.entity.job.protocol.SshProtocol; import com.usthe.common.entity.job.protocol.SshProtocol;
import com.usthe.common.entity.message.CollectRep; import com.usthe.common.entity.message.CollectRep;
@@ -19,6 +20,7 @@ import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.security.KeyPair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -56,8 +58,8 @@ public class SshCollectImpl extends AbstractCollect {
return; return;
} }
SshProtocol sshProtocol = metrics.getSsh(); SshProtocol sshProtocol = metrics.getSsh();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 3000; int timeout = 6000;
try { try {
timeout = Integer.parseInt(sshProtocol.getTimeout()); timeout = Integer.parseInt(sshProtocol.getTimeout());
} catch (Exception e) { } catch (Exception e) {
@@ -181,6 +183,13 @@ public class SshCollectImpl extends AbstractCollect {
.verify(timeout, TimeUnit.MILLISECONDS).getSession(); .verify(timeout, TimeUnit.MILLISECONDS).getSession();
if (StringUtils.hasText(sshProtocol.getPassword())) { if (StringUtils.hasText(sshProtocol.getPassword())) {
clientSession.addPasswordIdentity(sshProtocol.getPassword()); clientSession.addPasswordIdentity(sshProtocol.getPassword());
} else if (StringUtils.hasText(sshProtocol.getPublicKey())) {
KeyPair keyPair = KeyPairUtil.getKeyPairFromPublicKey(sshProtocol.getPublicKey());
if (keyPair != null) {
clientSession.addPublicKeyIdentity(keyPair);
}
} else {
throw new IllegalArgumentException("需填写账户登陆密码或公钥");
} }
// 进行认证 // 进行认证
if (!clientSession.auth().verify(timeout, TimeUnit.MILLISECONDS).isSuccess()) { if (!clientSession.auth().verify(timeout, TimeUnit.MILLISECONDS).isSuccess()) {

View File

@@ -38,8 +38,8 @@ public class TelnetCollectImpl extends AbstractCollect {
} }
TelnetProtocol telnet = metrics.getTelnet(); TelnetProtocol telnet = metrics.getTelnet();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 300; int timeout = 6000;
try { try {
timeout = Integer.parseInt(telnet.getTimeout()); timeout = Integer.parseInt(telnet.getTimeout());
} catch (Exception e) { } catch (Exception e) {

View File

@@ -12,6 +12,7 @@ import com.usthe.common.entity.job.Job;
import com.usthe.common.entity.job.Metrics; import com.usthe.common.entity.job.Metrics;
import com.usthe.common.util.AesUtil; import com.usthe.common.util.AesUtil;
import com.usthe.common.util.CommonConstants; import com.usthe.common.util.CommonConstants;
import com.usthe.common.util.GsonUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList; import java.util.ArrayList;
@@ -84,6 +85,26 @@ public class WheelTimerTask implements TimerTask {
while (iterator.hasNext()) { while (iterator.hasNext()) {
Map.Entry<String, JsonElement> entry = iterator.next(); Map.Entry<String, JsonElement> entry = iterator.next();
JsonElement element = entry.getValue(); JsonElement element = entry.getValue();
String key = entry.getKey();
// 替换KEY-VALUE情况的属性 比如http headers params
if (key != null && key.startsWith("^_^") && key.endsWith("^_^")) {
key = key.replaceAll("\\^_\\^", "");
Configmap param = configmap.get(key);
if (param.getType() == (byte) 3) {
String jsonValue = (String) param.getValue();
Map<String, String> map = GsonUtil.fromJson(jsonValue, Map.class);
if (map != null) {
map.forEach((name, value) -> {
if (name != null && !"".equals(name.trim())) {
jsonObject.addProperty(name, value);
}
});
}
}
iterator.remove();
continue;
}
// 替换正常的VALUE值
if (element.isJsonPrimitive()) { if (element.isJsonPrimitive()) {
// 判断是否含有特殊字符 替换 // 判断是否含有特殊字符 替换
String value = element.getAsString(); String value = element.getAsString();

View File

@@ -0,0 +1,48 @@
package com.usthe.collector.util;
import lombok.extern.slf4j.Slf4j;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* 密钥工具类
* @author tom
* @date 2022/4/2 17:04
*/
@Slf4j
public class KeyPairUtil {
private static KeyFactory keyFactory;
static {
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* 获取密钥对
*/
public static KeyPair getKeyPairFromPublicKey(String publicKeyStr) {
try {
if (publicKeyStr == null || "".equals(publicKeyStr)) {
return null;
}
// todo fix 公钥解析
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return new KeyPair(publicKey, null);
} catch (Exception e) {
log.info("[keyPair] parse failed, {}." + e.getMessage());
return null;
}
}
}

View File

@@ -28,7 +28,7 @@ public class Configmap {
private Object value; private Object value;
/** /**
* 参数类型 0:数字 1:字符串 2:加密串 * 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
* number,string,secret * number,string,secret
* 数字,非加密字符串,加密字符串 * 数字,非加密字符串,加密字符串
*/ */

View File

@@ -29,7 +29,7 @@ public class SshProtocol {
/** /**
* 超时时间 * 超时时间
*/ */
private String timeout = "3000"; private String timeout;
/** /**
* 用户名 * 用户名

View File

@@ -59,15 +59,15 @@ public class Param {
* 参数值 * 参数值
*/ */
@ApiModelProperty(value = "参数值", example = "8080", accessMode = READ_WRITE, position = 3) @ApiModelProperty(value = "参数值", example = "8080", accessMode = READ_WRITE, position = 3)
@Length(max = 255) @Length(max = 8126)
private String value; private String value;
/** /**
* 参数类型 0:数字 1:字符串 2:加密串 * 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
*/ */
@ApiModelProperty(value = "参数类型 0:数字 1:字符串 2:加密串", accessMode = READ_WRITE, position = 4) @ApiModelProperty(value = "参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串", accessMode = READ_WRITE, position = 4)
@Min(0) @Min(0)
@Max(2) @Max(3)
private byte type; private byte type;
/** /**

View File

@@ -110,29 +110,41 @@ public class ParamDefine {
@Convert(converter = JsonOptionListAttributeConverter.class) @Convert(converter = JsonOptionListAttributeConverter.class)
private List<Option> options; private List<Option> options;
/**
* 当type为key-value时有效,表示key的别名描述
*/
@ApiModelProperty(value = "当type为key-value时有效,表示key的别名描述", example = "Name", accessMode = READ_WRITE, position = 9)
private String keyAlias;
/**
* 当type为key-value时有效,表示value的别名描述
*/
@ApiModelProperty(value = "当type为key-value时有效,表示value的别名描述", example = "Value", accessMode = READ_WRITE, position = 10)
private String valueAlias;
/** /**
* 此条记录创建者 * 此条记录创建者
*/ */
@ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 9) @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 11)
private String creator; private String creator;
/** /**
* 此条记录最新修改者 * 此条记录最新修改者
*/ */
@ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 10) @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 12)
private String modifier; private String modifier;
/** /**
* 记录创建时间 * 记录创建时间
*/ */
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 11) @ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 13)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtCreate; private LocalDateTime gmtCreate;
/** /**
* 记录最新修改时间 * 记录最新修改时间
*/ */
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 12) @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 14)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtUpdate; private LocalDateTime gmtUpdate;

View File

@@ -236,6 +236,9 @@ public class MonitorServiceImpl implements MonitorService {
case "checkbox": case "checkbox":
// todo checkbox校验 // todo checkbox校验
break; break;
case "key-value":
// todo key-value校验
break;
// todo 更多参数定义与实际值格式校验 // todo 更多参数定义与实际值格式校验
default: default:
throw new IllegalArgumentException("ParamDefine type " + paramDefine.getType() + " is invalid."); throw new IllegalArgumentException("ParamDefine type " + paramDefine.getType() + " is invalid.");

View File

@@ -5,7 +5,7 @@ app: example
name: name:
zh-CN: 模拟应用类型 zh-CN: 模拟应用类型
en-US: EXAMPLE APP en-US: EXAMPLE APP
# 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串 # 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串, 3-map映射的json串
# 强制固定必须参数 - host # 强制固定必须参数 - host
configmap: configmap:
- key: host - key: host
@@ -16,6 +16,8 @@ configmap:
type: 1 type: 1
- key: password - key: password
type: 2 type: 2
- key: headers
type: 3
# 指标组列表 # 指标组列表
metrics: metrics:
# 第一个监控指标组 cpu # 第一个监控指标组 cpu
@@ -69,7 +71,7 @@ metrics:
ssl: false ssl: false
# 请求头内容 # 请求头内容
headers: headers:
apiVersion: v1 ^_^headers^_^: ^_^headers^_^
# 请求参数内容 # 请求参数内容
params: params:
param1: param1 param1: param1

View File

@@ -26,6 +26,8 @@ configmap:
type: 1 type: 1
- key: payload - key: payload
type: 1 type: 1
- key: headers
type: 3
# 指标组列表 # 指标组列表
metrics: metrics:
# 第一个监控指标组 cpu # 第一个监控指标组 cpu
@@ -58,6 +60,7 @@ metrics:
# 请求头内容 # 请求头内容
headers: headers:
content-type: ^_^contentType^_^ content-type: ^_^contentType^_^
^_^headers^_^: ^_^headers^_^
# 认证 # 认证
authorization: authorization:
# 认证方式: Basic Auth, Digest Auth, Bearer Token # 认证方式: Basic Auth, Digest Auth, Bearer Token

View File

@@ -16,6 +16,8 @@ configmap:
type: 1 type: 1
- key: password - key: password
type: 2 type: 2
- key: timeout
type: 0
# 指标组列表 # 指标组列表
metrics: metrics:
# 第一个监控指标组 basic # 第一个监控指标组 basic
@@ -44,6 +46,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}' script: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}'
# 响应数据解析方式oneRow, multiRow # 响应数据解析方式oneRow, multiRow
parseType: multiRow parseType: multiRow
@@ -75,6 +78,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}'" script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}'"
parseType: oneRow parseType: oneRow
@@ -107,6 +111,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}' script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}'
parseType: multiRow parseType: multiRow
@@ -139,6 +144,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}' script: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}'
parseType: oneRow parseType: oneRow
@@ -164,5 +170,6 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}' script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}'
parseType: multiRow parseType: multiRow

View File

@@ -48,4 +48,10 @@ param:
- label: PUT请求 - label: PUT请求
value: PUT value: PUT
- label: DELETE请求 - label: DELETE请求
value: DELETE value: DELETE
- field: headers
name: 请求Headers
type: key-value
required: false
keyAlias: Header Name
valueAlias: Header Value

View File

@@ -17,13 +17,6 @@ param:
range: '[0,65535]' range: '[0,65535]'
required: true required: true
defaultValue: 80 defaultValue: 80
- field: uri
name: 相对路径
type: text
# 当type为text时,用limit表示字符串限制大小
limit: 200
required: false
placeholder: 'API地址除IP端口外的路径 例如:/v2/book/bar'
- field: method - field: method
name: 请求方式 name: 请求方式
type: radio type: radio
@@ -38,21 +31,24 @@ param:
value: PUT value: PUT
- label: DELETE请求 - label: DELETE请求
value: DELETE value: DELETE
- field: uri
name: 相对路径
type: text
# 当type为text时,用limit表示字符串限制大小
limit: 200
required: false
placeholder: 'API地址除IP端口外的路径 例如:/v2/book/bar'
- field: ssl - field: ssl
name: 启用HTTPS name: 启用HTTPS
# 当type为boolean时,前端用switch展示开关 # 当type为boolean时,前端用switch展示开关
type: boolean type: boolean
required: true required: true
- field: username - field: headers
name: 用户名 name: 请求Headers
type: text type: key-value
# 当type为text时,用limit表示字符串限制大小
limit: 20
required: false
- field: password
name: 密码
type: password
required: false required: false
keyAlias: Header Name
valueAlias: Header Value
- field: contentType - field: contentType
name: Content-Type name: Content-Type
type: text type: text
@@ -63,3 +59,13 @@ param:
type: textarea type: textarea
placeholder: 'POST PUT请求时有效' placeholder: 'POST PUT请求时有效'
required: false required: false
- field: username
name: 用户名
type: text
# 当type为text时,用limit表示字符串限制大小
limit: 20
required: false
- field: password
name: 密码
type: password
required: false

View File

@@ -11,6 +11,12 @@ param:
required: true required: true
defaultValue: 22 defaultValue: 22
placeholder: '请输入端口' placeholder: '请输入端口'
- field: timeout
name: 超时时间
type: number
required: false
defaultValue: 6000
placeholder: '超时时间'
- field: username - field: username
name: 用户名 name: 用户名
type: text type: text
@@ -19,4 +25,4 @@ param:
- field: password - field: password
name: 密码 name: 密码
type: password type: password
required: true required: false

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -17,4 +17,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -24,4 +24,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -24,4 +24,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -34,7 +34,7 @@ CREATE TABLE param
id bigint not null auto_increment comment '参数ID', id bigint not null auto_increment comment '参数ID',
monitor_id bigint not null comment '监控ID', monitor_id bigint not null comment '监控ID',
field varchar(100) not null comment '参数标识符', field varchar(100) not null comment '参数标识符',
value varchar(255) comment '参数值,最大字符长度255', value varchar(8126) comment '参数值,最大字符长度8126',
type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串', type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串',
gmt_create timestamp default current_timestamp comment 'create time', gmt_create timestamp default current_timestamp comment 'create time',
gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', gmt_update datetime default current_timestamp on update current_timestamp comment 'update time',

View File

@@ -36,10 +36,10 @@ excludedResource:
# eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 # eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289
account: account:
- appId: admin - appId: admin
credential: admin@123. credential: admin
role: [role1,role2] role: [role1,role2]
- appId: tom - appId: tom
credential: tom@123. credential: tom
role: [role1,role2,role3] role: [role1,role2,role3]
- appId: lili - appId: lili
# 注意 Digest认证不支持加盐加密的密码账户 # 注意 Digest认证不支持加盐加密的密码账户

View File

@@ -34,7 +34,7 @@ CREATE TABLE param
id bigint not null auto_increment comment '参数ID', id bigint not null auto_increment comment '参数ID',
monitor_id bigint not null comment '监控ID', monitor_id bigint not null comment '监控ID',
field varchar(100) not null comment '参数标识符', field varchar(100) not null comment '参数标识符',
value varchar(255) comment '参数值,最大字符长度255', value varchar(8126) comment '参数值,最大字符长度8126',
type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串', type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串',
gmt_create timestamp default current_timestamp comment 'create time', gmt_create timestamp default current_timestamp comment 'create time',
gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', gmt_update datetime default current_timestamp on update current_timestamp comment 'update time',

View File

@@ -9,4 +9,7 @@ export class ParamDefine {
limit: number | undefined; limit: number | undefined;
//'[{"label":"GET请求","value":"GET"},{"label":"PUT请求","value":"PUT"}]' //'[{"label":"GET请求","value":"GET"},{"label":"PUT请求","value":"PUT"}]'
options!: any[]; options!: any[];
// 当type为key-value时有效,表示别名描述
keyAlias!: string;
valueAlias!: string;
} }

View File

@@ -86,7 +86,7 @@
>{{ paramDefine.name }} >{{ paramDefine.name }}
</nz-form-label> </nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n"> <nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate"> <nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input <input
[type]="passwordVisible ? 'text' : 'password'" [type]="passwordVisible ? 'text' : 'password'"
nz-input nz-input
@@ -147,6 +147,18 @@
</label> </label>
</nz-radio-group> </nz-radio-group>
</nz-form-control> </nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'key-value'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="params[i].value"
[id]="paramDefine.field"
keyAlias="Header Name"
valueAlias="Header Value"
></app-key-value-input>
</nz-form-control>
</nz-form-item> </nz-form-item>
<nz-divider></nz-divider> <nz-divider></nz-divider>

View File

@@ -78,7 +78,13 @@ export class MonitorEditComponent implements OnInit {
if (param === undefined) { if (param === undefined) {
param = new Param(); param = new Param();
param.field = define.field; param.field = define.field;
param.type = define.type === 'number' ? 0 : 1; if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else {
param.type = 1;
}
if (define.type === 'boolean') { if (define.type === 'boolean') {
param.value = false; param.value = false;
} }

View File

@@ -95,7 +95,7 @@
>{{ paramDefine.name }} >{{ paramDefine.name }}
</nz-form-label> </nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n"> <nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate"> <nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input <input
[type]="passwordVisible ? 'text' : 'password'" [type]="passwordVisible ? 'text' : 'password'"
nz-input nz-input
@@ -156,6 +156,18 @@
</label> </label>
</nz-radio-group> </nz-radio-group>
</nz-form-control> </nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'key-value'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="params[i].value"
[id]="paramDefine.field"
[keyAlias]="paramDefine.keyAlias ? paramDefine.keyAlias : 'Name'"
[valueAlias]="paramDefine.valueAlias ? paramDefine.valueAlias : 'Value'"
></app-key-value-input>
</nz-form-control>
</nz-form-item> </nz-form-item>
<nz-divider></nz-divider> <nz-divider></nz-divider>

View File

@@ -58,7 +58,13 @@ export class MonitorNewComponent implements OnInit {
this.paramDefines.forEach(define => { this.paramDefines.forEach(define => {
let param = new Param(); let param = new Param();
param.field = define.field; param.field = define.field;
param.type = define.type === 'number' ? 0 : 1; if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else {
param.type = 1;
}
if (define.type === 'boolean') { if (define.type === 'boolean') {
param.value = false; param.value = false;
} }

View File

@@ -1,2 +0,0 @@
### 公共通用小组件

View File

@@ -0,0 +1,12 @@
<div *ngFor="let item of keyValues; let i = index" nz-row>
<div nz-col nzSpan="10">
<input nz-input [placeholder]="keyAlias" [(ngModel)]="item.key" (ngModelChange)="onChange()" />
</div>
<div nz-col nzSpan="11">
<input nz-input [placeholder]="valueAlias" [(ngModel)]="item.value" (ngModelChange)="onChange()" />
</div>
<div nz-col nzSpan="3">
<i nz-icon nzType="plus-circle" class="dynamic-button" *ngIf="i === 0" (click)="addNew($event)"></i>
<i nz-icon nzType="minus-circle" class="dynamic-button" (click)="removeCurrent(i, $event)"></i>
</div>
</div>

View File

@@ -0,0 +1,12 @@
.dynamic-button {
cursor: pointer;
position: relative;
top: 20%;
font-size: 15px;
transition: all 0.3s;
margin-left: 12%;
}
.dynamic-button:hover {
font-size: 26px;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { KeyValueInputComponent } from './key-value-input.component';
describe('KeyValueInputComponent', () => {
let component: KeyValueInputComponent;
let fixture: ComponentFixture<KeyValueInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ KeyValueInputComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(KeyValueInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,63 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-key-value-input',
templateUrl: './key-value-input.component.html',
styleUrls: ['./key-value-input.component.less']
})
export class KeyValueInputComponent implements OnInit {
constructor() {}
@Input() value!: any;
@Output() readonly valueChange = new EventEmitter<string>();
@Input()
keyAlias: string = 'Key';
@Input()
valueAlias: string = 'Value';
keyValues: any[] = [];
ngOnInit(): void {
if (this.value == undefined) {
this.value = {
'': ''
};
} else {
this.value = JSON.parse(this.value);
}
Object.keys(this.value).map(item => {
this.keyValues.push({
key: item,
value: this.value[item]
});
});
}
addNew(e?: MouseEvent) {
if (e) {
e.preventDefault();
}
this.keyValues.push({
key: '',
value: ''
});
}
removeCurrent(index: number, e?: MouseEvent) {
if (e) {
e.preventDefault();
}
if (this.keyValues.length > 1) {
this.keyValues.splice(index, 1);
}
}
onChange() {
this.value = {};
this.keyValues.forEach(item => {
if (item != null && item.key != null) {
this.value[item.key] = item.value;
}
});
this.valueChange.emit(JSON.stringify(this.value));
}
}

View File

@@ -6,6 +6,7 @@ import { DelonACLModule } from '@delon/acl';
import { DelonFormModule } from '@delon/form'; import { DelonFormModule } from '@delon/form';
import { AlainThemeModule } from '@delon/theme'; import { AlainThemeModule } from '@delon/theme';
import { KeyValueInputComponent } from './components/key-value-input/key-value-input.component';
import { TimezonePipe } from './pipe/timezone.pipe'; import { TimezonePipe } from './pipe/timezone.pipe';
import { SHARED_DELON_MODULES } from './shared-delon.module'; import { SHARED_DELON_MODULES } from './shared-delon.module';
import { SHARED_ZORRO_MODULES } from './shared-zorro.module'; import { SHARED_ZORRO_MODULES } from './shared-zorro.module';
@@ -18,7 +19,7 @@ const THIRDMODULES: Array<Type<void>> = [];
// #region your components & directives // #region your components & directives
const COMPONENTS: Array<Type<void>> = []; const COMPONENTS: Array<Type<void>> = [KeyValueInputComponent];
const DIRECTIVES: Array<Type<void>> = [TimezonePipe]; const DIRECTIVES: Array<Type<void>> = [TimezonePipe];
// #endregion // #endregion