Compare commits

...
Author SHA1 Message Date
傲世孤尘 a31b0ebc95 !28 修复端口映射页面分页查询BUG
Merge pull request !28 from tan90/dev
2023-06-22 14:17:23 +00:00
陈浩杰 fe86d786f4 fix: 修复端口映射查询BUG 2023-06-22 15:51:50 +08:00
aoshiguchen c48404e3bb 更新官网赞助记录 2023-06-12 21:19:50 +08:00
aoshiguchen 5daec7195f 更新文档. 2023-06-09 23:22:56 +08:00
aoshiguchen 533675b367 更新文档.新增打赏记录 2023-06-09 21:43:32 +08:00
aoshiguchen e547895a9c 更新文档. 2023-06-09 21:27:23 +08:00
aoshiguchen 42b7163f5b 支持指定重连间隔、是否无限重连 2023-06-09 20:54:15 +08:00
aoshiguchen 2ceacc6916 支持配置文件、启动参数指定日志级别 2023-06-09 20:41:11 +08:00
aoshiguchen 124273e8d7 日志调整. 2023-06-09 13:07:25 +08:00
aoshiguchen 5a36c88875 支持域名带端口访问 2023-06-09 12:55:24 +08:00
aoshiguchen 70e6d2ab45 更新官网文档 2023-06-08 22:13:52 +08:00
22 changed files with 226 additions and 70 deletions
@@ -1,14 +1,22 @@
package org.dromara.neutrinoproxy.client; package org.dromara.neutrinoproxy.client;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.Solon; import org.noear.solon.Solon;
import org.noear.solon.SolonApp;
import org.noear.solon.annotation.SolonMain; import org.noear.solon.annotation.SolonMain;
import org.slf4j.LoggerFactory;
/** /**
* *
* @author: aoshiguchen * @author: aoshiguchen
* @date: 2022/6/16 * @date: 2022/6/16
*/ */
@Slf4j
@SolonMain @SolonMain
public class ProxyClient { public class ProxyClient {
@@ -20,6 +28,8 @@ public class ProxyClient {
setAlias("neutrino.proxy.client.jksPath", "jksPath"); setAlias("neutrino.proxy.client.jksPath", "jksPath");
setAlias("neutrino.proxy.client.keyStorePassword", "keyStorePassword"); setAlias("neutrino.proxy.client.keyStorePassword", "keyStorePassword");
setAlias("neutrino.proxy.client.licenseKey", "licenseKey"); setAlias("neutrino.proxy.client.licenseKey", "licenseKey");
// 设置日志级别
setLogLevel(app);
}); });
} }
@@ -34,4 +44,21 @@ public class ProxyClient {
Solon.cfg().put(key, val); Solon.cfg().put(key, val);
} }
} }
private static void setLogLevel(SolonApp app) {
String loggerLevel = app.cfg().get("neutrino.proxy.logger.level");
if (StringUtils.isBlank(loggerLevel)) {
return;
}
try {
Level level = Level.toLevel(loggerLevel);
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
for (Logger logger : loggerContext.getLoggerList()) {
logger.setLevel(level);
}
} catch (Exception e) {
log.error("日志级别设置失败", e);
}
}
} }
@@ -41,5 +41,12 @@ public class ProxyConfig {
private Integer threadCount; private Integer threadCount;
private String clientId; private String clientId;
private Boolean transferLogEnable; private Boolean transferLogEnable;
private Reconnection reconnection;
}
@Data
public static class Reconnection {
private Integer intervalSeconds;
private Boolean unlimited;
} }
} }
@@ -24,7 +24,7 @@ public class CmdChannelHandler extends SimpleChannelInboundHandler<ProxyMessage>
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception {
if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) { if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) {
log.info("Client CmdChannel recieved proxy message, type is {}", proxyMessage.getType()); log.debug("Client CmdChannel recieved proxy message, type is {}", proxyMessage.getType());
} }
Solon.context().getBean(Dispatcher.class).dispatch(ctx, proxyMessage); Solon.context().getBean(Dispatcher.class).dispatch(ctx, proxyMessage);
} }
@@ -24,7 +24,7 @@ public class ProxyChannelHandler extends SimpleChannelInboundHandler<ProxyMessag
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, ProxyMessage proxyMessage) throws Exception {
if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) { if (ProxyMessage.TYPE_HEARTBEAT != proxyMessage.getType()) {
log.info("Client ProxyChannel recieved proxy message, type is {}", proxyMessage.getType()); log.debug("Client ProxyChannel recieved proxy message, type is {}", proxyMessage.getType());
} }
Solon.context().getBean(Dispatcher.class).dispatch(ctx, proxyMessage); Solon.context().getBean(Dispatcher.class).dispatch(ctx, proxyMessage);
} }
@@ -71,7 +71,7 @@ public class ProxyChannelHandler extends SimpleChannelInboundHandler<ProxyMessag
ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage()); ctx.channel().writeAndFlush(ProxyMessage.buildHeartbeatMessage());
break; break;
case ALL_IDLE: case ALL_IDLE:
log.info("读写超时"); log.debug("读写超时");
ctx.close(); ctx.close();
break; break;
} }
@@ -49,10 +49,10 @@ public class ProxyClientService {
@Inject("realServerBootstrap") @Inject("realServerBootstrap")
private Bootstrap realServerBootstrap; private Bootstrap realServerBootstrap;
private volatile Channel channel; private volatile Channel channel;
/** // /**
* 重连间隔(秒) // * 重连间隔(秒)
*/ // */
private static final long RECONNECT_INTERVAL_SECONDS = 5; // private static final long RECONNECT_INTERVAL_SECONDS = 5;
/** /**
* 重连次数 * 重连次数
*/ */
@@ -69,7 +69,7 @@ public class ProxyClientService {
@Init @Init
public void init() { public void init() {
this.reconnectExecutor.scheduleWithFixedDelay(this::reconnect, 10, RECONNECT_INTERVAL_SECONDS, TimeUnit.SECONDS); this.reconnectExecutor.scheduleWithFixedDelay(this::reconnect, 10, proxyConfig.getClient().getReconnection().getIntervalSeconds(), TimeUnit.SECONDS);
this.workerGroup = new NioEventLoopGroup(proxyConfig.getClient().getThreadCount()); this.workerGroup = new NioEventLoopGroup(proxyConfig.getClient().getThreadCount());
realServerBootstrap.group(workerGroup); realServerBootstrap.group(workerGroup);
@@ -1,6 +1,7 @@
package org.dromara.neutrinoproxy.client.handler; package org.dromara.neutrinoproxy.client.handler;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.dromara.neutrinoproxy.client.config.ProxyConfig;
import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.ExceptionEnum; import org.dromara.neutrinoproxy.core.ExceptionEnum;
import org.dromara.neutrinoproxy.core.ProxyMessage; import org.dromara.neutrinoproxy.core.ProxyMessage;
@@ -10,6 +11,7 @@ import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.noear.solon.Solon; import org.noear.solon.Solon;
import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
/** /**
* 认证信息处理器 * 认证信息处理器
@@ -20,6 +22,8 @@ import org.noear.solon.annotation.Component;
@Match(type = Constants.ProxyDataTypeName.AUTH) @Match(type = Constants.ProxyDataTypeName.AUTH)
@Component @Component
public class ProxyMessageAuthHandler implements ProxyMessageHandler { public class ProxyMessageAuthHandler implements ProxyMessageHandler {
@Inject
private ProxyConfig proxyConfig;
@Override @Override
public void handle(ChannelHandlerContext context, ProxyMessage proxyMessage) { public void handle(ChannelHandlerContext context, ProxyMessage proxyMessage) {
String info = proxyMessage.getInfo(); String info = proxyMessage.getInfo();
@@ -30,7 +34,9 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
// 客户端认证失败,直接停止服务 // 客户端认证失败,直接停止服务
log.info("client auth failed , client stop."); log.info("client auth failed , client stop.");
context.channel().close(); context.channel().close();
Solon.stop(); if (!proxyConfig.getClient().getReconnection().getUnlimited()) {
Solon.stop();
}
} else if (ExceptionEnum.CONNECT_FAILED.getCode().equals(code) || } else if (ExceptionEnum.CONNECT_FAILED.getCode().equals(code) ||
ExceptionEnum.LICENSE_CANNOT_REPEAT_CONNECT.getCode().equals(code) ExceptionEnum.LICENSE_CANNOT_REPEAT_CONNECT.getCode().equals(code)
){ ){
@@ -12,14 +12,30 @@ neutrino:
read-idle-time: 8 read-idle-time: 8
write-idle-time: 3 write-idle-time: 3
all-idle-time-seconds: 8 all-idle-time-seconds: 8
logger:
# 日志级别
level: ${LOG_LEVEL:info}
client: client:
# 线程池相关配置,用于技术调优,可忽略
thread-count: 50 thread-count: 50
# 隧道SSL证书配置
key-store-password: ${STORE_PASS:123456} key-store-password: ${STORE_PASS:123456}
jks-path: ${JKS_PATH:classpath:/test.jks} jks-path: ${JKS_PATH:classpath:/test.jks}
# 服务端IP
server-ip: ${SERVER_IP:localhost} server-ip: ${SERVER_IP:localhost}
# 服务端端口(对应服务端app.yml中的tunnel.port、tunnel.ssl-port)
server-port: ${SERVER_PORT:9002} server-port: ${SERVER_PORT:9002}
# 是否启用SSL(注意:该配置必须和server-port对应上)
ssl-enable: ${SSL_ENABLE:true} ssl-enable: ${SSL_ENABLE:true}
obtain-license-interval: 5 # 客户端连接唯一凭证
license-key: ${LICENSE_KEY:} license-key: ${LICENSE_KEY:}
# 客户端唯一身份标识(可忽略,若不设置首次启动会自动生成)
client-id: ${CLIENT_ID:} client-id: ${CLIENT_ID:}
# 是否开启隧道传输报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${CLIENT_LOG:false} transfer-log-enable: ${CLIENT_LOG:false}
# 重连设置
reconnection:
# 重连间隔(秒)
interval-seconds: 10
# 是否开启无限重连(未开启时,客户端license不合法会自动停止应用,开启了则不会,请谨慎开启)
unlimited: false
@@ -1,15 +1,25 @@
package org.dromara.neutrinoproxy.server; package org.dromara.neutrinoproxy.server;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.solonplugins.job.annotation.EnableJob; import org.dromara.solonplugins.job.annotation.EnableJob;
import org.noear.solon.Solon; import org.noear.solon.Solon;
import org.noear.solon.SolonApp;
import org.noear.solon.annotation.SolonMain; import org.noear.solon.annotation.SolonMain;
import org.noear.solon.web.cors.CrossFilter; import org.noear.solon.web.cors.CrossFilter;
import org.slf4j.LoggerFactory;
import java.util.List;
/** /**
* *
* @author: aoshiguchen * @author: aoshiguchen
* @date: 2022/6/16 * @date: 2022/6/16
*/ */
@Slf4j
@EnableJob @EnableJob
@SolonMain @SolonMain
public class ProxyServer { public class ProxyServer {
@@ -18,6 +28,25 @@ public class ProxyServer {
Solon.start(ProxyServer.class, args, app -> { Solon.start(ProxyServer.class, args, app -> {
// 跨域支持。加-1 优先级更高 // 跨域支持。加-1 优先级更高
app.filter(-1, new CrossFilter().allowedOrigins("*")); app.filter(-1, new CrossFilter().allowedOrigins("*"));
// 设置日志级别
setLogLevel(app);
}); });
} }
private static void setLogLevel(SolonApp app) {
String loggerLevel = app.cfg().get("neutrino.proxy.logger.level");
if (StringUtils.isBlank(loggerLevel)) {
return;
}
try {
Level level = Level.toLevel(loggerLevel);
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
for (Logger logger : loggerContext.getLoggerList()) {
logger.setLevel(level);
}
} catch (Exception e) {
log.error("日志级别设置失败", e);
}
}
} }
@@ -31,7 +31,7 @@ public class VisitLogInterceptor implements RouterInterceptor {
} finally { } finally {
Date now = new Date(); Date now = new Date();
long elapsedTime = now.getTime() - startTime.getTime(); long elapsedTime = now.getTime() - startTime.getTime();
log.info("\n-----------------------------------------------------------------接口请求日志:\n{} url:{} 执行耗时:{}\n请求体参数:{}\n响应结果:{}\n客户端IP:{}\n", log.debug("\n-----------------------------------------------------------------接口请求日志:\n{} url:{} 执行耗时:{}\n请求体参数:{}\n响应结果:{}\n客户端IP:{}\n",
ctx.method(), ctx.path(), getElapsedTimeStr(elapsedTime), ctx.method(), ctx.path(), getElapsedTimeStr(elapsedTime),
JSONObject.toJSONString(ctx.paramMap()), JSONObject.toJSONString(ctx.paramMap()),
JSONObject.toJSONString(JSONObject.toJSONString(ctx.result)), JSONObject.toJSONString(JSONObject.toJSONString(ctx.result)),
@@ -17,6 +17,6 @@ public class DemoJob implements IJobHandler {
@Override @Override
public void execute(String param) throws Exception { public void execute(String param) throws Exception {
System.out.println("DemoJob execute param:" + param); log.debug("DemoJob execute param: {}", param);
} }
} }
@@ -127,7 +127,7 @@ public class ProxyTunnelChannelHandler extends SimpleChannelInboundHandler<Proxy
switch (event.state()) { switch (event.state()) {
case READER_IDLE: case READER_IDLE:
// 读超时,断开连接 // 读超时,断开连接
log.info("读超时"); log.debug("读超时");
ctx.channel().close(); ctx.channel().close();
break; break;
case WRITER_IDLE: case WRITER_IDLE:
@@ -50,7 +50,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
} }
}); });
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync(); bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
log.info("Http代理服务启动成功!"); log.info("Http代理服务启动成功!port:{}", proxyConfig.getServer().getHttpProxyPort());
} catch (Exception e) { } catch (Exception e) {
log.error("http proxy start err!", e); log.error("http proxy start err!", e);
} }
@@ -163,7 +163,8 @@ public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteB
} }
// 域名 // 域名
String domain = line.substring(6); String domain = line.substring(6);
return domain; // 去掉域名后面的端口号
return domain.replaceAll(":.*", "");
} }
return null; return null;
} }
@@ -59,7 +59,7 @@ public class HttpsProxy implements EventListener<AppLoadEndEvent> {
} }
}); });
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync(); bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpsProxyPort()).sync();
log.info("Https代理服务启动成功!"); log.info("Https代理服务启动成功!port:{}", proxyConfig.getServer().getHttpsProxyPort());
} catch (Exception e) { } catch (Exception e) {
log.error("https proxy start err!", e); log.error("https proxy start err!", e);
} }
@@ -61,7 +61,7 @@ public class JobLogService implements IJobCallback {
String msg = ""; String msg = "";
if (null == throwable) { if (null == throwable) {
msg = "执行成功"; msg = "执行成功";
log.info("job[id={},name={}]执行完毕", jobInfo.getId(), jobInfo.getName()); log.debug("job[id={},name={}]执行完毕", jobInfo.getId(), jobInfo.getName());
} else { } else {
log.error("job[id={},name={}]执行异常", jobInfo.getId(), jobInfo.getName(), throwable); log.error("job[id={},name={}]执行异常", jobInfo.getId(), jobInfo.getName(), throwable);
msg = "执行异常:\r\n" + ExceptionUtils.getStackTrace(throwable); msg = "执行异常:\r\n" + ExceptionUtils.getStackTrace(throwable);
@@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import ma.glasnost.orika.MapperFacade;
import org.apache.ibatis.solon.annotation.Db;
import org.dromara.neutrinoproxy.server.base.db.DBInitialize; import org.dromara.neutrinoproxy.server.base.db.DBInitialize;
import org.dromara.neutrinoproxy.server.base.page.PageInfo; import org.dromara.neutrinoproxy.server.base.page.PageInfo;
import org.dromara.neutrinoproxy.server.base.page.PageQuery; import org.dromara.neutrinoproxy.server.base.page.PageQuery;
@@ -31,14 +33,10 @@ import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO;
import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO; import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO;
import org.dromara.neutrinoproxy.server.dal.entity.UserDO; import org.dromara.neutrinoproxy.server.dal.entity.UserDO;
import org.dromara.neutrinoproxy.server.util.ParamCheckUtil; import org.dromara.neutrinoproxy.server.util.ParamCheckUtil;
import ma.glasnost.orika.MapperFacade;
import org.apache.ibatis.solon.annotation.Db;
import org.dromara.neutrinoproxy.server.controller.res.proxy.*;
import org.dromara.neutrinoproxy.server.util.ProxyUtil; import org.dromara.neutrinoproxy.server.util.ProxyUtil;
import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Init; import org.noear.solon.annotation.Init;
import org.noear.solon.annotation.Inject; import org.noear.solon.annotation.Inject;
import org.noear.solon.core.Lifecycle;
import org.noear.solon.core.bean.LifecycleBean; import org.noear.solon.core.bean.LifecycleBean;
import java.util.*; import java.util.*;
@@ -51,25 +49,25 @@ import java.util.stream.Collectors;
*/ */
@Component @Component
public class PortMappingService implements LifecycleBean { public class PortMappingService implements LifecycleBean {
@Inject @Inject
private MapperFacade mapperFacade; private MapperFacade mapperFacade;
@Db @Db
private PortMappingMapper portMappingMapper; private PortMappingMapper portMappingMapper;
@Db @Db
private LicenseMapper licenseMapper; private LicenseMapper licenseMapper;
@Db @Db
private UserMapper userMapper; private UserMapper userMapper;
@Db @Db
private PortPoolMapper portPoolMapper; private PortPoolMapper portPoolMapper;
@Inject @Inject
private VisitorChannelService visitorChannelService; private VisitorChannelService visitorChannelService;
@Inject @Inject
private PortPoolService portPoolService; private PortPoolService portPoolService;
@Inject @Inject
private ProxyConfig proxyConfig; private ProxyConfig proxyConfig;
@Inject @Inject
private DBInitialize dbInitialize; private DBInitialize dbInitialize;
public PageInfo<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) { public PageInfo<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) {
Page<PortMappingListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize()); Page<PortMappingListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
@@ -77,6 +75,13 @@ public class PortMappingService implements LifecycleBean {
//描述字段为模糊查询,在应用层处理,否则sqlite不支持 //描述字段为模糊查询,在应用层处理,否则sqlite不支持
req.setDescription("%" + req.getDescription() + "%"); req.setDescription("%" + req.getDescription() + "%");
} }
// 协议名称转换
if (StringUtils.isNotBlank(req.getProtocal())) {
NetworkProtocolEnum networkProtocolEnum = NetworkProtocolEnum.of(req.getProtocal());
req.setProtocal(networkProtocolEnum.getDesc());
}
List<PortMappingDO> list = portMappingMapper.selectPortMappingByCondition(req); List<PortMappingDO> list = portMappingMapper.selectPortMappingByCondition(req);
List<PortMappingListRes> respList = mapperFacade.mapAsList(list, PortMappingListRes.class); List<PortMappingListRes> respList = mapperFacade.mapAsList(list, PortMappingListRes.class);
if (CollectionUtils.isEmpty(list)) { if (CollectionUtils.isEmpty(list)) {
@@ -113,10 +118,7 @@ public class PortMappingService implements LifecycleBean {
} }
}); });
//sorted [userId asc] [licenseId asc] [createTime asc] //sorted [userId asc] [licenseId asc] [createTime asc]
respList = respList.stream().sorted(Comparator.comparing(PortMappingListRes::getUserId) respList = respList.stream().sorted(Comparator.comparing(PortMappingListRes::getUserId).thenComparing(PortMappingListRes::getLicenseId).thenComparing(PortMappingListRes::getCreateTime)).collect(Collectors.toList());
.thenComparing(PortMappingListRes::getLicenseId)
.thenComparing(PortMappingListRes::getCreateTime))
.collect(Collectors.toList());
return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize()); return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
} }
@@ -149,9 +151,7 @@ public class PortMappingService implements LifecycleBean {
// 更新VisitorChannel // 更新VisitorChannel
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO); visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
// 更新域名映射 // 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
} }
return new PortMappingCreateRes(); return new PortMappingCreateRes();
@@ -188,14 +188,11 @@ public class PortMappingService implements LifecycleBean {
// 更新VisitorChannel // 更新VisitorChannel
visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO); visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO);
// 删除老的域名映射 // 删除老的域名映射
if (NetworkProtocolEnum.isHttp(oldPortMappingDO.getProtocal()) && if (NetworkProtocolEnum.isHttp(oldPortMappingDO.getProtocal()) && StrUtil.isNotBlank(oldPortMappingDO.getSubdomain())) {
StrUtil.isNotBlank(oldPortMappingDO.getSubdomain())) {
ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain()); ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain());
} }
// 更新域名映射 // 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
} }
return new PortMappingUpdateRes(); return new PortMappingUpdateRes();
@@ -206,16 +203,7 @@ public class PortMappingService implements LifecycleBean {
if (null == portMappingDO) { if (null == portMappingDO) {
return null; return null;
} }
PortMappingDetailRes res = new PortMappingDetailRes() PortMappingDetailRes res = new PortMappingDetailRes().setId(portMappingDO.getId()).setLicenseId(portMappingDO.getLicenseId()).setServerPort(portMappingDO.getServerPort()).setClientIp(portMappingDO.getClientIp()).setClientPort(portMappingDO.getClientPort()).setIsOnline(portMappingDO.getIsOnline()).setEnable(portMappingDO.getEnable()).setCreateTime(portMappingDO.getCreateTime()).setUpdateTime(portMappingDO.getUpdateTime());
.setId(portMappingDO.getId())
.setLicenseId(portMappingDO.getLicenseId())
.setServerPort(portMappingDO.getServerPort())
.setClientIp(portMappingDO.getClientIp())
.setClientPort(portMappingDO.getClientPort())
.setIsOnline(portMappingDO.getIsOnline())
.setEnable(portMappingDO.getEnable())
.setCreateTime(portMappingDO.getCreateTime())
.setUpdateTime(portMappingDO.getUpdateTime());
LicenseDO license = licenseMapper.findById(portMappingDO.getLicenseId()); LicenseDO license = licenseMapper.findById(portMappingDO.getLicenseId());
if (null != license) { if (null != license) {
@@ -268,8 +256,7 @@ public class PortMappingService implements LifecycleBean {
// 更新VisitorChannel // 更新VisitorChannel
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO); visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
// 更新域名映射 // 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain()); ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain());
} }
} }
@@ -295,10 +282,7 @@ public class PortMappingService implements LifecycleBean {
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) { if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
return; return;
} }
List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<PortMappingDO>() List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<PortMappingDO>().eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc()).isNotNull(PortMappingDO::getSubdomain));
.eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc())
.isNotNull(PortMappingDO::getSubdomain)
);
if (CollectionUtil.isEmpty(portMappingDOList)) { if (CollectionUtil.isEmpty(portMappingDOList)) {
return; return;
} }
@@ -1,4 +1,5 @@
server: server:
# 服务端web端口,用于支持HTTP接口,管理后台页面访问
port: ${WEB_PORT:8888} port: ${WEB_PORT:8888}
neutrino: neutrino:
@@ -12,30 +13,48 @@ neutrino:
read-idle-time: 30 read-idle-time: 30
write-idle-time: 5 write-idle-time: 5
all-idle-time-seconds: 0 all-idle-time-seconds: 0
logger:
# 日志级别
level: ${LOG_LEVEL:info}
# 隧道相关配置-用于维持服务端与客户端的通信
tunnel: tunnel:
# 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 2 boss-thread-count: 2
work-thread-count: 10 work-thread-count: 10
# 隧道非SSL端口
port: ${OPEN_PORT:9000} port: ${OPEN_PORT:9000}
# 隧道SSL端口
ssl-port: ${SSL_PORT:9002} ssl-port: ${SSL_PORT:9002}
# 隧道SSL证书配置
key-store-password: ${STORE_PASS:123456} key-store-password: ${STORE_PASS:123456}
key-manager-password: ${MGR_PASS:123456} key-manager-password: ${MGR_PASS:123456}
jks-path: ${JKS_PATH:classpath:/test.jks} jks-path: ${JKS_PATH:classpath:/test.jks}
# 是否开启隧道传输报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${TUNNEL_LOG:false} transfer-log-enable: ${TUNNEL_LOG:false}
server: server:
# 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 5 boss-thread-count: 5
work-thread-count: 20 work-thread-count: 20
# http代理端口,默认80
http-proxy-port: ${HTTP_PROXY_PORT:80} http-proxy-port: ${HTTP_PROXY_PORT:80}
# https代理端口,默认443 (需要配置域名、证书)
https-proxy-port: ${HTTPS_PROXY_PORT:443} https-proxy-port: ${HTTPS_PROXY_PORT:443}
# 如果不配置,则不支持域名映射 # 如果不配置,则不支持域名映射
domain-name: ${DOMAIN_NAME:} domain-name: ${DOMAIN_NAME:}
# https证书配置
key-store-password: ${HTTPS_STORE_PASS:} key-store-password: ${HTTPS_STORE_PASS:}
jks-path: ${HTTPS_JKS_PATH:} jks-path: ${HTTPS_JKS_PATH:}
# 是否开启代理服务报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${SERVER_LOG:false} transfer-log-enable: ${SERVER_LOG:false}
data: data:
db: db:
# 数据库类型,目前支持sqlite、mysql、mariadb
type: ${DB_TYPE:sqlite} type: ${DB_TYPE:sqlite}
# 数据库连接URL
url: ${DB_URL:jdbc:sqlite:data.db} url: ${DB_URL:jdbc:sqlite:data.db}
# 数据库用户名
username: ${DB_USER:} username: ${DB_USER:}
# 数据库密码
password: ${DB_PASSWORD:} password: ${DB_PASSWORD:}
#添加MIME印射(如果有需要?) #添加MIME印射(如果有需要?)
@@ -7,7 +7,7 @@ article: false
## 1、 部署服务端 ## 1、 部署服务端
### 1.1、 Docker一键部署 ### 1.1、 Docker一键部署
> 当前最新版本为1.8.2,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.8.2` 指定版本安装,推荐使用`latest`直接安装最新版。 > 当前最新版本为1.8.4,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.8.4` 指定版本安装,推荐使用`latest`直接安装最新版。
#### 使用默认sqlite数据库 #### 使用默认sqlite数据库
```shell ```shell
@@ -19,6 +19,9 @@ server:
neutrino: neutrino:
proxy: proxy:
logger:
# 日志级别
level: ${LOG_LEVEL:info}
# 隧道相关配置-用于维持服务端与客户端的通信 # 隧道相关配置-用于维持服务端与客户端的通信
tunnel: tunnel:
# 线程池相关配置,用于技术调优,可忽略 # 线程池相关配置,用于技术调优,可忽略
@@ -32,7 +35,8 @@ neutrino:
key-store-password: ${STORE_PASS:123456} key-store-password: ${STORE_PASS:123456}
key-manager-password: ${MGR_PASS:123456} key-manager-password: ${MGR_PASS:123456}
jks-path: ${JKS_PATH:classpath:/test.jks} jks-path: ${JKS_PATH:classpath:/test.jks}
# 代理服务相关配置 # 是否开启隧道传输报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${TUNNEL_LOG:false}
server: server:
# 线程池相关配置,用于技术调优,可忽略 # 线程池相关配置,用于技术调优,可忽略
boss-thread-count: 5 boss-thread-count: 5
@@ -46,9 +50,11 @@ neutrino:
# https证书配置 # https证书配置
key-store-password: ${HTTPS_STORE_PASS:} key-store-password: ${HTTPS_STORE_PASS:}
jks-path: ${HTTPS_JKS_PATH:} jks-path: ${HTTPS_JKS_PATH:}
# 是否开启代理服务报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${SERVER_LOG:false}
data: data:
db: db:
# 数据库类型,目前支持sqlite、mysql两种 # 数据库类型,目前支持sqlite、mysql、mariadb
type: ${DB_TYPE:sqlite} type: ${DB_TYPE:sqlite}
# 数据库连接URL # 数据库连接URL
url: ${DB_URL:jdbc:sqlite:data.db} url: ${DB_URL:jdbc:sqlite:data.db}
@@ -0,0 +1,38 @@
---
title: 客户端配置
date: 2023-06-09 21:15:35
permalink: /pages/50ce10/
---
# 以下是最新的客户端配置格式(app.yml)
```yml
neutrino:
proxy:
logger:
# 日志级别
level: ${LOG_LEVEL:info}
client:
# 线程池相关配置,用于技术调优,可忽略
thread-count: 50
# 隧道SSL证书配置
key-store-password: ${STORE_PASS:123456}
jks-path: ${JKS_PATH:classpath:/test.jks}
# 服务端IP
server-ip: ${SERVER_IP:localhost}
# 服务端端口(对应服务端app.yml中的tunnel.port、tunnel.ssl-port)
server-port: ${SERVER_PORT:9002}
# 是否启用SSL(注意:该配置必须和server-port对应上)
ssl-enable: ${SSL_ENABLE:true}
# 客户端连接唯一凭证
license-key: ${LICENSE_KEY:}
# 客户端唯一身份标识(可忽略,若不设置首次启动会自动生成)
client-id: ${CLIENT_ID:}
# 是否开启隧道传输报文日志(日志级别为debug时开启才有效)
transfer-log-enable: ${CLIENT_LOG:false}
# 重连设置
reconnection:
# 重连间隔(秒)
interval-seconds: 10
# 是否开启无限重连(未开启时,客户端license不合法会自动停止应用,开启了则不会,请谨慎开启)
unlimited: false
```
@@ -0,0 +1,21 @@
---
title: 2023年
date: 2023-06-09 21:33:17
permalink: /pages/cded59/
---
| 日期 | 渠道 | 金额 |昵称| 备注 |
|:-----------|:---|:---|:-|:--------|
| 2023-06-09 |Gitee捐助| 10 |失败女神| 感谢您的开源项目! |
| 2023-06-09 |Gitee捐助| 50 |Admin| 感谢您的开源项目! |
| 2023-06-09 |微信红包| 50 |李阳| 开源无限好 |
| 2023-06-09 |Gitee捐助| 50 |罗宾| 感谢您的开源项目!|
| 2023-06-07 |Gitee捐助| 50 |罗宾| 感谢您的开源项目!|
| 2023-06-03 |Gitee捐助| 50 |Admin| 感谢您的开源项目!|
| 2023-05-29 |微信红包| 20 |TYY| |
| 2023-05-29 |Gitee捐助| 10 |笑看| 感谢您的开源项目!|
| 2023-05-26 |微信红包| 50 |至少还有满天星光照耀你| |
| 2023-02-23 |Gitee捐助| 10 |Yohanes|感谢您的开源项目! |
| 2023-02-23 |Gitee捐助| 20 |jam_lee|感谢您的开源项目!希望能支持域名映射|
| 2023-02-10 |Gitee捐助| 5 |实习两年半|感谢您的开源项目!|
| 2023-02-02 |Gitee捐助| 10 |阳光很暖|感谢您的开源项目!|
+2
View File
@@ -30,6 +30,8 @@
- [ ] 支持调整服务端端口、证书 - [ ] 支持调整服务端端口、证书
- [ ] 服务端版本、协议版本、客户端版本管理 - [ ] 服务端版本、协议版本、客户端版本管理
- [ ] 服务端日志 - [ ] 服务端日志
- [ ] 支持持管理后台动态调整日志级别。
- [ ] 动态调整转发、报文、心跳输出
- client+计划启动 - client+计划启动
- [ ] 安卓客户端 - [ ] 安卓客户端
- [ ] 基于electron-egg的多平台客户端 - [ ] 基于electron-egg的多平台客户端