mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 00:06:38 +08:00
gost通讯改为ws
This commit is contained in:
@@ -3,6 +3,8 @@ package com.admin.common.dto;
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Max;
|
||||
|
||||
@Data
|
||||
public class ForwardDto {
|
||||
@@ -15,4 +17,11 @@ public class ForwardDto {
|
||||
|
||||
@NotBlank(message = "远程地址不能为空")
|
||||
private String remoteAddr;
|
||||
|
||||
/**
|
||||
* 入口端口(可选,为空时自动分配)
|
||||
*/
|
||||
@Min(value = 1, message = "端口号不能小于1")
|
||||
@Max(value = 65535, message = "端口号不能大于65535")
|
||||
private Integer inPort;
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.admin.common.dto;
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Max;
|
||||
|
||||
@Data
|
||||
public class ForwardUpdateDto {
|
||||
@@ -21,4 +23,11 @@ public class ForwardUpdateDto {
|
||||
|
||||
@NotBlank(message = "远程地址不能为空")
|
||||
private String remoteAddr;
|
||||
|
||||
/**
|
||||
* 入口端口(可选,为空时自动分配)
|
||||
*/
|
||||
@Min(value = 1, message = "端口号不能小于1")
|
||||
@Max(value = 65535, message = "端口号不能大于65535")
|
||||
private Integer inPort;
|
||||
}
|
||||
@@ -13,8 +13,9 @@ public class NodeDto {
|
||||
@NotBlank(message = "节点名称不能为空")
|
||||
private String name;
|
||||
|
||||
@NotNull(message = "控制端口不能为空")
|
||||
@Min(value = 1, message = "端口号必须在1-65535之间")
|
||||
@Max(value = 65535, message = "端口号必须在1-65535之间")
|
||||
private Integer port;
|
||||
@NotBlank(message = "入口IP不能为空")
|
||||
private String ip;
|
||||
|
||||
@NotBlank(message = "服务器ip不能为空")
|
||||
private String serverIp;
|
||||
}
|
||||
@@ -14,6 +14,9 @@ public class NodeUpdateDto {
|
||||
@NotBlank(message = "节点名称不能为空")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "节点IP不能为空")
|
||||
@NotBlank(message = "入口IP不能为空")
|
||||
private String ip;
|
||||
|
||||
@NotBlank(message = "服务器ip不能为空")
|
||||
private String serverIp;
|
||||
}
|
||||
@@ -46,4 +46,10 @@ public class TunnelDto {
|
||||
|
||||
// 协议类型(隧道转发时使用:tls、tcp、mtls),默认为tls
|
||||
private String protocol;
|
||||
|
||||
// TCP监听地址,默认为0.0.0.0
|
||||
private String tcpListenAddr = "0.0.0.0";
|
||||
|
||||
// UDP监听地址,默认为0.0.0.0
|
||||
private String udpListenAddr = "0.0.0.0";
|
||||
}
|
||||
@@ -8,6 +8,44 @@ public class TunnelListDto {
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 入口IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 入口端口范围开始
|
||||
*/
|
||||
private Integer inPortSta;
|
||||
|
||||
/**
|
||||
* 入口端口范围结束
|
||||
*/
|
||||
private Integer inPortEnd;
|
||||
|
||||
/**
|
||||
* 出口IP
|
||||
*/
|
||||
private String outIp;
|
||||
|
||||
/**
|
||||
* 出口端口范围开始
|
||||
*/
|
||||
private Integer outIpSta;
|
||||
|
||||
/**
|
||||
* 出口端口范围结束
|
||||
*/
|
||||
private Integer outIpEnd;
|
||||
|
||||
/**
|
||||
* 隧道类型(1-端口转发,2-隧道转发)
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 协议类型
|
||||
*/
|
||||
private String protocol;
|
||||
}
|
||||
|
||||
@@ -37,4 +37,10 @@ public class TunnelUpdateDto {
|
||||
@Min(value = 1, message = "出口端口结束必须大于等于0")
|
||||
@Max(value = 65535, message = "出口端口结束不能超过65535")
|
||||
private Integer outIpEnd;
|
||||
|
||||
// TCP监听地址
|
||||
private String tcpListenAddr;
|
||||
|
||||
// UDP监听地址
|
||||
private String udpListenAddr;
|
||||
}
|
||||
@@ -12,16 +12,17 @@ import com.admin.service.SpeedLimitService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@Service
|
||||
public class CheckGostConfigAsync {
|
||||
|
||||
@Resource
|
||||
@@ -33,39 +34,17 @@ public class CheckGostConfigAsync {
|
||||
@Resource
|
||||
private SpeedLimitService speedLimitService;
|
||||
|
||||
/**
|
||||
* 启动后10秒执行一次,然后每10分钟执行一次
|
||||
* 清理孤立的Gost配置项
|
||||
*/
|
||||
@Scheduled(initialDelay = 10000, fixedRate = 600000)
|
||||
public void cleanOrphanedGostConfigs() {
|
||||
log.info("开始清理孤立的Gost配置项");
|
||||
|
||||
List<Node> activeNodes = nodeService.list(new QueryWrapper<Node>().eq("status", 1));
|
||||
log.info("找到 {} 个活跃节点", activeNodes.size());
|
||||
|
||||
for (Node node : activeNodes) {
|
||||
cleanNodeConfigs(node);
|
||||
}
|
||||
|
||||
log.info("Gost配置清理任务完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理单个节点的配置
|
||||
* 清理孤立的Gost配置项
|
||||
*/
|
||||
private void cleanNodeConfigs(Node node) {
|
||||
String nodeAddress = node.getIp() + ":" + node.getPort();
|
||||
|
||||
try {
|
||||
GostConfigDto gostConfig = GostUtil.GetConfig(nodeAddress, node.getSecret());
|
||||
|
||||
@Async
|
||||
public void cleanNodeConfigs(String node_id, GostConfigDto gostConfig) {
|
||||
Node node = nodeService.getById(node_id);
|
||||
if (node != null) {
|
||||
cleanOrphanedServices(gostConfig, node);
|
||||
cleanOrphanedChains(gostConfig, node);
|
||||
cleanOrphanedLimiters(gostConfig, node);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("清理节点 {} 配置时发生错误", nodeAddress, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +55,7 @@ public class CheckGostConfigAsync {
|
||||
if (gostConfig.getServices() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String nodeAddress = node.getIp() + ":" + node.getPort();
|
||||
|
||||
|
||||
for (ConfigItem service : gostConfig.getServices()) {
|
||||
safeExecute(() -> {
|
||||
@@ -95,8 +73,15 @@ public class CheckGostConfigAsync {
|
||||
if (Objects.equals(type, "tcp")) { // 只处理TCP,避免重复处理
|
||||
Forward forward = forwardService.getById(forwardId);
|
||||
if (forward == null) {
|
||||
log.warn("删除孤立的服务: {} (节点: {})", service.getName(), nodeAddress);
|
||||
GostUtil.DeleteService(nodeAddress, forwardId+"_"+userId+"_"+userTunnelId, node.getSecret());
|
||||
log.warn("删除孤立的服务: {} (节点: {})", service.getName(), node.getId());
|
||||
GostUtil.DeleteService(node.getId(), forwardId+"_"+userId+"_"+userTunnelId);
|
||||
}
|
||||
}
|
||||
if (Objects.equals(type, "tls")) {
|
||||
Forward forward = forwardService.getById(forwardId);
|
||||
if (forward == null) {
|
||||
log.warn("删除孤立的服务: {} (节点: {})", service.getName(), node.getId());
|
||||
GostUtil.DeleteRemoteService(node.getId(), forwardId+"_"+userId+"_"+userTunnelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,8 +97,7 @@ public class CheckGostConfigAsync {
|
||||
return;
|
||||
}
|
||||
|
||||
String nodeAddress = node.getIp() + ":" + node.getPort();
|
||||
|
||||
|
||||
for (ConfigItem chain : gostConfig.getChains()) {
|
||||
safeExecute(() -> {
|
||||
String[] serviceIds = parseServiceName(chain.getName());
|
||||
@@ -126,8 +110,8 @@ public class CheckGostConfigAsync {
|
||||
if (Objects.equals(type, "chains")) {
|
||||
Forward forward = forwardService.getById(forwardId);
|
||||
if (forward == null) {
|
||||
log.warn("删除孤立的链: {} (节点: {})", chain.getName(), nodeAddress);
|
||||
GostUtil.DeleteChains(nodeAddress, forwardId+"_"+userId+"_"+userTunnelId, node.getSecret());
|
||||
log.warn("删除孤立的链: {} (节点: {})", chain.getName(), node.getId());
|
||||
GostUtil.DeleteChains(node.getId(), forwardId+"_"+userId+"_"+userTunnelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,14 +127,13 @@ public class CheckGostConfigAsync {
|
||||
return;
|
||||
}
|
||||
|
||||
String nodeAddress = node.getIp() + ":" + node.getPort();
|
||||
|
||||
|
||||
for (ConfigItem limiter : gostConfig.getLimiters()) {
|
||||
safeExecute(() -> {
|
||||
SpeedLimit speedLimit = speedLimitService.getById(limiter.getName());
|
||||
if (speedLimit == null) {
|
||||
log.warn("删除孤立的限流器: {} (节点: {})", limiter.getName(), nodeAddress);
|
||||
GostUtil.DeleteLimiters(nodeAddress, Long.parseLong(limiter.getName()), node.getSecret());
|
||||
log.warn("删除孤立的限流器: {} (节点: {})", limiter.getName(), node.getId());
|
||||
GostUtil.DeleteLimiters(node.getId(), Long.parseLong(limiter.getName()));
|
||||
}
|
||||
}, "清理限流器 " + limiter.getName());
|
||||
}
|
||||
|
||||
@@ -235,17 +235,15 @@ public class DelayQueueManager implements CommandLineRunner {
|
||||
}
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), userId, userTunnel.getId());
|
||||
String nodeAddress = buildNodeAddress(inNode);
|
||||
|
||||
// 暂停主服务
|
||||
GostDto result = GostUtil.PauseService(nodeAddress, serviceName, inNode.getSecret());
|
||||
GostDto result = GostUtil.PauseService(inNode.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeMapper.selectById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
String outNodeAddress = buildNodeAddress(outNode);
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNodeAddress, serviceName, outNode.getSecret());
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
if (!"OK".equals(remoteResult.getMsg())) {
|
||||
log.warn("暂停远端服务失败,转发ID:{},用户ID:{},服务名:{},结果:{}",
|
||||
forward.getId(), userId, serviceName, remoteResult.getMsg());
|
||||
@@ -297,16 +295,7 @@ public class DelayQueueManager implements CommandLineRunner {
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化用户账号到期延时任务
|
||||
* 查询所有非管理员的正常用户,为有到期时间且未过期的用户创建延时任务
|
||||
@@ -455,17 +444,15 @@ public class DelayQueueManager implements CommandLineRunner {
|
||||
}
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userTunnel.getUserId()), userTunnel.getId());
|
||||
String nodeAddress = buildNodeAddress(inNode);
|
||||
|
||||
// 暂停服务
|
||||
GostDto result = GostUtil.PauseService(nodeAddress, serviceName, inNode.getSecret());
|
||||
GostDto result = GostUtil.PauseService(inNode.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeMapper.selectById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
String outNodeAddress = buildNodeAddress(outNode);
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNodeAddress, serviceName, outNode.getSecret());
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
if (!"OK".equals(remoteResult.getMsg())) {
|
||||
log.warn("暂停远端服务失败,转发ID:{},用户ID:{},隧道ID:{},服务名:{},结果:{}",
|
||||
forward.getId(), userTunnel.getUserId(), userTunnel.getTunnelId(), serviceName, remoteResult.getMsg());
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.admin.common.task;
|
||||
|
||||
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SaveConfigAsync {
|
||||
|
||||
|
||||
@Async
|
||||
public void run(String addr, String secret){
|
||||
try {
|
||||
GostUtil.SaveConfig(addr, secret);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,212 +2,99 @@ package com.admin.common.utils;
|
||||
|
||||
import com.admin.common.dto.GostConfigDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.entity.Tunnel;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.aspectj.apache.bcel.generic.RET;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class GostUtil {
|
||||
|
||||
private static final String API_BASE_URL = "/api/config/";
|
||||
private static final String LIMITERS_ENDPOINT = "limiters";
|
||||
private static final String SERVICES_ENDPOINT = "services";
|
||||
private static final String CHAINS_ENDPOINT = "chains";
|
||||
|
||||
|
||||
public static GostDto SaveConfig(String addr, String secret) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("format", "json");
|
||||
|
||||
if (!addr.contains("[") && addr.indexOf(':') != addr.lastIndexOf(':')) {
|
||||
// 这是IPv6地址,找到最后一个冒号(端口分隔符)
|
||||
int lastColonIndex = addr.lastIndexOf(':');
|
||||
String ipPart = addr.substring(0, lastColonIndex);
|
||||
String portPart = addr.substring(lastColonIndex);
|
||||
addr = "[" + ipPart + "]" + portPart;
|
||||
}
|
||||
|
||||
String url = "https://" + addr + "/api/config?format=json";
|
||||
return HttpUtils.post(url, data, secret);
|
||||
}
|
||||
|
||||
public static GostConfigDto GetConfig(String addr, String secret) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("format", "json");
|
||||
|
||||
if (!addr.contains("[") && addr.indexOf(':') != addr.lastIndexOf(':')) {
|
||||
// 这是IPv6地址,找到最后一个冒号(端口分隔符)
|
||||
int lastColonIndex = addr.lastIndexOf(':');
|
||||
String ipPart = addr.substring(0, lastColonIndex);
|
||||
String portPart = addr.substring(lastColonIndex);
|
||||
addr = "[" + ipPart + "]" + portPart;
|
||||
}
|
||||
|
||||
String url = "https://" + addr + "/api/config?format=json";
|
||||
return HttpUtils.get(url, secret);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加限流器配置
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 限流器名称
|
||||
* @param speed 限速值(MB)
|
||||
* @param secret 认证密钥
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto AddLimiters(String addr, Long name, String speed, String secret) {
|
||||
public static GostDto AddLimiters(Long node_id, Long name, String speed) {
|
||||
JSONObject data = createLimiterData(name, speed);
|
||||
String url = buildUrl(addr, LIMITERS_ENDPOINT);
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "AddLimiters");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新限流器配置
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 限流器名称
|
||||
* @param speed 限速值(MB)
|
||||
* @param secret 认证密钥
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto UpdateLimiters(String addr, Long name, String speed, String secret) {
|
||||
public static GostDto UpdateLimiters(Long node_id, Long name, String speed) {
|
||||
JSONObject data = createLimiterData(name, speed);
|
||||
String url = buildUrl(addr, LIMITERS_ENDPOINT + "/" + name);
|
||||
return HttpUtils.put(url, data, secret);
|
||||
JSONObject req = new JSONObject();
|
||||
req.put("limiter", name + "");
|
||||
req.put("data", data);
|
||||
return WebSocketServer.send_msg(node_id, req, "UpdateLimiters");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除限流器配置
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 限流器名称
|
||||
* @param secret 认证密钥
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto DeleteLimiters(String addr, Long name, String secret) {
|
||||
String url = buildUrl(addr, LIMITERS_ENDPOINT + "/" + name);
|
||||
return HttpUtils.delete(url, secret);
|
||||
public static GostDto DeleteLimiters(Long node_id, Long name) {
|
||||
JSONObject req = new JSONObject();
|
||||
req.put("limiter", name + "");
|
||||
return WebSocketServer.send_msg(node_id, req, "DeleteLimiters");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建限流器数据
|
||||
*/
|
||||
private static JSONObject createLimiterData(Long name, String speed) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("name", name.toString());
|
||||
JSONArray limits = new JSONArray();
|
||||
limits.add("$ " + speed + "MB " + speed + "MB");
|
||||
data.put("limits", limits);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加服务配置(支持端口转发和隧道转发)
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 服务名称
|
||||
* @param in_port 监听端口
|
||||
* @param limiter 限流器ID
|
||||
* @param remoteAddr 远程地址(端口转发时使用)
|
||||
* @param secret 认证密钥
|
||||
* @param fow_type 转发类型:1=端口转发,2=隧道转发
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto AddService(String addr, String name, Integer in_port, Integer limiter, String remoteAddr, String secret, Integer fow_type) {
|
||||
public static GostDto AddService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel) {
|
||||
JSONArray services = new JSONArray();
|
||||
String[] protocols = {"tcp", "udp"};
|
||||
for (String protocol : protocols) {
|
||||
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type);
|
||||
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel);
|
||||
services.add(service);
|
||||
}
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch");
|
||||
return HttpUtils.post(url, services, secret);
|
||||
return WebSocketServer.send_msg(node_id, services, "AddService");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新服务配置(批量更新TCP和UDP服务)
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 服务名称
|
||||
* @param in_port 监听端口
|
||||
* @param limiter 限流器ID
|
||||
* @param remoteAddr 远程地址(端口转发时使用)
|
||||
* @param secret 认证密钥
|
||||
* @param fow_type 转发类型:1=端口转发,2=隧道转发
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto UpdateService(String addr, String name, Integer in_port, Integer limiter, String remoteAddr, String secret, Integer fow_type) {
|
||||
public static GostDto UpdateService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel) {
|
||||
JSONArray services = new JSONArray();
|
||||
String[] protocols = {"tcp", "udp"};
|
||||
for (String protocol : protocols) {
|
||||
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type);
|
||||
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel);
|
||||
services.add(service);
|
||||
}
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch");
|
||||
return HttpUtils.put(url, services, secret);
|
||||
return WebSocketServer.send_msg(node_id, services, "UpdateService");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除服务配置(批量删除TCP和UDP服务)
|
||||
*
|
||||
* @param addr 服务器地址
|
||||
* @param name 服务名称
|
||||
* @param secret 认证密钥
|
||||
* @return 请求结果
|
||||
*/
|
||||
public static GostDto DeleteService(String addr, String name, String secret) {
|
||||
public static GostDto DeleteService(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(name + "_tcp");
|
||||
services.add(name + "_udp");
|
||||
data.put("services", services);
|
||||
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch");
|
||||
return HttpUtils.delete(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "DeleteService");
|
||||
}
|
||||
|
||||
|
||||
public static GostDto PauseService(String addr, String name, String secret) {
|
||||
public static GostDto PauseService(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(name + "_tcp");
|
||||
services.add(name + "_udp");
|
||||
data.put("services", services);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch/pause");
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "PauseService");
|
||||
}
|
||||
|
||||
public static GostDto ResumeService(String addr, String name, String secret) {
|
||||
public static GostDto ResumeService(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(name + "_tcp");
|
||||
services.add(name + "_udp");
|
||||
data.put("services", services);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch/resume");
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "ResumeService");
|
||||
}
|
||||
|
||||
public static GostDto PauseRemoteService(String addr, String name, String secret) {
|
||||
public static GostDto PauseRemoteService(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(name + "_tls");
|
||||
data.put("services", services);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch/pause");
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "PauseRemoteService");
|
||||
}
|
||||
|
||||
public static GostDto ResumeRemoteService(String addr, String name, String secret) {
|
||||
public static GostDto ResumeRemoteService(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(name + "_tls");
|
||||
data.put("services", services);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/batch/resume");
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "ResumeRemoteService");
|
||||
}
|
||||
|
||||
public static GostDto AddChains(String addr, String name, String remoteAddr, String secret, String protocol) {
|
||||
public static GostDto AddChains(Long node_id, String name, String remoteAddr, String protocol) {
|
||||
JSONObject dialer = new JSONObject();
|
||||
dialer.put("type", protocol);
|
||||
|
||||
@@ -234,11 +121,10 @@ public class GostUtil {
|
||||
data.put("name", name + "_chains");
|
||||
data.put("hops", hops);
|
||||
|
||||
String url = buildUrl(addr, CHAINS_ENDPOINT);
|
||||
return HttpUtils.post(url, data, secret);
|
||||
return WebSocketServer.send_msg(node_id, data, "AddChains");
|
||||
}
|
||||
|
||||
public static GostDto UpdateChains(String addr, String name, String remoteAddr, String secret, String protocol) {
|
||||
public static GostDto UpdateChains(Long node_id, String name, String remoteAddr, String protocol) {
|
||||
JSONObject dialer = new JSONObject();
|
||||
dialer.put("type", protocol);
|
||||
|
||||
@@ -264,18 +150,19 @@ public class GostUtil {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("name", name + "_chains");
|
||||
data.put("hops", hops);
|
||||
|
||||
String url = buildUrl(addr, CHAINS_ENDPOINT + "/" + name + "_chains");
|
||||
return HttpUtils.put(url, data, secret);
|
||||
JSONObject req = new JSONObject();
|
||||
req.put("chain", name + "_chains");
|
||||
req.put("data", data);
|
||||
return WebSocketServer.send_msg(node_id, req, "UpdateChains");
|
||||
}
|
||||
|
||||
|
||||
public static GostDto DeleteChains(String addr, String name, String secret) {
|
||||
String url = buildUrl(addr, CHAINS_ENDPOINT + "/" + name + "_chains");
|
||||
return HttpUtils.delete(url, secret);
|
||||
public static GostDto DeleteChains(Long node_id, String name) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chain", name + "_chains");
|
||||
return WebSocketServer.send_msg(node_id, data, "DeleteChains");
|
||||
}
|
||||
|
||||
public static GostDto AddRemoteService(String addr, String name, Integer out_port, String remoteAddr, String secret, String protocol) {
|
||||
public static GostDto AddRemoteService(Long node_id, String name, Integer out_port, String remoteAddr, String protocol) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("name", name + "_tls");
|
||||
data.put("addr", ":" + out_port);
|
||||
@@ -293,12 +180,12 @@ public class GostUtil {
|
||||
nodes.add(node);
|
||||
forwarder.put("nodes", nodes);
|
||||
data.put("forwarder", forwarder);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT);
|
||||
return HttpUtils.post(url, data, secret);
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(data);
|
||||
return WebSocketServer.send_msg(node_id, services, "AddService");
|
||||
}
|
||||
|
||||
|
||||
public static GostDto UpdateRemoteService(String addr, String name, Integer out_port, String remoteAddr, String secret) {
|
||||
public static GostDto UpdateRemoteService(Long node_id, String name, Integer out_port, String remoteAddr) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("name", name + "_tls");
|
||||
data.put("addr", ":" + out_port);
|
||||
@@ -316,24 +203,36 @@ public class GostUtil {
|
||||
nodes.add(node);
|
||||
forwarder.put("nodes", nodes);
|
||||
data.put("forwarder", forwarder);
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/" + name + "_tls");
|
||||
return HttpUtils.put(url, data, secret);
|
||||
JSONArray services = new JSONArray();
|
||||
services.add(data);
|
||||
return WebSocketServer.send_msg(node_id, services, "UpdateService");
|
||||
}
|
||||
|
||||
|
||||
public static GostDto DeleteRemoteService(String addr, String name, String secret) {
|
||||
String url = buildUrl(addr, SERVICES_ENDPOINT + "/" + name + "_tls");
|
||||
return HttpUtils.delete(url, secret);
|
||||
public static GostDto DeleteRemoteService(Long node_id, String name) {
|
||||
JSONArray data = new JSONArray();
|
||||
data.add(name + "_tls");
|
||||
JSONObject req = new JSONObject();
|
||||
req.put("services", data);
|
||||
return WebSocketServer.send_msg(node_id, req, "DeleteService");
|
||||
}
|
||||
|
||||
private static JSONObject createLimiterData(Long name, String speed) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("name", name.toString());
|
||||
JSONArray limits = new JSONArray();
|
||||
limits.add("$ " + speed + "MB " + speed + "MB");
|
||||
data.put("limits", limits);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个服务配置
|
||||
*/
|
||||
private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type) {
|
||||
private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type, Tunnel tunnel) {
|
||||
JSONObject service = new JSONObject();
|
||||
service.put("name", name + "_" + protocol);
|
||||
service.put("addr", ":" + in_port);
|
||||
if (Objects.equals(protocol, "tcp")){
|
||||
service.put("addr", tunnel.getTcpListenAddr() + ":" + in_port);
|
||||
}else {
|
||||
service.put("addr", tunnel.getUdpListenAddr() + ":" + in_port);
|
||||
}
|
||||
|
||||
// 添加限流器配置
|
||||
if (limiter != null) {
|
||||
@@ -357,9 +256,6 @@ public class GostUtil {
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建处理器配置
|
||||
*/
|
||||
private static JSONObject createHandler(String protocol, String name, Integer fow_type) {
|
||||
JSONObject handler = new JSONObject();
|
||||
handler.put("type", protocol);
|
||||
@@ -372,18 +268,12 @@ public class GostUtil {
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建监听器配置
|
||||
*/
|
||||
private static JSONObject createListener(String protocol) {
|
||||
JSONObject listener = new JSONObject();
|
||||
listener.put("type", protocol);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建转发器配置
|
||||
*/
|
||||
private static JSONObject createForwarder(String protocol, String remoteAddr) {
|
||||
JSONObject forwarder = new JSONObject();
|
||||
JSONArray nodes = new JSONArray();
|
||||
@@ -395,33 +285,12 @@ public class GostUtil {
|
||||
return forwarder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为端口转发
|
||||
*/
|
||||
private static boolean isPortForwarding(Integer fow_type) {
|
||||
return fow_type != null && fow_type == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为隧道转发
|
||||
*/
|
||||
private static boolean isTunnelForwarding(Integer fow_type) {
|
||||
return fow_type != null && fow_type != 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建API URL
|
||||
*/
|
||||
private static String buildUrl(String addr, String endpoint) {
|
||||
// 如果是IPv6地址(包含多个冒号且不包含方括号),需要用方括号包裹IP部分
|
||||
if (!addr.contains("[") && addr.indexOf(':') != addr.lastIndexOf(':')) {
|
||||
// 这是IPv6地址,找到最后一个冒号(端口分隔符)
|
||||
int lastColonIndex = addr.lastIndexOf(':');
|
||||
String ipPart = addr.substring(0, lastColonIndex);
|
||||
String portPart = addr.substring(lastColonIndex);
|
||||
addr = "[" + ipPart + "]" + portPart;
|
||||
}
|
||||
return "https://" + addr + API_BASE_URL + endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,9 @@ package com.admin.common.utils;
|
||||
|
||||
import com.admin.common.dto.GostConfigDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.task.SaveConfigAsync;
|
||||
import com.admin.config.RestTemplateConfig;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -29,7 +14,6 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@@ -41,251 +25,7 @@ import java.util.*;
|
||||
* 支持GET和POST请求,支持表单和JSON格式的请求体
|
||||
*/
|
||||
@Component
|
||||
public class HttpUtils implements ApplicationContextAware {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
// 10秒超时配置
|
||||
private static final int TIMEOUT_SECONDS = 10;
|
||||
private static final int TIMEOUT_MILLISECONDS = TIMEOUT_SECONDS * 1000;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
HttpUtils.applicationContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SaveConfigAsync Bean
|
||||
*/
|
||||
private static SaveConfigAsync getSaveConfigAsync() {
|
||||
try {
|
||||
return applicationContext.getBean(SaveConfigAsync.class);
|
||||
} catch (Exception e) {
|
||||
logger.warn("无法获取SaveConfigAsync Bean: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从URL中提取IP和端口
|
||||
*/
|
||||
private static String extractIpAndPortFromUrl(String url) {
|
||||
try {
|
||||
URI uri = URI.create(url);
|
||||
String host = uri.getHost();
|
||||
int port = uri.getPort();
|
||||
if (port == -1) {
|
||||
port = uri.getScheme().equals("https") ? 443 : 80;
|
||||
}
|
||||
return host + ":" + port;
|
||||
} catch (Exception e) {
|
||||
logger.warn("无法从URL提取IP和端口: {}", url);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步保存配置
|
||||
*/
|
||||
private static void asyncSaveConfig(String url, String secret) {
|
||||
try {
|
||||
SaveConfigAsync saveConfigAsync = getSaveConfigAsync();
|
||||
if (saveConfigAsync != null) {
|
||||
String ipAndPort = extractIpAndPortFromUrl(url);
|
||||
saveConfigAsync.run(ipAndPort, secret);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("异步保存配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义错误处理器,不抛出异常,允许获取所有状态码的响应
|
||||
*/
|
||||
private static class NoOpResponseErrorHandler implements ResponseErrorHandler {
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse response) throws IOException {
|
||||
// 返回 false,让 RestTemplate 不认为任何状态码是错误
|
||||
// 这样就可以正常获取 4xx 和 5xx 的响应体
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建带超时配置的RestTemplate
|
||||
*/
|
||||
@SneakyThrows
|
||||
private static RestTemplate createRestTemplateWithTimeout() {
|
||||
|
||||
// 创建RestTemplate
|
||||
RestTemplate restTemplate = new RestTemplate(RestTemplateConfig.generateHttpRequestFactory());
|
||||
restTemplate.setErrorHandler(new NoOpResponseErrorHandler());
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
public class HttpUtils{
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public static GostConfigDto get(String url, String secret) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
String auth = secret + ":" + secret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encodedAuth);
|
||||
RestTemplate restTemplate = createRestTemplateWithTimeout();
|
||||
HttpEntity<Object> entity = new HttpEntity<>("", headers);
|
||||
try {
|
||||
ResponseEntity<GostConfigDto> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
GostConfigDto.class
|
||||
);
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GostConfigDto gostDto = new GostConfigDto();
|
||||
return gostDto;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GostDto post(String url, Object requestBody, String secret) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
String auth = secret + ":" + secret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encodedAuth);
|
||||
HttpEntity<Object> entity = new HttpEntity<>(requestBody, headers);
|
||||
RestTemplate restTemplate = createRestTemplateWithTimeout();
|
||||
try {
|
||||
ResponseEntity<GostDto> response = restTemplate.postForEntity(url, entity, GostDto.class);
|
||||
GostDto body = response.getBody();
|
||||
if (body.getMsg() != null && body.getMsg().contains("exists")) {
|
||||
body.setMsg("OK");
|
||||
}
|
||||
|
||||
if (!url.contains("/api/config?format=json")) {
|
||||
asyncSaveConfig(url, secret);
|
||||
}
|
||||
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GostDto gostDto = new GostDto();
|
||||
gostDto.setCode(500);
|
||||
gostDto.setMsg("请求失败");
|
||||
return gostDto;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GostDto put(String url, Object requestBody, String secret) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
String auth = secret + ":" + secret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encodedAuth);
|
||||
HttpEntity<Object> entity = new HttpEntity<>(requestBody, headers);
|
||||
RestTemplate restTemplate = createRestTemplateWithTimeout();
|
||||
try {
|
||||
ResponseEntity<GostDto> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.PUT,
|
||||
entity,
|
||||
GostDto.class
|
||||
);
|
||||
GostDto body = response.getBody();
|
||||
asyncSaveConfig(url, secret);
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GostDto gostDto = new GostDto();
|
||||
gostDto.setCode(500);
|
||||
gostDto.setMsg("请求失败");
|
||||
return gostDto;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GostDto delete(String url, String secret) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
|
||||
// Basic Auth
|
||||
String auth = secret + ":" + secret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encodedAuth);
|
||||
|
||||
HttpEntity<Void> entity = new HttpEntity<>(headers);
|
||||
RestTemplate restTemplate = createRestTemplateWithTimeout();
|
||||
|
||||
try {
|
||||
ResponseEntity<GostDto> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.DELETE,
|
||||
entity,
|
||||
GostDto.class
|
||||
);
|
||||
GostDto body = response.getBody();
|
||||
if (body != null && body.getMsg() != null && body.getMsg().contains("not found")) {
|
||||
body.setMsg("OK");
|
||||
}
|
||||
asyncSaveConfig(url, secret);
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GostDto gostDto = new GostDto();
|
||||
gostDto.setCode(500);
|
||||
gostDto.setMsg("请求失败");
|
||||
return gostDto;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GostDto delete(String url, JSONObject data, String secret) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
|
||||
// Basic Auth
|
||||
String auth = secret + ":" + secret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encodedAuth);
|
||||
|
||||
HttpEntity<JSONObject> entity = new HttpEntity<>(data, headers);
|
||||
RestTemplate restTemplate = createRestTemplateWithTimeout();
|
||||
|
||||
try {
|
||||
ResponseEntity<GostDto> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.DELETE,
|
||||
entity,
|
||||
GostDto.class
|
||||
);
|
||||
GostDto body = response.getBody();
|
||||
if (body != null && body.getMsg() != null && body.getMsg().contains("not found")) {
|
||||
body.setMsg("OK");
|
||||
}
|
||||
asyncSaveConfig(url, secret);
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
GostDto gostDto = new GostDto();
|
||||
gostDto.setCode(500);
|
||||
gostDto.setMsg("请求失败");
|
||||
return gostDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class JwtUtil {
|
||||
payload.put("iat", now.getTime() / 1000); // 发布时间
|
||||
payload.put("exp", expireDate.getTime() / 1000); // 过期时间
|
||||
payload.put("user", user.getUser());
|
||||
payload.put("name", user.getName());
|
||||
payload.put("name", user.getUser());
|
||||
payload.put("role_id", user.getRoleId());
|
||||
|
||||
String payloadJson = JSON.toJSONString(payload);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.admin.common.utils;
|
||||
|
||||
|
||||
import com.admin.common.dto.GostConfigDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.task.CheckGostConfigAsync;
|
||||
import com.admin.entity.Node;
|
||||
import com.admin.service.NodeService;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
@@ -14,8 +17,11 @@ import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@@ -24,25 +30,62 @@ public class WebSocketServer extends TextWebSocketHandler {
|
||||
@Resource
|
||||
NodeService nodeService;
|
||||
|
||||
// 存储所有活跃的 WebSocket 连接
|
||||
@Resource
|
||||
CheckGostConfigAsync checkGostConfigAsync;
|
||||
|
||||
// 存储所有活跃的 WebSocket 连接(
|
||||
private static final CopyOnWriteArraySet<WebSocketSession> activeSessions = new CopyOnWriteArraySet<>();
|
||||
|
||||
// 存储节点ID和对应的WebSocket session映射
|
||||
private static final ConcurrentHashMap<Long, WebSocketSession> nodeSessions = new ConcurrentHashMap<>();
|
||||
|
||||
// 为每个session提供锁对象,防止并发发送消息
|
||||
private static final ConcurrentHashMap<String, Object> sessionLocks = new ConcurrentHashMap<>();
|
||||
|
||||
// 存储等待响应的请求,key为requestId,value为CompletableFuture
|
||||
private static final ConcurrentHashMap<String, CompletableFuture<GostDto>> pendingRequests = new ConcurrentHashMap<>();
|
||||
|
||||
//接受客户端消息
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
try {
|
||||
if (StringUtils.isNoneBlank(message.getPayload())) {
|
||||
//log.info("收到消息: {}", message.getPayload());
|
||||
|
||||
String id = session.getAttributes().get("id").toString();
|
||||
String type = session.getAttributes().get("type").toString();
|
||||
|
||||
// 先发送确认消息
|
||||
sendToUser(session, "ok");
|
||||
|
||||
if (message.getPayload().contains("memory_usage")){
|
||||
// 先发送确认消息
|
||||
sendToUser(session, "{\"type\":\"call\"}");
|
||||
} else if (message.getPayload().contains("config_report")) {
|
||||
log.info("收到消息: {}", message.getPayload());
|
||||
JSONObject jsonObject = JSONObject.parseObject(message.getPayload());
|
||||
String string = jsonObject.getString("data");
|
||||
GostConfigDto gostConfigDto = JSONObject.parseObject(string, GostConfigDto.class);
|
||||
checkGostConfigAsync.cleanNodeConfigs(id, gostConfigDto);
|
||||
} else if (message.getPayload().contains("requestId")) {
|
||||
log.info("收到消息: {}", message.getPayload());
|
||||
// 处理命令响应消息
|
||||
try {
|
||||
JSONObject responseJson = JSONObject.parseObject(message.getPayload());
|
||||
String requestId = responseJson.getString("requestId");
|
||||
String responseMessage = responseJson.getString("message");
|
||||
|
||||
if (requestId != null) {
|
||||
CompletableFuture<GostDto> future = pendingRequests.remove(requestId);
|
||||
if (future != null) {
|
||||
GostDto result = new GostDto();
|
||||
result.setMsg(responseMessage != null ? responseMessage : "无响应消息");
|
||||
future.complete(result);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理响应消息失败: {}", e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
log.info("收到消息: {}", message.getPayload());
|
||||
}
|
||||
|
||||
// 如果是节点类型,转发消息给其他会话
|
||||
if (Objects.equals(type, "1")) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
@@ -70,21 +113,30 @@ public class WebSocketServer extends TextWebSocketHandler {
|
||||
try {
|
||||
String id = session.getAttributes().get("id").toString();
|
||||
String type = session.getAttributes().get("type").toString();
|
||||
|
||||
if (!Objects.equals(type, "1")) {
|
||||
// 网页管理员连接
|
||||
activeSessions.add(session);
|
||||
}else {
|
||||
Node byId = nodeService.getById(id);
|
||||
} else {
|
||||
// 客户端节点连接
|
||||
Long nodeId = Long.valueOf(id);
|
||||
nodeSessions.put(nodeId, session);
|
||||
|
||||
// 更新节点状态为在线
|
||||
Node byId = nodeService.getById(nodeId);
|
||||
if (byId != null) {
|
||||
byId.setStatus(1);
|
||||
nodeService.updateById(byId);
|
||||
|
||||
// 广播节点上线状态给所有管理员
|
||||
JSONObject res = new JSONObject();
|
||||
res.put("id", id);
|
||||
res.put("type", "status");
|
||||
res.put("data", 1);
|
||||
broadcastMessage(res.toJSONString());
|
||||
}
|
||||
|
||||
}
|
||||
log.info("WebSocket 连接建立成功 - id: {}, type: {}, 当前连接数: {}", id, type, activeSessions.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("建立连接时发生异常: {}", e.getMessage(), e);
|
||||
@@ -100,25 +152,35 @@ public class WebSocketServer extends TextWebSocketHandler {
|
||||
String sessionId = session.getId();
|
||||
|
||||
if (!Objects.equals(type, "1")) {
|
||||
// 连接关闭
|
||||
activeSessions.remove(session);
|
||||
}else {
|
||||
Node byId = nodeService.getById(id);
|
||||
} else {
|
||||
// 客户端节点连接关闭
|
||||
Long nodeId = Long.valueOf(id);
|
||||
nodeSessions.remove(nodeId);
|
||||
|
||||
// 更新节点状态为离线
|
||||
Node byId = nodeService.getById(nodeId);
|
||||
if (byId != null) {
|
||||
byId.setStatus(0);
|
||||
nodeService.updateById(byId);
|
||||
|
||||
JSONObject res = new JSONObject();
|
||||
res.put("id", id);
|
||||
res.put("type", "status");
|
||||
res.put("data", 0);
|
||||
broadcastMessage(res.toJSONString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 清理session锁对象
|
||||
sessionLocks.remove(sessionId);
|
||||
|
||||
log.info("WebSocket 连接关闭 - id: {}, sessionId: {}, 关闭状态: {}, 当前连接数: {}",
|
||||
id, sessionId, status, activeSessions.size());
|
||||
|
||||
// 清理该节点的待处理请求
|
||||
if (Objects.equals(type, "1")) {
|
||||
clearPendingRequestsForNode(Long.valueOf(id));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("关闭连接时发生异常: {}", e.getMessage(), e);
|
||||
@@ -139,15 +201,34 @@ public class WebSocketServer extends TextWebSocketHandler {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送WebSocket消息失败 [sessionId={}]: {}", sessionId, e.getMessage());
|
||||
activeSessions.remove(socketSession);
|
||||
sessionLocks.remove(sessionId);
|
||||
cleanupSession(socketSession);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activeSessions.remove(socketSession);
|
||||
if (socketSession != null) {
|
||||
sessionLocks.remove(socketSession.getId());
|
||||
}
|
||||
cleanupSession(socketSession);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理失效的session,自动识别是节点session还是管理员session
|
||||
*/
|
||||
private static void cleanupSession(WebSocketSession session) {
|
||||
if (session == null) return;
|
||||
|
||||
String sessionId = session.getId();
|
||||
|
||||
// 清理session锁
|
||||
sessionLocks.remove(sessionId);
|
||||
|
||||
boolean removedFromAdmin = activeSessions.remove(session);
|
||||
|
||||
if (!removedFromAdmin) {
|
||||
nodeSessions.entrySet().removeIf(entry -> {
|
||||
if (entry.getValue() == session) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,4 +238,69 @@ public class WebSocketServer extends TextWebSocketHandler {
|
||||
sendToUser(session, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理指定节点的待处理请求
|
||||
*/
|
||||
private static void clearPendingRequestsForNode(Long nodeId) {
|
||||
// 完成所有待处理的请求,设置为连接断开错误
|
||||
pendingRequests.entrySet().removeIf(entry -> {
|
||||
CompletableFuture<GostDto> future = entry.getValue();
|
||||
if (!future.isDone()) {
|
||||
GostDto errorResult = new GostDto();
|
||||
errorResult.setMsg("节点连接已断开");
|
||||
future.complete(errorResult);
|
||||
}
|
||||
return true; // 移除所有请求
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static GostDto send_msg(Long node_id, Object msg, String type) {
|
||||
WebSocketSession nodeSession = nodeSessions.get(node_id);
|
||||
|
||||
if (nodeSession == null) {
|
||||
GostDto result = new GostDto();
|
||||
result.setMsg("节点不在线");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!nodeSession.isOpen()) {
|
||||
nodeSessions.remove(node_id);
|
||||
sessionLocks.remove(nodeSession.getId());
|
||||
GostDto result = new GostDto();
|
||||
result.setMsg("节点连接已断开");
|
||||
return result;
|
||||
}
|
||||
|
||||
// 生成唯一的请求ID
|
||||
String requestId = UUID.randomUUID().toString();
|
||||
|
||||
// 创建CompletableFuture用于等待响应
|
||||
CompletableFuture<GostDto> future = new CompletableFuture<>();
|
||||
pendingRequests.put(requestId, future);
|
||||
|
||||
try {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("type", type);
|
||||
data.put("data", msg);
|
||||
data.put("requestId", requestId);
|
||||
sendToUser(nodeSession, data.toJSONString());
|
||||
GostDto result = future.get(10, TimeUnit.SECONDS);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
pendingRequests.remove(requestId);
|
||||
GostDto result = new GostDto();
|
||||
if (e instanceof java.util.concurrent.TimeoutException) {
|
||||
result.setMsg("等待响应超时");
|
||||
} else {
|
||||
result.setMsg("发送消息失败: " + e.getMessage());
|
||||
}
|
||||
log.error("发送消息到节点{}失败: {}", node_id, e.getMessage(), e);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -35,14 +35,10 @@ public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
|
||||
String secret = serverHttpRequest.getServletRequest().getParameter("secret");
|
||||
String type = serverHttpRequest.getServletRequest().getParameter("type");
|
||||
if (Objects.equals(type, "1")) {
|
||||
String client_ip = serverHttpRequest.getServletRequest().getParameter("client_ip");
|
||||
Node node = nodeService.getOne(new QueryWrapper<Node>().eq("secret", secret));
|
||||
if (node == null) return false;
|
||||
attributes.put("id", node.getId());
|
||||
node.setStatus(1);
|
||||
if (node.getIp() == null){
|
||||
node.setIp(client_ip);
|
||||
}
|
||||
nodeService.updateById(node);
|
||||
}else {
|
||||
boolean b = JwtUtil.validateToken(secret);
|
||||
|
||||
@@ -44,7 +44,6 @@ public class FlowController extends BaseController {
|
||||
|
||||
// 常量定义
|
||||
private static final String SUCCESS_RESPONSE = "ok";
|
||||
private static final String ERROR_RESPONSE = "err1";
|
||||
private static final String DEFAULT_USER_TUNNEL_ID = "0";
|
||||
private static final int FLOW_TYPE_UPLOAD_ONLY = 1;
|
||||
private static final int FLOW_TYPE_BIDIRECTIONAL = 2;
|
||||
@@ -73,7 +72,7 @@ public class FlowController extends BaseController {
|
||||
public String uploadFlowData(@RequestBody List<FlowDto> flowDataList, String secret) {
|
||||
// 1. 验证节点权限
|
||||
if (!isValidNode(secret)) {
|
||||
return ERROR_RESPONSE;
|
||||
return SUCCESS_RESPONSE;
|
||||
}
|
||||
|
||||
List<FlowDto> validFlowData = flowDataList;
|
||||
@@ -266,13 +265,13 @@ public class FlowController extends BaseController {
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (node != null) {
|
||||
String serviceName = buildServiceName(forwardId, userId, userTunnelId);
|
||||
GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
GostUtil.PauseService(node.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,13 +295,13 @@ public class FlowController extends BaseController {
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (node != null) {
|
||||
String serviceName = buildServiceName(forwardId, userId, userTunnelId);
|
||||
GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
GostUtil.PauseService(node.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,13 +391,13 @@ public class FlowController extends BaseController {
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (node != null) {
|
||||
String serviceName = buildServiceName(String.valueOf(forward.getId()), userId, userTunnelId);
|
||||
GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
GostUtil.PauseService(node.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,13 +422,13 @@ public class FlowController extends BaseController {
|
||||
// 查找该转发对应的正确userTunnelId
|
||||
String actualUserTunnelId = findActualUserTunnelId(userId, forward.getTunnelId().toString());
|
||||
String serviceName = buildServiceName(String.valueOf(forward.getId()), userId, actualUserTunnelId);
|
||||
GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
GostUtil.PauseService(node.getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == 2) { // TUNNEL_TYPE_TUNNEL_FORWARD
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
GostUtil.PauseRemoteService(outNode.getId(), serviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ public class Node extends BaseEntity {
|
||||
|
||||
private String ip;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private String serverIp;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,4 +77,8 @@ public class Tunnel extends BaseEntity {
|
||||
* 协议类型(隧道转发时使用:tls、tcp、mtls)
|
||||
*/
|
||||
private String protocol;
|
||||
|
||||
private String tcpListenAddr;
|
||||
|
||||
private String udpListenAddr;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ public class User extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
private String user;
|
||||
|
||||
private String pwd;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.ForwardDto;
|
||||
import com.admin.common.dto.ForwardUpdateDto;
|
||||
import com.admin.common.dto.ForwardWithTunnelDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.task.SaveConfigAsync;
|
||||
import com.admin.common.utils.GostUtil;
|
||||
import com.admin.common.utils.JwtUtil;
|
||||
import com.admin.entity.*;
|
||||
@@ -15,13 +13,12 @@ import com.admin.service.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -35,6 +32,7 @@ import java.util.stream.Collectors;
|
||||
* @author QAQ
|
||||
* @since 2025-06-03
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> implements ForwardService {
|
||||
|
||||
@@ -86,7 +84,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
// 4. 分配端口
|
||||
PortAllocation portAllocation = allocatePorts(tunnel);
|
||||
PortAllocation portAllocation = allocatePorts(tunnel, forwardDto.getInPort());
|
||||
if (portAllocation.isHasError()) {
|
||||
return R.err(portAllocation.getErrorMessage());
|
||||
}
|
||||
@@ -97,15 +95,22 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return R.err("端口转发创建失败");
|
||||
}
|
||||
|
||||
// 6. 调用Gost服务创建转发
|
||||
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter());
|
||||
// 6. 获取所需的节点信息
|
||||
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
||||
if (nodeInfo.isHasError()) {
|
||||
this.removeById(forward.getId());
|
||||
return R.err(nodeInfo.getErrorMessage());
|
||||
}
|
||||
|
||||
// 7. 调用Gost服务创建转发
|
||||
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter(),
|
||||
nodeInfo, permissionResult.getUserTunnel());
|
||||
|
||||
if (gostResult.getCode() != 0) {
|
||||
this.removeById(forward.getId());
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@@ -155,14 +160,31 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
// 5. 更新Forward对象
|
||||
Forward updatedForward = updateForwardEntity(forwardUpdateDto, existForward, tunnel);
|
||||
|
||||
// 6. 调用Gost服务更新转发
|
||||
R gostResult = updateGostServices(updatedForward, tunnel,
|
||||
permissionResult != null ? permissionResult.getLimiter() : null);
|
||||
// 6. 获取所需的节点信息
|
||||
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
||||
if (nodeInfo.isHasError()) {
|
||||
return R.err(nodeInfo.getErrorMessage());
|
||||
}
|
||||
|
||||
// 7. 调用Gost服务更新转发
|
||||
R gostResult;
|
||||
if (isTunnelChanged(existForward, forwardUpdateDto)) {
|
||||
// 隧道变化时:先删除原配置,再创建新配置
|
||||
gostResult = updateGostServicesWithTunnelChange(existForward, updatedForward, tunnel,
|
||||
permissionResult != null ? permissionResult.getLimiter() : null,
|
||||
nodeInfo, permissionResult != null ? permissionResult.getUserTunnel() : null);
|
||||
} else {
|
||||
// 隧道未变化时:直接更新配置
|
||||
gostResult = updateGostServices(updatedForward, tunnel,
|
||||
permissionResult != null ? permissionResult.getLimiter() : null,
|
||||
nodeInfo, permissionResult != null ? permissionResult.getUserTunnel() : null);
|
||||
}
|
||||
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 7. 保存更新
|
||||
updatedForward.setStatus(1);
|
||||
// 8. 保存更新
|
||||
boolean result = this.updateById(updatedForward);
|
||||
return result ? R.ok("端口转发更新成功") : R.err("端口转发更新失败");
|
||||
}
|
||||
@@ -185,19 +207,27 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
// 4. 权限检查(仅普通用户需要)
|
||||
UserTunnel userTunnel = null;
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
if (!hasUserTunnelPermission(currentUser.getUserId(), tunnel.getId().intValue())) {
|
||||
userTunnel = getUserTunnel(currentUser.getUserId(), tunnel.getId().intValue());
|
||||
if (userTunnel == null) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 调用Gost服务删除转发
|
||||
R gostResult = deleteGostServices(forward, tunnel);
|
||||
// 5. 获取所需的节点信息
|
||||
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
||||
if (nodeInfo.isHasError()) {
|
||||
return R.err(nodeInfo.getErrorMessage());
|
||||
}
|
||||
|
||||
// 6. 调用Gost服务删除转发
|
||||
R gostResult = deleteGostServices(forward, tunnel, nodeInfo, userTunnel);
|
||||
if (gostResult.getCode() != 0) {
|
||||
return gostResult;
|
||||
}
|
||||
|
||||
// 6. 删除转发记录
|
||||
// 7. 删除转发记录
|
||||
boolean result = this.removeById(id);
|
||||
if (result) {
|
||||
// 归还用户转发条数(普通用户才需要归还)
|
||||
@@ -260,6 +290,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
// 4. 恢复服务时需要额外检查
|
||||
UserTunnel userTunnel = null;
|
||||
if (targetStatus == FORWARD_STATUS_ACTIVE) {
|
||||
if (tunnel.getStatus() != TUNNEL_STATUS_ACTIVE) {
|
||||
return R.err("隧道已禁用,无法恢复服务");
|
||||
@@ -271,49 +302,50 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
if (flowCheckResult.getCode() != 0) {
|
||||
return flowCheckResult;
|
||||
}
|
||||
|
||||
userTunnel = getUserTunnel(currentUser.getUserId(), tunnel.getId().intValue());
|
||||
if (userTunnel == null) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 权限检查(仅普通用户需要)
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||
if (!hasUserTunnelPermission(currentUser.getUserId(), tunnel.getId().intValue())) {
|
||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID && userTunnel == null) {
|
||||
userTunnel = getUserTunnel(currentUser.getUserId(), tunnel.getId().intValue());
|
||||
if (userTunnel == null) {
|
||||
return R.err("你没有该隧道权限");
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 调用Gost服务
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (node == null) {
|
||||
return R.err("节点不存在");
|
||||
// 6. 获取所需的节点信息
|
||||
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
||||
if (nodeInfo.isHasError()) {
|
||||
return R.err(nodeInfo.getErrorMessage());
|
||||
}
|
||||
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
// 7. 调用Gost服务
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), userTunnel);
|
||||
GostDto gostResult;
|
||||
|
||||
if ("PauseService".equals(gostMethod)) {
|
||||
gostResult = GostUtil.PauseService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
gostResult = GostUtil.PauseService(nodeInfo.getInNode().getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时暂停远端服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD && nodeInfo.getOutNode() != null) {
|
||||
GostDto remoteResult = GostUtil.PauseRemoteService(nodeInfo.getOutNode().getId(), serviceName);
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gostResult = GostUtil.ResumeService(node.getIp() + ":" + node.getPort(), serviceName, node.getSecret());
|
||||
gostResult = GostUtil.ResumeService(nodeInfo.getInNode().getId(), serviceName);
|
||||
|
||||
// 隧道转发需要同时恢复远端服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostDto remoteResult = GostUtil.ResumeRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD && nodeInfo.getOutNode() != null) {
|
||||
GostDto remoteResult = GostUtil.ResumeRemoteService(nodeInfo.getOutNode().getId(), serviceName);
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(operation + "远端服务失败:" + remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +354,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return R.err(operation + "服务失败:" + gostResult.getMsg());
|
||||
}
|
||||
|
||||
// 7. 更新转发状态
|
||||
// 8. 更新转发状态
|
||||
forward.setStatus(targetStatus);
|
||||
forward.setUpdatedTime(System.currentTimeMillis());
|
||||
boolean result = this.updateById(forward);
|
||||
@@ -365,12 +397,32 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return forward;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所需的节点信息
|
||||
*/
|
||||
private NodeInfo getRequiredNodes(Tunnel tunnel) {
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
if (inNode == null) {
|
||||
return NodeInfo.error("入口节点不存在");
|
||||
}
|
||||
|
||||
Node outNode = null;
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode == null) {
|
||||
return NodeInfo.error("出口节点不存在");
|
||||
}
|
||||
}
|
||||
|
||||
return NodeInfo.success(inNode, outNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户权限和限制
|
||||
*/
|
||||
private UserPermissionResult checkUserPermissions(UserInfo currentUser, Tunnel tunnel, Long excludeForwardId) {
|
||||
if (currentUser.getRoleId() == ADMIN_ROLE_ID) {
|
||||
return UserPermissionResult.success(null);
|
||||
return UserPermissionResult.success(null, null);
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
@@ -404,7 +456,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return UserPermissionResult.error(quotaCheckResult.getMsg());
|
||||
}
|
||||
|
||||
return UserPermissionResult.success(userTunnel.getSpeedId());
|
||||
return UserPermissionResult.success(userTunnel.getSpeedId(), userTunnel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,15 +525,33 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
/**
|
||||
* 分配端口
|
||||
*/
|
||||
private PortAllocation allocatePorts(Tunnel tunnel) {
|
||||
Integer inPort = allocateInPort(tunnel);
|
||||
if (inPort == null) {
|
||||
return PortAllocation.error("隧道入口端口已满,无法分配新端口");
|
||||
private PortAllocation allocatePorts(Tunnel tunnel, Integer specifiedInPort) {
|
||||
return allocatePorts(tunnel, specifiedInPort, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配端口
|
||||
*/
|
||||
private PortAllocation allocatePorts(Tunnel tunnel, Integer specifiedInPort, Long excludeForwardId) {
|
||||
Integer inPort;
|
||||
|
||||
if (specifiedInPort != null) {
|
||||
// 用户指定了入口端口,需要检查是否可用
|
||||
if (!isInPortAvailable(tunnel, specifiedInPort, excludeForwardId)) {
|
||||
return PortAllocation.error("指定的入口端口 " + specifiedInPort + " 已被占用或不在允许范围内");
|
||||
}
|
||||
inPort = specifiedInPort;
|
||||
} else {
|
||||
// 用户未指定端口时自动分配
|
||||
inPort = allocateInPort(tunnel, excludeForwardId);
|
||||
if (inPort == null) {
|
||||
return PortAllocation.error("隧道入口端口已满,无法分配新端口");
|
||||
}
|
||||
}
|
||||
|
||||
Integer outPort = null;
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
outPort = allocateOutPort(tunnel);
|
||||
outPort = allocateOutPort(tunnel, excludeForwardId);
|
||||
if (outPort == null) {
|
||||
return PortAllocation.error("隧道出口端口已满,无法分配新端口");
|
||||
}
|
||||
@@ -513,13 +583,27 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
Forward forward = new Forward();
|
||||
BeanUtils.copyProperties(forwardUpdateDto, forward);
|
||||
|
||||
// 如果隧道ID发生变化,需要重新分配端口
|
||||
if (!existForward.getTunnelId().equals(forwardUpdateDto.getTunnelId())) {
|
||||
PortAllocation portAllocation = allocatePorts(tunnel);
|
||||
// 处理端口分配逻辑
|
||||
boolean tunnelChanged = !existForward.getTunnelId().equals(forwardUpdateDto.getTunnelId());
|
||||
boolean inPortChanged = forwardUpdateDto.getInPort() != null &&
|
||||
!Objects.equals(forwardUpdateDto.getInPort(), existForward.getInPort());
|
||||
|
||||
if (tunnelChanged || inPortChanged) {
|
||||
// 隧道变化或入口端口变化时需要重新分配
|
||||
Integer specifiedInPort = forwardUpdateDto.getInPort();
|
||||
// 如果没有指定新端口但隧道未变化,保持原端口
|
||||
if (specifiedInPort == null && !tunnelChanged) {
|
||||
specifiedInPort = existForward.getInPort();
|
||||
}
|
||||
|
||||
PortAllocation portAllocation = allocatePorts(tunnel, specifiedInPort, forwardUpdateDto.getId());
|
||||
if (portAllocation.isHasError()) {
|
||||
throw new RuntimeException(portAllocation.getErrorMessage());
|
||||
}
|
||||
forward.setInPort(portAllocation.getInPort());
|
||||
forward.setOutPort(portAllocation.getOutPort());
|
||||
} else {
|
||||
// 隧道未变化,保持原端口
|
||||
// 隧道和端口都未变化,保持原端口
|
||||
forward.setInPort(existForward.getInPort());
|
||||
forward.setOutPort(existForward.getOutPort());
|
||||
}
|
||||
@@ -531,27 +615,33 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
/**
|
||||
* 创建Gost服务
|
||||
*/
|
||||
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter,
|
||||
NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), userTunnel);
|
||||
|
||||
// 隧道转发需要创建链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
R chainResult = createChainService(inNode, serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol());
|
||||
R chainResult = createChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol());
|
||||
if (chainResult.getCode() != 0) {
|
||||
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
|
||||
return chainResult;
|
||||
}
|
||||
|
||||
R remoteResult = createRemoteService(tunnel.getOutNodeId().intValue(), serviceName, forward, tunnel.getProtocol());
|
||||
R remoteResult = createRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol());
|
||||
if (remoteResult.getCode() != 0) {
|
||||
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
|
||||
GostUtil.DeleteRemoteService(nodeInfo.getOutNode().getId(), serviceName);
|
||||
return remoteResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 创建主服务
|
||||
R serviceResult = createMainService(inNode, serviceName, forward, limiter, tunnel.getType());
|
||||
R serviceResult = createMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel);
|
||||
if (serviceResult.getCode() != 0) {
|
||||
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
|
||||
if (nodeInfo.getOutNode() != null) {
|
||||
GostUtil.DeleteRemoteService(nodeInfo.getOutNode().getId(), serviceName);
|
||||
}
|
||||
return serviceResult;
|
||||
}
|
||||
return R.ok();
|
||||
@@ -560,19 +650,19 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
/**
|
||||
* 更新Gost服务
|
||||
*/
|
||||
private R updateGostServices(Forward forward, Tunnel tunnel, Integer limiter) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
private R updateGostServices(Forward forward, Tunnel tunnel, Integer limiter,
|
||||
NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), userTunnel);
|
||||
|
||||
// 隧道转发需要更新链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
R chainResult = updateChainService(inNode, serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol());
|
||||
R chainResult = updateChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol());
|
||||
if (chainResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return chainResult;
|
||||
}
|
||||
|
||||
R remoteResult = updateRemoteService(tunnel.getOutNodeId().intValue(), serviceName, forward, tunnel.getProtocol());
|
||||
R remoteResult = updateRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol());
|
||||
if (remoteResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return remoteResult;
|
||||
@@ -580,7 +670,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
// 更新主服务
|
||||
R serviceResult = updateMainService(inNode, serviceName, forward, limiter, tunnel.getType());
|
||||
R serviceResult = updateMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel);
|
||||
if (serviceResult.getCode() != 0) {
|
||||
updateForwardStatusToError(forward);
|
||||
return serviceResult;
|
||||
@@ -589,30 +679,64 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 隧道变化时更新Gost服务:先删除原配置,再创建新配置
|
||||
*/
|
||||
private R updateGostServicesWithTunnelChange(Forward existForward, Forward updatedForward, Tunnel newTunnel,
|
||||
Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||
// 1. 获取原隧道信息
|
||||
Tunnel oldTunnel = tunnelService.getById(existForward.getTunnelId());
|
||||
if (oldTunnel == null) {
|
||||
return R.err("原隧道不存在,无法删除旧配置");
|
||||
}
|
||||
|
||||
// 2. 获取原隧道的节点信息
|
||||
NodeInfo oldNodeInfo = getRequiredNodes(oldTunnel);
|
||||
if (oldNodeInfo.isHasError()) {
|
||||
log.warn("获取原隧道{}的节点信息失败: {}", oldTunnel.getId(), oldNodeInfo.getErrorMessage());
|
||||
} else {
|
||||
// 3. 删除原有的Gost服务配置
|
||||
R deleteResult = deleteGostServices(existForward, oldTunnel, oldNodeInfo, userTunnel);
|
||||
if (deleteResult.getCode() != 0) {
|
||||
// 删除失败时记录日志,但不影响后续创建(可能原配置已不存在)
|
||||
log.warn("删除原隧道{}的Gost配置失败: {}", oldTunnel.getId(), deleteResult.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 创建新的Gost服务配置
|
||||
R createResult = createGostServices(updatedForward, newTunnel, limiter, nodeInfo, userTunnel);
|
||||
if (createResult.getCode() != 0) {
|
||||
updateForwardStatusToError(updatedForward);
|
||||
return R.err("创建新隧道配置失败: " + createResult.getMsg());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Gost服务
|
||||
*/
|
||||
private R deleteGostServices(Forward forward, Tunnel tunnel) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId());
|
||||
Node inNode = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
private R deleteGostServices(Forward forward, Tunnel tunnel, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), userTunnel);
|
||||
|
||||
// 删除主服务
|
||||
GostDto serviceResult = GostUtil.DeleteService(inNode.getIp() + ":" + inNode.getPort(), serviceName, inNode.getSecret());
|
||||
GostDto serviceResult = GostUtil.DeleteService(nodeInfo.getInNode().getId(), serviceName);
|
||||
if (!isGostOperationSuccess(serviceResult)) {
|
||||
return R.err(serviceResult.getMsg());
|
||||
}
|
||||
|
||||
// 隧道转发需要删除链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
GostDto chainResult = GostUtil.DeleteChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, inNode.getSecret());
|
||||
GostDto chainResult = GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
|
||||
if (!isGostOperationSuccess(chainResult)) {
|
||||
return R.err(chainResult.getMsg());
|
||||
}
|
||||
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
GostDto remoteResult = GostUtil.DeleteRemoteService(outNode.getIp() + ":" + outNode.getPort(), serviceName, outNode.getSecret());
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(remoteResult.getMsg());
|
||||
if (nodeInfo.getOutNode() != null) {
|
||||
GostDto remoteResult = GostUtil.DeleteRemoteService(nodeInfo.getOutNode().getId(), serviceName);
|
||||
if (!isGostOperationSuccess(remoteResult)) {
|
||||
return R.err(remoteResult.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,28 +748,29 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
*/
|
||||
private R createChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol) {
|
||||
String remoteAddr = outIp + ":" + outPort;
|
||||
GostDto result = GostUtil.AddChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret(), protocol);
|
||||
if (outIp.contains(":")) {
|
||||
remoteAddr = "[" + outIp + "]:" + outPort;
|
||||
}
|
||||
GostDto result = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol);
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建远程服务
|
||||
*/
|
||||
private R createRemoteService(Integer outNodeId, String serviceName, Forward forward, String protocol) {
|
||||
Node outNode = nodeService.getNodeById(outNodeId.longValue());
|
||||
GostDto result = GostUtil.AddRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
private R createRemoteService(Node outNode, String serviceName, Forward forward, String protocol) {
|
||||
GostDto result = GostUtil.AddRemoteService(outNode.getId(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret(), protocol);
|
||||
forward.getRemoteAddr(), protocol);
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建主服务
|
||||
*/
|
||||
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType) {
|
||||
GostDto result = GostUtil.AddService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel) {
|
||||
GostDto result = GostUtil.AddService(inNode.getId(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel);
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
}
|
||||
|
||||
@@ -653,12 +778,14 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
* 更新链服务
|
||||
*/
|
||||
private R updateChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol) {
|
||||
|
||||
// 创建新链
|
||||
String remoteAddr = outIp + ":" + outPort;
|
||||
GostDto createResult = GostUtil.UpdateChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret(), protocol);
|
||||
if (outIp.contains(":")) {
|
||||
remoteAddr = "[" + outIp + "]:" + outPort;
|
||||
}
|
||||
GostDto createResult = GostUtil.UpdateChains(inNode.getId(), serviceName, remoteAddr, protocol);
|
||||
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
createResult = GostUtil.AddChains(inNode.getIp() + ":" + inNode.getPort(), serviceName, remoteAddr, inNode.getSecret(), protocol);
|
||||
createResult = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol);
|
||||
}
|
||||
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
||||
}
|
||||
@@ -666,16 +793,15 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
/**
|
||||
* 更新远程服务
|
||||
*/
|
||||
private R updateRemoteService(Integer outNodeId, String serviceName, Forward forward, String protocol) {
|
||||
Node outNode = nodeService.getNodeById(outNodeId.longValue());
|
||||
private R updateRemoteService(Node outNode, String serviceName, Forward forward, String protocol) {
|
||||
// 创建新远程服务
|
||||
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getId(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret());
|
||||
forward.getRemoteAddr());
|
||||
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
createResult = GostUtil.AddRemoteService(outNode.getIp() + ":" + outNode.getPort(),
|
||||
createResult = GostUtil.AddRemoteService(outNode.getId(),
|
||||
serviceName, forward.getOutPort(),
|
||||
forward.getRemoteAddr(), outNode.getSecret(),protocol);
|
||||
forward.getRemoteAddr(),protocol);
|
||||
}
|
||||
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
||||
}
|
||||
@@ -683,15 +809,14 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
/**
|
||||
* 更新主服务
|
||||
*/
|
||||
private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType) {
|
||||
GostDto result = GostUtil.UpdateService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel) {
|
||||
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel);
|
||||
|
||||
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
result = GostUtil.AddService(inNode.getIp() + ":" + inNode.getPort(), serviceName,
|
||||
result = GostUtil.AddService(inNode.getId(), serviceName,
|
||||
forward.getInPort(), limiter, forward.getRemoteAddr(),
|
||||
inNode.getSecret(), tunnelType);
|
||||
tunnelType, tunnel);
|
||||
}
|
||||
|
||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||
@@ -705,13 +830,6 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
this.updateById(forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有用户隧道权限
|
||||
*/
|
||||
private boolean hasUserTunnelPermission(Integer userId, Integer tunnelId) {
|
||||
return getUserTunnel(userId, tunnelId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户隧道关系
|
||||
*/
|
||||
@@ -750,17 +868,67 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的入口端口
|
||||
* 检查指定的入口端口是否可用
|
||||
*/
|
||||
private Integer allocateInPort(Tunnel tunnel) {
|
||||
private boolean isInPortAvailable(Tunnel tunnel, Integer port) {
|
||||
return isInPortAvailable(tunnel, port, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定的入口端口是否可用(可排除指定的转发ID)
|
||||
*/
|
||||
private boolean isInPortAvailable(Tunnel tunnel, Integer port, Long excludeForwardId) {
|
||||
// 检查端口是否在隧道允许的范围内
|
||||
if (port < tunnel.getInPortSta() || port > tunnel.getInPortEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取所有使用相同入口节点的隧道
|
||||
List<Tunnel> tunnelsWithSameInNode = tunnelService.list(new QueryWrapper<Tunnel>().eq("in_node_id", tunnel.getInNodeId()));
|
||||
Set<Long> tunnelIds = tunnelsWithSameInNode.stream()
|
||||
.map(Tunnel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 获取这些隧道的所有转发已使用的入口端口
|
||||
List<Forward> usedForwards = this.list(new QueryWrapper<Forward>().in("tunnel_id", tunnelIds));
|
||||
// 获取这些隧道的所有转发已使用的入口端口(排除指定的转发ID)
|
||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<Forward>().in("tunnel_id", tunnelIds);
|
||||
if (excludeForwardId != null) {
|
||||
queryWrapper.ne("id", excludeForwardId);
|
||||
}
|
||||
|
||||
List<Forward> usedForwards = this.list(queryWrapper);
|
||||
Set<Integer> usedInPorts = usedForwards.stream()
|
||||
.map(Forward::getInPort)
|
||||
.filter(portNum -> portNum != null)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 检查端口是否已被占用
|
||||
return !usedInPorts.contains(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的入口端口
|
||||
*/
|
||||
private Integer allocateInPort(Tunnel tunnel) {
|
||||
return allocateInPort(tunnel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的入口端口(可排除指定的转发ID)
|
||||
*/
|
||||
private Integer allocateInPort(Tunnel tunnel, Long excludeForwardId) {
|
||||
// 获取所有使用相同入口节点的隧道
|
||||
List<Tunnel> tunnelsWithSameInNode = tunnelService.list(new QueryWrapper<Tunnel>().eq("in_node_id", tunnel.getInNodeId()));
|
||||
Set<Long> tunnelIds = tunnelsWithSameInNode.stream()
|
||||
.map(Tunnel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 获取这些隧道的所有转发已使用的入口端口(排除指定的转发ID)
|
||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<Forward>().in("tunnel_id", tunnelIds);
|
||||
if (excludeForwardId != null) {
|
||||
queryWrapper.ne("id", excludeForwardId);
|
||||
}
|
||||
|
||||
List<Forward> usedForwards = this.list(queryWrapper);
|
||||
Set<Integer> usedInPorts = usedForwards.stream()
|
||||
.map(Forward::getInPort)
|
||||
.filter(port -> port != null)
|
||||
@@ -779,14 +947,26 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
* 为隧道分配一个可用的出口端口
|
||||
*/
|
||||
private Integer allocateOutPort(Tunnel tunnel) {
|
||||
return allocateOutPort(tunnel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为隧道分配一个可用的出口端口(可排除指定的转发ID)
|
||||
*/
|
||||
private Integer allocateOutPort(Tunnel tunnel, Long excludeForwardId) {
|
||||
// 获取所有使用相同出口节点的隧道
|
||||
List<Tunnel> tunnelsWithSameOutNode = tunnelService.list(new QueryWrapper<Tunnel>().eq("out_node_id", tunnel.getOutNodeId()));
|
||||
Set<Long> tunnelIds = tunnelsWithSameOutNode.stream()
|
||||
.map(Tunnel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 获取这些隧道的所有转发已使用的出口端口
|
||||
List<Forward> usedForwards = this.list(new QueryWrapper<Forward>().in("tunnel_id", tunnelIds));
|
||||
// 获取这些隧道的所有转发已使用的出口端口(排除指定的转发ID)
|
||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<Forward>().in("tunnel_id", tunnelIds);
|
||||
if (excludeForwardId != null) {
|
||||
queryWrapper.ne("id", excludeForwardId);
|
||||
}
|
||||
|
||||
List<Forward> usedForwards = this.list(queryWrapper);
|
||||
Set<Integer> usedOutPorts = usedForwards.stream()
|
||||
.map(Forward::getOutPort)
|
||||
.filter(port -> port != null)
|
||||
@@ -802,16 +982,10 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建服务名称,确保管理员和用户操作的一致性
|
||||
* 构建服务名称,优化后减少重复查询
|
||||
*/
|
||||
private String buildServiceName(Long forwardId, Integer userId, Integer tunnelId) {
|
||||
// 根据userId和tunnelId查询UserTunnel获取正确的user_tunnel_id
|
||||
UserTunnel userTunnel = userTunnelService.getOne(new QueryWrapper<UserTunnel>()
|
||||
.eq("user_id", userId)
|
||||
.eq("tunnel_id", tunnelId));
|
||||
|
||||
private String buildServiceName(Long forwardId, Integer userId, Integer tunnelId, UserTunnel userTunnel) {
|
||||
int userTunnelId = (userTunnel != null) ? userTunnel.getId() : 0;
|
||||
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
@@ -825,7 +999,6 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
private final Integer userId;
|
||||
private final Integer roleId;
|
||||
private final String userName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,19 +1009,21 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Integer limiter;
|
||||
private final UserTunnel userTunnel;
|
||||
|
||||
private UserPermissionResult(boolean hasError, String errorMessage, Integer limiter) {
|
||||
private UserPermissionResult(boolean hasError, String errorMessage, Integer limiter, UserTunnel userTunnel) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.limiter = limiter;
|
||||
this.userTunnel = userTunnel;
|
||||
}
|
||||
|
||||
public static UserPermissionResult success(Integer limiter) {
|
||||
return new UserPermissionResult(false, null, limiter);
|
||||
public static UserPermissionResult success(Integer limiter, UserTunnel userTunnel) {
|
||||
return new UserPermissionResult(false, null, limiter, userTunnel);
|
||||
}
|
||||
|
||||
public static UserPermissionResult error(String errorMessage) {
|
||||
return new UserPermissionResult(true, errorMessage, null);
|
||||
return new UserPermissionResult(true, errorMessage, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,4 +1052,30 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
||||
return new PortAllocation(true, errorMessage, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点信息封装类
|
||||
*/
|
||||
@Data
|
||||
private static class NodeInfo {
|
||||
private final boolean hasError;
|
||||
private final String errorMessage;
|
||||
private final Node inNode;
|
||||
private final Node outNode;
|
||||
|
||||
private NodeInfo(boolean hasError, String errorMessage, Node inNode, Node outNode) {
|
||||
this.hasError = hasError;
|
||||
this.errorMessage = errorMessage;
|
||||
this.inNode = inNode;
|
||||
this.outNode = outNode;
|
||||
}
|
||||
|
||||
public static NodeInfo success(Node inNode, Node outNode) {
|
||||
return new NodeInfo(false, null, inNode, outNode);
|
||||
}
|
||||
|
||||
public static NodeInfo error(String errorMessage) {
|
||||
return new NodeInfo(true, errorMessage, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements No
|
||||
node.setId(nodeUpdateDto.getId());
|
||||
node.setName(nodeUpdateDto.getName());
|
||||
node.setIp(nodeUpdateDto.getIp());
|
||||
node.setServerIp(nodeUpdateDto.getServerIp());
|
||||
node.setUpdatedTime(System.currentTimeMillis());
|
||||
return node;
|
||||
}
|
||||
@@ -297,15 +298,68 @@ public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements No
|
||||
StringBuilder command = new StringBuilder();
|
||||
|
||||
// 第一部分:下载安装脚本
|
||||
command.append("curl -L https://raw.githubusercontent.com/bqlpfy/forward-panel/refs/heads/main/install.sh")
|
||||
command.append("curl -L https://ghproxy.com/https://raw.githubusercontent.com/bqlpfy/forward-panel/refs/heads/main/install.sh")
|
||||
.append(" -o ./install.sh && chmod +x ./install.sh && ");
|
||||
|
||||
// 处理服务器地址,如果是IPv6需要添加方括号
|
||||
String processedServerAddr = processServerAddress(serverAddr);
|
||||
|
||||
// 第二部分:执行安装脚本(去掉-u参数)
|
||||
command.append("./install.sh")
|
||||
.append(" -a ").append(serverAddr) // 服务器地址
|
||||
.append(" -p ").append(node.getPort()) // 节点端口
|
||||
.append(" -s ").append(node.getSecret()); // 节点密钥
|
||||
.append(" -a ").append(processedServerAddr) // 服务器地址
|
||||
.append(" -s ").append(node.getSecret()); // 节点密钥
|
||||
|
||||
return command.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务器地址,确保IPv6地址被方括号包裹
|
||||
*
|
||||
* @param serverAddr 原始服务器地址,格式可能为 host:port
|
||||
* @return 处理后的服务器地址
|
||||
*/
|
||||
private String processServerAddress(String serverAddr) {
|
||||
if (StrUtil.isBlank(serverAddr)) {
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
// 如果已经被方括号包裹,直接返回
|
||||
if (serverAddr.startsWith("[")) {
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
// 查找最后一个冒号,分离主机和端口
|
||||
int lastColonIndex = serverAddr.lastIndexOf(':');
|
||||
if (lastColonIndex == -1) {
|
||||
// 没有端口号,直接检查是否需要包裹
|
||||
return isIPv6Address(serverAddr) ? "[" + serverAddr + "]" : serverAddr;
|
||||
}
|
||||
|
||||
String host = serverAddr.substring(0, lastColonIndex);
|
||||
String port = serverAddr.substring(lastColonIndex);
|
||||
|
||||
// 检查主机部分是否为IPv6地址
|
||||
if (isIPv6Address(host)) {
|
||||
return "[" + host + "]" + port;
|
||||
}
|
||||
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为IPv6地址
|
||||
*
|
||||
* @param address 地址字符串(不包含端口号)
|
||||
* @return 是否为IPv6地址
|
||||
*/
|
||||
private boolean isIPv6Address(String address) {
|
||||
// IPv6地址包含多个冒号,至少2个
|
||||
if (!address.contains(":")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算冒号数量,IPv6地址至少有2个冒号
|
||||
long colonCount = address.chars().filter(ch -> ch == ':').count();
|
||||
return colonCount >= 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,10 +289,9 @@ public class SpeedLimitServiceImpl extends ServiceImpl<SpeedLimitMapper, SpeedLi
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
|
||||
GostDto gostResult = GostUtil.AddLimiters(
|
||||
buildNodeAddress(node),
|
||||
node.getId(),
|
||||
speedLimit.getId(),
|
||||
speedInMBps,
|
||||
node.getSecret()
|
||||
speedInMBps
|
||||
);
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
@@ -308,14 +307,13 @@ public class SpeedLimitServiceImpl extends ServiceImpl<SpeedLimitMapper, SpeedLi
|
||||
private R updateGostLimiter(SpeedLimit speedLimit, Tunnel tunnel) {
|
||||
String speedInMBps = convertBitsToMBps(speedLimit.getSpeed());
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
String nodeAddress = buildNodeAddress(node);
|
||||
|
||||
// 尝试更新限速器
|
||||
GostDto gostResult = GostUtil.UpdateLimiters(nodeAddress, speedLimit.getId(), speedInMBps, node.getSecret());
|
||||
GostDto gostResult = GostUtil.UpdateLimiters(node.getId(), speedLimit.getId(), speedInMBps);
|
||||
|
||||
// 如果限速器不存在,则创建新的
|
||||
if (gostResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||
gostResult = GostUtil.AddLimiters(nodeAddress, speedLimit.getId(), speedInMBps, node.getSecret());
|
||||
gostResult = GostUtil.AddLimiters(node.getId(), speedLimit.getId(), speedInMBps);
|
||||
}
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
@@ -330,7 +328,7 @@ public class SpeedLimitServiceImpl extends ServiceImpl<SpeedLimitMapper, SpeedLi
|
||||
*/
|
||||
private R deleteGostLimiter(Long speedLimitId, Tunnel tunnel) {
|
||||
Node node = nodeService.getNodeById(tunnel.getInNodeId());
|
||||
GostDto gostResult = GostUtil.DeleteLimiters(buildNodeAddress(node), speedLimitId, node.getSecret());
|
||||
GostDto gostResult = GostUtil.DeleteLimiters(node.getId(), speedLimitId);
|
||||
|
||||
return isGostOperationSuccess(gostResult) ? R.ok() : R.err(gostResult.getMsg());
|
||||
}
|
||||
@@ -345,16 +343,6 @@ public class SpeedLimitServiceImpl extends ServiceImpl<SpeedLimitMapper, SpeedLi
|
||||
speedLimitService.updateById(speedLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将比特率转换为兆字节每秒
|
||||
*
|
||||
|
||||
@@ -193,6 +193,14 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
|
||||
existingTunnel.setInPortSta(tunnelUpdateDto.getInPortSta());
|
||||
existingTunnel.setInPortEnd(tunnelUpdateDto.getInPortEnd());
|
||||
|
||||
// 更新TCP和UDP监听地址
|
||||
if (StrUtil.isNotBlank(tunnelUpdateDto.getTcpListenAddr())) {
|
||||
existingTunnel.setTcpListenAddr(tunnelUpdateDto.getTcpListenAddr());
|
||||
}
|
||||
if (StrUtil.isNotBlank(tunnelUpdateDto.getUdpListenAddr())) {
|
||||
existingTunnel.setUdpListenAddr(tunnelUpdateDto.getUdpListenAddr());
|
||||
}
|
||||
|
||||
if (existingTunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
existingTunnel.setOutIpSta(tunnelUpdateDto.getOutIpSta());
|
||||
existingTunnel.setOutIpEnd(tunnelUpdateDto.getOutIpEnd());
|
||||
@@ -400,7 +408,7 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
|
||||
|
||||
// 设置入口节点信息
|
||||
tunnel.setInNodeId(tunnelDto.getInNodeId());
|
||||
tunnel.setInIp(inNode.getIp());
|
||||
tunnel.setInIp(inNode.getServerIp());
|
||||
|
||||
// 设置流量计算类型
|
||||
tunnel.setFlow(tunnelDto.getFlow());
|
||||
@@ -415,6 +423,12 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
|
||||
tunnel.setProtocol(null);
|
||||
}
|
||||
|
||||
// 设置TCP和UDP监听地址
|
||||
tunnel.setTcpListenAddr(StrUtil.isNotBlank(tunnelDto.getTcpListenAddr()) ?
|
||||
tunnelDto.getTcpListenAddr() : "0.0.0.0");
|
||||
tunnel.setUdpListenAddr(StrUtil.isNotBlank(tunnelDto.getUdpListenAddr()) ?
|
||||
tunnelDto.getUdpListenAddr() : "0.0.0.0");
|
||||
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
@@ -496,7 +510,7 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
|
||||
|
||||
// 设置出口参数
|
||||
tunnel.setOutNodeId(tunnelDto.getOutNodeId());
|
||||
tunnel.setOutIp(outNode.getIp());
|
||||
tunnel.setOutIp(outNode.getServerIp());
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
@@ -651,6 +665,14 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
|
||||
TunnelListDto dto = new TunnelListDto();
|
||||
dto.setId(tunnel.getId().intValue());
|
||||
dto.setName(tunnel.getName());
|
||||
dto.setIp(tunnel.getInIp());
|
||||
dto.setInPortSta(tunnel.getInPortSta());
|
||||
dto.setInPortEnd(tunnel.getInPortEnd());
|
||||
dto.setOutIp(tunnel.getOutIp());
|
||||
dto.setOutIpSta(tunnel.getOutIpSta());
|
||||
dto.setOutIpEnd(tunnel.getOutIpEnd());
|
||||
dto.setType(tunnel.getType());
|
||||
dto.setProtocol(tunnel.getProtocol());
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
@@ -573,7 +573,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
String serviceName = buildServiceName(forward.getId(), userId, userTunnel.getId());
|
||||
|
||||
// 删除主服务
|
||||
GostUtil.DeleteService(buildNodeAddress(inNode), serviceName, inNode.getSecret());
|
||||
GostUtil.DeleteService(inNode.getId(), serviceName);
|
||||
|
||||
// 如果是隧道转发,还需要删除链和远程服务
|
||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||
@@ -591,8 +591,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
private void deleteGostTunnelForwardServices(Tunnel tunnel, String serviceName, Node inNode) {
|
||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||
if (outNode != null) {
|
||||
GostUtil.DeleteChains(buildNodeAddress(inNode), serviceName, inNode.getSecret());
|
||||
GostUtil.DeleteRemoteService(buildNodeAddress(outNode), serviceName, outNode.getSecret());
|
||||
GostUtil.DeleteChains(inNode.getId(), serviceName);
|
||||
GostUtil.DeleteRemoteService(outNode.getId(), serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,15 +621,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户隧道权限
|
||||
@@ -700,7 +691,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
private UserPackageDto.UserInfoDto buildUserInfoDto(User user) {
|
||||
UserPackageDto.UserInfoDto userInfo = new UserPackageDto.UserInfoDto();
|
||||
userInfo.setId(user.getId());
|
||||
userInfo.setName(user.getName());
|
||||
userInfo.setUser(user.getUser());
|
||||
userInfo.setStatus(user.getStatus());
|
||||
userInfo.setFlow(user.getFlow());
|
||||
|
||||
@@ -368,9 +368,8 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
|
||||
|
||||
// 1. 先删除主服务
|
||||
if (inNode != null) {
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
try {
|
||||
GostUtil.DeleteService(inNodeAddress, serviceName, inNode.getSecret());
|
||||
GostUtil.DeleteService(inNode.getId(), serviceName);
|
||||
} catch (Exception e) {
|
||||
// 主服务删除失败,记录但继续
|
||||
}
|
||||
@@ -378,9 +377,8 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
|
||||
|
||||
// 2. 如果是隧道转发,删除远端服务
|
||||
if (tunnel.getType() == 1 && outNode != null && !outNode.getId().equals(inNode != null ? inNode.getId() : null)) {
|
||||
String outNodeAddress = buildNodeAddress(outNode);
|
||||
try {
|
||||
GostUtil.DeleteRemoteService(outNodeAddress, serviceName, outNode.getSecret());
|
||||
GostUtil.DeleteRemoteService(outNode.getId(), serviceName);
|
||||
} catch (Exception e) {
|
||||
// 远端服务删除失败,记录但继续
|
||||
}
|
||||
@@ -388,9 +386,8 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
|
||||
|
||||
// 3. 如果是隧道转发,最后删除转发链
|
||||
if (tunnel.getType() == 1 && inNode != null) {
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
try {
|
||||
GostUtil.DeleteChains(inNodeAddress, serviceName, inNode.getSecret());
|
||||
GostUtil.DeleteChains(inNode.getId(), serviceName);
|
||||
} catch (Exception e) {
|
||||
// 转发链删除失败,记录但继续
|
||||
}
|
||||
@@ -431,16 +428,7 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
|
||||
return forwardId + "_" + userId + "_" + userTunnelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建节点地址
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @return 节点地址字符串
|
||||
*/
|
||||
private String buildNodeAddress(Node node) {
|
||||
return node.getIp() + ":" + node.getPort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查用户隧道是否启用且有到期时间
|
||||
*
|
||||
@@ -507,14 +495,12 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
|
||||
}
|
||||
|
||||
// 5. 批量更新该用户在该隧道下所有转发的限速配置(只更新入口节点)
|
||||
String inNodeAddress = buildNodeAddress(inNode);
|
||||
|
||||
for (Forward forward : userTunnelForwards) {
|
||||
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnel.getId());
|
||||
|
||||
// 6. 更新入口节点的主服务限速配置(使用批量UpdateService接口)
|
||||
GostUtil.UpdateService(inNodeAddress, serviceName, forward.getInPort(), speedId,
|
||||
forward.getRemoteAddr(), inNode.getSecret(), tunnel.getType());
|
||||
GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), speedId,
|
||||
forward.getRemoteAddr(), tunnel.getType(), tunnel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user