diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/DefaultDomainStatusEnum.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/DefaultDomainStatusEnum.java new file mode 100644 index 00000000..65608f78 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/DefaultDomainStatusEnum.java @@ -0,0 +1,19 @@ +package org.dromara.neutrinoproxy.server.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 默认域名状态枚举 + * @author Mirac + * @date 21/7/2024 + */ +@Getter +@AllArgsConstructor +public enum DefaultDomainStatusEnum { + ENABLE(1, "默认域名"), + DISABLE(2, "关闭"); + + private Integer status; + private String desc; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java index ed149c80..e66cd7a3 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java @@ -60,7 +60,6 @@ public enum ExceptionConstant { PORT_CANNOT_REPEAT_MAPPING(14001, "服务端口[{}]不能重复映射"), AN_UNSUPPORTED_PROTOCOL(14002, "不支持的协议[{}]!"), PORT_MAPPING_SUBDONAME_CONNOT_REPEAT(14003, "子域名不能重复使用!"), - // 调度管理(15000) JOB_INFO_NOT_EXIST(15000, "调度管理记录不存在"), SYSTEM_ERROR(500, "系统异常"), @@ -72,7 +71,8 @@ public enum ExceptionConstant { // 安全组管理(17000) SECURITY_GROUP_NOT_EXIST(17000, "安全组不存在"), SECURITY_RULE_NOT_EXIST(17001, "安全规则不存在"), - + // 域名映射管理(18000) + DOMAIN_NAME_CANNOT_REPEAT(18000, "域名不能重复"), ; private int code; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/HttpsStatusEnum.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/HttpsStatusEnum.java new file mode 100644 index 00000000..ab1c5981 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/HttpsStatusEnum.java @@ -0,0 +1,19 @@ +package org.dromara.neutrinoproxy.server.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 仅Https状态枚举 + * @author Mirac + * @date 21/7/2024 + */ +@Getter +@AllArgsConstructor +public enum HttpsStatusEnum { + ONLY_HTTPS(1, "仅HTTPS"), + DISABLE_ONLY_HTTPS(2, "关闭仅HTTPS"); + + private Integer status; + private String desc; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainController.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainController.java new file mode 100644 index 00000000..a8110314 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainController.java @@ -0,0 +1,54 @@ +package org.dromara.neutrinoproxy.server.controller; + +import cn.hutool.core.util.StrUtil; +import org.dromara.neutrinoproxy.server.base.page.PageInfo; +import org.dromara.neutrinoproxy.server.base.page.PageQuery; +import org.dromara.neutrinoproxy.server.base.rest.Authorization; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainCreateReq; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainListReq; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainUpdateReq; +import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingCreateReq; +import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainListRes; +import org.dromara.neutrinoproxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.*; +import org.dromara.neutrinoproxy.server.service.DomainService; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.UploadedFile; +import org.noear.solon.lang.Nullable; + +import javax.sound.midi.Soundbank; +import java.io.IOException; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Mapping("/domain") +@Controller +public class DomainController { + @Inject + private DomainService domainService; + + @Mapping("/page") + public PageInfo page(PageQuery pageQuery, DomainListReq req) { + return domainService.page(pageQuery, req); + } + + @Post + @Mapping(path = "/create") + @Authorization(onlyAdmin = true) + public void create(DomainCreateReq req, UploadedFile jks) throws IOException { + ParamCheckUtil.checkNotNull(req, "req"); + ParamCheckUtil.checkNotEmpty(req.getDomain(), "domain"); + domainService.create(req, jks); + } + + @Post + @Mapping(path = "/update") + @Authorization(onlyAdmin = true) + public void update(DomainUpdateReq req, UploadedFile jks) throws IOException { + ParamCheckUtil.checkNotNull(req, "req"); + ParamCheckUtil.checkNotEmpty(req.getDomain(), "domain"); + domainService.update(req, jks); + } +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainCreateReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainCreateReq.java new file mode 100644 index 00000000..a3d92884 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainCreateReq.java @@ -0,0 +1,29 @@ +package org.dromara.neutrinoproxy.server.controller.req.proxy; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import org.dromara.neutrinoproxy.server.constant.DefaultDomainStatusEnum; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; +import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; +import org.noear.solon.core.handle.UploadedFile; +import org.noear.solon.lang.Nullable; + +import java.util.Date; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Data +public class DomainCreateReq { + /** + * 主域名 + */ + private String domain; + + /** + * KeyStore密码 + */ + private String keyStorePassword; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainListReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainListReq.java new file mode 100644 index 00000000..e4f6964a --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainListReq.java @@ -0,0 +1,21 @@ +package org.dromara.neutrinoproxy.server.controller.req.proxy; + +import lombok.Data; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Data +public class DomainListReq { + /** + * 用户ID + */ + private Integer userId; + /** + * 启用状态 + * {@link EnableStatusEnum} + */ + private Integer enable; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateReq.java new file mode 100644 index 00000000..588c33b4 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateReq.java @@ -0,0 +1,25 @@ +package org.dromara.neutrinoproxy.server.controller.req.proxy; + +import lombok.Data; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Data +public class DomainUpdateReq { + /** + * id + */ + private Integer id; + + /** + * 主域名 + */ + private String domain; + + /** + * KeyStore密码 + */ + private String keyStorePassword; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/res/proxy/DomainListRes.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/res/proxy/DomainListRes.java new file mode 100644 index 00000000..22947e07 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/res/proxy/DomainListRes.java @@ -0,0 +1,65 @@ +package org.dromara.neutrinoproxy.server.controller.res.proxy; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; +import org.dromara.neutrinoproxy.server.constant.DefaultDomainStatusEnum; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; +import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; + +import java.util.Date; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Accessors(chain = true) +@Data +public class DomainListRes { + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 主域名 + */ + private String domain; + + /** + * 用户ID + */ + private Integer userId; + + /** + * 用户名 + */ + private String userName; + + /** + * 是否是默认域名(1、是 2、否) + * {@link DefaultDomainStatusEnum} + */ + private Integer isDefault; + + /** + * 强制使用HTTPS(1、是 2、否) + * {@link HttpsStatusEnum} + */ + private Integer forceHttps; + + /** + * 启用状态 + * {@link EnableStatusEnum} + */ + private Integer enable; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java new file mode 100644 index 00000000..f1e25998 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java @@ -0,0 +1,28 @@ +package org.dromara.neutrinoproxy.server.dal; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.neutrinoproxy.server.controller.req.system.PortGroupListReq; +import org.dromara.neutrinoproxy.server.controller.res.system.PortGroupListRes; +import org.dromara.neutrinoproxy.server.dal.entity.DomainNameDO; +import org.dromara.neutrinoproxy.server.dal.entity.PortGroupDO; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +@Mapper +public interface DomainMapper extends BaseMapper { + default DomainNameDO checkRepeat(String domain, Set excludeIds) { + return this.selectOne(new LambdaQueryWrapper() + .eq(DomainNameDO::getDomain, domain) + .notIn(DomainNameDO::getId, excludeIds) + .last("limit 1") + ); + } +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainNameDO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainNameDO.java new file mode 100644 index 00000000..c01b153f --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainNameDO.java @@ -0,0 +1,106 @@ +/** + * 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.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.dromara.neutrinoproxy.server.constant.DefaultDomainStatusEnum; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; +import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; +import org.dromara.neutrinoproxy.server.constant.OnlineStatusEnum; +import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainListRes; +import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingListRes; + +import java.util.Date; + +/** + * 域名 + * @author: Mirac + * @date: 2024/7/21 + */ +@ToString +@Accessors(chain = true) +@Data +@TableName("domain_name") +public class DomainNameDO { + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 主域名 + */ + private String domain; + + /** + * 用户ID + */ + private Integer userId; + + /** + * SSL证书文件内容 + */ + private byte[] jks; + + /** + * KeyStore密码 + */ + private String keyStorePassword; + + /** + * 是否是默认域名(1、是 2、否) + * {@link DefaultDomainStatusEnum} + */ + private Integer isDefault; + + /** + * 强制使用HTTPS(1、是 2、否) + * {@link HttpsStatusEnum} + */ + private Integer forceHttps; + + /** + * 启用状态 + * {@link EnableStatusEnum} + */ + private Integer enable; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + public DomainListRes toRes() { + DomainListRes res = new DomainListRes(); + BeanUtil.copyProperties(this, res); + return res; + } +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java new file mode 100644 index 00000000..5cf91441 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java @@ -0,0 +1,137 @@ +package org.dromara.neutrinoproxy.server.service; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.solon.plugins.pagination.Page; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.solon.annotation.Db; +import org.dromara.neutrinoproxy.server.base.page.PageInfo; +import org.dromara.neutrinoproxy.server.base.page.PageQuery; +import org.dromara.neutrinoproxy.server.base.rest.SystemContextHolder; +import org.dromara.neutrinoproxy.server.constant.DefaultDomainStatusEnum; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; +import org.dromara.neutrinoproxy.server.constant.ExceptionConstant; +import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainCreateReq; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainListReq; +import org.dromara.neutrinoproxy.server.controller.req.proxy.DomainUpdateReq; +import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainListRes; +import org.dromara.neutrinoproxy.server.dal.DomainMapper; +import org.dromara.neutrinoproxy.server.dal.UserMapper; +import org.dromara.neutrinoproxy.server.dal.entity.DomainNameDO; +import org.dromara.neutrinoproxy.server.dal.entity.UserDO; +import org.dromara.neutrinoproxy.server.util.ParamCheckUtil; +import org.h2.schema.Domain; +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.UploadedFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author Mirac + * @date 21/7/2024 + */ +@Slf4j +@Component +public class DomainService { + @Db + private DomainMapper domainMapper; + @Db + private UserMapper userMapper; + + public PageInfo page(PageQuery pageQuery, DomainListReq req) { +// Page page = new Page<>(pageQuery.getCurrent(), pageQuery.getSize()); +// LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() +// .eq(req.getEnable() != null, DomainNameDO::getEnable, req.getEnable()) +// .eq(req.getUserId() != null, DomainNameDO::getUserId, req.getUserId()); +// Page page = domainMapper.selectPage(page, queryWrapper); + Page page = domainMapper.selectPage(new Page<>(pageQuery.getCurrent(), pageQuery.getSize()), new LambdaQueryWrapper() + .eq(req.getEnable() != null, DomainNameDO::getEnable, req.getEnable()) + .eq(req.getUserId() != null, DomainNameDO::getUserId, req.getUserId()) + .orderByAsc(Arrays.asList(DomainNameDO::getUserId, DomainNameDO::getId)) + ); + List resList = page.getRecords().stream().map(DomainNameDO::toRes).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(page.getRecords())) { + return PageInfo.of(resList, page); + } + Set userIds = resList.stream().map(DomainListRes::getUserId).collect(Collectors.toSet()); + List userDOList = userMapper.findByIds(userIds); + Map userMap = userDOList.stream().collect(Collectors.toMap(UserDO::getId, c -> c)); + for (DomainListRes item : resList) { + UserDO userDO = userMap.get(item.getUserId()); + if (null != userDO) { + item.setUserName(userDO.getName()); + } + } + return PageInfo.of(resList, page); + } + + /** + * 创建域名 + * @param req + */ + public void create(DomainCreateReq req, UploadedFile jks) throws IOException { + DomainNameDO domainNameDO = new DomainNameDO(); + if (null != jks) { + ParamCheckUtil.checkNotEmpty(req.getKeyStorePassword(), "keyStorePassword"); + + InputStream content = jks.getContent(); + byte[] byteArray = toByteArray(content); + domainNameDO.setJks(byteArray); + domainNameDO.setKeyStorePassword(req.getKeyStorePassword()); + } + Integer userId = SystemContextHolder.getUserId(); + Date now = new Date(); + domainNameDO.setDomain(req.getDomain()); + domainNameDO.setIsDefault(DefaultDomainStatusEnum.DISABLE.getStatus()); + domainNameDO.setForceHttps(HttpsStatusEnum.DISABLE_ONLY_HTTPS.getStatus()); + domainNameDO.setUserId(userId); + domainNameDO.setEnable(EnableStatusEnum.ENABLE.getStatus()); + domainNameDO.setCreateTime(now); + domainNameDO.setUpdateTime(now); + domainMapper.insert(domainNameDO); + } + + /** + * 将 InputStream 转换为 byte[] + */ + private byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[1024]; + while ((nRead = input.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + return buffer.toByteArray(); + } + + public void update(DomainUpdateReq req, UploadedFile jks) throws IOException { + DomainNameDO domainNameCheck = domainMapper.checkRepeat(req.getDomain(), Sets.newHashSet(req.getId())); + ParamCheckUtil.checkMustNull(domainNameCheck, ExceptionConstant.DOMAIN_NAME_CANNOT_REPEAT); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(DomainNameDO::getId, req.getId()); + if (null != jks) { + ParamCheckUtil.checkNotEmpty(req.getKeyStorePassword(), "keyStorePassword"); + + InputStream content = jks.getContent(); + byte[] byteArray = toByteArray(content); + updateWrapper.set(DomainNameDO::getKeyStorePassword, req.getKeyStorePassword()); + updateWrapper.set(DomainNameDO::getJks, byteArray); + } + updateWrapper.set(DomainNameDO::getDomain, req.getDomain()); + updateWrapper.set(DomainNameDO::getUpdateTime, new Date()); + int update = domainMapper.update(updateWrapper); + System.out.println(update); + } +}