diff --git a/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java b/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java index 07fd3cd..9c242ea 100644 --- a/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java +++ b/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java @@ -430,6 +430,15 @@ public class TunnelServiceImpl extends ServiceImpl impleme if (hasNodeChanges && tunnelUpdateDto.getInNodeId() != null) { List backupChains = deepCopyChainTunnels(oldChainTunnels); + Set oldEntryNodeIds = oldChainTunnels.stream() + .filter(ct -> ct.getChainType() != null && ct.getChainType() == 1) + .map(ChainTunnel::getNodeId) + .collect(Collectors.toSet()); + + Set newEntryNodeIds = tunnelUpdateDto.getInNodeId().stream() + .map(ChainTunnel::getNodeId) + .collect(Collectors.toSet()); + List nodeIds = new ArrayList<>(); Map nodes = new HashMap<>(); @@ -511,6 +520,8 @@ public class TunnelServiceImpl extends ServiceImpl impleme chainTunnelService.saveBatch(newChainTunnels); + syncForwardsForEntryNodeChanges(existingTunnel.getId(), oldEntryNodeIds, newEntryNodeIds); + } catch (Exception e) { chainTunnelService.saveBatch(backupChains); rebuildGostConfig(backupChains, existingTunnel); @@ -1153,5 +1164,148 @@ public class TunnelServiceImpl extends ServiceImpl impleme } } + private void syncForwardsForEntryNodeChanges(Long tunnelId, Set oldEntryNodeIds, Set newEntryNodeIds) { + Set addedNodeIds = new HashSet<>(newEntryNodeIds); + addedNodeIds.removeAll(oldEntryNodeIds); + + Set removedNodeIds = new HashSet<>(oldEntryNodeIds); + removedNodeIds.removeAll(newEntryNodeIds); + + if (addedNodeIds.isEmpty() && removedNodeIds.isEmpty()) { + return; + } + + List forwards = forwardService.list( + new QueryWrapper().eq("tunnel_id", tunnelId.intValue()) + ); + + if (forwards.isEmpty()) { + return; + } + + Tunnel tunnel = this.getById(tunnelId); + if (tunnel == null) { + return; + } + + for (Forward forward : forwards) { + if (forward.getStatus() != 1) { + continue; + } + + UserTunnel userTunnel = userTunnelService.getOne( + new QueryWrapper() + .eq("user_id", forward.getUserId()) + .eq("tunnel_id", tunnelId.intValue()) + ); + + for (Long removedNodeId : removedNodeIds) { + ForwardPort forwardPort = forwardPortService.getOne( + new QueryWrapper() + .eq("forward_id", forward.getId()) + .eq("node_id", removedNodeId) + ); + + if (forwardPort != null) { + String serviceName = buildForwardServiceName(forward.getId(), forward.getUserId(), userTunnel); + JSONArray services = new JSONArray(); + services.add(serviceName + "_tcp"); + services.add(serviceName + "_udp"); + GostUtil.DeleteService(removedNodeId, services); + + forwardPortService.removeById(forwardPort.getId()); + } + } + + for (Long addedNodeId : addedNodeIds) { + ForwardPort existingPort = forwardPortService.getOne( + new QueryWrapper() + .eq("forward_id", forward.getId()) + .eq("node_id", addedNodeId) + ); + + if (existingPort != null) { + continue; + } + + List existingPorts = forwardPortService.list( + new QueryWrapper().eq("forward_id", forward.getId()) + ); + + Integer targetPort = null; + if (!existingPorts.isEmpty()) { + targetPort = existingPorts.get(0).getPort(); + } + + Integer allocatedPort = allocatePortForNode(addedNodeId, targetPort, forward.getId()); + if (allocatedPort == null) { + System.err.println("Failed to allocate port on node " + addedNodeId + " for forward " + forward.getId()); + continue; + } + + ForwardPort newForwardPort = new ForwardPort(); + newForwardPort.setForwardId(forward.getId()); + newForwardPort.setNodeId(addedNodeId); + newForwardPort.setPort(allocatedPort); + forwardPortService.save(newForwardPort); + + Node node = nodeService.getById(addedNodeId); + if (node != null) { + String serviceName = buildForwardServiceName(forward.getId(), forward.getUserId(), userTunnel); + Integer limiter = (userTunnel != null && userTunnel.getSpeedId() != null) ? userTunnel.getSpeedId() : null; + GostUtil.AddAndUpdateService(serviceName, limiter, node, forward, newForwardPort, tunnel, "AddService"); + } + } + } + } + + private Integer allocatePortForNode(Long nodeId, Integer preferredPort, Long forwardId) { + Node node = nodeService.getById(nodeId); + if (node == null || node.getPort() == null) { + return null; + } + + Set usedPorts = new HashSet<>(); + + List chainTunnels = chainTunnelService.list( + new QueryWrapper().eq("node_id", nodeId) + ); + for (ChainTunnel ct : chainTunnels) { + if (ct.getPort() != null) { + usedPorts.add(ct.getPort()); + } + } + + List forwardPorts = forwardPortService.list( + new QueryWrapper() + .eq("node_id", nodeId) + .ne("forward_id", forwardId) + ); + for (ForwardPort fp : forwardPorts) { + if (fp.getPort() != null) { + usedPorts.add(fp.getPort()); + } + } + + List availablePorts = parsePorts(node.getPort()); + + if (preferredPort != null && availablePorts.contains(preferredPort) && !usedPorts.contains(preferredPort)) { + return preferredPort; + } + + for (Integer port : availablePorts) { + if (!usedPorts.contains(port)) { + return port; + } + } + + return null; + } + + private String buildForwardServiceName(Long forwardId, Integer userId, UserTunnel userTunnel) { + int userTunnelId = (userTunnel != null) ? userTunnel.getId() : 0; + return forwardId + "_" + userId + "_" + userTunnelId; + } + }