部分修改

This commit is contained in:
xiaojie
2024-03-04 17:38:52 +08:00
parent 6f6a57eda5
commit 6d26c58843
9 changed files with 861 additions and 31 deletions
@@ -7,10 +7,10 @@
<el-select v-model="listQuery.licenseId" placeholder="请选择license" filterable clearable style="margin-right:10px;width: 135px;">
<el-option v-for="item in licenseList" :key="item.key" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.protocal" placeholder="请选择协议" clearable style="margin-right:10px;width: 120px;">
<!-- <el-select v-model="listQuery.protocal" placeholder="请选择协议" clearable style="margin-right:10px;width: 120px;">
<el-option v-for="item in protocalList" :key="item.name" :label="item.name" :value="item.name" :disabled="!item.enable" />
</el-select>
<el-input v-model="listQuery.serverPort" type="text" style="width:145px;margin-right:10px" class="filter-item" placeholder="请输入服务端端口" :maxlength="5" show-word-limit />
</el-select> -->
<el-input v-model="listQuery.domain" type="text" style="width:145px;margin-right:10px" class="filter-item" placeholder="请输入域名关键词" :maxlength="5" show-word-limit />
<el-select v-model="listQuery.isOnline" placeholder="请选择在线状态" clearable style="width:145px;margin-right:10px">
<el-option v-for="item in selectObj.onlineOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
@@ -277,6 +277,7 @@ export default {
listQuery: {
current: 1,
size: 10,
domain: null,
importance: undefined,
title: undefined,
type: undefined,
@@ -0,0 +1,128 @@
package org.dromara.neutrinoproxy.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.neutrinoproxy.server.base.page.PageInfo;
import org.dromara.neutrinoproxy.server.base.page.PageQuery;
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
import org.dromara.neutrinoproxy.server.constant.ExceptionConstant;
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
import org.dromara.neutrinoproxy.server.controller.req.proxy.*;
import org.dromara.neutrinoproxy.server.controller.res.proxy.*;
import org.dromara.neutrinoproxy.server.service.DomainMappingService;
import org.dromara.neutrinoproxy.server.service.PortMappingService;
import org.dromara.neutrinoproxy.server.util.ParamCheckUtil;
import org.noear.solon.annotation.*;
/**
* @author: aoshiguchen
* @date: 2023/4/2
*/
@Slf4j
@Mapping("/domain")
@Controller
public class DomainController {
@Inject
private ProxyConfig proxyConfig;
@Inject
private DomainMappingService domainMappingService;
@Get
@Mapping("/bind-info")
public String bindInfo () {
return proxyConfig.getServer().getTcp().getDomainName();
}
@Get
@Mapping("/page")
public PageInfo<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) {
ParamCheckUtil.checkNotNull(pageQuery, "pageQuery");
return domainMappingService.page(pageQuery, req);
}
@Post
@Mapping("/modify")
public PortMappingCreateRes create(PortMappingCreateReq req) {
ParamCheckUtil.checkNotNull(req, "req");
ParamCheckUtil.checkNotNull(req.getLicenseId(), "licenseId");
ParamCheckUtil.checkNotNull(req.getServerPort(), "serverPort");
ParamCheckUtil.checkNotNull(req.getClientPort(), "clientPort");
ParamCheckUtil.checkNotEmpty(req.getProtocal(), "protocal");
ParamCheckUtil.checkMaxLength(req.getDescription(), 50, "描述", "50");
ParamCheckUtil.checkBytesDesc(req.getUpLimitRate(), "upLimitRate");
ParamCheckUtil.checkBytesDesc(req.getDownLimitRate(), "downLimitRate");
if (StringUtils.isBlank(req.getClientIp())) {
// 没传客户端ip,默认为127.0.0.1
req.setClientIp("127.0.0.1");
}
NetworkProtocolEnum networkProtocolEnum = NetworkProtocolEnum.of(req.getProtocal());
ParamCheckUtil.checkNotNull(networkProtocolEnum, ExceptionConstant.AN_UNSUPPORTED_PROTOCOL, req.getProtocal());
if (networkProtocolEnum != NetworkProtocolEnum.HTTP) {
// 目前仅HTTP支持绑定域名
req.setSubdomain(null);
}
req.setProtocal(networkProtocolEnum.getDesc());
if (null == req.getProxyResponses()) {
req.setProxyResponses(0);
}
if (null == req.getProxyTimeoutMs()) {
req.setProxyTimeoutMs(0L);
}
if (null == req.getSecurityGroupId()) {
req.setSecurityGroupId(0);
}
return domainMappingService.create(req);
}
@Get
@Mapping("/one/{id}")
public DomainMappingDto one(Integer id) {
return domainMappingService.one(id);
}
@Post
@Mapping("/update/enable-status")
public PortMappingUpdateEnableStatusRes updateEnableStatus(PortMappingUpdateEnableStatusReq req) {
ParamCheckUtil.checkNotNull(req, "req");
ParamCheckUtil.checkNotNull(req.getId(), "id");
ParamCheckUtil.checkNotNull(req.getEnable(), "enable");
return domainMappingService.updateEnableStatus(req);
}
@Post
@Mapping("/delete")
public void delete(PortMappingDeleteReq req) {
ParamCheckUtil.checkNotNull(req, "req");
ParamCheckUtil.checkNotNull(req.getId(), "id");
domainMappingService.delete(req.getId());
}
/**
* 绑定安全组
* @param req portMappingId和securityGroupId
*/
@Post
@Mapping("/bind/security-group")
public void bindSecurityGroup(PortMappingBindSecurityGroupReq req) {
domainMappingService.portBindSecurityGroup(req.getId(), req.getSecurityGroupId());
}
/**
* 安全组解绑
* @param id 端口映射Id
*/
@Post
@Mapping("/unbind/security-group")
public void unbindSecurityGroup(Integer id) {
domainMappingService.portUnbindSecurityGroup(id);
}
}
@@ -1,27 +0,0 @@
package org.dromara.neutrinoproxy.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Get;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
/**
* @author: aoshiguchen
* @date: 2023/4/2
*/
@Slf4j
@Mapping("/domain")
@Controller
public class DomainNameController {
@Inject
private ProxyConfig proxyConfig;
@Get
@Mapping("/bind-info")
public String bindInfo () {
return proxyConfig.getServer().getTcp().getDomainName();
}
}
@@ -0,0 +1,31 @@
package org.dromara.neutrinoproxy.server.controller.res.proxy;
import lombok.Data;
import lombok.experimental.Accessors;
import org.dromara.neutrinoproxy.server.dal.entity.DomainMappingDO;
/**
*
* @author xiaojie
* @date
*/
@Accessors(chain = true)
@Data
public class DomainMappingDto extends DomainMappingDO {
/**
* licenseId
*/
private String licenseName;
/**
* 用户ID
*/
private Integer userId;
/**
* 用户名称
*/
private String userName;
}
@@ -0,0 +1,112 @@
package org.dromara.neutrinoproxy.server.dal;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingListReq;
import org.dromara.neutrinoproxy.server.dal.entity.DomainMappingDO;
import org.dromara.neutrinoproxy.server.dal.entity.DomainMappingDO;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
*
* @author: aoshiguchen
* @date: 2022/8/8
*/
@Mapper
public interface DomainMappingMapper extends BaseMapper<DomainMappingDO> {
default DomainMappingDO findById(Integer id) {
return this.selectById(id);
}
default void updateEnableStatus(Integer id, Integer enable, Date updateTime) {
this.update(null, new LambdaUpdateWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getId, id)
.set(DomainMappingDO::getEnable, enable)
.set(DomainMappingDO::getUpdateTime, updateTime)
);
}
default DomainMappingDO findByDomain(String domain, Set<Integer> excludeIds) {
return this.selectOne(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getDomain, domain)
.notIn(!CollectionUtil.isEmpty(excludeIds), DomainMappingDO::getId, excludeIds)
.last("limit 1")
);
}
/**
* 校验域名是否重复
* @param subdomain
* @return
*/
default Boolean checkRepeatByDomain(String subdomain, Set<Integer> excludeIds) {
return StringUtils.isEmpty(subdomain)? Boolean.FALSE : this.selectCount(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getDomain, subdomain)
.notIn(!CollectionUtil.isEmpty(excludeIds), DomainMappingDO::getId, excludeIds)
).intValue() > 0;
}
default List<DomainMappingDO> findEnableListByLicenseId(Integer licenseId) {
return this.selectList(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getLicenseId, licenseId)
.eq(DomainMappingDO::getEnable, EnableStatusEnum.ENABLE.getStatus())
);
}
default List<DomainMappingDO> findListByServerPort(Integer serverPort) {
return this.selectList(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getServerPort, serverPort)
);
}
default List<DomainMappingDO> findListByLicenseId(Integer licenseId) {
return this.selectList(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getLicenseId, licenseId)
);
}
default DomainMappingDO findByLicenseIdAndServerPort(Integer licenseId, Integer serverPort) {
return this.selectOne(new LambdaQueryWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getLicenseId, licenseId)
.eq(DomainMappingDO::getServerPort, serverPort)
.last("limit 1")
);
}
default void updateOnlineStatus(Integer licenseId,Integer serverPort, Integer isOnline, Date updateTime) {
this.update(null, new LambdaUpdateWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getLicenseId, licenseId)
.eq(DomainMappingDO::getServerPort, serverPort)
.set(DomainMappingDO::getIsOnline, isOnline)
.set(DomainMappingDO::getUpdateTime, updateTime)
);
}
default void updateOnlineStatus(Integer licenseId, Integer isOnline, Date updateTime) {
this.update(null, new LambdaUpdateWrapper<DomainMappingDO>()
.eq(DomainMappingDO::getLicenseId, licenseId)
.set(DomainMappingDO::getIsOnline, isOnline)
.set(DomainMappingDO::getUpdateTime, updateTime)
);
}
default void updateOnlineStatus(Integer isOnline, Date updateTime) {
this.update(null, new LambdaUpdateWrapper<DomainMappingDO>()
.set(DomainMappingDO::getIsOnline, isOnline)
.set(DomainMappingDO::getUpdateTime, updateTime)
);
}
List<DomainMappingDO> selectDomainMappingByCondition(IPage<DomainMappingDO> page, @Param("req") PortMappingListReq req);
}
@@ -0,0 +1,110 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.dromara.neutrinoproxy.server.dal.entity;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
import org.dromara.neutrinoproxy.server.constant.OnlineStatusEnum;
import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainMappingDto;
import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingListRes;
import java.util.Date;
/**
* 端口映射
* @author: aoshiguchen
* @date: 2022/8/8
*/
@ToString
@Accessors(chain = true)
@Data
@TableName("domain_mapping")
public class DomainMappingDO {
@TableId(type = IdType.AUTO)
private Integer id;
/**
* licenseId
*/
private Integer licenseId;
/**
* 协议
*/
private String protocal;
/**
* 域名
*/
private String domain;
/**
* 描述
*/
private String description;
/**
* 目标地址(licenseId客户端,ip:port
*/
private String targetPath;
/**
* 上传限速
*/
private String upLimitRate;
/**
* 下载限速
*/
private String downLimitRate;
/**
* 是否在线
* {@link OnlineStatusEnum}
*/
@TableField(exist=false)
private Integer isOnline;
/**
* 启用状态
* {@link EnableStatusEnum}
*/
private Integer enable;
/**
* 安全组Id
*/
private Integer securityGroupId = 0; // 设置为null不生效,不知道为啥
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
public DomainMappingDto toRes() {
return (DomainMappingDto) this;
}
}
@@ -0,0 +1,441 @@
package org.dromara.neutrinoproxy.server.service;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.solon.plugins.pagination.Page;
import com.google.common.collect.Sets;
import org.apache.ibatis.solon.annotation.Db;
import org.dromara.neutrinoproxy.server.base.db.DBInitialize;
import org.dromara.neutrinoproxy.server.base.page.PageInfo;
import org.dromara.neutrinoproxy.server.base.page.PageQuery;
import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig;
import org.dromara.neutrinoproxy.server.base.rest.SystemContextHolder;
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
import org.dromara.neutrinoproxy.server.constant.ExceptionConstant;
import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
import org.dromara.neutrinoproxy.server.constant.OnlineStatusEnum;
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingCreateReq;
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingListReq;
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingUpdateEnableStatusReq;
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingUpdateReq;
import org.dromara.neutrinoproxy.server.controller.res.proxy.*;
import org.dromara.neutrinoproxy.server.dal.DomainMappingMapper;
import org.dromara.neutrinoproxy.server.dal.LicenseMapper;
import org.dromara.neutrinoproxy.server.dal.PortPoolMapper;
import org.dromara.neutrinoproxy.server.dal.UserMapper;
import org.dromara.neutrinoproxy.server.dal.entity.*;
import org.dromara.neutrinoproxy.server.service.bo.FlowLimitBO;
import org.dromara.neutrinoproxy.server.util.ParamCheckUtil;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
import org.dromara.neutrinoproxy.server.util.StringUtil;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Init;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.bean.LifecycleBean;
import org.noear.solon.core.runtime.NativeDetector;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
* 域名解析 service
* author xiaojie
*/
@Component
public class DomainMappingService implements LifecycleBean {
@Db
private DomainMappingMapper domainMappingMapper;
@Db
private LicenseMapper licenseMapper;
@Db
private UserMapper userMapper;
@Db
private PortPoolMapper portPoolMapper;
@Inject
private VisitorChannelService visitorChannelService;
@Inject
private PortPoolService portPoolService;
@Inject
private ProxyConfig proxyConfig;
@Inject
private DBInitialize dbInitialize;
@Inject
private LicenseService licenseService;
/** 端口到安全组Id的映射 */
private final Map<String, Integer> mappingDomainToSecurityGroupMap = new ConcurrentHashMap<>();
/**
* 域名 到 域名解析id的映射
*/
private final Cache<Integer, Integer> domainToDomainMappingIdCache = CacheUtil.newLRUCache(500, 1000 * 60 * 10);
// 域名解析id到licenseId
private final Cache<Integer, Integer> idToLicenseIdCache = CacheUtil.newLRUCache(500, 1000 * 60 * 10);
// 流量限制缓存
private final Cache<Integer, FlowLimitBO> flowLimitCache = CacheUtil.newLRUCache(500, 1000 * 60 * 5);
public PageInfo<DomainMappingDto> page(PageQuery pageQuery, PortMappingListReq req) {
if (StringUtils.isNotEmpty(req.getDescription())) {
//描述字段为模糊查询,在应用层处理,否则sqlite不支持
req.setDescription("%" + req.getDescription() + "%");
}
// 协议名称转换
if (StringUtils.isNotBlank(req.getProtocal())) {
NetworkProtocolEnum networkProtocolEnum = NetworkProtocolEnum.of(req.getProtocal());
req.setProtocal(networkProtocolEnum.getDesc());
}
Page<DomainMappingDO> page = new Page<>(pageQuery.getCurrent(), pageQuery.getSize());
List<DomainMappingDO> list = domainMappingMapper.selectDomainMappingByCondition(page, req);
List<DomainMappingDto> respList = list.stream().map(DomainMappingDO::toRes).collect(Collectors.toList());
if (CollectionUtils.isEmpty(list)) {
return PageInfo.of(respList, page.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
Set<Integer> licenseIds = respList.stream().map(DomainMappingDto::getLicenseId).collect(Collectors.toSet());
List<LicenseDO> licenseList = licenseMapper.findByIds(licenseIds);
if (CollectionUtil.isEmpty(licenseList)) {
return PageInfo.of(respList, page.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
Set<Integer> userIds = licenseList.stream().map(LicenseDO::getUserId).collect(Collectors.toSet());
List<UserDO> userList = userMapper.findByIds(userIds);
Map<Integer, LicenseDO> licenseMap = licenseList.stream().collect(Collectors.toMap(LicenseDO::getId, Function.identity()));
Map<Integer, UserDO> userMap = userList.stream().collect(Collectors.toMap(UserDO::getId, Function.identity()));
respList.forEach(item -> {
LicenseDO license = licenseMap.get(item.getLicenseId());
if (null == license) {
return;
}
item.setLicenseName(license.getName());
item.setUserId(license.getUserId());
UserDO user = userMap.get(license.getUserId());
if (null == user) {
return;
}
item.setUserName(user.getName());
// if (StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(item.getDomain())) {
// item.setDomain(item.getDomain() + "." + proxyConfig.getServer().getTcp().getDomainName());
// }
if (NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal())) {
item.setProtocal("HTTP(S)");
}
});
//sorted [userId asc] [licenseId asc] [createTime asc]
respList = respList.stream()
.filter(e -> null != e.getUserId())
.sorted(Comparator.comparing(DomainMappingDto::getUserId).thenComparing(DomainMappingDto::getLicenseId).thenComparing(DomainMappingDto::getCreateTime)).collect(Collectors.toList());
return PageInfo.of(respList, page.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
}
public PortMappingCreateRes modify(DomainMappingDto req) {
LicenseDO licenseDO = licenseMapper.findById(req.getLicenseId());
ParamCheckUtil.checkNotNull(licenseDO, ExceptionConstant.LICENSE_NOT_EXIST);
if (!SystemContextHolder.isAdmin()) {
// 临时处理,如果当前用户不是管理员,则操作userId不能为1
ParamCheckUtil.checkExpression(!licenseDO.getUserId().equals(1), ExceptionConstant.NO_PERMISSION_VISIT);
}
PortPoolDO portPoolDO = portPoolMapper.findByPort(req.getDomain());
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
ParamCheckUtil.checkExpression(null == domainMappingMapper.findByDomain(req.getDomain(), null), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getDomain());
ParamCheckUtil.checkExpression(!domainMappingMapper.checkRepeatByDomain(req.getDomain(), null), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT);
Date now = new Date();
PortMappingDO portMappingDO = ;
portMappingDO.setLicenseId(req.getLicenseId());
portMappingDO.setProtocal(req.getProtocal());
portMappingDO.setSubdomain(req.getSubdomain());
portMappingDO.setServerPort(req.getServerPort());
portMappingDO.setClientIp(req.getClientIp());
portMappingDO.setClientPort(req.getClientPort());
portMappingDO.setUpLimitRate(req.getUpLimitRate());
portMappingDO.setDownLimitRate(req.getDownLimitRate());
portMappingDO.setProxyResponses(req.getProxyResponses());
portMappingDO.setProxyTimeoutMs(req.getProxyTimeoutMs());
portMappingDO.setDescription(req.getDescription());
portMappingDO.setIsOnline(OnlineStatusEnum.OFFLINE.getStatus());
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
portMappingDO.setCreateTime(now);
portMappingDO.setUpdateTime(now);
domainMappingMapper.insert(portMappingDO);
// 更新VisitorChannel
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
// 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
}
updateMappingDomainToSecurityGroupMap(portMappingDO.getServerPort(), req.getSecurityGroupId());
// 更新端口到映射的缓存
domainToDomainMappingIdCache.put(req.getServerPort(), portMappingDO.getId());
// 更新端口映射到licenseId的缓存
idToLicenseIdCache.put(portMappingDO.getId(), portMappingDO.getLicenseId());
// 刷新流量限制缓存
refreshFlowLimitCache(portMappingDO.getId(), portMappingDO.getUpLimitRate(), portMappingDO.getDownLimitRate());
return new PortMappingCreateRes();
}
public PortMappingUpdateEnableStatusRes updateEnableStatus(PortMappingUpdateEnableStatusReq req) {
PortMappingDO portMappingDO = domainMappingMapper.findById(req.getId());
ParamCheckUtil.checkNotNull(portMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
LicenseDO licenseDO = licenseMapper.findById(portMappingDO.getLicenseId());
ParamCheckUtil.checkNotNull(licenseDO, ExceptionConstant.LICENSE_NOT_EXIST);
if (!SystemContextHolder.isAdmin()) {
ParamCheckUtil.checkExpression(!licenseDO.getUserId().equals(1), ExceptionConstant.NO_PERMISSION_VISIT);
}
domainMappingMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
// 更新VisitorChannel
portMappingDO.setEnable(req.getEnable());
if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(req.getEnable())) {
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
} else {
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
}
return new PortMappingUpdateEnableStatusRes();
}
public void delete(Integer id) {
PortMappingDO portMappingDO = domainMappingMapper.findById(id);
ParamCheckUtil.checkNotNull(portMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
LicenseDO licenseDO = licenseMapper.findById(portMappingDO.getLicenseId());
if (null != licenseDO && !SystemContextHolder.isAdmin()) {
// 临时处理,如果当前用户不是管理员,则操作userId不能为1
ParamCheckUtil.checkExpression(!licenseDO.getUserId().equals(1), ExceptionConstant.NO_PERMISSION_VISIT);
}
domainMappingMapper.deleteById(id);
// 更新VisitorChannel
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
// 更新域名映射
if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain());
}
updateMappingDomainToSecurityGroupMap(portMappingDO.getServerPort(), null);
// 删除id到licenseId的映射
idToLicenseIdCache.remove(id);
// 删除流量限制缓存
flowLimitCache.remove(id);
}
public void portBindSecurityGroup(Integer portMappingId, Integer groupId) {
PortMappingDO mappingDO = domainMappingMapper.findById(portMappingId);
if (mappingDO == null) {
throw new RuntimeException("指定的端口映射不存在");
}
mappingDO.setSecurityGroupId(groupId);
mappingDO.setUpdateTime(new Date());
domainMappingMapper.updateById(mappingDO);
updateMappingDomainToSecurityGroupMap(mappingDO.getServerPort(), groupId);
}
public void portUnbindSecurityGroup(Integer portMappingId) {
PortMappingDO mappingDO = domainMappingMapper.findById(portMappingId);
if (mappingDO == null) {
throw new RuntimeException("指定的端口映射不存在");
}
mappingDO.setSecurityGroupId(0);
mappingDO.setUpdateTime(new Date());
domainMappingMapper.updateById(mappingDO);
updateMappingDomainToSecurityGroupMap(mappingDO.getServerPort(), null);
}
/**
* 根据license查询可用的端口映射列表
*
* @param licenseId
* @return
*/
public List<PortMappingDO> findEnableListByLicenseId(Integer licenseId) {
return domainMappingMapper.findEnableListByLicenseId(licenseId);
}
public Integer getSecurityGroupIdByMappingPort(Integer port) {
return mappingDomainToSecurityGroupMap.get(port);
}
/**
* 服务端项目停止、启动时,更新在线状态为离线
*/
@Init
public void init() {
// aot 阶段,不初始化
if (NativeDetector.isAotRuntime()) {
return;
}
// 服务刚启动,所以默认所有license都是离线状态。解决服务突然关闭,在线状态来不及更新的问题
domainMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
List<DomainMappingDO> allMappingDOList = domainMappingMapper.selectList(Wrappers.lambdaQuery(DomainMappingDO.class));
allMappingDOList.forEach(item -> {
Integer securityGroupId = item.getSecurityGroupId();
if (securityGroupId != null && securityGroupId > 0) {
updateMappingDomainToSecurityGroupMap(item.getDomain(), item.getSecurityGroupId());
}
// 更新端口到映射的缓存
domainToDomainMappingIdCache.put(item.getDomain(), item.getId());
// 更新端口映射到licenseId的缓存
idToLicenseIdCache.put(item.getId(), item.getLicenseId());
// 刷新流量限制缓存
refreshFlowLimitCache(item.getId(), item.getUpLimitRate(), item.getDownLimitRate());
});
// 未配置域名,则不需要处理域名映射逻辑
if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName())) {
return;
}
List<PortMappingDO> portMappingDOList = allMappingDOList.stream()
.filter(item -> NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal()) && item.getDomain() != null)
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(portMappingDOList)) {
return;
}
portMappingDOList.forEach(item -> {
if (StrUtil.isBlank(item.getSubdomain())) {
return;
}
ProxyUtil.setSubdomainToServerPort(item.getSubdomain(), item.getServerPort());
});
}
/**
* 刷新流量限制缓存
* @param id
* @param upLimitRate
* @param downLimitRate
*/
private void refreshFlowLimitCache(Integer id, String upLimitRate, String downLimitRate) {
if (null == id) {
return;
}
flowLimitCache.put(id, new FlowLimitBO()
.setUpLimitRate(StringUtil.parseBytes(upLimitRate))
.setDownLimitRate(StringUtil.parseBytes(downLimitRate))
);
}
/**
* 获取license的流量限制
* @param id
* @return
*/
public FlowLimitBO getFlowLimit(Integer id) {
FlowLimitBO res = flowLimitCache.get(id);
if (null == res) {
DomainMappingDO domainMappingDO = domainMappingMapper.findById(id);
if (null != domainMappingDO) {
refreshFlowLimitCache(id, domainMappingDO.getUpLimitRate(), domainMappingDO.getDownLimitRate());
res = flowLimitCache.get(id);
}
}
return res;
}
public Integer getDomainMappingIdByServerPort(Integer serverPort) {
if (null == serverPort) {
return null;
}
Integer id = domainToDomainMappingIdCache.get(serverPort);
if (null != id) {
return id;
}
List<DomainMappingDO> domainMappingDOList = domainMappingMapper.findListByServerPort(serverPort);
// 不存在 或者 有多条记录,都不处理
if (CollectionUtils.isEmpty(domainMappingDOList) || domainMappingDOList.size() > 1) {
return null;
}
id = domainMappingDOList.get(0).getId();
domainToDomainMappingIdCache.put(serverPort, id);
return id;
}
public Integer getLicenseIdById(Integer id) {
Integer licenseId = idToLicenseIdCache.get(id);
if (null == licenseId) {
DomainMappingDO domainMappingDO = domainMappingMapper.findById(id);
if (null != domainMappingDO) {
licenseId = domainMappingDO.getLicenseId();
idToLicenseIdCache.put(id, licenseId);
}
}
return licenseId;
}
public FlowLimitBO getFlowLimitByServerPort(Integer serverPort) {
Integer id = getPortMappingIdByServerPort(serverPort);
if (null == id) {
return null;
}
FlowLimitBO res = getFlowLimit(id);
if (null == res || (null == res.getUpLimitRate() && null == res.getDownLimitRate())) {
Integer licenseId = getLicenseIdById(id);
if (null != licenseId) {
res = licenseService.getFlowLimit(licenseId);
}
}
return res;
}
private void updateMappingDomainToSecurityGroupMap(Integer serverPort, Integer securityGroupId) {
if (securityGroupId == null || securityGroupId == 0) {
mappingDomainToSecurityGroupMap.remove(serverPort);
return;
}
mappingDomainToSecurityGroupMap.put(serverPort, securityGroupId);
}
@Override
public void start() throws Throwable {
}
/**
* 服务端项目停止、启动时,更新在线状态为离线
*/
@Override
public void stop() throws Throwable {
domainMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
}
public DomainMappingDto one(Integer id) {
DomainMappingDO domainMappingDO = domainMappingMapper.findById(id);
if (null == domainMappingDO) {
return null;
}
DomainMappingDto res = (DomainMappingDto)domainMappingDO;
LicenseDO license = licenseMapper.findById(domainMappingDO.getLicenseId());
if (null != license) {
res.setLicenseName(license.getName());
res.setUserId(license.getUserId());
UserDO user = userMapper.findById(license.getUserId());
if (null != user) {
res.setUserName(user.getName());
}
}
return res;
}
}
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.neutrinoproxy.server.dal.PortMappingMapper">
<select id="selectDomainMappingByCondition" resultType="org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO">
SELECT pm.*
FROM port_mapping pm
LEFT JOIN license l ON pm.license_id = l.`id`
<where>
<if test="req.userId != null">
AND l.user_id = #{req.userId}
</if>
<if test="req.licenseId != null">
AND pm.license_id = #{req.licenseId}
</if>
<if test="req.domain != null">
AND pm.domain = #{req.domain}
</if>
<if test="req.isOnline != null">
AND l.is_online = #{req.isOnline}
</if>
<if test="req.enable != null">
AND pm.enable = #{req.enable}
</if>
<if test="req.protocal != null and req.protocal != ''">
AND pm.protocal = #{req.protocal}
</if>
<if test="req.description != null and req.description != '' ">
AND pm.description like #{req.description}
</if>
</where>
order by pm.id asc
</select>
</mapper>
@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.neutrinoproxy.server.dal.PortMappingMapper">
<select id="selectPortMappingByCondition" resultType="org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO">
<select id="selectPortMappingByCondition" resultType="org.dromara.neutrinoproxy.server.dal.entity.DomainMappingDO">
SELECT pm.* from port_mapping pm left join license on pm.license_id = license.`id`
<where>
<if test="req.userId != null">