Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
843a58e99b | ||
|
|
ec4f8cbd7f | ||
|
|
25fab99723 | ||
|
|
f0769fb8fd | ||
|
|
6d6e58af52 | ||
|
|
e9e9ce8c90 | ||
|
|
1e1b376883 | ||
|
|
9cb5eb3225 | ||
|
|
5b38bb6b22 | ||
|
|
2b28d4eff5 | ||
|
|
42f39ddb0f | ||
|
|
fabc575c9e | ||
|
|
3e1f14917b | ||
|
|
f17c341325 | ||
|
|
5bf21b7c2c | ||
|
|
252a6ac7c1 | ||
|
|
0b78367b9f | ||
|
|
bb91abae00 | ||
|
|
1e4312fcb1 | ||
|
|
f9009bb869 | ||
|
|
c4e1ae2a17 | ||
|
|
d2fc7b6d45 | ||
|
|
4d4c06e4b1 | ||
|
|
98cc7ead6e | ||
|
|
e1be4523ed | ||
|
|
99ddfa65f3 | ||
|
|
31e36d1939 | ||
|
|
f46ae98867 | ||
|
|
f87370397e | ||
|
|
ce6634d51c | ||
|
|
f43d447ea0 | ||
|
|
baafc9bf21 | ||
|
|
56226831f9 | ||
|
|
b3e15225b3 | ||
|
|
e725cdbb28 | ||
|
|
480867e655 | ||
|
|
a5c568392b | ||
|
|
bbb0e5dca3 | ||
|
|
2b80279256 | ||
|
|
b7b4432d09 | ||
|
|
a7269229b0 | ||
|
|
f84695d665 | ||
|
|
4258845985 | ||
|
|
87e6c066ed | ||
|
|
2ea8a5890f | ||
|
|
89ea802245 | ||
|
|
88387003e3 | ||
|
|
b1797686f1 | ||
|
|
9a08a7c72e | ||
|
|
0f15bbc94d | ||
|
|
e454510967 | ||
|
|
090c129f60 | ||
|
|
8b41038d13 | ||
|
|
980d6d1a46 | ||
|
|
6b805089ba | ||
|
|
d81cbb3f7b | ||
|
|
b6b91b69ab | ||
|
|
3185934ff9 | ||
|
|
238eb64c04 | ||
|
|
31e3d56f9b |
@@ -4,12 +4,21 @@
|
||||
<p align="center">
|
||||
<a href='https://gitee.com/dromara/neutrino-proxy/stargazers'><img src='https://gitee.com/dromara/neutrino-proxy/badge/star.svg?theme=dark' alt='star'></img></a>
|
||||
<a href='https://gitee.com/dromara/neutrino-proxy/members'><img src='https://gitee.com/dromara/neutrino-proxy/badge/fork.svg?theme=dark' alt='fork'></img></a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-8+-red.svg" />
|
||||
</a>
|
||||
<a href="./LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-red" alt="license MIT">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# 1、简介
|
||||
- 中微子代理(neutrino-proxy)是一个基于netty的、开源的java内网穿透项目。
|
||||
- 技术栈:Solon、MybatisPlus、Netty
|
||||
- 遵循MIT许可,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。
|
||||
- 官网地址1:https://neutrino-proxy.dromara.org
|
||||
- 官网地址2:https://dromara.gitee.io/neutrino-proxy
|
||||
- 快速上手请[点击这里](https://dromara.gitee.io/neutrino-proxy/pages/793dcb)
|
||||
|
||||
# 2、名称由来
|
||||
中微子,是轻子的一种,是组成自然界的最基本的粒子之一。它十分微小、不带电,可自由穿过地球,以接近光速运动,与其他物质的相互作用十分微弱,号称宇宙间的“隐身人”。
|
||||
@@ -19,11 +28,11 @@
|
||||
因此以中微子命名,寓意着中微子代理拥有中微子"安全"、"快速"、"穿透力强"的特点。
|
||||
|
||||
# 3、运行示例
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
# 4、项目结构
|
||||
- neutrino-proxy
|
||||
@@ -183,6 +192,9 @@ neutrino.proxy.client.license-key=b0a907332b474b25897c4dcb31fc7eb6
|
||||
<a href="https://gitee.com/ixxxk" target="_blank">
|
||||
<img src="assets/developer/tany.png" width="12%">
|
||||
</a>
|
||||
<a href="https://gitee.com/zoo-plus" target="_blank">
|
||||
<img src="assets/developer/zoo-plus.png" width="12%">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# ❤️ 感谢
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 804 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 217 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 502 KiB |
@@ -0,0 +1,113 @@
|
||||
# 内网穿透神器NeutrinoProxy 1.8.0版本发布
|
||||
|
||||
## 项目简介
|
||||
- [中微子代理(neutrino-proxy)](https://gitee.com/dromara/neutrino-proxy) 是一款基于netty的内网穿透神器。该项目采用最为宽松的MIT协议,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。
|
||||
- Gitee地址:https://gitee.com/dromara/neutrino-proxy
|
||||
- 官网地址:http://neutrino-proxy.dromara.org
|
||||
- 服务端管理后台截图:
|
||||
<img src="./home.png" width="100%"/>
|
||||
|
||||
## 主要特点:
|
||||
- 1、流量监控:首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。
|
||||
- 2、用户/License:支持多用户、多客户端使用。后台禁用实时生效。
|
||||
- 3、端口池:对外端口统一管理,支持用户、License独占端口。
|
||||
- 4、端口映射:新增、编辑、删除、禁用实时生效。
|
||||
- 5、Docker:服务端支持Docker一键部署。
|
||||
- 6、SSL证书:支持SSL,保护您的信息安全。
|
||||
- 7、域名映射:支持绑定子域名,方便本地调试三方回调
|
||||
- 8、采用最为宽松的MIT协议,免去你的后顾之忧
|
||||
|
||||
## 快速使用
|
||||
> 更多使用姿势、细节请通过官网或结尾微信二维码加我备注"中微子代理"入群交流。
|
||||
|
||||
### 1、 部署服务端
|
||||
#### 1.1、 Docker一键部署
|
||||
> 当前最新版本为1.8.0,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.8.0` 指定版本安装,推荐使用`latest`直接安装最新版。
|
||||
|
||||
##### 使用默认sqlite数据库
|
||||
```shell
|
||||
docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \
|
||||
-d --restart=always --name neutrino-proxy \
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
|
||||
```
|
||||
|
||||
##### 指定自己的mysql数据库
|
||||
- 在服务器上创建目录:/root/neutrino-proxy/config
|
||||
- 在该目录下创建`app.yml`文本文件,并配置如下内容:
|
||||
```yml
|
||||
neutrino:
|
||||
data:
|
||||
db:
|
||||
type: mysql
|
||||
# 自己的数据库实例,创建一个空的名为'neutrino-proxy'的数据库即可,首次启动服务端会自动初始化
|
||||
url: jdbc:mysql://xxxx:3306/neutrino-proxy?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false
|
||||
driver-class: com.mysql.jdbc.Driver
|
||||
# 数据库帐号
|
||||
username: xxx
|
||||
# 数据库密码
|
||||
password: xxx
|
||||
```
|
||||
- 然后执行如下命令:
|
||||
```shell
|
||||
docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \
|
||||
-v /root/neutrino-proxy/config:/root/neutrino-proxy/config \
|
||||
-d --restart=always --name neutrino \
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
|
||||
```
|
||||
|
||||
#### 1.2、使用jar包自行部署
|
||||
- 首先确保服务器上已安装java8运行环境
|
||||
- 打开[发行版页面](https://gitee.com/dromara/neutrino-proxy/releases),下载最新的release包:`neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`
|
||||
- 在服务器上新建部署目录:`/work/projects/neutrino-proxy-server`
|
||||
- 将` neutrino-proxy-server.jar`、`neutrino-proxy-admin.zip`上传至服务器部署目录。
|
||||
- 解压`neutrino-proxy-admin.zip`文件
|
||||
- 执行命令`java -jar neutrino-proxy-server.jar`启动服务端完成部署,默认使用sqlite数据库。
|
||||
- 若需要指定自己的mysql数据库,同样的需要在当前目录下新建`app.yml`文件,文件内容同上。执行命令`java -jar neutrino-proxy-server.jar config=app.yml`启动服务端完成部署
|
||||
- 可参照 https://gitee.com/dromara/neutrino-proxy/blob/master/bin/server_start.sh 使用shell脚本启动服务端。
|
||||
|
||||
### 2、管理后台配置
|
||||
- 服务端部署成功后,访问`http://{服务端IP}:8888`打开后台管理页面。
|
||||
- 使用默认的管理员帐号登录:admin/123456
|
||||
- 打开`代理配置>License管理`页面,可以看到系统已经自动为管理员初始化了一条License记录,复制该`LicenseKey`备用,后续客户端配置需要。
|
||||
- 打开`代理配置>端口映射`页面,可以看到系统已经自动为初始化了几条端口映射。可根据需要自行添加、修改。这里我们以`9101 -> 127.0.0.1:8080`映射为例
|
||||
|
||||
### 3、启动客户端
|
||||
- 首先确保本地已安装java8运行环境
|
||||
- 打开[发行版页面](https://gitee.com/dromara/neutrino-proxy/releases),下载最新的release包:`neutrino-proxy-client.jar`
|
||||
- 在本地`neutrino-proxy-client.jar`同级别目录下新建`app.yml`文件,并配置如下内容:
|
||||
```yml
|
||||
neutrino:
|
||||
proxy:
|
||||
client:
|
||||
# ssl证书密钥(使用jjar包内自带的证书,则此处无需修改)
|
||||
key-store-password: 123456
|
||||
# ssl证书管理密钥(使用jjar包内自带的证书,则此处无需修改。自定义证书,则此处配置对应的路径)
|
||||
jks-path: classpath:/test.jks
|
||||
# 代理服务端IP
|
||||
server-ip: localhost
|
||||
# 代理服务端IP, 若是非ssl端口,则ssl-enable需要配置为false
|
||||
server-port: 9002
|
||||
# 是否启用ssl
|
||||
ssl-enable: true
|
||||
# licenseKey,客户端凭证。此处需要配置刚刚从管理后台复制的LicenseKey
|
||||
license-key: xxxx
|
||||
```
|
||||
- 执行命令`java -jar neutrino-proxy-client.jar`启动客户端
|
||||
- 查看服务端License管理,刷新页面,对应的License在线状态为`在线`,则表明客户端已正常连接。
|
||||
|
||||
### 4、代理验证
|
||||
- 本地启动被代理服务,如:redis、本地web项目、本地mysql等等
|
||||
- 先确保本地能正常访问被代理服务,如果本地都不能访问,不用想代理更不可能!!!
|
||||
- 通过服务端IP+9101(上面License配置的端口映射重的服务端端口)访问本地被代理服务
|
||||
|
||||
## 联系我们
|
||||
笔者时间、能力有限,且开源项目非一朝一夕之事,存在众多问题亦在所难免。使用、学习过程中有任何问题欢迎大家与我联系。
|
||||
|
||||
对项目有什么想法或者建议,可以加我微信拉交流群,或者创建[issues](https://gitee.com/dromara/neutrino-proxy/issues),一起完善项目
|
||||
|
||||
- 微信号:yuyunshize
|
||||
- Email: aoshiguchen@dromara.org
|
||||
- 中微子代理官网:http://neutrino-proxy.dromara.org
|
||||
- 中微子代理仓库:https://gitee.com/dromara/neutrino-proxy
|
||||
- 微信二维码:
|
||||
<img src="MyWeChatQRCode.jpeg" width="100%"/>
|
||||
@@ -0,0 +1,38 @@
|
||||
# 本地启动SpringBoot,提供hello接口以供测试:
|
||||
|
||||
## 1、10线程 100连接直接测试
|
||||
> wrk -t 10 -c 100 -d 1s http://localhost:8080/hello
|
||||
测试结果:
|
||||
```
|
||||
10 threads and 100 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 10.51ms 5.82ms 53.28ms 83.61%
|
||||
Req/Sec 0.99k 123.01 1.21k 81.82%
|
||||
10797 requests in 1.10s, 1.35MB read
|
||||
Requests/sec: 9799.85
|
||||
Transfer/sec: 1.23MB
|
||||
```
|
||||
## 2、10线程 100连接走端口转发测试
|
||||
> wrk -t 10 -c 100 -d 1s http://localhost:9105/hello
|
||||
测试结果:
|
||||
```
|
||||
10 threads and 100 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 126.37ms 163.69ms 971.32ms 81.43%
|
||||
Req/Sec 24.04 21.06 80.00 84.62%
|
||||
70 requests in 1.02s, 8.96KB read
|
||||
Requests/sec: 68.46
|
||||
Transfer/sec: 8.76K
|
||||
```
|
||||
## 3、10线程 100连接走域名转发测试
|
||||
> wrk -t 10 -c 100 -d 1s http://test1.neutrino-proxy.asgc.fun/hello
|
||||
测试结果:
|
||||
```
|
||||
10 threads and 100 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 327.86ms 218.93ms 903.28ms 56.76%
|
||||
Req/Sec 30.65 20.46 88.00 62.67%
|
||||
259 requests in 1.02s, 44.77KB read
|
||||
Requests/sec: 253.75
|
||||
Transfer/sec: 43.86KB
|
||||
```
|
||||
@@ -1,16 +1,17 @@
|
||||
<template>
|
||||
<div class="app-container calendar-list-container">
|
||||
<div class="filter-container" style="display:flex">
|
||||
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable style="margin-right:10px">
|
||||
<el-select v-model="listQuery.userId" placeholder="请选择用户" clearable style="margin-right:10px;width: 120px;">
|
||||
<el-option v-for="item in userList" :key="item.loginName" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<el-select v-model="listQuery.licenseId" placeholder="请选择license" clearable style="margin-right:10px">
|
||||
<el-select v-model="listQuery.licenseId" placeholder="请选择license" 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">
|
||||
<el-option v-for="item in protocalList" :key="item.name" :label="item.name" :value="item.name" :disabled="!item.enable"/>
|
||||
<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:150px;margin-right:10px" class="filter-item"
|
||||
<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 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" />
|
||||
@@ -18,6 +19,8 @@
|
||||
<el-select v-model="listQuery.enable" placeholder="请选择启用状态" clearable style="width:145px;margin-right:10px">
|
||||
<el-option v-for="item in selectObj.statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-input v-model="listQuery.description" type="text" style="width:145px;margin-right:10px" class="filter-item"
|
||||
placeholder="请输入描述详情" show-word-limit />
|
||||
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">{{
|
||||
$t('table.search') }}</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary"
|
||||
@@ -61,6 +64,12 @@
|
||||
<span>{{ scope.row.clientIp }}:{{ scope.row.clientPort }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" :label="$t('table.desc')" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.description }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="150px" align="center" :label="$t('table.createTime')">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
@@ -128,7 +137,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('协议')" prop="protocal">
|
||||
<el-select style="width: 280px;" class="filter-item" v-model="temp.protocal" placeholder="请选择">
|
||||
<el-option v-for="item in protocalList" :key="item.name" :label="item.name" :value="item.name" :disabled="!item.enable">
|
||||
<el-option v-for="item in protocalList" :key="item.name" :label="item.name" :value="item.name"
|
||||
:disabled="!item.enable">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -144,11 +154,14 @@
|
||||
<el-form-item :label="$t('客户端端口')" prop="clientPort">
|
||||
<el-input v-model="temp.clientPort"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('域名')" prop="subdomain" v-if="temp.protocal === 'HTTP'">
|
||||
<el-form-item :label="$t('域名')" prop="subdomain" v-if="temp.protocal === 'HTTP' && domainName && domainName != ''">
|
||||
<el-input v-model="temp.subdomain">
|
||||
<template slot="append">.{{domainName}}</template>
|
||||
<template slot="append">.{{ domainName }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('描述')" prop="description">
|
||||
<el-input v-model="temp.description"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="
|
||||
@@ -222,7 +235,8 @@ export default {
|
||||
license: undefined,
|
||||
port: undefined,
|
||||
isOnline: undefined,
|
||||
enable: undefined
|
||||
enable: undefined,
|
||||
description: undefined
|
||||
|
||||
},
|
||||
importanceOptions: [1, 2, 3],
|
||||
@@ -261,7 +275,8 @@ export default {
|
||||
licenseId: [{ required: true, message: '请选择License', trigger: 'blur,change' }],
|
||||
serverPort: [{ required: true, message: '请输入服务端端口', trigger: 'blur' }],
|
||||
clientIp: [{ required: true, message: '请输入客户端IP', trigger: 'blur' }],
|
||||
clientPort: [{ required: true, message: '请输入客户端端口', trigger: 'blur' }]
|
||||
clientPort: [{ required: true, message: '请输入客户端端口', trigger: 'blur' }],
|
||||
protocal: [{ required: true, message: '请选择协议', trigger: 'blur' }]
|
||||
},
|
||||
downloadLoading: false,
|
||||
countryColumns: [
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM openjdk:17-jdk-alpine
|
||||
#同步时间
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||
apk update && apk add wget unzip vim && apk add -U tzdata && \
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
|
||||
RUN mkdir -p /root/neutrino-proxy/config
|
||||
WORKDIR /root/neutrino-proxy
|
||||
COPY ../neutrino-proxy-client/target/neutrino-proxy-client.jar /root/neutrino-proxy/neutrino-proxy-client.jar
|
||||
COPY ../neutrino-proxy-client/src/main/resources/app.yml /root/neutrino-proxy/config
|
||||
#VOLUME ["/root/neutrino-proxy"]
|
||||
ENTRYPOINT ["java","-jar","neutrino-proxy-client.jar","config=./config/app.yml"]
|
||||
|
||||
#docker run -it -d --restart=always --name np_client -e SERVER_IP=127.0.0.1
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>neutrino-proxy-parent</artifactId>
|
||||
<artifactId>neutrino-proxy</artifactId>
|
||||
<groupId>org.dromara.neutrino-proxy</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
|
||||
+3
-1
@@ -61,7 +61,9 @@ public class ClientChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
cause.printStackTrace();
|
||||
if (ctx.channel().isActive()) {
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package org.dromara.neutrinoproxy.client.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import org.dromara.neutrinoproxy.client.config.ProxyConfig;
|
||||
import org.dromara.neutrinoproxy.client.util.ProxyUtil;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
@@ -86,7 +87,7 @@ public class ProxyClientService {
|
||||
if (proxyConfig.getClient().getSslEnable()) {
|
||||
ch.pipeline().addLast(createSslHandler());
|
||||
}
|
||||
|
||||
// ch.pipeline().addFirst(new LoggingHandler(ProxyClientService.class));
|
||||
ch.pipeline().addLast(new ProxyMessageDecoder(proxyConfig.getProtocol().getMaxFrameLength(),
|
||||
proxyConfig.getProtocol().getLengthFieldOffset(), proxyConfig.getProtocol().getLengthFieldLength(),
|
||||
proxyConfig.getProtocol().getLengthAdjustment(), proxyConfig.getProtocol().getInitialBytesToStrip()));
|
||||
|
||||
@@ -14,10 +14,10 @@ neutrino:
|
||||
all-idle-time-seconds: 0
|
||||
client:
|
||||
thread-count: 50
|
||||
key-store-password: 123456
|
||||
jks-path: classpath:/test.jks
|
||||
server-ip: localhost
|
||||
server-port: 9002
|
||||
ssl-enable: true
|
||||
key-store-password: ${STORE_PASS:123456}
|
||||
jks-path: ${JKS_PATH:classpath:/test.jks}
|
||||
server-ip: ${SERVER_IP:localhost}
|
||||
server-port: ${SERVER_PORT:9002}
|
||||
ssl-enable: ${SSL_ENABLE:true}
|
||||
obtain-license-interval: 5
|
||||
license-key: b0a907332b474b25897c4dcb31fc7eb6
|
||||
license-key: ${LICENSE_KEY:b0a907332b474b25897c4dcb31fc7eb6}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration>
|
||||
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-client/app.log"/>
|
||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
||||
<property name="ENCODE" value="utf8" />
|
||||
<!-- <property name="ENCODE" value="utf8" />-->
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
@@ -10,7 +10,7 @@
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
<charset>${ENCODE}</charset>
|
||||
<!-- <charset>${ENCODE}</charset>-->
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
<charset>${ENCODE}</charset>
|
||||
<!-- <charset>${ENCODE}</charset>-->
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>neutrino-proxy-parent</artifactId>
|
||||
<artifactId>neutrino-proxy</artifactId>
|
||||
<groupId>org.dromara.neutrino-proxy</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM openjdk:8-jdk-alpine
|
||||
FROM openjdk:17-jdk-alpine
|
||||
#同步时间
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||
apk update && apk add wget unzip vim && apk add -U tzdata && \
|
||||
@@ -6,10 +6,10 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
RUN mkdir -p /root/neutrino-proxy/config
|
||||
RUN mkdir -p /root/neutrino-proxy/neutrino-proxy-admin
|
||||
WORKDIR /root/neutrino-proxy
|
||||
COPY neutrino-proxy-server/target/neutrino-proxy-server.jar /root/neutrino-proxy/neutrino-proxy-server.jar
|
||||
COPY neutrino-proxy-server/src/main/resources/app.yml /root/neutrino-proxy/config
|
||||
COPY neutrino-proxy-admin/dist /root/neutrino-proxy/neutrino-proxy-admin/dist
|
||||
COPY ../neutrino-proxy-server/target/neutrino-proxy-server.jar /root/neutrino-proxy/neutrino-proxy-server.jar
|
||||
COPY ../neutrino-proxy-server/src/main/resources/app.yml /root/neutrino-proxy/config
|
||||
COPY ../neutrino-proxy-admin/dist /root/neutrino-proxy/neutrino-proxy-admin/dist
|
||||
#VOLUME ["/root/neutrino-proxy"]
|
||||
ENTRYPOINT ["java","-jar","neutrino-proxy-server.jar","config=./config/app.yml"]
|
||||
|
||||
#docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 -v /root/neutrino-proxy/config:/root/neutrino-proxy/config -d --restart=always --name neutrino registry.cn-hangzhou.aliyuncs.com/asgc/aoshiguchen-docker-images:1.7
|
||||
#docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 -v /root/neutrino-proxy/config:/root/neutrino-proxy/config -d --restart=always --name neutrino registry.cn-hangzhou.aliyuncs.com/asgc/aoshiguchen-docker-images:1.7
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>neutrino-proxy-parent</artifactId>
|
||||
<artifactId>neutrino-proxy</artifactId>
|
||||
<groupId>org.dromara.neutrino-proxy</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
@@ -52,8 +52,8 @@
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
|
||||
-5
@@ -45,11 +45,6 @@ public class DbConfig {
|
||||
*/
|
||||
@Inject("${neutrino.data.db.url}")
|
||||
private String url;
|
||||
/**
|
||||
* 驱动类
|
||||
*/
|
||||
@Inject("${neutrino.data.db.driver-class}")
|
||||
private String driverClass;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
|
||||
+11
-4
@@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.dromara.neutrinoproxy.server.constant.DbTypeEnum;
|
||||
import org.apache.ibatis.solon.annotation.Db;
|
||||
import org.dromara.neutrinoproxy.server.constant.DbTypeEnum;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
@@ -32,7 +32,14 @@ public class DbConfiguration {
|
||||
return dataSource;
|
||||
} else if (DbTypeEnum.MYSQL == dbTypeEnum) {
|
||||
HikariDataSource dataSource = new HikariDataSource();
|
||||
dataSource.setDriverClassName(dbConfig.getDriverClass());
|
||||
String driver = "com.mysql.cj.jdbc.Driver";
|
||||
try {
|
||||
Class.forName(driver);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// 对类名的判断,异常则说明不存在:
|
||||
driver = "com.mysql.jdbc.Driver";
|
||||
}
|
||||
dataSource.setDriverClassName(driver);
|
||||
dataSource.setJdbcUrl(dbConfig.getUrl());
|
||||
dataSource.setMinimumIdle(5);
|
||||
dataSource.setMaximumPoolSize(20);
|
||||
@@ -57,13 +64,13 @@ public class DbConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public void db1_ext2(@Db("db") MybatisConfiguration config){
|
||||
public void db1_ext2(@Db("db") MybatisConfiguration config) {
|
||||
config.getTypeHandlerRegistry().register("fun.asgc.neutrino.proxy.server.dal");
|
||||
config.setDefaultEnumTypeHandler(null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MybatisSqlSessionFactoryBuilder factoryBuilderNew(){
|
||||
public MybatisSqlSessionFactoryBuilder factoryBuilderNew() {
|
||||
return new MybatisSqlSessionFactoryBuilderImpl();
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -45,6 +45,7 @@ public class ProxyConfig {
|
||||
private Integer bossThreadCount;
|
||||
private Integer workThreadCount;
|
||||
private String domainName;
|
||||
private Integer httpProxyPort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -41,6 +41,8 @@ public enum ExceptionConstant {
|
||||
PARAMS_NOT_NULL(10, "参数[{}]不能为空"),
|
||||
PARAMS_NOT_EMPTY(11, "参数[{}]不能为空"),
|
||||
|
||||
FILED_LENGTH_OUT(12 ,"{}不能超出长度{}"),
|
||||
|
||||
// 用户管理(11000)
|
||||
// license管理(12000)
|
||||
LICENSE_NAME_CANNOT_REPEAT(12000, "license名称不能重复"),
|
||||
|
||||
+21
-19
@@ -32,24 +32,25 @@ public class PortMappingController {
|
||||
return portMappingService.page(pageQuery, req);
|
||||
}
|
||||
|
||||
@Post
|
||||
@Mapping("/create")
|
||||
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");
|
||||
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("");
|
||||
}
|
||||
@Post
|
||||
@Mapping("/create")
|
||||
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");
|
||||
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);
|
||||
}
|
||||
|
||||
return portMappingService.create(req);
|
||||
}
|
||||
@@ -62,6 +63,7 @@ public class PortMappingController {
|
||||
ParamCheckUtil.checkNotNull(req.getServerPort(), "serverPort");
|
||||
ParamCheckUtil.checkNotNull(req.getClientPort(), "clientPort");
|
||||
ParamCheckUtil.checkNotEmpty(req.getProtocal(), "protocal");
|
||||
ParamCheckUtil.checkMaxLength(req.getDescription(), 50, "描述", "50");
|
||||
if (StringUtils.isBlank(req.getClientIp())) {
|
||||
// 没传客户端ip,默认为127.0.0.1
|
||||
req.setClientIp("127.0.0.1");
|
||||
@@ -70,7 +72,7 @@ public class PortMappingController {
|
||||
ParamCheckUtil.checkNotNull(networkProtocolEnum, ExceptionConstant.AN_UNSUPPORTED_PROTOCOL, req.getProtocal());
|
||||
if (networkProtocolEnum != NetworkProtocolEnum.HTTP) {
|
||||
// 目前仅HTTP支持绑定域名
|
||||
req.setSubdomain("");
|
||||
req.setSubdomain(null);
|
||||
}
|
||||
|
||||
return portMappingService.update(req);
|
||||
|
||||
+4
@@ -54,4 +54,8 @@ public class PortMappingCreateReq {
|
||||
* 客户端端口
|
||||
*/
|
||||
private Integer clientPort;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
|
||||
+5
@@ -48,6 +48,11 @@ public class PortMappingListReq {
|
||||
* 服务端口号
|
||||
*/
|
||||
private Integer serverPort;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
|
||||
/**
|
||||
* 是否在线
|
||||
|
||||
+4
@@ -58,4 +58,8 @@ public class PortMappingUpdateReq {
|
||||
* 客户端端口
|
||||
*/
|
||||
private Integer clientPort;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
|
||||
+4
@@ -89,6 +89,10 @@ public class PortMappingListRes {
|
||||
* {@link EnableStatusEnum}
|
||||
*/
|
||||
private Integer enable;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ 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.toolkit.StringUtils;
|
||||
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
|
||||
import org.dromara.neutrinoproxy.server.controller.req.proxy.PortMappingListReq;
|
||||
import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO;
|
||||
@@ -48,7 +49,7 @@ public interface PortMappingMapper extends BaseMapper<PortMappingDO> {
|
||||
* @return
|
||||
*/
|
||||
default Boolean checkRepeatBySubdomain(String subdomain, Set<Integer> excludeIds) {
|
||||
return this.selectCount(new LambdaQueryWrapper<PortMappingDO>()
|
||||
return StringUtils.isEmpty(subdomain)? Boolean.FALSE : this.selectCount(new LambdaQueryWrapper<PortMappingDO>()
|
||||
.eq(PortMappingDO::getSubdomain, subdomain)
|
||||
.notIn(!CollectionUtil.isEmpty(excludeIds), PortMappingDO::getId, excludeIds)
|
||||
).intValue() > 0;
|
||||
|
||||
+5
@@ -68,6 +68,11 @@ public class PortMappingDO {
|
||||
* 客户端端口
|
||||
*/
|
||||
private Integer clientPort;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 是否在线
|
||||
* {@link OnlineStatusEnum}
|
||||
|
||||
+31
-27
@@ -39,7 +39,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
||||
private ProxyConfig proxyConfig;
|
||||
@Override
|
||||
public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable {
|
||||
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
|
||||
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName()) || null == proxyConfig.getServer().getHttpProxyPort()) {
|
||||
log.info("no config domain name,nonsupport http proxy.");
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
||||
ch.pipeline().addLast(new VisitorChannelHandler());
|
||||
}
|
||||
});
|
||||
bootstrap.bind("0.0.0.0", 80).sync();
|
||||
bootstrap.bind("0.0.0.0", proxyConfig.getServer().getHttpProxyPort()).sync();
|
||||
log.info("Http代理服务启动成功!");
|
||||
} catch (Exception e) {
|
||||
log.error("http proxy start err!", e);
|
||||
@@ -72,7 +72,32 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
String host = getHost(byteBuf);
|
||||
|
||||
byte[] bytes = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(bytes);
|
||||
byteBuf.resetReaderIndex();
|
||||
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
|
||||
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (null == proxyChannel) {
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(ProxyUtil.getVisitorIdByChannel(channel), bytes));
|
||||
|
||||
// 增加流量计数
|
||||
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(channel);
|
||||
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
|
||||
});
|
||||
|
||||
String visitorId = ProxyUtil.getVisitorIdByChannel(ctx.channel());
|
||||
if (StringUtils.isNotBlank(visitorId)) {
|
||||
proxyAttachment.execute();
|
||||
return;
|
||||
}
|
||||
|
||||
String host = getHost(bytes);
|
||||
if (StringUtils.isBlank(host)) {
|
||||
ctx.channel().close();
|
||||
return;
|
||||
@@ -102,26 +127,8 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
||||
return;
|
||||
}
|
||||
|
||||
String visitorId = ProxyUtil.newVisitorId();
|
||||
Channel visitorChannel = ctx.channel();
|
||||
|
||||
byte[] bytes = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(bytes);
|
||||
ProxyAttachment proxyAttachment = new ProxyAttachment(ctx.channel(), bytes, (channel, buf) -> {
|
||||
Channel proxyChannel = channel.attr(Constants.NEXT_CHANNEL).get();
|
||||
if (null == proxyChannel) {
|
||||
// 该端口还没有代理客户端
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
proxyChannel.writeAndFlush(ProxyMessage.buildTransferMessage(visitorId, bytes));
|
||||
|
||||
// 增加流量计数
|
||||
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(visitorChannel);
|
||||
Solon.context().getBean(FlowReportService.class).addWriteByte(visitorChannelAttachInfo.getLicenseId(), bytes.length);
|
||||
});
|
||||
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, visitorChannel, serverPort);
|
||||
visitorId = ProxyUtil.newVisitorId();
|
||||
ProxyUtil.addVisitorChannelToCmdChannel(cmdChannel, visitorId, ctx.channel(), serverPort);
|
||||
ProxyUtil.addProxyConnectAttachment(visitorId, proxyAttachment);
|
||||
cmdChannel.writeAndFlush(ProxyMessage.buildConnectMessage(visitorId).setData(lanInfo.getBytes()));
|
||||
}
|
||||
@@ -167,10 +174,7 @@ public class HttpProxy implements EventListener<AppLoadEndEvent> {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private String getHost(ByteBuf byteBuf) {
|
||||
byte[] buf = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(buf);
|
||||
byteBuf.resetReaderIndex();
|
||||
private String getHost(byte[] buf) {
|
||||
String req = new String(buf);
|
||||
String[] lines = req.split("\r\n");
|
||||
String firstLine = lines[0];
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package org.dromara.neutrinoproxy.server.proxy.core;
|
||||
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessageDecoder;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessageEncoder;
|
||||
import org.dromara.neutrinoproxy.core.util.FileUtil;
|
||||
@@ -121,6 +122,7 @@ public class ProxyServerRunner implements EventListener<AppLoadEndEvent> {
|
||||
}
|
||||
|
||||
private void proxyServerCommonInitHandler(SocketChannel ch) {
|
||||
// ch.pipeline().addFirst(new LoggingHandler(ProxyServerRunner.class));
|
||||
ch.pipeline().addLast(new ProxyMessageDecoder(proxyConfig.getProtocol().getMaxFrameLength(),
|
||||
proxyConfig.getProtocol().getLengthFieldOffset(), proxyConfig.getProtocol().getLengthFieldLength(),
|
||||
proxyConfig.getProtocol().getLengthAdjustment(), proxyConfig.getProtocol().getInitialBytesToStrip()));
|
||||
|
||||
+3
@@ -106,6 +106,9 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ProxyMessa
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
if (ctx.channel().isActive()) {
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
-4
@@ -1,5 +1,6 @@
|
||||
package org.dromara.neutrinoproxy.server.proxy.handler;
|
||||
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.core.ProxyDataTypeEnum;
|
||||
import org.dromara.neutrinoproxy.core.ProxyMessage;
|
||||
@@ -29,12 +30,17 @@ public class ProxyMessageDisconnectHandler implements ProxyMessageHandler {
|
||||
return;
|
||||
}
|
||||
// 代理连接没有连上服务器由控制连接发送用户端断开连接消息
|
||||
String visitorId = proxyMessage.getInfo();
|
||||
Channel userChannel = ProxyUtil.removeVisitorChannelFromCmdChannel(ctx.channel(), visitorId);
|
||||
if (null != userChannel) {
|
||||
Channel visitorChannel = ctx.channel().attr(Constants.NEXT_CHANNEL).get();
|
||||
if (null != visitorChannel) {
|
||||
// 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||
userChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
visitorChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
// String visitorId = proxyMessage.getInfo();
|
||||
// Channel visitorChannel = ProxyUtil.removeVisitorChannelFromCmdChannel(ctx.channel(), visitorId);
|
||||
// if (null != visitorChannel) {
|
||||
// // 数据发送完成后再关闭连接,解决http1.0数据传输问题
|
||||
// visitorChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ public class ProxyMessageTransferHandler implements ProxyMessageHandler {
|
||||
buf.writeBytes(proxyMessage.getData());
|
||||
visitorChannel.writeAndFlush(buf);
|
||||
|
||||
// 关闭http响应通道
|
||||
// ProxyUtil.closeHttpProxyResponseChannel(visitorChannel);
|
||||
|
||||
// 增加流量计数
|
||||
VisitorChannelAttachInfo visitorChannelAttachInfo = ProxyUtil.getAttachInfo(visitorChannel);
|
||||
Solon.context().getBean(FlowReportService.class).addReadByte(visitorChannelAttachInfo.getLicenseId(), proxyMessage.getData().length);
|
||||
|
||||
+224
-218
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -45,7 +46,6 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/8
|
||||
*/
|
||||
@@ -71,246 +71,252 @@ public class PortMappingService implements LifecycleBean {
|
||||
@Inject
|
||||
private DBInitialize dbInitialize;
|
||||
|
||||
public PageInfo<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) {
|
||||
Page<PortMappingListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
|
||||
public PageInfo<PortMappingListRes> page(PageQuery pageQuery, PortMappingListReq req) {
|
||||
Page<PortMappingListRes> result = PageHelper.startPage(pageQuery.getCurrent(), pageQuery.getSize());
|
||||
if (StringUtils.isNotEmpty(req.getDescription())) {
|
||||
//描述字段为模糊查询,在应用层处理,否则sqlite不支持
|
||||
req.setDescription("%" + req.getDescription() + "%");
|
||||
}
|
||||
List<PortMappingDO> list = portMappingMapper.selectPortMappingByCondition(req);
|
||||
List<PortMappingListRes> respList = mapperFacade.mapAsList(list, PortMappingListRes.class);
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
|
||||
}
|
||||
|
||||
List<PortMappingDO> list = portMappingMapper.selectPortMappingByCondition(req);
|
||||
List<PortMappingListRes> respList = mapperFacade.mapAsList(list, PortMappingListRes.class);
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
|
||||
}
|
||||
Set<Integer> licenseIds = respList.stream().map(PortMappingListRes::getLicenseId).collect(Collectors.toSet());
|
||||
List<LicenseDO> licenseList = licenseMapper.findByIds(licenseIds);
|
||||
if (CollectionUtil.isEmpty(licenseList)) {
|
||||
return PageInfo.of(respList, result.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()));
|
||||
|
||||
Set<Integer> licenseIds = respList.stream().map(PortMappingListRes::getLicenseId).collect(Collectors.toSet());
|
||||
List<LicenseDO> licenseList = licenseMapper.findByIds(licenseIds);
|
||||
if (CollectionUtil.isEmpty(licenseList)) {
|
||||
return PageInfo.of(respList, result.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().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) {
|
||||
item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getDomainName());
|
||||
}
|
||||
});
|
||||
//sorted [userId asc] [licenseId asc] [createTime asc]
|
||||
respList = respList.stream().sorted(Comparator.comparing(PortMappingListRes::getUserId)
|
||||
.thenComparing(PortMappingListRes::getLicenseId)
|
||||
.thenComparing(PortMappingListRes::getCreateTime))
|
||||
.collect(Collectors.toList());
|
||||
return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
|
||||
}
|
||||
|
||||
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().getDomainName()) && StrUtil.isNotBlank(item.getSubdomain())) {
|
||||
item.setDomain(item.getSubdomain() + "." + proxyConfig.getServer().getDomainName());
|
||||
}
|
||||
});
|
||||
//sorted [userId asc] [licenseId asc] [createTime asc]
|
||||
respList = respList.stream().sorted(Comparator.comparing(PortMappingListRes::getUserId)
|
||||
.thenComparing(PortMappingListRes::getLicenseId)
|
||||
.thenComparing(PortMappingListRes::getCreateTime))
|
||||
.collect(Collectors.toList());
|
||||
return PageInfo.of(respList, result.getTotal(), pageQuery.getCurrent(), pageQuery.getSize());
|
||||
}
|
||||
public PortMappingCreateRes create(PortMappingCreateReq 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.getServerPort());
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), null), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort());
|
||||
ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), null), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT);
|
||||
|
||||
public PortMappingCreateRes create(PortMappingCreateReq 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.getServerPort());
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), null), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort());
|
||||
ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), null), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT);
|
||||
Date now = new Date();
|
||||
PortMappingDO portMappingDO = new 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.setDescription(req.getDescription());
|
||||
portMappingDO.setIsOnline(OnlineStatusEnum.OFFLINE.getStatus());
|
||||
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
|
||||
portMappingDO.setCreateTime(now);
|
||||
portMappingDO.setUpdateTime(now);
|
||||
portMappingMapper.insert(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
|
||||
}
|
||||
return new PortMappingCreateRes();
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
PortMappingDO portMappingDO = new 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.setIsOnline(OnlineStatusEnum.OFFLINE.getStatus());
|
||||
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
|
||||
portMappingDO.setCreateTime(now);
|
||||
portMappingDO.setUpdateTime(now);
|
||||
portMappingMapper.insert(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
|
||||
}
|
||||
return new PortMappingCreateRes();
|
||||
}
|
||||
public PortMappingUpdateRes update(PortMappingUpdateReq 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.getServerPort());
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort());
|
||||
ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT);
|
||||
|
||||
public PortMappingUpdateRes update(PortMappingUpdateReq 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.getServerPort());
|
||||
ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST);
|
||||
ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort());
|
||||
ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT);
|
||||
// 查询原端口映射
|
||||
PortMappingDO oldPortMappingDO = portMappingMapper.findById(req.getId());
|
||||
ParamCheckUtil.checkNotNull(oldPortMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
|
||||
|
||||
// 查询原端口映射
|
||||
PortMappingDO oldPortMappingDO = portMappingMapper.findById(req.getId());
|
||||
ParamCheckUtil.checkNotNull(oldPortMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
|
||||
PortMappingDO portMappingDO = new PortMappingDO();
|
||||
portMappingDO.setId(req.getId());
|
||||
portMappingDO.setProtocal(req.getProtocal());
|
||||
portMappingDO.setSubdomain(req.getSubdomain());
|
||||
portMappingDO.setLicenseId(req.getLicenseId());
|
||||
portMappingDO.setServerPort(req.getServerPort());
|
||||
portMappingDO.setClientIp(req.getClientIp());
|
||||
portMappingDO.setClientPort(req.getClientPort());
|
||||
portMappingDO.setDescription(req.getDescription());
|
||||
portMappingDO.setUpdateTime(new Date());
|
||||
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
|
||||
portMappingMapper.updateById(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO);
|
||||
// 删除老的域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(oldPortMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(oldPortMappingDO.getSubdomain())) {
|
||||
ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain());
|
||||
}
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
|
||||
}
|
||||
return new PortMappingUpdateRes();
|
||||
}
|
||||
|
||||
PortMappingDO portMappingDO = new PortMappingDO();
|
||||
portMappingDO.setId(req.getId());
|
||||
portMappingDO.setProtocal(req.getProtocal());
|
||||
portMappingDO.setSubdomain(req.getSubdomain());
|
||||
portMappingDO.setLicenseId(req.getLicenseId());
|
||||
portMappingDO.setServerPort(req.getServerPort());
|
||||
portMappingDO.setClientIp(req.getClientIp());
|
||||
portMappingDO.setClientPort(req.getClientPort());
|
||||
portMappingDO.setUpdateTime(new Date());
|
||||
portMappingDO.setEnable(EnableStatusEnum.ENABLE.getStatus());
|
||||
portMappingMapper.updateById(portMappingDO);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO);
|
||||
// 删除老的域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(oldPortMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(oldPortMappingDO.getSubdomain())) {
|
||||
ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain());
|
||||
}
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(proxyConfig.getServer().getDomainName()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort());
|
||||
}
|
||||
return new PortMappingUpdateRes();
|
||||
}
|
||||
public PortMappingDetailRes detail(Integer id) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.findById(id);
|
||||
if (null == portMappingDO) {
|
||||
return null;
|
||||
}
|
||||
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());
|
||||
|
||||
public PortMappingDetailRes detail(Integer id) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.findById(id);
|
||||
if (null == portMappingDO) {
|
||||
return null;
|
||||
}
|
||||
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());
|
||||
LicenseDO license = licenseMapper.findById(portMappingDO.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());
|
||||
}
|
||||
}
|
||||
|
||||
LicenseDO license = licenseMapper.findById(portMappingDO.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;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public PortMappingUpdateEnableStatusRes updateEnableStatus(PortMappingUpdateEnableStatusReq req) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.findById(req.getId());
|
||||
ParamCheckUtil.checkNotNull(portMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
|
||||
|
||||
public PortMappingUpdateEnableStatusRes updateEnableStatus(PortMappingUpdateEnableStatusReq req) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
portMappingMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date());
|
||||
|
||||
portMappingMapper.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);
|
||||
}
|
||||
|
||||
// 更新VisitorChannel
|
||||
portMappingDO.setEnable(req.getEnable());
|
||||
if (EnableStatusEnum.ENABLE == EnableStatusEnum.of(req.getEnable())) {
|
||||
visitorChannelService.addVisitorChannelByPortMapping(portMappingDO);
|
||||
} else {
|
||||
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
|
||||
}
|
||||
return new PortMappingUpdateEnableStatusRes();
|
||||
}
|
||||
|
||||
return new PortMappingUpdateEnableStatusRes();
|
||||
}
|
||||
public void delete(Integer id) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.findById(id);
|
||||
ParamCheckUtil.checkNotNull(portMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST);
|
||||
|
||||
public void delete(Integer id) {
|
||||
PortMappingDO portMappingDO = portMappingMapper.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);
|
||||
}
|
||||
|
||||
LicenseDO licenseDO = licenseMapper.findById(portMappingDO.getLicenseId());
|
||||
if (null != licenseDO && !SystemContextHolder.isAdmin()) {
|
||||
// 临时处理,如果当前用户不是管理员,则操作userId不能为1
|
||||
ParamCheckUtil.checkExpression(!licenseDO.getUserId().equals(1), ExceptionConstant.NO_PERMISSION_VISIT);
|
||||
}
|
||||
portMappingMapper.deleteById(id);
|
||||
|
||||
portMappingMapper.deleteById(id);
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain());
|
||||
}
|
||||
}
|
||||
|
||||
// 更新VisitorChannel
|
||||
visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO);
|
||||
// 更新域名映射
|
||||
if (NetworkProtocolEnum.HTTP.getDesc().equals(portMappingDO.getProtocal()) &&
|
||||
StrUtil.isNotBlank(portMappingDO.getSubdomain())) {
|
||||
ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据license查询可用的端口映射列表
|
||||
*
|
||||
* @param licenseId
|
||||
* @return
|
||||
*/
|
||||
public List<PortMappingDO> findEnableListByLicenseId(Integer licenseId) {
|
||||
return portMappingMapper.findEnableListByLicenseId(licenseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据license查询可用的端口映射列表
|
||||
* @param licenseId
|
||||
* @return
|
||||
*/
|
||||
public List<PortMappingDO> findEnableListByLicenseId(Integer licenseId) {
|
||||
return portMappingMapper.findEnableListByLicenseId(licenseId);
|
||||
}
|
||||
/**
|
||||
* 服务端项目停止、启动时,更新在线状态为离线
|
||||
*/
|
||||
@Init
|
||||
public void init() {
|
||||
portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
|
||||
|
||||
/**
|
||||
* 服务端项目停止、启动时,更新在线状态为离线
|
||||
*/
|
||||
@Init
|
||||
public void init() {
|
||||
portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
|
||||
// 未配置域名,则不需要处理域名映射逻辑
|
||||
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
|
||||
return;
|
||||
}
|
||||
List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<PortMappingDO>()
|
||||
.eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc())
|
||||
.isNotNull(PortMappingDO::getSubdomain)
|
||||
);
|
||||
if (CollectionUtil.isEmpty(portMappingDOList)) {
|
||||
return;
|
||||
}
|
||||
portMappingDOList.forEach(item -> {
|
||||
if (StrUtil.isBlank(item.getSubdomain())) {
|
||||
return;
|
||||
}
|
||||
ProxyUtil.setSubdomainToServerPort(item.getSubdomain(), item.getServerPort());
|
||||
});
|
||||
}
|
||||
|
||||
// 未配置域名,则不需要处理域名映射逻辑
|
||||
if (StrUtil.isBlank(proxyConfig.getServer().getDomainName())) {
|
||||
return;
|
||||
}
|
||||
List<PortMappingDO> portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper<PortMappingDO>()
|
||||
.eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc())
|
||||
.isNotNull(PortMappingDO::getSubdomain)
|
||||
);
|
||||
if (CollectionUtil.isEmpty(portMappingDOList)) {
|
||||
return;
|
||||
}
|
||||
portMappingDOList.forEach(item -> {
|
||||
if (StrUtil.isBlank(item.getSubdomain())) {
|
||||
return;
|
||||
}
|
||||
ProxyUtil.setSubdomainToServerPort(item.getSubdomain(), item.getServerPort());
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void start() throws Throwable {
|
||||
|
||||
@Override
|
||||
public void start() throws Throwable {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端项目停止、启动时,更新在线状态为离线
|
||||
*/
|
||||
@Override
|
||||
public void stop() throws Throwable {
|
||||
portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
|
||||
}
|
||||
/**
|
||||
* 服务端项目停止、启动时,更新在线状态为离线
|
||||
*/
|
||||
@Override
|
||||
public void stop() throws Throwable {
|
||||
portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date());
|
||||
}
|
||||
}
|
||||
|
||||
+77
-61
@@ -9,82 +9,98 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/8/1
|
||||
*/
|
||||
public class ParamCheckUtil {
|
||||
|
||||
public static void checkNotNull(Object obj, String name) {
|
||||
if (null == obj) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_NULL, name);
|
||||
}
|
||||
}
|
||||
public static void checkNotNull(Object obj, String name) {
|
||||
if (null == obj) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_NULL, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(String str, String name) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(String str, String name) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Collection collection, String name) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Collection collection, String name) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Map map, String name) {
|
||||
if (null == map || map.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Map map, String name) {
|
||||
if (null == map || map.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Set set, String name) {
|
||||
if (null == set || set.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Set set, String name) {
|
||||
if (null == set || set.isEmpty()) {
|
||||
throw ServiceException.create(ExceptionConstant.PARAMS_NOT_EMPTY, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkMustNull(Object obj, ExceptionConstant constant, Object... params) {
|
||||
if (null != obj) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkMustNull(Object obj, ExceptionConstant constant, Object... params) {
|
||||
if (null != obj) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void checkNotNull(Object obj, ExceptionConstant constant, Object... params) {
|
||||
if (null == obj) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkNotNull(Object obj, ExceptionConstant constant, Object... params) {
|
||||
if (null == obj) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(String str, ExceptionConstant constant, Object... params) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(String str, ExceptionConstant constant, Object... params) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Collection collection, ExceptionConstant constant, Object... params) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Collection collection, ExceptionConstant constant, Object... params) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Map map, ExceptionConstant constant, Object... params) {
|
||||
if (null == map || map.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Map map, ExceptionConstant constant, Object... params) {
|
||||
if (null == map || map.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Set set, ExceptionConstant constant, Object... params) {
|
||||
if (null == set || set.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
public static void checkNotEmpty(Set set, ExceptionConstant constant, Object... params) {
|
||||
if (null == set || set.isEmpty()) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkExpression(boolean expression, ExceptionConstant constant, Object... params) {
|
||||
if (!expression) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check max length , if the string length exceeded ,an exception is thrown
|
||||
* if the string is empty , it's not checked
|
||||
* 检查最大长度,超出长度,则抛出异常 . 如果字符串本身为空,则不进行检查
|
||||
*
|
||||
* @param str String
|
||||
* @param maxLength Maximum length
|
||||
* @param params Exception parameters
|
||||
* @throws {@link ServiceException}
|
||||
*/
|
||||
public static void checkMaxLength(String str, int maxLength, Object... params) {
|
||||
if (!StrUtil.isEmpty(str) && maxLength < str.length()) {
|
||||
throw ServiceException.create(ExceptionConstant.FILED_LENGTH_OUT, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkExpression(boolean expression, ExceptionConstant constant, Object... params) {
|
||||
if (!expression) {
|
||||
throw ServiceException.create(constant, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -2,7 +2,9 @@ package org.dromara.neutrinoproxy.server.util;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.neutrinoproxy.core.ChannelAttribute;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment;
|
||||
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
|
||||
@@ -353,4 +355,36 @@ public class ProxyUtil {
|
||||
public static Integer getServerPortBySubdomain(String subdomain) {
|
||||
return subdomainToServerPort.get(subdomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭http响应channel
|
||||
* @param channel
|
||||
* @return
|
||||
*/
|
||||
public static void closeHttpProxyResponseChannel(Channel channel) {
|
||||
if (null == channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
String visitorId = getVisitorIdByChannel(channel); // channel.attr(Constants.VISITOR_ID).get();
|
||||
if (StringUtils.isBlank(visitorId)) {
|
||||
return;
|
||||
}
|
||||
ProxyAttachment proxyAttachment = ProxyUtil.getProxyConnectAttachment(visitorId);
|
||||
if (null != proxyAttachment) {
|
||||
tryClose(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭channel
|
||||
* @param channel
|
||||
*/
|
||||
public static void tryClose(Channel channel) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
server:
|
||||
port: 8888
|
||||
port: ${WEB_PORT:8888}
|
||||
|
||||
neutrino:
|
||||
proxy:
|
||||
protocol:
|
||||
max-frame-length: 2097152
|
||||
max-frame-length: ${MAX_FRAME_LENGTH:2097152}
|
||||
length-field-offset: 0
|
||||
length-field-length: 4
|
||||
initial-bytes-to-strip: 0
|
||||
@@ -15,20 +15,20 @@ neutrino:
|
||||
server:
|
||||
boss-thread-count: 10
|
||||
work-thread-count: 60
|
||||
port: 9000
|
||||
ssl-port: 9002
|
||||
key-store-password: 123456
|
||||
key-manager-password: 123456
|
||||
jks-path: classpath:/test.jks
|
||||
# 如果不配置,则不支持代理http
|
||||
domain-name:
|
||||
port: ${OPEN_PORT:9000}
|
||||
ssl-port: ${SSL_PORT:9002}
|
||||
key-store-password: ${STORE_PASS:123456}
|
||||
key-manager-password: ${MGR_PASS:123456}
|
||||
jks-path: ${JKS_PATH:classpath:/test.jks}
|
||||
http-proxy-port: ${HTTP_PROXY_PORT:80}
|
||||
# 如果不配置,则不支持域名映射
|
||||
domain-name: ${DOMAIN_NAME:}
|
||||
data:
|
||||
db:
|
||||
type: sqlite
|
||||
url: jdbc:sqlite:data.db
|
||||
driver-class: org.sqlite.JDBC
|
||||
username:
|
||||
password:
|
||||
type: ${DB_TYPE:sqlite}
|
||||
url: ${DB_URL:jdbc:sqlite:data.db}
|
||||
username: ${DB_USER:}
|
||||
password: ${DB_PASSWORD:}
|
||||
|
||||
#添加MIME印射(如果有需要?)
|
||||
#是否启用静态文件服务。(可不配,默认为启用)
|
||||
@@ -50,4 +50,4 @@ mybatis.db:
|
||||
cacheEnabled: false
|
||||
mapUnderscoreToCamelCase: true
|
||||
globalConfig: #全局配置(要与 GlobalConfig 类的属性一一对应)
|
||||
banner: true
|
||||
banner: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration>
|
||||
<property name="LOG_FILE" value="/work/projects/neutrino-proxy-server/app.log"/>
|
||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{50} - %msg%n"/>
|
||||
<property name="ENCODE" value="utf8" />
|
||||
<!-- <property name="ENCODE" value="utf8" />-->
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
@@ -10,7 +10,7 @@
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
<charset>${ENCODE}</charset>
|
||||
<!-- <charset>${ENCODE}</charset>-->
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
<charset>${ENCODE}</charset>
|
||||
<!-- <charset>${ENCODE}</charset>-->
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<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>
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS `user` (
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `I_user_login_name` (`login_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#用户token表
|
||||
CREATE TABLE IF NOT EXISTS `user_token` (
|
||||
@@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS `user_token` (
|
||||
KEY `I_user_token_user_id` (`user_id`),
|
||||
KEY `I_user_token_token` (`token`),
|
||||
KEY `I_user_token_expiration_time` (`expiration_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#端口池
|
||||
CREATE TABLE IF NOT EXISTS `port_pool` (
|
||||
@@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS `port_pool` (
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `I_port_pool_port` (`port`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#端口分组
|
||||
CREATE TABLE IF NOT EXISTS `port_group` (
|
||||
@@ -48,7 +48,7 @@ CREATE TABLE IF NOT EXISTS `port_group` (
|
||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='端口分组';
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#############################代理配置相关表#############################
|
||||
#license表
|
||||
@@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS `license` (
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `I_license_key` (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#端口映射表
|
||||
CREATE TABLE IF NOT EXISTS `port_mapping` (
|
||||
@@ -75,12 +75,13 @@ CREATE TABLE IF NOT EXISTS `port_mapping` (
|
||||
`client_ip` varchar(20) NOT NULL COMMENT '客户端IP',
|
||||
`client_port` int NOT NULL COMMENT '客户端端口',
|
||||
`is_online` int NOT NULL COMMENT '是否在线(1、在线 2、离线)',
|
||||
`description` varchar(100) DEFAULT NULL COMMENT '描述',
|
||||
`enable` int NOT NULL COMMENT '是否启用(1、启用 2、禁用)',
|
||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `I_port_mapping_server_port` (`server_port`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
#############################日志管理相关表#############################
|
||||
#用户登录记录表
|
||||
CREATE TABLE IF NOT EXISTS `user_login_record` (
|
||||
@@ -91,7 +92,7 @@ CREATE TABLE IF NOT EXISTS `user_login_record` (
|
||||
`type` int NOT NULL COMMENT '类型(1、登录 2、登出)',
|
||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
#客户端连接记录表
|
||||
CREATE TABLE IF NOT EXISTS `client_connect_record` (
|
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
@@ -103,7 +104,7 @@ CREATE TABLE IF NOT EXISTS `client_connect_record` (
|
||||
`err` text DEFAULT NULL COMMENT '异常信息',
|
||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
#############################调度管理相关表#############################
|
||||
#触发器信息表
|
||||
CREATE TABLE IF NOT EXISTS `job_info` (
|
||||
@@ -119,7 +120,7 @@ CREATE TABLE IF NOT EXISTS `job_info` (
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `I_job_info_handler` (`handler`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#触发器日志表
|
||||
CREATE TABLE IF NOT EXISTS `job_log` (
|
||||
@@ -134,7 +135,7 @@ CREATE TABLE IF NOT EXISTS `job_log` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `I_job_log_create_time` (`create_time`) USING BTREE,
|
||||
KEY `I_job_log_code` (`code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#############################报表管理相关表#############################
|
||||
#流量统计报表-分钟(保留24小时)
|
||||
@@ -152,7 +153,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_minute` (
|
||||
KEY `I_flow_report_minute_date` (`date`) USING BTREE,
|
||||
KEY `I_flow_report_minute_user_id` (`user_id`),
|
||||
KEY `I_flow_report_minute_license_id` (`license_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#流量统计报表-小时(保留60天)
|
||||
CREATE TABLE IF NOT EXISTS `flow_report_hour` (
|
||||
@@ -169,7 +170,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_hour` (
|
||||
KEY `I_flow_report_hour_date` (`date`) USING BTREE,
|
||||
KEY `I_flow_report_hour_user_id` (`user_id`),
|
||||
KEY `I_flow_report_hour_license_id` (`license_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#流量统计报表-天(保留1年)
|
||||
CREATE TABLE IF NOT EXISTS `flow_report_day` (
|
||||
@@ -186,7 +187,7 @@ CREATE TABLE IF NOT EXISTS `flow_report_day` (
|
||||
KEY `I_flow_report_day_date` (`date`) USING BTREE,
|
||||
KEY `I_flow_report_day_user_id` (`user_id`),
|
||||
KEY `I_flow_report_day_license_id` (`license_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
#流量统计报表-月(全量保留)
|
||||
CREATE TABLE IF NOT EXISTS `flow_report_month` (
|
||||
@@ -203,4 +204,4 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` (
|
||||
KEY `I_flow_report_month_date` (`date`) USING BTREE,
|
||||
KEY `I_flow_report_month_user_id` (`user_id`),
|
||||
KEY `I_flow_report_month_license_id` (`license_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -10,4 +10,4 @@ CREATE TABLE IF NOT EXISTS `port_group` (
|
||||
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='端口分组';
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='端口分组';
|
||||
@@ -1,2 +1,3 @@
|
||||
ALTER TABLE port_mapping ADD `protocal` varchar(10) NOT NULL DEFAULT 'TCP' COMMENT '协议';
|
||||
ALTER TABLE port_mapping ADD `subdomain` varchar(50) DEFAULT NULL COMMENT '子域名(仅HTTP时有效)';
|
||||
ALTER TABLE port_mapping ADD `subdomain` varchar(50) DEFAULT NULL COMMENT '子域名(仅HTTP时有效)';
|
||||
ALTER TABLE port_mapping ADD `description` varchar(100) DEFAULT NULL COMMENT '描述';
|
||||
@@ -69,6 +69,7 @@ CREATE TABLE IF NOT EXISTS `port_mapping` (
|
||||
`client_ip` VARCHAR(20) NOT NULL,
|
||||
`client_port` INTEGER NOT NULL,
|
||||
`is_online` INTEGER(2) NOT NULL,
|
||||
`description` VARCHAR(100) DEFAULT NULL,
|
||||
`enable` INTEGER(2) NOT NULL,
|
||||
`create_time` INTEGER(20) NOT NULL,
|
||||
`update_time` INTEGER(20) NOT NULL
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#port_mapping
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`, `client_ip`, `client_port`, `is_online`, `enable`, `create_time`, `update_time`) VALUES
|
||||
(1, 1, 9101, 'HTTP', 'test1', '127.0.0.1', 8080, 2, 1, STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`, `client_ip`, `client_port`, `is_online`, `enable`, `create_time`, `update_time`) VALUES
|
||||
(2, 1, 9102, 'TCP', '', '127.0.0.1', 3306, 2, 1, STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`, `client_ip`, `client_port`, `is_online`, `enable`, `create_time`, `update_time`) VALUES
|
||||
(3, 1, 9103, 'HTTP', 'test2', '127.0.0.1', 8081, 2, 1, STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`,`client_ip`, `client_port`, `is_online`, `enable`,`description`,`create_time`, `update_time`) VALUES
|
||||
(1, 1, 9101, 'HTTP', 'test1', '127.0.0.1', 8080, 2, 1,'test1', STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`, `client_ip`, `client_port`, `is_online`, `enable`,`description`, `create_time`, `update_time`) VALUES
|
||||
(2, 1, 9102, 'TCP', '', '127.0.0.1', 3306, 2, 1,'test2', STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
INSERT INTO port_mapping(`id`, `license_id`, `server_port`, `protocal`, `subdomain`, `client_ip`, `client_port`, `is_online`, `enable`,`description`, `create_time`, `update_time`) VALUES
|
||||
(3, 1, 9103, 'HTTP', 'test2', '127.0.0.1', 8081, 2, 1,'test3', STRFTIME('%s000', 'NOW'), STRFTIME('%s000', 'NOW'));
|
||||
@@ -1,2 +1,3 @@
|
||||
ALTER TABLE port_mapping ADD `protocal` varchar(10) NOT NULL DEFAULT 'TCP';
|
||||
ALTER TABLE port_mapping ADD `subdomain` varchar(50) DEFAULT NULL;
|
||||
ALTER TABLE port_mapping ADD `subdomain` varchar(50) DEFAULT NULL;
|
||||
ALTER TABLE port_mapping ADD `description` varchar(100);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -5,14 +5,15 @@ permalink: /pages/793dcb
|
||||
article: false
|
||||
---
|
||||
|
||||
接下来您讲学习到如何快速上手
|
||||
## 1、 部署服务端
|
||||
### 1.1、 Docker一键部署
|
||||
> 当前最新版本为1.8.0,下面的脚本中,可以使用:`registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.8.0` 指定版本安装,推荐使用`latest`直接安装最新版。
|
||||
|
||||
#### 使用默认sqlite数据库
|
||||
```shell
|
||||
docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \
|
||||
-d --restart=always --name neutrino-proxy \
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.7.1
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
|
||||
```
|
||||
|
||||
#### 指定自己的mysql数据库
|
||||
@@ -29,14 +30,14 @@ neutrino:
|
||||
# 数据库帐号
|
||||
username: xxx
|
||||
# 数据库密码
|
||||
password: wHCvf3@hmw^D*
|
||||
password: xxx
|
||||
```
|
||||
- 然后执行如下命令:
|
||||
```shell
|
||||
docker run -it -p 9000-9200:9000-9200/tcp -p 8888:8888 \
|
||||
-v /root/neutrino-proxy/config:/root/neutrino-proxy/config \
|
||||
-d --restart=always --name neutrino \
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:1.7.1
|
||||
registry.cn-hangzhou.aliyuncs.com/asgc/neutrino-proxy:latest
|
||||
```
|
||||
|
||||
### 1.2、使用jar包自行部署
|
||||
@@ -76,7 +77,7 @@ neutrino:
|
||||
# licenseKey,客户端凭证。此处需要配置刚刚从管理后台复制的LicenseKey
|
||||
license-key: xxxx
|
||||
```
|
||||
- 执行命令`java -jar neutrino-proxy-client.jar`启动客户端
|
||||
- 执行命令`java -jar neutrino-proxy-client.jar config=app.yml`启动客户端
|
||||
- 查看服务端License管理,刷新页面,对应的License在线状态为`在线`,则表明客户端已正常连接。
|
||||
|
||||
## 4、代理验证
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: 关于端口
|
||||
date: 2023-04-05 18:20:18
|
||||
permalink: /pages/68badb/
|
||||
---
|
||||
|
||||
# 端口池
|
||||
> 端口池用于统一、集中管理服务端代理对外开放的代理端口。
|
||||
|
||||
- 1、这里不包含服务端启动本身所需要的一些特殊端口,如:WEB端口、对客户端开放的端口(9000、9002)、HTTP代理端口。
|
||||
- 2、端口映射里所有的服务端端口,都包含在端口池内。方便了统一管理、安全组/防火墙设置。
|
||||
|
||||
# 端口池分组
|
||||
> 顾名思义,它是对端口池下的端口做了一个划分。
|
||||
|
||||
- 1、允许同一个分组内的端口拥有特定的归属。比如:全局分组下的端口,进行代理时所有用户共享,这里极端情况下涉及到端口抢占问题。而用户分组下的端口,归属于某一个特定的用户。
|
||||
License分组下的端口,归属于某一个特定的License。
|
||||
- 2、该划分,使得特定的用户、License能够独占一部分端口。为后续更多场景下代理,避免端口被占用提供了支持。
|
||||
|
||||
# 端口映射
|
||||
> 当前版本代理功能的基本单元。
|
||||
|
||||
- 1、该映射由一个四元组唯一标识(代理服务端IP、代理服务端端口、被代理服务局域网IP、被代理服务端口)。
|
||||
- 2、由于本项目为个人版,代理服务端仅支持一个节点,所以四元组中的服务端IP可以省略,不在端口映射管理中体现。
|
||||
- 3、代理成功的情况下,通过访问代理服务端IP+端口,可以达到访问内网被代理服务IP+端口的同等效果
|
||||
- 4、例如:本地启动一个socket服务,需要和异地的同事联调。可以通过中微子代理jetbrains客户端插件(后续会推出)将本地socket端口代理至公网。同事通过访问代理服务端公网IP+端口进行调试。
|
||||
|
||||
# 基础端口说明
|
||||
- WEB端口:服务端API、后台管理访问端口,默认为:8888
|
||||
- 服务端等待客户端连接的端口,非SSL:默认9000,SSL端口:默认9000(若不需要SSL支持,可不配置SSL端口)
|
||||
- HTTP代理端口:默认80,用于域名映射,若无需域名映射,可以忽略。
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: 域名映射
|
||||
date: 2023-04-05 18:46:12
|
||||
permalink: /pages/269a2e/
|
||||
---
|
||||
|
||||
# 什么是域名映射?
|
||||
域名映射是将指定端口映射绑定到某一个子域名上,通过直接访问域名的方式,达到访问被代理服务的目的。
|
||||
|
||||
# 域名映射能解决什么问题?
|
||||
- 1、当服务端端口需要经常变动的时候,可以通过固定域名的方式,使得开发调试能够长期稳定的进行,不用每次更改访问地址。
|
||||
- 2、使用域名可以使用有意义的名称方便记忆
|
||||
- 3、调试某些第三方回调时,回调地址不能填写IP+端口的形式,此时需要域名来完成
|
||||
|
||||
# 域名映射的大致流程?
|
||||
> 例如你有一个已备案的域名:asgc.fun,以下是使用域名映射的大致流程
|
||||
|
||||
## 情况1:代理服务端使用80端口
|
||||
- 1、DNS解析配置泛域名解析 proxy.asgc.fun 指向代理服务端IP
|
||||
- 2、代理服务端app.yml配置域名:proxy.asgc.fun,部署
|
||||
- 3、服务端管理后台新建端口映射,协议选择:HTTP,设置好子域名,如:test1
|
||||
- 4、配置好客户端并启动,代理映射完成
|
||||
- 5、通过访问: http://test1.proxy.asgc.fun 实现访问被代理服务
|
||||
|
||||
## 情况2:代理服务端不使用80端口,80端口被nginx占用
|
||||
> 假设代理服务端使用82作为HTTP代理端口
|
||||
|
||||
- 1、DNS解析配置泛域名解析 proxy.asgc.fun 指向代理服务端IP
|
||||
- 2、nginx 80端口下将 *.proxy.asgc.fun 请求转到82端口,并附带原有请求头(特别是`Host`)
|
||||
- 3、代理服务端app.yml配置域名:proxy.asgc.fun,部署
|
||||
- 4、服务端管理后台新建端口映射,协议选择:HTTP,设置好子域名,如:test1
|
||||
- 5、配置好客户端并启动,代理映射完成
|
||||
- 6、通过访问: http://test1.proxy.asgc.fun 实现访问被代理服务
|
||||
|
||||
# 所有的代理映射都能绑定域名吗?
|
||||
不行的。之所以能实现域名绑定,原因是HTTP请求附带了Host请求头,包含了请求的域名信息,代理服务端能根据请求的域名做分发。而TCP协议本身并不包含域名,因此无法实现。
|
||||
所以,目前只有HTTP(涵盖HTTPS)协议能支持。
|
||||
@@ -11,6 +11,8 @@ bannerBg: none # auto => 网格纹背景(有bodyBgImg时无背景),默认 | no
|
||||
features: # 可选的
|
||||
- title: 流量监控
|
||||
details: 首页图表、报表管理多维度流量监控。全方位掌握实时、历史代理数据。
|
||||
- title: 域名映射
|
||||
details: HTTP代理绑定子域名,开发调试三方回调更方便。
|
||||
- title: 用户/License
|
||||
details: 支持多用户、多客户端使用。后台禁用实时生效。
|
||||
- title: 端口池
|
||||
@@ -19,8 +21,8 @@ features: # 可选的
|
||||
details: 新增、编辑、删除、禁用实时生效。
|
||||
- title: Docker
|
||||
details: 服务端支持Docker一键部署。
|
||||
- title: SSL证书
|
||||
details: 支持SSL,保护您的信息安全。
|
||||
# - title: SSL证书
|
||||
# details: 支持SSL,保护您的信息安全。
|
||||
|
||||
# 文章列表显示方式: detailed 默认,显示详细版文章列表(包括作者、分类、标签、摘要、分页等)| simple => 显示简约版文章列表(仅标题和日期)| none 不显示文章列表
|
||||
postList: none
|
||||
@@ -129,35 +131,34 @@ postList: none
|
||||
你也可以在Issue查看别人提的问题和给出解决方案。
|
||||
|
||||
或者加入我们的交流群:
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<img :src="$withBase('/img/qrcode/wxq.png')" class="no-zoom" style="width:120px;margin: 10px;">
|
||||
<p>中微子 微信群(添加我微信备注"进群")</p>
|
||||
</td>
|
||||
<!--
|
||||
<td align="center" valign="middle">
|
||||
<img src="https://cdn.staticaly.com/gh/xugaoyi/blog-gitalk-comment@master/img/0.4pp7r95mdai0.jpeg" class="no-zoom" style="width:120px;margin: 10px;">
|
||||
<p>vdoing微信群(添加我微信备注"进群")</p>
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
<!--
|
||||
### 🐞提供bug反馈或建议
|
||||
|
||||
提交问题反馈请说明正在使用环境以及相关问题
|
||||
|
||||
- [Gitee issue](https://gitee.com/dromara/neutrino-proxy/issues)
|
||||
|
||||
[//]: # (- [Github issue](https://github.com/dromara/hutool/issues))
|
||||
|
||||
<div align="center">
|
||||
<img :src="$withBase('/img/qrcode/wxq.png')" class="no-zoom" style="width:200px;margin: 10px;">
|
||||
<p>中微子 微信群(添加我微信备注"进群")</p>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<h2 id="🌈特别赞助"><a href="#🌈特别赞助" class="header-anchor">#</a> 🌈特别赞助</h2>
|
||||
<p align="center">等待有缘人赞助</p>
|
||||
<!--
|
||||
<div class="cardListContainer"><div class="card-list"><a href="https://datayi.cn/w/nPN4JJe9" target="_blank" class="card-item row-3" style="background-color:#102863;--random-color:#102863;color:#FFFFFF;"><img src="/images/friends/ad/postcat.png" class="no-zoom"> <div><p class="name">Postcat</p> <p class="desc">开源 API 管理工具,接口文档、接口测试、Mock...</p></div></a> <a href="https://gitee.com/dromara/MaxKey" target="_blank" class="card-item row-3" style="background-color:#9ed3e5;--random-color:#9ed3e5;color:#1f2328;"><img src="/images/friends/ad/maxkey.png" class="no-zoom"> <div><p class="name">MaxKey</p> <p class="desc"><br>业界领先的单点登录</p></div></a> <a href="https://www.xiaonuo.vip" target="_blank" class="card-item row-3" style="background-color:#1373CC;--random-color:#1373CC;color:#ffffff;"><img src="/images/friends/ad/snowy.png" class="no-zoom"> <div><p class="name">Snowy</p> <p class="desc">国内首个国密前后端分离快速开发平台,中国式工作流、独家双模式租户</p></div></a></div><div class="language-yaml line-numbers-mode"><pre class="language-yaml codecopy-enabled"><code><span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Postcat
|
||||
<span class="token key atrule">desc</span><span class="token punctuation">:</span> 开源 API 管理工具,接口文档、接口测试、Mock<span class="token punctuation">...</span>
|
||||
<span class="token key atrule">avatar</span><span class="token punctuation">:</span> /images/friends/ad/postcat.png
|
||||
<span class="token key atrule">link</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//datayi.cn/w/nPN4JJe9
|
||||
<span class="token key atrule">bgColor</span><span class="token punctuation">:</span> <span class="token string">'#102863'</span>
|
||||
<span class="token key atrule">textColor</span><span class="token punctuation">:</span> <span class="token string">'#FFFFFF'</span>
|
||||
<span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> MaxKey
|
||||
<span class="token key atrule">desc</span><span class="token punctuation">:</span> </br<span class="token punctuation">></span>业界领先的单点登录
|
||||
<span class="token key atrule">avatar</span><span class="token punctuation">:</span> /images/friends/ad/maxkey.png
|
||||
<span class="token key atrule">link</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//gitee.com/dromara/MaxKey
|
||||
<span class="token key atrule">bgColor</span><span class="token punctuation">:</span> <span class="token string">'#9ed3e5'</span>
|
||||
<span class="token key atrule">textColor</span><span class="token punctuation">:</span> <span class="token string">'#1f2328'</span>
|
||||
<span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Snowy
|
||||
<span class="token key atrule">desc</span><span class="token punctuation">:</span> 国内首个国密前后端分离快速开发平台,中国式工作流、独家双模式租户
|
||||
<span class="token key atrule">avatar</span><span class="token punctuation">:</span> /images/friends/ad/snowy.png
|
||||
<span class="token key atrule">link</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//www.xiaonuo.vip
|
||||
<span class="token key atrule">bgColor</span><span class="token punctuation">:</span> <span class="token string">'#1373CC'</span>
|
||||
<span class="token key atrule">textColor</span><span class="token punctuation">:</span> <span class="token string">'#ffffff'</span>
|
||||
</code><i class="code-copy" title="Copy to clipboard"><svg style="color:#aaa;font-size:14px" t="1572422231464" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3201" width="14" height="14"><path d="M866.461538 39.384615H354.461538c-43.323077 0-78.769231 35.446154-78.76923 78.769231v39.384616h472.615384c43.323077 0 78.769231 35.446154 78.769231 78.76923v551.384616h39.384615c43.323077 0 78.769231-35.446154 78.769231-78.769231V118.153846c0-43.323077-35.446154-78.769231-78.769231-78.769231z m-118.153846 275.692308c0-43.323077-35.446154-78.769231-78.76923-78.769231H157.538462c-43.323077 0-78.769231 35.446154-78.769231 78.769231v590.769231c0 43.323077 35.446154 78.769231 78.769231 78.769231h512c43.323077 0 78.769231-35.446154 78.76923-78.769231V315.076923z m-354.461538 137.846154c0 11.815385-7.876923 19.692308-19.692308 19.692308h-157.538461c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h157.538461c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z m157.538461 315.076923c0 11.815385-7.876923 19.692308-19.692307 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h315.076923c11.815385 0 19.692308 7.876923 19.692307 19.692308v39.384615z m78.769231-157.538462c0 11.815385-7.876923 19.692308-19.692308 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h393.846153c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z" p-id="3202"></path></svg></i></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div></div>
|
||||
-->
|
||||
|
||||
<h2 id="🤝-dromara-组织项目"><a href="#🤝-dromara-组织项目" class="header-anchor">#</a> 🤝 dromara 组织项目</h2>
|
||||
@@ -285,6 +286,13 @@ postList: none
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2>🤝 友情开源项目</h2>
|
||||
<p>
|
||||
<a href="https://gitee.com/noear/solon" target="_blank" class="friends-item" style="display: flex;">
|
||||
<img class="no-zoom friends-item-img hover-alt" :src="$withBase('/img/logo/solon.png')" msg="一个高效的应用开发框架:更快、更小、更简单。" style="margin-left: 15px;"/><div style="margin-left: 10px;font-size: 30px;">Solon</div>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!-- AD -->
|
||||
<div class="wwads-cn wwads-horizontal page-wwads" data-id="136"></div>
|
||||
<style>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</parent>
|
||||
|
||||
<groupId>org.dromara.neutrino-proxy</groupId>
|
||||
<artifactId>neutrino-proxy-parent</artifactId>
|
||||
<artifactId>neutrino-proxy</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>${revision}</version>
|
||||
|
||||
@@ -29,11 +29,12 @@
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
<revision>1.0-SNAPSHOT</revision>
|
||||
|
||||
<java.version>1.8</java.version>
|
||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<java.version>17</java.version>
|
||||
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
||||
<maven-flatten.version>1.1.0</maven-flatten.version>
|
||||
<org.projectlombok.version>1.18.4</org.projectlombok.version>
|
||||
<org.projectlombok.version>1.18.26</org.projectlombok.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -41,7 +42,7 @@
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.22.Final</version>
|
||||
<version>4.1.91.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -94,10 +95,15 @@
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.39.2.1</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>mysql</groupId>-->
|
||||
<!-- <artifactId>mysql-connector-java</artifactId>-->
|
||||
<!-- <version>5.1.49</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.49</version>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.0.32</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
|
||||
@@ -17,7 +17,7 @@ export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
||||
|
||||
#初始化文件夹
|
||||
cd ..
|
||||
cd ../..
|
||||
rm -rf $adminDeployDir
|
||||
if [ ! -d "$deployDir" ];then
|
||||
mkdir $deployDir
|
||||
@@ -17,7 +17,7 @@ export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
||||
|
||||
#初始化文件夹
|
||||
cd ..
|
||||
cd ../..
|
||||
rm -rf $adminDeployDir
|
||||
if [ ! -d "$deployDir" ];then
|
||||
mkdir $deployDir
|
||||
@@ -9,7 +9,7 @@ deployDir="deploy"
|
||||
clientDeployDir=$deployDir"/client"
|
||||
|
||||
#切到项目根目录
|
||||
cd ..
|
||||
cd ../..
|
||||
#初始化文件夹
|
||||
if [ ! -d "$deployDir" ];then
|
||||
mkdir $deployDir
|
||||
@@ -8,7 +8,7 @@ export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/li
|
||||
mkdir -p /work/$NAME/heapError/
|
||||
|
||||
NAME=neutrino-proxy-client
|
||||
WORK=$PWD/../deploy/client
|
||||
WORK=$PWD/../../deploy/client
|
||||
OUT=$WORK/$NAME.out
|
||||
JAR_PATH=$WORK
|
||||
|
||||
@@ -9,7 +9,7 @@ deployDir="deploy"
|
||||
serverDeployDir=$deployDir"/server"
|
||||
|
||||
#切到项目根目录
|
||||
cd ..
|
||||
cd ../..
|
||||
#初始化文件夹
|
||||
if [ ! -d "$deployDir" ];then
|
||||
mkdir $deployDir
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 镜像版本,每次更新版本时需要调整
|
||||
ImageVer=1.0.0
|
||||
ImageName=neutrino-proxy
|
||||
DockerFilePath=$PWD/../..
|
||||
|
||||
#echo '打包jar...'
|
||||
sh ./server_build.sh
|
||||
#echo '打包管理后台...'
|
||||
sh ./admin_build_docker.sh
|
||||
|
||||
# 删除老的本地镜像
|
||||
docker rmi -f $(docker images | grep $ImageName | awk '{print $3}')
|
||||
# 构建镜像
|
||||
docker build -t $ImageName:$ImageVer -t $ImageName:latest $DockerFilePath
|
||||
|
||||
@@ -8,7 +8,7 @@ export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/li
|
||||
mkdir -p /work/$NAME/heapError/
|
||||
|
||||
NAME=neutrino-proxy-server
|
||||
WORK=$PWD/../deploy/server
|
||||
WORK=$PWD/../../deploy/server
|
||||
OUT=$WORK/$NAME.out
|
||||
JAR_PATH=$WORK
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
@rem basic params
|
||||
set ENV=dev
|
||||
set NODE_VERSION=v13.12.0
|
||||
set MODULE_NAME=neutrino-proxy-admin
|
||||
@rem 判断当前node版本是否符合,如果不符合切换node版本
|
||||
for /f "tokens=1" %%v in ('node -v') do set v=%%v
|
||||
if not "%v%" == "%NODE_VERSION%" (
|
||||
echo Node.js version is not %NODE_VERSION%.
|
||||
@rem 判断是否安装nvm
|
||||
set "nvm_home=%NVM_HOME%"
|
||||
set "nvm_symlink=%NVM_SYMLINK%"
|
||||
if not defined nvm_home (
|
||||
echo nvm is not installed,then use nvm to install node %NODE_VERSION%.
|
||||
) else if not defined nvm_symlink (
|
||||
echo nvm is not installed,then use nvm to install node %NODE_VERSION%.
|
||||
) else (
|
||||
echo nvm is installed.
|
||||
nvm use %NODE_VERSION%
|
||||
)
|
||||
)
|
||||
cd %~dp0 && cd ../../%MODULE_NAME%
|
||||
@rem 检查dist文件夹是否存在,存在就删除,/s选项表示将目录及其所有子目录一起删除,/q选项表示不需要确认操作
|
||||
if exist dist (
|
||||
rmdir /s /q dist
|
||||
)
|
||||
@rem 等待npm执行完毕
|
||||
call npm install
|
||||
npm run build:%ENV% && pause
|
||||
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
@rem basic params
|
||||
set ENV=docker
|
||||
set NODE_VERSION=v13.12.0
|
||||
set MODULE_NAME=neutrino-proxy-admin
|
||||
@rem 判断当前node版本是否符合,如果不符合切换node版本
|
||||
for /f "tokens=1" %%v in ('node -v') do set v=%%v
|
||||
if not "%v%" == "%NODE_VERSION%" (
|
||||
echo Node.js version is not %NODE_VERSION%.
|
||||
@rem 判断是否安装nvm
|
||||
set "nvm_home=%NVM_HOME%"
|
||||
set "nvm_symlink=%NVM_SYMLINK%"
|
||||
if not defined nvm_home (
|
||||
echo nvm is not installed,then use nvm to install node %NODE_VERSION%.
|
||||
) else if not defined nvm_symlink (
|
||||
echo nvm is not installed,then use nvm to install node %NODE_VERSION%.
|
||||
) else (
|
||||
echo nvm is installed.
|
||||
nvm use %NODE_VERSION%
|
||||
)
|
||||
)
|
||||
cd %~dp0 && cd ../../%MODULE_NAME%
|
||||
@rem 检查dist文件夹是否存在,存在就删除,/s选项表示将目录及其所有子目录一起删除,/q选项表示不需要确认操作
|
||||
if exist dist (
|
||||
rmdir /s /q dist
|
||||
)
|
||||
@rem 等待npm执行完毕
|
||||
call npm install
|
||||
npm run build:%ENV% && pause
|
||||
@@ -4,5 +4,5 @@ chcp 65001
|
||||
@rem basic params
|
||||
set MODULE_NAME="neutrino-proxy-client"
|
||||
@rem step
|
||||
cd %~dp0 && cd ../
|
||||
cd %~dp0 && cd ../../
|
||||
call mvn clean package -pl %MODULE_NAME% -am -Dmaven.test.skip=true
|
||||
@@ -6,5 +6,5 @@ set MODULE_NAME="neutrino-proxy-client"
|
||||
set JAR_NAME="neutrino-proxy-client.jar"
|
||||
set JVM_OPTS="-Xmx256m" "-Xms256m"
|
||||
@rem step
|
||||
cd %~dp0 && cd ../%MODULE_NAME%/target
|
||||
cd %~dp0 && cd ../../%MODULE_NAME%/target
|
||||
java -jar %JVM_OPTS% %JAR_NAME%
|
||||
@@ -4,5 +4,5 @@ chcp 65001
|
||||
@rem basic params
|
||||
set MODULE_NAME="neutrino-proxy-server"
|
||||
@rem step
|
||||
cd %~dp0 && cd ../
|
||||
cd %~dp0 && cd ../../
|
||||
call mvn clean package -pl %MODULE_NAME% -am -Dmaven.test.skip=true
|
||||
@@ -6,5 +6,5 @@ set MODULE_NAME="neutrino-proxy-server"
|
||||
set JAR_NAME="neutrino-proxy-server.jar"
|
||||
set JVM_OPTS="-Xmx256m" "-Xms256m"
|
||||
@rem step
|
||||
cd %~dp0 && cd ../%MODULE_NAME%/target
|
||||
cd %~dp0 && cd ../../%MODULE_NAME%/target
|
||||
java -jar %JVM_OPTS% %JAR_NAME%
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
# 1.x剩余规划
|
||||
- [ ] 参考鹊桥,增加对域名的支持
|
||||
- [ ] 官网上线
|
||||
- [ ] 增加针对https的支持
|
||||
|
||||
# Bug
|
||||
- windows环境下直接运行发布版的jar包,日志输出乱码
|
||||
|
||||
Reference in New Issue
Block a user