diff --git a/springboot-backend/src/main/java/com/admin/common/dto/NodeDto.java b/springboot-backend/src/main/java/com/admin/common/dto/NodeDto.java index 9e5a995..3985eff 100644 --- a/springboot-backend/src/main/java/com/admin/common/dto/NodeDto.java +++ b/springboot-backend/src/main/java/com/admin/common/dto/NodeDto.java @@ -16,6 +16,10 @@ public class NodeDto { @NotBlank(message = "服务器ip不能为空") private String serverIp; + private String serverIpV4; + + private String serverIpV6; + @NotBlank(message = "可用端口不能为空") private String port; @@ -25,4 +29,4 @@ public class NodeDto { private String udpListenAddr = "0.0.0.0"; -} \ No newline at end of file +} diff --git a/springboot-backend/src/main/java/com/admin/common/dto/NodeUpdateDto.java b/springboot-backend/src/main/java/com/admin/common/dto/NodeUpdateDto.java index a26436e..27c3f18 100644 --- a/springboot-backend/src/main/java/com/admin/common/dto/NodeUpdateDto.java +++ b/springboot-backend/src/main/java/com/admin/common/dto/NodeUpdateDto.java @@ -17,6 +17,10 @@ public class NodeUpdateDto { @NotBlank(message = "服务器ip不能为空") private String serverIp; + private String serverIpV4; + + private String serverIpV6; + @NotBlank(message = "可用port不能为空") private String port; @@ -28,4 +32,4 @@ public class NodeUpdateDto { private String tcpListenAddr = "0.0.0.0"; private String udpListenAddr = "0.0.0.0"; -} \ No newline at end of file +} diff --git a/springboot-backend/src/main/java/com/admin/entity/Node.java b/springboot-backend/src/main/java/com/admin/entity/Node.java index e0aa420..32d21d8 100644 --- a/springboot-backend/src/main/java/com/admin/entity/Node.java +++ b/springboot-backend/src/main/java/com/admin/entity/Node.java @@ -20,9 +20,13 @@ public class Node extends BaseEntity { private String name; - private String secret; - - private String serverIp; + private String secret; + + private String serverIp; + + private String serverIpV4; + + private String serverIpV6; private String version; @@ -45,5 +49,4 @@ public class Node extends BaseEntity { * Lower value appears first. */ private Integer inx; - } diff --git a/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java b/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java index b8f75e4..b711317 100644 --- a/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java +++ b/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java @@ -21,7 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; - + import javax.annotation.Resource; import java.util.HashMap; import java.util.ArrayList; @@ -54,21 +54,28 @@ public class NodeServiceImpl extends ServiceImpl implements No @Override - public R createNode(NodeDto nodeDto) { - validatePortRange(nodeDto.getPort()); - Node node = new Node(); - node.setSecret(IdUtil.simpleUUID()); - node.setStatus(0); - node.setPort(nodeDto.getPort()); - node.setName(nodeDto.getName()); - node.setServerIp(nodeDto.getServerIp()); - long currentTime = System.currentTimeMillis(); - node.setCreatedTime(currentTime); - node.setUpdatedTime(currentTime); - node.setInterfaceName(nodeDto.getInterfaceName()); - this.save(node); - return R.ok(); - } + public R createNode(NodeDto nodeDto) { + validatePortRange(nodeDto.getPort()); + + String normalizedV4 = normalizeV4(nodeDto.getServerIpV4(), nodeDto.getServerIp()); + String normalizedV6 = normalizeV6(nodeDto.getServerIpV6(), nodeDto.getServerIp()); + String primaryServerIp = pickPrimaryServerIp(normalizedV4, normalizedV6, nodeDto.getServerIp()); + + Node node = new Node(); + node.setSecret(IdUtil.simpleUUID()); + node.setStatus(0); + node.setPort(nodeDto.getPort()); + node.setName(nodeDto.getName()); + node.setServerIp(primaryServerIp); + node.setServerIpV4(normalizedV4); + node.setServerIpV6(normalizedV6); + long currentTime = System.currentTimeMillis(); + node.setCreatedTime(currentTime); + node.setUpdatedTime(currentTime); + node.setInterfaceName(nodeDto.getInterfaceName()); + this.save(node); + return R.ok(); + } @Override public R getAllNodes() { @@ -388,22 +395,71 @@ public class NodeServiceImpl extends ServiceImpl implements No } - private Node buildUpdateNode(NodeUpdateDto nodeUpdateDto) { - validatePortRange(nodeUpdateDto.getPort()); - Node node = new Node(); - node.setId(nodeUpdateDto.getId()); - node.setName(nodeUpdateDto.getName()); - node.setServerIp(nodeUpdateDto.getServerIp()); - node.setPort(nodeUpdateDto.getPort()); - node.setHttp(nodeUpdateDto.getHttp()); - node.setTls(nodeUpdateDto.getTls()); - node.setSocks(nodeUpdateDto.getSocks()); - node.setUpdatedTime(System.currentTimeMillis()); - node.setInterfaceName(nodeUpdateDto.getInterfaceName()); - node.setTcpListenAddr(nodeUpdateDto.getTcpListenAddr()); - node.setUdpListenAddr(nodeUpdateDto.getUdpListenAddr()); - return node; - } + private Node buildUpdateNode(NodeUpdateDto nodeUpdateDto) { + validatePortRange(nodeUpdateDto.getPort()); + + String normalizedV4 = normalizeV4(nodeUpdateDto.getServerIpV4(), nodeUpdateDto.getServerIp()); + String normalizedV6 = normalizeV6(nodeUpdateDto.getServerIpV6(), nodeUpdateDto.getServerIp()); + String primaryServerIp = pickPrimaryServerIp(normalizedV4, normalizedV6, nodeUpdateDto.getServerIp()); + + Node node = new Node(); + node.setId(nodeUpdateDto.getId()); + node.setName(nodeUpdateDto.getName()); + node.setServerIp(primaryServerIp); + node.setServerIpV4(normalizedV4); + node.setServerIpV6(normalizedV6); + node.setPort(nodeUpdateDto.getPort()); + node.setHttp(nodeUpdateDto.getHttp()); + node.setTls(nodeUpdateDto.getTls()); + node.setSocks(nodeUpdateDto.getSocks()); + node.setUpdatedTime(System.currentTimeMillis()); + node.setInterfaceName(nodeUpdateDto.getInterfaceName()); + node.setTcpListenAddr(nodeUpdateDto.getTcpListenAddr()); + node.setUdpListenAddr(nodeUpdateDto.getUdpListenAddr()); + return node; + } + + private String pickPrimaryServerIp(String serverIpV4, String serverIpV6, String fallback) { + if (StrUtil.isNotBlank(serverIpV4)) { + return serverIpV4.trim(); + } + if (StrUtil.isNotBlank(serverIpV6)) { + return serverIpV6.trim(); + } + return fallback != null ? fallback.trim() : null; + } + + private String normalizeV4(String serverIpV4, String legacyServerIp) { + if (StrUtil.isNotBlank(serverIpV4)) { + return serverIpV4.trim(); + } + if (StrUtil.isNotBlank(legacyServerIp) && looksLikeIpv4(legacyServerIp.trim())) { + return legacyServerIp.trim(); + } + return null; + } + + private String normalizeV6(String serverIpV6, String legacyServerIp) { + if (StrUtil.isNotBlank(serverIpV6)) { + return serverIpV6.trim(); + } + if (StrUtil.isNotBlank(legacyServerIp) && looksLikeIpv6(legacyServerIp.trim())) { + return legacyServerIp.trim(); + } + return null; + } + + private boolean looksLikeIpv4(String value) { + // 仅用于判定地址族(不解析域名) + Pattern ipv4 = Pattern.compile("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); + return ipv4.matcher(value).matches(); + } + + private boolean looksLikeIpv6(String value) { + // 粗略判定 IPv6(与 GostUtil.processServerAddress 一致思路) + long colonCount = value.chars().filter(ch -> ch == ':').count(); + return colonCount >= 2; + } private void validatePortRange(String port) { diff --git a/springboot-backend/src/main/resources/schema.sql b/springboot-backend/src/main/resources/schema.sql index 1e258cb..ef324e8 100644 --- a/springboot-backend/src/main/resources/schema.sql +++ b/springboot-backend/src/main/resources/schema.sql @@ -29,6 +29,8 @@ CREATE TABLE IF NOT EXISTS node ( name VARCHAR(100) NOT NULL, secret VARCHAR(100) NOT NULL, server_ip VARCHAR(100) NOT NULL, + server_ip_v4 VARCHAR(100), + server_ip_v6 VARCHAR(100), port TEXT NOT NULL, interface_name VARCHAR(200), version VARCHAR(100),