24 Commits

Author SHA1 Message Date
macbookpro
38ca56dbba 【fix】 2025-07-30 19:47:37 +08:00
wujiawei
ec35d1c296 [update] 添加官网项目 2025-07-29 23:55:10 +08:00
macbookpro
3754ee8701 【fix】 format README.md 2025-07-22 19:25:57 +08:00
macbookpro
49f9e96c29 【fix】 format README.md 2025-07-22 17:08:44 +08:00
wujiawei
ac38d8dfd7 [update] 1.3.4-JDK24-SNAPSHOT 2025-07-21 22:18:05 +08:00
wujiawei
6142105ac3 [update] 1.3.3-JDK24 2025-07-21 22:07:44 +08:00
wujiawei
ff3681fc46 [update] 1.3.1-JDK24 2025-07-21 16:59:53 +08:00
wujiawei
9e1a47f4e9 [update] 1.3.1-JDK24 2025-07-19 22:57:56 +08:00
wujiawei
13858a0b7c [add] 修改中央仓库https://central.sonatype.com/account 2025-07-17 19:25:36 +08:00
wujiawei
caff71be87 [add] 修改中央仓库https://central.sonatype.com/account 2025-07-17 00:06:24 +08:00
wujiawei
04c683cb9a [add] 修改中央仓库https://central.sonatype.com/account 2025-07-16 23:49:03 +08:00
wujiawei
e5f53b520b 【fix】授权调整 2025-07-16 21:49:01 +08:00
wujiawei
50b2b1866c 【fix】授权调整 2025-07-16 21:30:09 +08:00
wujiawei
841c24a6ed 【fix】注释udp、通道关闭验证通道关联通道是否存在 2025-07-15 16:20:06 +08:00
wujiawei
e387a43437 【fix】ui 调整 2025-07-15 15:17:19 +08:00
wujiawei
438198bcc5 【fix】ui 调整 2025-07-15 15:16:07 +08:00
wujiawei
afe1a1417f 【fix】ui 调整 2025-07-15 13:24:36 +08:00
wujiawei
d9d82a99d5 【fix】ui 调整 2025-07-15 13:23:53 +08:00
wujiawei
c5aeb6f0f7 【fix】proxy代理支持令牌验证 2025-07-14 18:54:10 +08:00
wujiawei
f8f485a14a 【fix】本地代理支持代理日志记录 2025-07-13 14:52:18 +08:00
wujiawei
7178974abb 【fix】 本地socket代理通道添加属性。目标ip、目标端口、访客ID 2025-07-12 12:07:40 +08:00
wujiawei
f2fede4a96 【fix】 添加版本记录 2025-07-09 22:15:54 +08:00
wujiawei
74eab26478 [add] 修改inethost字段长度 2025-07-01 20:28:44 +08:00
wujiawei
faabae3d19 [add] 添加socks授权验证 2025-06-26 21:00:20 +08:00
269 changed files with 7621 additions and 486 deletions

View File

@@ -7,15 +7,15 @@
#### 操作步骤拥有公网ip的服务器开发6001web、7001端口tcp然后执行命令启动服务端
```shell
docker run -d -it -p 6001:6001 -p 7001:7001 -e spring.profiles.active=prod -e MAIN_DB_HOST=localhost:3306 -e MAIN_DB_PASSWORD=root -e MAIN_DB_PASSWORD=root --name wu-lazy-cloud-heartbeat-server-start registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server-start:1.3.1-JDK24-SNAPSHOT
docker run -d -it -p 6001:6001 -p 7001:7001 -e spring.profiles.active=prod -e MAIN_DB_HOST=localhost:3306 -e MAIN_DB_PASSWORD=root -e MAIN_DB_PASSWORD=root --name wu-lazy-cloud-heartbeat-server-start registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server-start:1.3.1-JDK24
```
#### 操作步骤:杭州本地机房所在网络服务器启动客户端、你老家所在网络中启动客户端,命令如下
```shell
docker run -d -it --privileged --name hangzhou-client --restart=always -e spring.lazy.netty.client.inet-host=公网IP -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="hangzhou-jifang" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24-SNAPSHOT
docker run -d -it --privileged --name hangzhou-client --restart=always -e spring.lazy.netty.client.inet-host=公网IP -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="hangzhou-jifang" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
```
```shell
docker run -d -it --privileged --name my-home-client --restart=always -e spring.lazy.netty.client.inet-host=公网IP -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="my-home" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24-SNAPSHOT
docker run -d -it --privileged --name my-home-client --restart=always -e spring.lazy.netty.client.inet-host=公网IP -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="my-home" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
```
#### 操作步骤:配置端口
![client_permeate_port_pool.png](client_permeate_port_pool.png)

View File

@@ -33,6 +33,8 @@
</p>
#### 项目介绍
[官网](http://wlcn.wu-framework.cn/)
[体验地址](http://demo-wlcn.wu-framework.cn/wlcn/index.html#/login)
wu-lazy-cloud-network
是一款基于([wu-framework-parent](https://gitee.com/wujiawei1207537021/wu-framework-parent)孵化出的项目内部使用Lazy
@@ -51,6 +53,7 @@ ORM操作数据库使用环境JDK24 Spring Boot 3.5.0。的网络穿透、渗
[UI](https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network-server-ui)
### 内网穿透
| 模块 | 所属层级 | 描述 | 快照版本 | 发布版本 |
@@ -89,36 +92,36 @@ ORM操作数据库使用环境JDK24 Spring Boot 3.5.0。的网络穿透、渗
### 项目结构
| 模块 | 版本 | 描述 |
|------------------------------------------------------------------------------------------------------------|----------------------|------------------------------|
| [wu-lazy-cloud-heartbeat-common](wu-lazy-cloud-heartbeat-common) | 1.3.1-JDK24-SNAPSHOT | 内网穿透公共模块(声明接口、枚举、常量、适配器、解析器) |
| [wu-lazy-cloud-heartbeat-client](wu-lazy-cloud-heartbeat-client) | 1.3.1-JDK24-SNAPSHOT | 客户端(支持二次开发) |
| [wu-lazy-cloud-heartbeat-server](wu-lazy-cloud-heartbeat-server) | 1.3.1-JDK24-SNAPSHOT | 服务端(支持二次开发) |
| [wu-lazy-cloud-heartbeat-client-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start) | 1.3.1-JDK24-SNAPSHOT | 客户端样例 |
| [wu-lazy-cloud-heartbeat-server-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-client-start) | 1.3.1-JDK24-SNAPSHOT | 服务端样例 |
| 模块 | 版本 | 描述 |
|------------------------------------------------------------------------------------------------------------|-------------|------------------------------|
| [wu-lazy-cloud-heartbeat-common](wu-lazy-cloud-heartbeat-common) | 1.3.1-JDK24 | 内网穿透公共模块(声明接口、枚举、常量、适配器、解析器) |
| [wu-lazy-cloud-heartbeat-client](wu-lazy-cloud-heartbeat-client) | 1.3.1-JDK24 | 客户端(支持二次开发) |
| [wu-lazy-cloud-heartbeat-server](wu-lazy-cloud-heartbeat-server) | 1.3.1-JDK24 | 服务端(支持二次开发) |
| [wu-lazy-cloud-heartbeat-client-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start) | 1.3.1-JDK24 | 客户端样例 |
| [wu-lazy-cloud-heartbeat-server-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-client-start) | 1.3.1-JDK24 | 服务端样例 |
### 使用技术
| 框架 | 版本 | 描述 |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|--------------|
| spring-boot | 3.0.7 | springboot框架 |
| [wu-framework-web](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-framework-web) | 1.3.1-JDK24-SNAPSHOT | web容器 |
| [Lazy -ORM](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-inner-intergration/wu-database-parent) | 1.3.1-JDK24-SNAPSHOT | ORM |
| mysql-connector-j | 8.0.33 | mysql驱动 |
| [wu-authorization-server-platform-starter](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-smart-platform/wu-authorization-server-platform-starter) | 1.3.1-JDK24-SNAPSHOT | 用户授权体系 |
| 框架 | 版本 | 描述 |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|--------------|
| spring-boot | 3.0.7 | springboot框架 |
| [wu-framework-web](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-framework-web) | 1.3.1-JDK24 | web容器 |
| [Lazy -ORM](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-inner-intergration/wu-database-parent) | 1.3.1-JDK24 | ORM |
| mysql-connector-j | 8.0.33 | mysql驱动 |
| [wu-authorization-server-platform-starter](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-smart-platform/wu-authorization-server-platform-starter) | 1.3.1-JDK24 | 用户授权体系 |
### 使用环境
IDEA
Mac、Windows
JAVA >=13
JAVA >=17
MAVEN
### 启动[install.md](install.md)
docker启动
docker run -d -it -p 18080:18080 --name wu-lazy-cloud-heartbeat-server registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24-SNAPSHOT
docker run -d -it -p 18080:18080 --name wu-lazy-cloud-heartbeat-server registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24
http://127.0.0.1:18080/swagger-ui/index.html

View File

@@ -28,7 +28,7 @@ spec:
containers:
- name: wu-lazy-cloud-heartbeat-local-client-start
image: >-
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24-SNAPSHOT
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
env:
- name: spring.lazy.netty.client.inet-host
value: 124.222.48.62

View File

@@ -9,7 +9,7 @@
```shell
docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 --name wlcn-s registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24-SNAPSHOT
docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 --name wlcn-s registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24
```
##### 启动客户端
@@ -24,5 +24,5 @@ docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 --nam
- 打开浏览器访问界面 http://127.0.0.1:6004/netty-client-local-ui/index.html
```shell
docker run -d -it --privileged -p 6004:6004 --name wlcn-c --restart=always -e spring.lazy.netty.client.inet-host=192.168.xx.xx -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="wlcn-c" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24-SNAPSHOT
docker run -d -it --privileged -p 6004:6004 --name wlcn-c --restart=always -e spring.lazy.netty.client.inet-host=192.168.xx.xx -e spring.lazy.netty.client.inet-port=7001 -e spring.lazy.netty.client.client-id="wlcn-c" registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
```

224
pom.xml
View File

@@ -8,14 +8,33 @@
<parent>
<artifactId>wu-framework-parent</artifactId>
<groupId>top.wu2020</groupId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<artifactId>wu-lazy-cloud-network</artifactId>
<packaging>pom</packaging>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
<description>云上云下</description>
<name>wlcn项目</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<modules>
<!-- 云上服务组件 -->
@@ -38,6 +57,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@@ -57,12 +77,14 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.5.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot-starter.version}</version>
</dependency>
</dependencies>
@@ -71,7 +93,7 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-dependencies</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -79,200 +101,6 @@
</dependencyManagement>
<profiles>
<profile>
<id>oss</id>
<build>
<plugins>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<locale>en_US</locale>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
</plugin>
<!-- GPG -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- flatten -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-->发布的地址<-->
<distributionManagement>
<snapshotRepository>
<id>oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>oss-snapshots</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>native</id>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Spring-Boot-Native-Processed>true</Spring-Boot-Native-Processed>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-tiny:latest</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
<executions>
<execution>
<id>process-aot</id>
<goals>
<goal>process-aot</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<requiredVersion>22.3</requiredVersion>
</configuration>
<executions>
<execution>
<id>add-reachability-metadata</id>
<goals>
<goal>add-reachability-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>nativeTest</id>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-test-aot</id>
<goals>
<goal>process-test-aot</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<requiredVersion>22.3</requiredVersion>
</configuration>
<executions>
<execution>
<id>native-test</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -40,7 +40,12 @@
[add] 新增代理流量监控
[add] docker仓库上架dockerhub搜索https://hub.docker.com/search?q=wlcn
#### 1.3.1-JDK24-SNAPSHOT
[add] 新增代理流量监控
#### 1.3.2-JDK24
[add] 新增代理请求日志、代理返回日志 通过配置spring.lazy.netty.protocol.proxy.enableProxyLog=true
[add] 升级使用jdk24、springboot 3.5.0
[add] 添加socks授权验证。通过配置spring.lazy.netty.protocol.proxy.authentication=true
#### 1.3.3-JDK24
[fix] 优化代理日志记录性能影响支持自定义spring.lazy.netty.protocol.proxy.sendMsgQueue、sendMsgQueue
[add] 服务上架dockerhub
#### 下一版本计划

12
website/BuildDocker.md Normal file
View File

@@ -0,0 +1,12 @@
### 构建docker镜像
```shell
#docker login --username=1207537021@qq.com registry.cn-hangzhou.aliyuncs.com
docker build -t registry.cn-hangzhou.aliyuncs.com/wu-lazy/wlcn-website:1.3.1-JDK24 .
docker push registry.cn-hangzhou.aliyuncs.com/wu-lazy/wlcn-website:1.3.1-JDK24
```

2
website/Dockerfile Normal file
View File

@@ -0,0 +1,2 @@
FROM registry.cn-hangzhou.aliyuncs.com/wu-library/nginx:1.13.0-alpine
COPY dist /usr/share/nginx/html/

241
website/README.md Normal file
View File

@@ -0,0 +1,241 @@
# Wu-Lazy-Cloud-Network 官方网站
这是 Wu-Lazy-Cloud-Network 项目的官方网站,基于 Vue 3 + Element Plus 构建。
## 🚀 快速开始
### 环境要求
- Node.js 16.0+
- npm 8.0+ 或 yarn 1.22+
### 安装依赖
```bash
# 使用 npm
npm install
# 或使用 yarn
yarn install
```
### 开发模式
```bash
# 启动开发服务器
npm run dev
# 或使用 yarn
yarn dev
```
开发服务器将在 `http://localhost:3000` 启动。
### 构建生产版本
```bash
# 构建生产版本
npm run build
# 或使用 yarn
yarn build
```
构建文件将输出到 `dist` 目录。
### 预览生产版本
```bash
# 预览生产版本
npm run preview
# 或使用 yarn
yarn preview
```
## 📁 项目结构
```
website/
├── public/ # 静态资源
├── src/
│ ├── assets/ # 资源文件
│ │ ├── images/ # 图片资源
│ │ └── styles/ # 样式文件
│ ├── components/ # 公共组件
│ │ ├── Header.vue # 头部导航
│ │ └── Footer.vue # 底部组件
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # 状态管理
│ ├── utils/ # 工具函数
│ │ └── index.js
│ ├── views/ # 页面组件
│ │ ├── Home.vue # 首页
│ │ ├── Features.vue # 功能特性
│ │ ├── Architecture.vue # 系统架构
│ │ ├── Docs.vue # 使用文档
│ │ ├── Download.vue # 下载安装
│ │ └── About.vue # 关于我们
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── index.html # HTML 模板
├── package.json # 项目配置
├── vite.config.js # Vite 配置
└── README.md # 项目说明
```
## 🎨 页面说明
### 首页 (Home.vue)
- 项目介绍和核心功能展示
- 技术栈和快速开始指南
- 统计数据展示
### 功能特性 (Features.vue)
- 详细的功能介绍
- 内网穿透、网络代理、流量监控等功能说明
- 安全特性展示
### 系统架构 (Architecture.vue)
- 整体架构图
- 模块架构说明
- 数据流架构
- 技术栈介绍
### 使用文档 (Docs.vue)
- 快速开始指南
- 安装部署说明
- 配置参数详解
- 使用指南和故障排除
### 下载安装 (Download.vue)
- 版本信息
- 多种下载方式
- 部署指南
- 系统要求
### 关于我们 (About.vue)
- 项目背景和核心价值
- 开发团队介绍
- 技术栈详情
- 发展路线图
- 联系方式
## 🛠️ 技术栈
- **Vue 3**: 渐进式 JavaScript 框架
- **Vue Router**: 官方路由管理器
- **Pinia**: 状态管理库
- **Element Plus**: Vue 3 组件库
- **Vite**: 下一代前端构建工具
- **SCSS**: CSS 预处理器
- **Prism.js**: 代码高亮
- **Marked**: Markdown 解析器
## 🎯 主要功能
### 响应式设计
- 支持桌面端和移动端
- 自适应布局
- 触摸友好的交互
### 代码高亮
- 支持多种编程语言
- 语法高亮显示
- 复制代码功能
### 性能优化
- 路由懒加载
- 组件按需加载
- 图片懒加载
- 代码分割
### SEO 友好
- 语义化 HTML
- Meta 标签优化
- 结构化数据
## 🔧 开发指南
### 添加新页面
1.`src/views/` 目录下创建新的 Vue 组件
2.`src/router/index.js` 中添加路由配置
3.`src/components/Header.vue` 中添加导航链接
### 添加新组件
1.`src/components/` 目录下创建组件
2. 在需要使用的页面中导入并使用
### 样式开发
- 使用 SCSS 编写样式
- 遵循 BEM 命名规范
- 使用 CSS 变量管理主题色彩
- 响应式设计使用媒体查询
### 代码规范
- 使用 ESLint 进行代码检查
- 使用 Prettier 进行代码格式化
- 遵循 Vue 3 组合式 API 规范
- 组件命名使用 PascalCase
- 文件名使用 kebab-case
## 📦 构建部署
### 构建生产版本
```bash
npm run build
```
### 部署到静态服务器
构建完成后,将 `dist` 目录的内容部署到 Web 服务器即可。
### Docker 部署
```dockerfile
# 使用 nginx 镜像
FROM nginx:alpine
# 复制构建文件
COPY dist/ /usr/share/nginx/html/
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
# 暴露端口
EXPOSE 80
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
```
## 🤝 贡献指南
1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request
## 📄 许可证
本项目采用 [Apache License 2.0](LICENSE) 许可证。
## 📞 联系我们
- **邮箱**: 1207537021@qq.com
- **项目地址**:
- [Gitee](https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network)
- [GitHub](https://github.com/wujiawei1207537021/wu-lazy-cloud-network)
---
**版本**: 1.0.0
**更新时间**: 2024年12月
**维护者**: 吴佳伟

16
website/index.html Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Wu-Lazy-Cloud-Network 是一款基于 Spring Boot 3.5.0 和 JDK 24 开发的高性能网络穿透和代理工具,支持 TCP、HTTP、SOCKS 协议。" />
<meta name="keywords" content="内网穿透,网络代理,Spring Boot,Netty,Vue3" />
<meta name="author" content="吴佳伟" />
<title>Wu-Lazy-Cloud-Network - 高性能网络穿透和代理工具</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

42
website/package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "wu-lazy-cloud-network-website",
"version": "1.3.1",
"description": "Wu-Lazy-Cloud-Network 官方网站",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.7.0",
"element-plus": "^2.4.4",
"marked": "^9.1.6",
"pinia": "^2.1.7",
"prismjs": "^1.29.0",
"vue": "^3.4.0",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.2",
"@vue/eslint-config-prettier": "^8.0.0",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"prettier": "^3.1.1",
"sass": "^1.69.5",
"vite": "^5.0.8",
"vite-plugin-compression": "^0.5.1"
},
"keywords": [
"vue3",
"vite",
"element-plus",
"wu-lazy-cloud-network",
"内网穿透",
"网络代理"
],
"author": "吴佳伟 <1207537021@qq.com>",
"license": "Apache-2.0"
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><circle cx="16" cy="16" r="14" fill="#409eff"/><path d="M8 16l6 6 10-10" stroke="white" stroke-width="2" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 185 B

58
website/src/App.vue Normal file
View File

@@ -0,0 +1,58 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
onMounted(() => {
// 页面加载完成后的初始化逻辑
console.log('Wu-Lazy-Cloud-Network 官网已加载')
})
</script>
<style lang="scss">
#app {
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
}
// 滚动条样式
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>

View File

@@ -0,0 +1,417 @@
// 组件样式
// 按钮样式
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 24px;
border-radius: 6px;
font-weight: 500;
text-decoration: none;
transition: all 0.3s ease;
border: none;
cursor: pointer;
font-size: 14px;
&:hover {
transform: translateY(-1px);
}
&.btn-primary {
background: var(--primary-color);
color: white;
&:hover {
background: #337ecc;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
}
&.btn-outline {
background: transparent;
color: var(--primary-color);
border: 2px solid var(--primary-color);
&:hover {
background: var(--primary-color);
color: white;
}
}
&.btn-large {
padding: 16px 32px;
font-size: 16px;
}
&.btn-small {
padding: 8px 16px;
font-size: 12px;
}
}
// 卡片样式
.card {
background: white;
border-radius: 12px;
box-shadow: var(--shadow-light);
transition: all 0.3s ease;
&.card-hover {
&:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-dark);
}
}
}
// 代码块样式
.code-block {
background: #f8f9fa;
border-radius: 8px;
padding: 16px;
margin: 16px 0;
overflow-x: auto;
pre {
margin: 0;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.5;
code {
background: none;
padding: 0;
border-radius: 0;
font-size: inherit;
}
}
}
// 容器样式
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
@media (max-width: 768px) {
padding: 0 16px;
}
}
// 网格布局
.grid {
display: grid;
gap: 20px;
&.grid-2 {
grid-template-columns: repeat(2, 1fr);
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
&.grid-3 {
grid-template-columns: repeat(3, 1fr);
@media (max-width: 1024px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
&.grid-4 {
grid-template-columns: repeat(4, 1fr);
@media (max-width: 1024px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
}
// 弹性布局
.flex {
display: flex;
&.flex-center {
align-items: center;
justify-content: center;
}
&.flex-between {
align-items: center;
justify-content: space-between;
}
&.flex-column {
flex-direction: column;
}
&.flex-wrap {
flex-wrap: wrap;
}
&.flex-1 {
flex: 1;
}
}
// 间距工具类
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mt-3 { margin-top: 24px; }
.mt-4 { margin-top: 32px; }
.mt-5 { margin-top: 40px; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }
.mb-3 { margin-bottom: 24px; }
.mb-4 { margin-bottom: 32px; }
.mb-5 { margin-bottom: 40px; }
.ml-0 { margin-left: 0; }
.ml-1 { margin-left: 8px; }
.ml-2 { margin-left: 16px; }
.ml-3 { margin-left: 24px; }
.ml-4 { margin-left: 32px; }
.ml-5 { margin-left: 40px; }
.mr-0 { margin-right: 0; }
.mr-1 { margin-right: 8px; }
.mr-2 { margin-right: 16px; }
.mr-3 { margin-right: 24px; }
.mr-4 { margin-right: 32px; }
.mr-5 { margin-right: 40px; }
.pt-0 { padding-top: 0; }
.pt-1 { padding-top: 8px; }
.pt-2 { padding-top: 16px; }
.pt-3 { padding-top: 24px; }
.pt-4 { padding-top: 32px; }
.pt-5 { padding-top: 40px; }
.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: 8px; }
.pb-2 { padding-bottom: 16px; }
.pb-3 { padding-bottom: 24px; }
.pb-4 { padding-bottom: 32px; }
.pb-5 { padding-bottom: 40px; }
.pl-0 { padding-left: 0; }
.pl-1 { padding-left: 8px; }
.pl-2 { padding-left: 16px; }
.pl-3 { padding-left: 24px; }
.pl-4 { padding-left: 32px; }
.pl-5 { padding-left: 40px; }
.pr-0 { padding-right: 0; }
.pr-1 { padding-right: 8px; }
.pr-2 { padding-right: 16px; }
.pr-3 { padding-right: 24px; }
.pr-4 { padding-right: 32px; }
.pr-5 { padding-right: 40px; }
// 文本工具类
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-primary { color: var(--text-primary); }
.text-regular { color: var(--text-regular); }
.text-secondary { color: var(--text-secondary); }
.text-placeholder { color: var(--text-placeholder); }
.text-success { color: var(--success-color); }
.text-warning { color: var(--warning-color); }
.text-danger { color: var(--danger-color); }
.text-info { color: var(--info-color); }
.font-bold { font-weight: bold; }
.font-normal { font-weight: normal; }
.font-light { font-weight: 300; }
.text-sm { font-size: 12px; }
.text-base { font-size: 14px; }
.text-lg { font-size: 16px; }
.text-xl { font-size: 18px; }
.text-2xl { font-size: 20px; }
.text-3xl { font-size: 24px; }
.text-4xl { font-size: 28px; }
// 显示工具类
.d-none { display: none; }
.d-block { display: block; }
.d-inline { display: inline; }
.d-inline-block { display: inline-block; }
.d-flex { display: flex; }
.d-grid { display: grid; }
// 响应式工具类
@media (max-width: 768px) {
.d-md-none { display: none; }
.d-md-block { display: block; }
.d-md-flex { display: flex; }
.d-md-grid { display: grid; }
}
@media (max-width: 1024px) {
.d-lg-none { display: none; }
.d-lg-block { display: block; }
.d-lg-flex { display: flex; }
.d-lg-grid { display: grid; }
}
// 动画工具类
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.slide-up {
animation: slideUp 0.5s ease-out;
}
.slide-down {
animation: slideDown 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
// 加载动画
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
// 徽章样式
.badge {
display: inline-block;
padding: 4px 8px;
font-size: 12px;
font-weight: 500;
border-radius: 4px;
text-align: center;
&.badge-primary {
background: var(--primary-color);
color: white;
}
&.badge-success {
background: var(--success-color);
color: white;
}
&.badge-warning {
background: var(--warning-color);
color: white;
}
&.badge-danger {
background: var(--danger-color);
color: white;
}
&.badge-info {
background: var(--info-color);
color: white;
}
}
// 标签样式
.tag {
display: inline-block;
padding: 4px 12px;
font-size: 12px;
font-weight: 500;
border-radius: 16px;
background: var(--background-light);
color: var(--text-regular);
margin: 2px;
&:hover {
background: var(--primary-color);
color: white;
}
}
// 分割线
.divider {
height: 1px;
background: var(--border-light);
margin: 20px 0;
}
// 空状态
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--text-secondary);
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-title {
font-size: 18px;
font-weight: 500;
margin-bottom: 8px;
}
.empty-description {
font-size: 14px;
line-height: 1.5;
}
}

View File

@@ -0,0 +1,232 @@
// 引入组件样式
//@import './components.scss';
// 全局样式变量
:root {
--primary-color: #409eff;
--success-color: #67c23a;
--warning-color: #e6a23c;
--danger-color: #f56c6c;
--info-color: #909399;
--text-primary: #303133;
--text-regular: #606266;
--text-secondary: #909399;
--text-placeholder: #c0c4cc;
--border-color: #dcdfe6;
--border-light: #e4e7ed;
--border-lighter: #ebeef5;
--border-extra-light: #f2f6fc;
--background-color: #ffffff;
--background-light: #f5f7fa;
--shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
--shadow-base: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
--shadow-dark: 0 4px 8px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}
// 全局样式
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--text-primary);
background-color: var(--background-color);
}
// 通用工具类
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.flex {
display: flex;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-column {
display: flex;
flex-direction: column;
}
// 按钮样式
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
&.btn-primary {
background-color: var(--primary-color);
color: white;
&:hover {
background-color: #66b1ff;
}
}
&.btn-outline {
background-color: transparent;
border: 2px solid var(--primary-color);
color: var(--primary-color);
&:hover {
background-color: var(--primary-color);
color: white;
}
}
&.btn-large {
padding: 16px 32px;
font-size: 16px;
}
}
// 卡片样式
.card {
background: white;
border-radius: 8px;
box-shadow: var(--shadow-light);
padding: 24px;
margin-bottom: 20px;
&.card-hover {
transition: all 0.3s ease;
&:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-dark);
}
}
}
// 标题样式
.title {
font-weight: 600;
margin-bottom: 16px;
&.title-large {
font-size: 32px;
line-height: 1.2;
}
&.title-medium {
font-size: 24px;
line-height: 1.3;
}
&.title-small {
font-size: 18px;
line-height: 1.4;
}
}
// 段落样式
.paragraph {
line-height: 1.6;
color: var(--text-regular);
margin-bottom: 16px;
}
// 代码块样式
.code-block {
background-color: #f6f8fa;
border: 1px solid var(--border-light);
border-radius: 6px;
padding: 16px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.5;
overflow-x: auto;
&.code-block-dark {
background-color: #2d3748;
color: #e2e8f0;
border-color: #4a5568;
}
}
// 响应式设计
@media (max-width: 768px) {
.container {
padding: 0 16px;
}
.title-large {
font-size: 24px !important;
}
.title-medium {
font-size: 20px !important;
}
.btn-large {
padding: 12px 24px !important;
font-size: 14px !important;
}
}
// 动画效果
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.3s ease;
}
.slide-up-enter-from {
transform: translateY(30px);
opacity: 0;
}
.slide-up-leave-to {
transform: translateY(-30px);
opacity: 0;
}

View File

@@ -0,0 +1,189 @@
<template>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3 class="footer-title">Wu-Lazy-Cloud-Network</h3>
<p class="footer-description">
基于 Spring Boot 3.5.0 JDK 24 开发的高性能网络穿透和代理工具
支持 TCPHTTPSOCKS 协议提供完整的内网穿透网络代理流量监控等功能
</p>
<div class="footer-social">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="social-link">
<el-icon><Platform /></el-icon>
</a>
<a href="https://github.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="social-link">
<el-icon><Platform /></el-icon>
</a>
<a href="mailto:1207537021@qq.com" class="social-link">
<el-icon><Message /></el-icon>
</a>
</div>
</div>
<div class="footer-section">
<h4 class="footer-subtitle">功能特性</h4>
<ul class="footer-links">
<li><router-link to="/features#penetration">内网穿透</router-link></li>
<li><router-link to="/features#proxy">网络代理</router-link></li>
<li><router-link to="/features#monitor">流量监控</router-link></li>
<li><router-link to="/features#route">路由管理</router-link></li>
</ul>
</div>
<div class="footer-section">
<h4 class="footer-subtitle">技术文档</h4>
<ul class="footer-links">
<li><router-link to="/docs">使用文档</router-link></li>
<li><router-link to="/architecture">系统架构</router-link></li>
<li><router-link to="/download">下载安装</router-link></li>
<li><a href="https://gitee.com/wujiawei1207537021/wu-framework-parent" target="_blank">Lazy ORM</a></li>
</ul>
</div>
<div class="footer-section">
<h4 class="footer-subtitle">技术支持</h4>
<ul class="footer-links">
<li><a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/issues" target="_blank">问题反馈</a></li>
<li><a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/pulls" target="_blank">贡献代码</a></li>
<li><a href="mailto:1207537021@qq.com">联系我们</a></li>
<li><router-link to="/about">关于我们</router-link></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<div class="footer-copyright">
<p>&copy; 2024 Wu-Lazy-Cloud-Network. All rights reserved.</p>
<p> <a href="mailto:1207537021@qq.com">吴佳伟</a> 开发和维护</p>
</div>
<div class="footer-license">
<p>基于 <a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache License 2.0</a> 开源协议</p>
</div>
</div>
</div>
</footer>
</template>
<script setup>
import { Platform, Message } from '@element-plus/icons-vue'
</script>
<style lang="scss" scoped>
.footer {
background: var(--background-light);
border-top: 1px solid var(--border-light);
padding: 60px 0 20px;
margin-top: 80px;
}
.footer-content {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
gap: 40px;
margin-bottom: 40px;
@media (max-width: 768px) {
grid-template-columns: 1fr;
gap: 30px;
}
}
.footer-section {
.footer-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
.footer-description {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 20px;
}
.footer-subtitle {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
}
.footer-social {
display: flex;
gap: 12px;
.social-link {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 6px;
background: white;
color: var(--text-regular);
text-decoration: none;
transition: all 0.3s ease;
border: 1px solid var(--border-light);
&:hover {
color: var(--primary-color);
border-color: var(--primary-color);
transform: translateY(-2px);
}
.el-icon {
font-size: 18px;
}
}
}
.footer-links {
list-style: none;
li {
margin-bottom: 8px;
a {
color: var(--text-regular);
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: var(--primary-color);
}
}
}
}
.footer-bottom {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 20px;
border-top: 1px solid var(--border-light);
@media (max-width: 768px) {
flex-direction: column;
gap: 16px;
text-align: center;
}
p {
color: var(--text-secondary);
font-size: 14px;
margin: 0;
a {
color: var(--primary-color);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

View File

@@ -0,0 +1,215 @@
<template>
<header class="header" :class="{ 'header-scrolled': isScrolled }">
<div class="container">
<div class="header-content">
<div class="logo">
<router-link to="/" class="logo-link">
<el-icon class="logo-icon"><Connection /></el-icon>
<span class="logo-text">Wu-Lazy-Cloud-Network</span>
</router-link>
</div>
<nav class="nav" :class="{ 'nav-mobile': isMobileMenuOpen }">
<router-link to="/" class="nav-link" @click="closeMobileMenu">首页</router-link>
<router-link to="/features" class="nav-link" @click="closeMobileMenu">功能特性</router-link>
<router-link to="/architecture" class="nav-link" @click="closeMobileMenu">系统架构</router-link>
<router-link to="/docs" class="nav-link" @click="closeMobileMenu">使用文档</router-link>
<router-link to="/download" class="nav-link" @click="closeMobileMenu">下载安装</router-link>
<router-link to="/about" class="nav-link" @click="closeMobileMenu">关于我们</router-link>
</nav>
<div class="header-actions">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="btn btn-outline">
<el-icon><Platform /></el-icon>
Gitee
</a>
<a href="https://github.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="btn btn-outline">
<el-icon><Platform /></el-icon>
GitHub
</a>
</div>
<div class="mobile-menu-toggle" @click="toggleMobileMenu">
<el-icon><Menu /></el-icon>
</div>
</div>
</div>
</header>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { Connection, Platform, Menu } from '@element-plus/icons-vue'
const isScrolled = ref(false)
const isMobileMenuOpen = ref(false)
const handleScroll = () => {
isScrolled.value = window.scrollY > 50
}
const toggleMobileMenu = () => {
isMobileMenuOpen.value = !isMobileMenuOpen.value
}
const closeMobileMenu = () => {
isMobileMenuOpen.value = false
}
onMounted(() => {
window.addEventListener('scroll', handleScroll)
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
</script>
<style lang="scss" scoped>
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-light);
transition: all 0.3s ease;
&.header-scrolled {
background: rgba(255, 255, 255, 0.98);
box-shadow: var(--shadow-light);
}
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
}
.logo {
.logo-link {
display: flex;
align-items: center;
text-decoration: none;
color: var(--text-primary);
font-weight: 600;
font-size: 18px;
&:hover {
color: var(--primary-color);
}
}
.logo-icon {
font-size: 24px;
color: var(--primary-color);
margin-right: 8px;
}
.logo-text {
@media (max-width: 768px) {
display: none;
}
}
}
.nav {
display: flex;
align-items: center;
gap: 32px;
.nav-link {
text-decoration: none;
color: var(--text-regular);
font-weight: 500;
transition: color 0.3s ease;
position: relative;
&:hover,
&.router-link-active {
color: var(--primary-color);
}
&.router-link-active::after {
content: '';
position: absolute;
bottom: -8px;
left: 0;
right: 0;
height: 2px;
background: var(--primary-color);
border-radius: 1px;
}
}
@media (max-width: 768px) {
position: fixed;
top: 70px;
left: 0;
right: 0;
background: white;
flex-direction: column;
padding: 20px;
gap: 16px;
border-bottom: 1px solid var(--border-light);
transform: translateY(-100%);
opacity: 0;
transition: all 0.3s ease;
&.nav-mobile {
transform: translateY(0);
opacity: 1;
}
}
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
.btn {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 16px;
font-size: 14px;
.el-icon {
font-size: 16px;
}
}
@media (max-width: 768px) {
display: none;
}
}
.mobile-menu-toggle {
display: none;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
cursor: pointer;
border-radius: 6px;
transition: background-color 0.3s ease;
&:hover {
background-color: var(--background-light);
}
.el-icon {
font-size: 20px;
color: var(--text-regular);
}
@media (max-width: 768px) {
display: flex;
}
}
</style>

BIN
website/src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

22
website/src/main.js Normal file
View File

@@ -0,0 +1,22 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import './assets/styles/index.scss'
const app = createApp(App)
// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.mount('#app')

View File

@@ -0,0 +1,63 @@
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: { title: '首页' }
},
{
path: '/features',
name: 'Features',
component: () => import('@/views/Features.vue'),
meta: { title: '功能特性' }
},
{
path: '/architecture',
name: 'Architecture',
component: () => import('@/views/Architecture.vue'),
meta: { title: '系统架构' }
},
{
path: '/docs',
name: 'Docs',
component: () => import('@/views/Docs.vue'),
meta: { title: '使用文档' }
},
{
path: '/download',
name: 'Download',
component: () => import('@/views/Download.vue'),
meta: { title: '下载安装' }
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue'),
meta: { title: '关于我们' }
}
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
}
})
// 路由守卫
router.beforeEach((to, from, next) => {
// 设置页面标题
if (to.meta.title) {
document.title = `${to.meta.title} - Wu-Lazy-Cloud-Network`
}
next()
})
export default router

227
website/src/utils/index.js Normal file
View File

@@ -0,0 +1,227 @@
// 工具函数集合
/**
* 格式化文件大小
* @param {number} bytes 字节数
* @returns {string} 格式化后的文件大小
*/
export function formatFileSize(bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
/**
* 格式化时间
* @param {Date|string|number} date 日期
* @returns {string} 格式化后的时间
*/
export function formatDate(date) {
const d = new Date(date)
return d.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
/**
* 防抖函数
* @param {Function} func 要防抖的函数
* @param {number} wait 等待时间
* @returns {Function} 防抖后的函数
*/
export function debounce(func, wait) {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
/**
* 节流函数
* @param {Function} func 要节流的函数
* @param {number} limit 限制时间
* @returns {Function} 节流后的函数
*/
export function throttle(func, limit) {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
/**
* 复制文本到剪贴板
* @param {string} text 要复制的文本
* @returns {Promise<boolean>} 是否复制成功
*/
export async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text)
return true
} catch (err) {
// 降级方案
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try {
document.execCommand('copy')
document.body.removeChild(textArea)
return true
} catch (err) {
document.body.removeChild(textArea)
return false
}
}
}
/**
* 生成随机ID
* @param {number} length ID长度
* @returns {string} 随机ID
*/
export function generateId(length = 8) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return result
}
/**
* 检查是否为移动设备
* @returns {boolean} 是否为移动设备
*/
export function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
/**
* 获取浏览器信息
* @returns {object} 浏览器信息
*/
export function getBrowserInfo() {
const ua = navigator.userAgent
let browser = 'Unknown'
let version = 'Unknown'
if (ua.includes('Chrome')) {
browser = 'Chrome'
version = ua.match(/Chrome\/(\d+)/)?.[1] || 'Unknown'
} else if (ua.includes('Firefox')) {
browser = 'Firefox'
version = ua.match(/Firefox\/(\d+)/)?.[1] || 'Unknown'
} else if (ua.includes('Safari')) {
browser = 'Safari'
version = ua.match(/Version\/(\d+)/)?.[1] || 'Unknown'
} else if (ua.includes('Edge')) {
browser = 'Edge'
version = ua.match(/Edge\/(\d+)/)?.[1] || 'Unknown'
}
return { browser, version }
}
/**
* 获取操作系统信息
* @returns {string} 操作系统名称
*/
export function getOS() {
const ua = navigator.userAgent
if (ua.includes('Windows')) return 'Windows'
if (ua.includes('Mac')) return 'macOS'
if (ua.includes('Linux')) return 'Linux'
if (ua.includes('Android')) return 'Android'
if (ua.includes('iOS')) return 'iOS'
return 'Unknown'
}
/**
* 格式化数字
* @param {number} num 数字
* @param {number} digits 小数位数
* @returns {string} 格式化后的数字
*/
export function formatNumber(num, digits = 2) {
return Number(num).toLocaleString('zh-CN', {
minimumFractionDigits: digits,
maximumFractionDigits: digits
})
}
/**
* 验证邮箱格式
* @param {string} email 邮箱地址
* @returns {boolean} 是否为有效邮箱
*/
export function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
/**
* 验证URL格式
* @param {string} url URL地址
* @returns {boolean} 是否为有效URL
*/
export function isValidURL(url) {
try {
new URL(url)
return true
} catch {
return false
}
}
/**
* 获取URL参数
* @param {string} name 参数名
* @returns {string|null} 参数值
*/
export function getUrlParam(name) {
const urlParams = new URLSearchParams(window.location.search)
return urlParams.get(name)
}
/**
* 设置URL参数
* @param {string} name 参数名
* @param {string} value 参数值
*/
export function setUrlParam(name, value) {
const url = new URL(window.location)
url.searchParams.set(name, value)
window.history.replaceState({}, '', url)
}
/**
* 移除URL参数
* @param {string} name 参数名
*/
export function removeUrlParam(name) {
const url = new URL(window.location)
url.searchParams.delete(name)
window.history.replaceState({}, '', url)
}

863
website/src/views/About.vue Normal file
View File

@@ -0,0 +1,863 @@
<template>
<div class="about-page">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">关于我们</h1>
<p class="hero-subtitle">
了解 Wu-Lazy-Cloud-Network 项目的背景团队和愿景
</p>
</div>
</div>
</section>
<!-- Project Introduction -->
<section class="project-intro">
<div class="container">
<div class="section-header">
<h2 class="section-title">项目介绍</h2>
<p class="section-subtitle">基于现代化技术栈的高性能网络穿透和代理工具</p>
</div>
<div class="intro-content">
<div class="intro-text">
<h3>项目背景</h3>
<p>
Wu-Lazy-Cloud-Network 是一个基于 Spring Boot 3.5.0 JDK 24 开发的高性能网络穿透和代理工具
项目起源于对现有网络穿透工具的不足之处的思考旨在提供一个更加稳定高效易用的解决方案
</p>
<h3>核心价值</h3>
<ul>
<li><strong>高性能</strong>: 基于 Netty 异步网络框架支持高并发连接处理</li>
<li><strong>易用性</strong>: 提供 Web 管理界面支持一键部署和配置</li>
<li><strong>稳定性</strong>: 采用心跳机制和连接池管理确保服务稳定运行</li>
<li><strong>扩展性</strong>: 模块化设计支持自定义协议和过滤器</li>
<li><strong>安全性</strong>: 支持多种认证机制和加密传输</li>
</ul>
<h3>技术特色</h3>
<ul>
<li>基于最新的 JDK 24 Spring Boot 3.5.0</li>
<li>采用 Netty 高性能网络框架</li>
<li>支持多种协议TCPHTTPSOCKS</li>
<li>提供完整的流量监控和统计功能</li>
<li>支持集群部署和负载均衡</li>
</ul>
</div>
<div class="intro-stats">
<div class="stat-item">
<div class="stat-number">1.3.1</div>
<div class="stat-label">当前版本</div>
</div>
<div class="stat-item">
<div class="stat-number">10K+</div>
<div class="stat-label">并发连接</div>
</div>
<div class="stat-item">
<div class="stat-number">99.9%</div>
<div class="stat-label">可用性</div>
</div>
<div class="stat-item">
<div class="stat-number">&lt; 10ms</div>
<div class="stat-label">响应时间</div>
</div>
</div>
</div>
</div>
</section>
<!-- Team -->
<section class="team">
<div class="container">
<div class="section-header">
<h2 class="section-title">开发团队</h2>
<p class="section-subtitle">专业的开发团队致力于提供优质的网络解决方案</p>
</div>
<div class="team-content">
<div class="team-member">
<div class="member-avatar">
<el-icon><User /></el-icon>
</div>
<div class="member-info">
<h3>吴佳伟 (Jia Wei Wu)</h3>
<p class="member-title">项目负责人 & 主要开发者</p>
<p class="member-description">
拥有多年 Java 开发经验专注于高性能网络应用开发
负责项目的整体架构设计核心功能开发和性能优化
</p>
<div class="member-skills">
<span class="skill-tag">Java</span>
<span class="skill-tag">Spring Boot</span>
<span class="skill-tag">Netty</span>
<span class="skill-tag">网络编程</span>
<span class="skill-tag">系统架构</span>
</div>
<div class="member-contact">
<a href="mailto:1207537021@qq.com" class="contact-link">
<el-icon><Message /></el-icon>
1207537021@qq.com
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Technology Stack -->
<section class="technology-stack">
<div class="container">
<div class="section-header">
<h2 class="section-title">技术栈</h2>
<p class="section-subtitle">采用现代化的技术栈确保高性能和可靠性</p>
</div>
<div class="tech-categories">
<div class="tech-category">
<h3>后端框架</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="tech-info">
<h4>Spring Boot 3.5.0</h4>
<p>现代化的应用框架提供快速开发和部署能力</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Connection /></el-icon>
</div>
<div class="tech-info">
<h4>Netty</h4>
<p>高性能异步网络框架支持高并发连接处理</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Cpu /></el-icon>
</div>
<div class="tech-info">
<h4>JDK 24</h4>
<p>最新的 Java 开发工具包提供优秀的性能表现</p>
</div>
</div>
</div>
</div>
<div class="tech-category">
<h3>数据存储</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>MySQL 8.0</h4>
<p>主数据库存储配置和监控数据</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>H2</h4>
<p>开发环境数据库轻量级嵌入式数据库</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>Lazy ORM</h4>
<p>轻量级 ORM 框架提供类型安全的数据库操作</p>
</div>
</div>
</div>
</div>
<div class="tech-category">
<h3>部署运维</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><Box /></el-icon>
</div>
<div class="tech-info">
<h4>Docker</h4>
<p>容器化部署提供一致的运行环境</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Setting /></el-icon>
</div>
<div class="tech-info">
<h4>Maven</h4>
<p>构建自动化工具管理项目依赖</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="tech-info">
<h4>Spring Boot Actuator</h4>
<p>应用监控和管理提供健康检查和指标</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Roadmap -->
<section class="roadmap">
<div class="container">
<div class="section-header">
<h2 class="section-title">发展路线</h2>
<p class="section-subtitle">项目的未来发展规划和功能演进</p>
</div>
<div class="roadmap-timeline">
<div class="timeline-item completed">
<div class="timeline-marker"></div>
<div class="timeline-content">
<h3>v1.3.1 (当前版本)</h3>
<p class="timeline-date">2024年12月</p>
<ul>
<li>升级到 JDK 24 Spring Boot 3.5.0</li>
<li>优化网络性能和稳定性</li>
<li>增强安全性和认证机制</li>
<li>完善文档和部署指南</li>
</ul>
</div>
</div>
<div class="timeline-item current">
<div class="timeline-marker"></div>
<div class="timeline-content">
<h3>v1.4.0 (开发中)</h3>
<p class="timeline-date">2025年Q1</p>
<ul>
<li>支持 WebSocket 协议</li>
<li>增强集群管理功能</li>
<li>提供 RESTful API</li>
<li>优化用户界面体验</li>
</ul>
</div>
</div>
<div class="timeline-item planned">
<div class="timeline-marker"></div>
<div class="timeline-content">
<h3>v2.0.0 (规划中)</h3>
<p class="timeline-date">2025年Q2</p>
<ul>
<li>微服务架构重构</li>
<li>支持 Kubernetes 部署</li>
<li>提供 SDK 和插件机制</li>
<li>增强监控和告警功能</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Contact -->
<section class="contact">
<div class="container">
<div class="section-header">
<h2 class="section-title">联系我们</h2>
<p class="section-subtitle">欢迎反馈建议报告问题或参与项目贡献</p>
</div>
<div class="contact-content">
<div class="contact-methods">
<div class="contact-method">
<div class="method-icon">
<el-icon><Message /></el-icon>
</div>
<h3>邮箱联系</h3>
<p>通过邮件与我们联系我们会在24小时内回复</p>
<a href="mailto:1207537021@qq.com" class="contact-link">
1207537021@qq.com
</a>
</div>
<div class="contact-method">
<div class="method-icon">
<el-icon><Platform /></el-icon>
</div>
<h3>项目地址</h3>
<p>访问项目仓库查看源码提交问题或参与贡献</p>
<div class="project-links">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="contact-link">
<el-icon><Platform /></el-icon>
Gitee
</a>
<a href="https://github.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="contact-link">
<el-icon><Platform /></el-icon>
GitHub
</a>
</div>
</div>
<div class="contact-method">
<div class="method-icon">
<el-icon><Document /></el-icon>
</div>
<h3>文档支持</h3>
<p>查看详细的使用文档API 文档和开发指南</p>
<router-link to="/docs" class="contact-link">
<el-icon><Document /></el-icon>
查看文档
</router-link>
</div>
</div>
</div>
</div>
</section>
<!-- License -->
<section class="license">
<div class="container">
<div class="license-content">
<h2>开源许可证</h2>
<p>
本项目采用 <strong>Apache License 2.0</strong> 开源许可证
您可以自由使用修改和分发本软件但需要遵守许可证条款
</p>
<div class="license-actions">
<a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank" class="btn btn-outline">
<el-icon><Document /></el-icon>
查看许可证
</a>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
User,
Message,
Platform,
Document,
Monitor,
Connection,
Cpu,
// DataBase,
Box,
Setting
} from '@element-plus/icons-vue'
onMounted(() => {
// 页面初始化逻辑
})
</script>
<style lang="scss" scoped>
.about-page {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
}
// Section Header
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
// Project Introduction
.project-intro {
padding: 80px 0;
background: var(--background-color);
}
.intro-content {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 60px;
align-items: start;
@media (max-width: 768px) {
grid-template-columns: 1fr;
gap: 40px;
}
}
.intro-text {
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
p {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 20px;
}
ul {
list-style: none;
margin-bottom: 20px;
li {
color: var(--text-regular);
margin-bottom: 8px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
}
.intro-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
.stat-item {
text-align: center;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: var(--shadow-light);
.stat-number {
font-size: 24px;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 8px;
}
.stat-label {
color: var(--text-regular);
font-size: 14px;
}
}
}
// Team
.team {
padding: 80px 0;
background: var(--background-light);
}
.team-content {
max-width: 800px;
margin: 0 auto;
}
.team-member {
display: flex;
gap: 30px;
align-items: start;
@media (max-width: 768px) {
flex-direction: column;
text-align: center;
}
.member-avatar {
width: 100px;
height: 100px;
background: var(--primary-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.el-icon {
font-size: 40px;
color: white;
}
}
.member-info {
flex: 1;
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.member-title {
color: var(--primary-color);
font-weight: 500;
margin-bottom: 12px;
}
.member-description {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 16px;
}
.member-skills {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 16px;
.skill-tag {
background: var(--background-light);
color: var(--text-regular);
padding: 4px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 500;
}
}
.member-contact {
.contact-link {
color: var(--primary-color);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
&:hover {
text-decoration: underline;
}
}
}
}
}
// Technology Stack
.technology-stack {
padding: 80px 0;
background: var(--background-color);
}
.tech-categories {
max-width: 1200px;
margin: 0 auto;
}
.tech-category {
margin-bottom: 60px;
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 30px;
text-align: center;
}
}
.tech-items {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
}
.tech-item {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: var(--shadow-light);
.tech-icon {
width: 50px;
height: 50px;
background: var(--primary-color);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.el-icon {
font-size: 24px;
color: white;
}
}
.tech-info {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 4px;
}
p {
font-size: 14px;
color: var(--text-regular);
line-height: 1.4;
}
}
}
// Roadmap
.roadmap {
padding: 80px 0;
background: var(--background-light);
}
.roadmap-timeline {
max-width: 800px;
margin: 0 auto;
position: relative;
&::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: var(--border-light);
}
}
.timeline-item {
position: relative;
margin-bottom: 40px;
padding-left: 60px;
.timeline-marker {
position: absolute;
left: 11px;
top: 0;
width: 20px;
height: 20px;
border-radius: 50%;
border: 3px solid white;
}
&.completed .timeline-marker {
background: var(--success-color);
}
&.current .timeline-marker {
background: var(--primary-color);
}
&.planned .timeline-marker {
background: var(--warning-color);
}
.timeline-content {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: var(--shadow-light);
h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.timeline-date {
color: var(--text-regular);
font-size: 14px;
margin-bottom: 12px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
}
}
// Contact
.contact {
padding: 80px 0;
background: var(--background-color);
}
.contact-methods {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.contact-method {
text-align: center;
padding: 30px;
background: white;
border-radius: 12px;
box-shadow: var(--shadow-light);
.method-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
.el-icon {
font-size: 28px;
color: white;
}
}
h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
p {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 16px;
}
.contact-link {
color: var(--primary-color);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
font-weight: 500;
&:hover {
text-decoration: underline;
}
}
.project-links {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
}
}
// License
.license {
padding: 60px 0;
background: var(--background-light);
text-align: center;
.license-content {
max-width: 600px;
margin: 0 auto;
h2 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
p {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 20px;
}
.license-actions {
display: flex;
justify-content: center;
}
}
}
</style>

View File

@@ -0,0 +1,798 @@
<template>
<div class="architecture-page">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">系统架构</h1>
<p class="hero-subtitle">
基于现代化的技术栈采用模块化设计提供高性能和可靠性
</p>
</div>
</div>
</section>
<!-- Architecture Overview -->
<section class="architecture-overview">
<div class="container">
<div class="section-header">
<h2 class="section-title">整体架构</h2>
<p class="section-subtitle">采用分层架构设计各模块职责清晰便于维护和扩展</p>
</div>
<div class="architecture-diagram">
<div class="layer application-layer">
<h3>应用层</h3>
<div class="components">
<div class="component">Web UI</div>
<div class="component">REST API</div>
<div class="component">WebSocket</div>
</div>
</div>
<div class="layer business-layer">
<h3>业务层</h3>
<div class="components">
<div class="component">内网穿透</div>
<div class="component">网络代理</div>
<div class="component">流量监控</div>
<div class="component">路由管理</div>
</div>
</div>
<div class="layer protocol-layer">
<h3>协议层</h3>
<div class="components">
<div class="component">HTTP 代理</div>
<div class="component">SOCKS 代理</div>
<div class="component">TCP 协议</div>
<div class="component">自定义协议</div>
</div>
</div>
<div class="layer communication-layer">
<h3>通信层</h3>
<div class="components">
<div class="component">Netty 框架</div>
<div class="component">心跳机制</div>
<div class="component">连接池</div>
<div class="component">过滤器链</div>
</div>
</div>
<div class="layer data-layer">
<h3>数据层</h3>
<div class="components">
<div class="component">MySQL</div>
<div class="component">H2</div>
<div class="component">Lazy ORM</div>
</div>
</div>
</div>
</div>
</section>
<!-- Module Architecture -->
<section class="module-architecture">
<div class="container">
<div class="section-header">
<h2 class="section-title">模块架构</h2>
<p class="section-subtitle">松耦合的模块化设计支持独立开发和部署</p>
</div>
<div class="modules-grid">
<div class="module-card">
<div class="module-header">
<div class="module-icon">
<el-icon><Connection /></el-icon>
</div>
<h3>wu-lazy-cloud-heartbeat-common</h3>
</div>
<p class="module-description">公共基础模块提供接口定义枚举常量适配器等</p>
<div class="module-components">
<h4>核心组件</h4>
<ul>
<li>NettyMsg - 网络消息基类</li>
<li>ChannelContext - 通道上下文</li>
<li>ProtocolAdapter - 协议适配器</li>
<li>NettyUtils - 工具类</li>
</ul>
</div>
</div>
<div class="module-card">
<div class="module-header">
<div class="module-icon">
<el-icon><Server /></el-icon>
</div>
<h3>wu-lazy-cloud-heartbeat-server</h3>
</div>
<p class="module-description">服务端核心模块提供网络服务客户端管理端口映射等功能</p>
<div class="module-components">
<h4>核心组件</h4>
<ul>
<li>NettyTcpServerSocketApplicationListener</li>
<li>LazyServerPermeateServerMappingApplication</li>
<li>NettyServerVirtualRouteApplication</li>
<li>InitServerSocket</li>
</ul>
</div>
</div>
<div class="module-card">
<div class="module-header">
<div class="module-icon">
<el-icon><Monitor /></el-icon>
</div>
<h3>wu-lazy-cloud-heartbeat-client</h3>
</div>
<p class="module-description">客户端核心模块提供连接管理本地服务端口转发等功能</p>
<div class="module-components">
<h4>核心组件</h4>
<ul>
<li>NettyClientSocketApplicationListener</li>
<li>LazyClientPermeateClientMappingApplication</li>
<li>NettyClientVirtualRouteApplication</li>
<li>LazyNettyClientApplication</li>
</ul>
</div>
</div>
<div class="module-card">
<div class="module-header">
<div class="module-icon">
<el-icon><Share /></el-icon>
</div>
<h3>wu-lazy-cloud-heartbeat-protocol-proxy</h3>
</div>
<p class="module-description">代理协议模块提供 HTTP SOCKS 代理服务</p>
<div class="module-components">
<h4>核心组件</h4>
<ul>
<li>NettyHttpProxySocketApplicationListener</li>
<li>NettySocketProxySocketApplicationListener</li>
<li>NettyTcpProxyFilter</li>
<li>HttpProxyHandler</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Data Flow -->
<section class="data-flow">
<div class="container">
<div class="section-header">
<h2 class="section-title">数据流架构</h2>
<p class="section-subtitle">清晰的数据流向支持高效的数据传输和处理</p>
</div>
<div class="flow-diagrams">
<div class="flow-diagram">
<h3>内网穿透数据流</h3>
<div class="flow-steps">
<div class="flow-step">
<div class="step-icon">
<el-icon><User /></el-icon>
</div>
<div class="step-content">
<h4>访客请求</h4>
<p>外部用户连接到服务端访客端口</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Server /></el-icon>
</div>
<div class="step-content">
<h4>服务端处理</h4>
<p>服务端创建访客通道通过心跳通道转发数据</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="step-content">
<h4>客户端转发</h4>
<p>客户端接收数据并转发给本地服务</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Connection /></el-icon>
</div>
<div class="step-content">
<h4>响应返回</h4>
<p>本地服务响应数据通过心跳通道返回给访客</p>
</div>
</div>
</div>
</div>
<div class="flow-diagram">
<h3>代理数据流</h3>
<div class="flow-steps">
<div class="flow-step">
<div class="step-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="step-content">
<h4>客户端应用</h4>
<p>应用程序发起代理请求</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Share /></el-icon>
</div>
<div class="step-content">
<h4>代理客户端</h4>
<p>代理客户端接收请求并转发</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Server /></el-icon>
</div>
<div class="step-content">
<h4>代理服务端</h4>
<p>代理服务端处理请求并访问目标服务器</p>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<div class="step-icon">
<el-icon><Globe /></el-icon>
</div>
<div class="step-content">
<h4>目标服务器</h4>
<p>目标服务器处理请求并返回响应</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Technology Stack -->
<section class="technology-stack">
<div class="container">
<div class="section-header">
<h2 class="section-title">技术栈</h2>
<p class="section-subtitle">采用现代化的技术栈确保高性能和可靠性</p>
</div>
<div class="tech-categories">
<div class="tech-category">
<h3>核心框架</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="tech-info">
<h4>Spring Boot 3.5.0</h4>
<p>现代化的应用框架提供快速开发和部署能力</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Connection /></el-icon>
</div>
<div class="tech-info">
<h4>Netty</h4>
<p>高性能异步网络框架支持高并发连接处理</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Cpu /></el-icon>
</div>
<div class="tech-info">
<h4>JDK 24</h4>
<p>最新的 Java 开发工具包提供优秀的性能表现</p>
</div>
</div>
</div>
</div>
<div class="tech-category">
<h3>数据存储</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>MySQL 8.0</h4>
<p>主数据库存储配置和监控数据</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>H2</h4>
<p>开发环境数据库轻量级嵌入式数据库</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<div class="tech-info">
<h4>Lazy ORM</h4>
<p>轻量级 ORM 框架提供类型安全的数据库操作</p>
</div>
</div>
</div>
</div>
<div class="tech-category">
<h3>部署运维</h3>
<div class="tech-items">
<div class="tech-item">
<div class="tech-icon">
<el-icon><Box /></el-icon>
</div>
<div class="tech-info">
<h4>Docker</h4>
<p>容器化部署提供一致的运行环境</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Setting /></el-icon>
</div>
<div class="tech-info">
<h4>Maven</h4>
<p>构建自动化工具管理项目依赖</p>
</div>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Monitor /></el-icon>
</div>
<div class="tech-info">
<h4>Spring Boot Actuator</h4>
<p>应用监控和管理提供健康检查和指标</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
Connection,
// Server,
Monitor,
Share,
User,
// Globe,
Cpu,
// DataBase,
Box,
Setting
} from '@element-plus/icons-vue'
onMounted(() => {
// 页面初始化逻辑
})
</script>
<style lang="scss" scoped>
.architecture-page {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
}
// Section Header
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
// Architecture Overview
.architecture-overview {
padding: 80px 0;
background: var(--background-color);
}
.architecture-diagram {
max-width: 1000px;
margin: 0 auto;
.layer {
margin-bottom: 30px;
padding: 20px;
border-radius: 12px;
background: var(--background-light);
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
text-align: center;
}
.components {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
.component {
padding: 8px 16px;
background: var(--primary-color);
color: white;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
}
}
}
.application-layer {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
.component {
background: rgba(255, 255, 255, 0.2);
}
}
.business-layer {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
.component {
background: rgba(255, 255, 255, 0.2);
}
}
.protocol-layer {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
.component {
background: rgba(255, 255, 255, 0.2);
}
}
.communication-layer {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
color: white;
.component {
background: rgba(255, 255, 255, 0.2);
}
}
.data-layer {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
color: white;
.component {
background: rgba(255, 255, 255, 0.2);
}
}
}
// Module Architecture
.module-architecture {
padding: 80px 0;
background: var(--background-light);
}
.modules-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.module-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
transition: transform 0.3s ease;
&:hover {
transform: translateY(-4px);
}
.module-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
.module-icon {
width: 50px;
height: 50px;
background: var(--primary-color);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
.el-icon {
font-size: 24px;
color: white;
}
}
h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}
}
.module-description {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 20px;
}
.module-components {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
font-weight: bold;
}
}
}
}
}
// Data Flow
.data-flow {
padding: 80px 0;
background: var(--background-color);
}
.flow-diagrams {
max-width: 1200px;
margin: 0 auto;
}
.flow-diagram {
margin-bottom: 60px;
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 30px;
text-align: center;
}
}
.flow-steps {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 20px;
.flow-step {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 200px;
.step-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
.el-icon {
font-size: 24px;
color: white;
}
}
.step-content {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
p {
font-size: 14px;
color: var(--text-regular);
line-height: 1.4;
}
}
}
.flow-arrow {
font-size: 24px;
color: var(--primary-color);
font-weight: bold;
@media (max-width: 768px) {
transform: rotate(90deg);
}
}
}
// Technology Stack
.technology-stack {
padding: 80px 0;
background: var(--background-light);
}
.tech-categories {
max-width: 1200px;
margin: 0 auto;
}
.tech-category {
margin-bottom: 60px;
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 30px;
text-align: center;
}
}
.tech-items {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
}
.tech-item {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: var(--shadow-light);
.tech-icon {
width: 50px;
height: 50px;
background: var(--primary-color);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.el-icon {
font-size: 24px;
color: white;
}
}
.tech-info {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 4px;
}
p {
font-size: 14px;
color: var(--text-regular);
line-height: 1.4;
}
}
}
</style>

967
website/src/views/Docs.vue Normal file
View File

@@ -0,0 +1,967 @@
<template>
<div class="docs-page">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">使用文档</h1>
<p class="hero-subtitle">
详细的使用指南和配置说明帮助您快速上手 Wu-Lazy-Cloud-Network
</p>
</div>
</div>
</section>
<!-- Docs Navigation -->
<section class="docs-navigation">
<div class="container">
<div class="docs-nav">
<div class="nav-item" @click="scrollToSection('quick-start')">
<el-icon><Timer /></el-icon>
<span>快速开始</span>
</div>
<div class="nav-item" @click="scrollToSection('installation')">
<el-icon><Download /></el-icon>
<span>安装部署</span>
</div>
<div class="nav-item" @click="scrollToSection('configuration')">
<el-icon><Setting /></el-icon>
<span>配置说明</span>
</div>
<div class="nav-item" @click="scrollToSection('usage')">
<el-icon><Document /></el-icon>
<span>使用指南</span>
</div>
<div class="nav-item" @click="scrollToSection('troubleshooting')">
<el-icon><Tools /></el-icon>
<span>故障排除</span>
</div>
</div>
</div>
</section>
<!-- Quick Start -->
<section class="docs-section" id="quick-start">
<div class="container">
<div class="section-header">
<h2 class="section-title">快速开始</h2>
<p class="section-subtitle">几分钟内完成部署和配置</p>
</div>
<div class="docs-content">
<div class="step-guide">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h3>启动服务端</h3>
<p>使用 Docker 快速启动服务端</p>
<div class="code-block">
<pre><code class="language-bash">docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 \
--name wlcn-server \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24</code></pre>
</div>
<div class="step-notes">
<h4>端口说明</h4>
<ul>
<li><strong>6001</strong>: Web 管理界面</li>
<li><strong>7001</strong>: TCP 连接端口</li>
<li><strong>8001</strong>: HTTP 代理端口</li>
<li><strong>9001</strong>: SOCKS 代理端口</li>
</ul>
</div>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h3>启动客户端</h3>
<p>配置客户端连接到服务端</p>
<div class="code-block">
<pre><code class="language-bash">docker run -d -it --privileged -p 6004:6004 \
--name wlcn-client \
--restart=always \
-e spring.lazy.netty.client.inet-host=YOUR_SERVER_IP \
-e spring.lazy.netty.client.inet-port=7001 \
-e spring.lazy.netty.client.client-id="your-client-id" \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24</code></pre>
</div>
<div class="step-notes">
<h4>环境变量说明</h4>
<ul>
<li><strong>inet-host</strong>: 服务端 IP 地址</li>
<li><strong>inet-port</strong>: 服务端 TCP 端口</li>
<li><strong>client-id</strong>: 客户端唯一标识</li>
</ul>
</div>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h3>访问管理界面</h3>
<p>通过 Web 界面进行配置和管理</p>
<div class="code-block">
<pre><code class="language-bash"># 服务端管理界面
http://127.0.0.1:6001/netty-server-ui/index.html
# 客户端管理界面
http://127.0.0.1:6004/netty-client-local-ui/index.html</code></pre>
</div>
<div class="step-notes">
<h4>默认登录信息</h4>
<ul>
<li><strong>用户名</strong>: admin</li>
<li><strong>密码</strong>: admin</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Installation -->
<section class="docs-section" id="installation">
<div class="container">
<div class="section-header">
<h2 class="section-title">安装部署</h2>
<p class="section-subtitle">支持多种部署方式满足不同环境需求</p>
</div>
<div class="docs-content">
<div class="deployment-methods">
<div class="method-card">
<div class="method-header">
<el-icon><Box /></el-icon>
<h3>Docker 部署</h3>
</div>
<p>推荐使用 Docker 进行部署简单快速</p>
<div class="method-steps">
<h4>服务端部署</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24
# 启动容器
docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 \
--name wlcn-server \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24</code></pre>
</div>
<h4>客户端部署</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
# 启动容器
docker run -d -it --privileged -p 6004:6004 \
--name wlcn-client \
--restart=always \
-e spring.lazy.netty.client.inet-host=YOUR_SERVER_IP \
-e spring.lazy.netty.client.inet-port=7001 \
-e spring.lazy.netty.client.client-id="your-client-id" \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24</code></pre>
</div>
</div>
</div>
<div class="method-card">
<div class="method-header">
<el-icon><Download /></el-icon>
<h3>源码部署</h3>
</div>
<p>从源码编译部署适合开发环境</p>
<div class="method-steps">
<h4>环境要求</h4>
<ul>
<li>JDK 24 JDK 17+</li>
<li>Maven 3.6+</li>
<li>MySQL 8.0+ (可选)</li>
</ul>
<h4>编译部署</h4>
<div class="code-block">
<pre><code class="language-bash"># 克隆项目
git clone https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network.git
cd wu-lazy-cloud-network
# 编译项目
mvn clean package -DskipTests
# 启动服务端
cd wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start
java -jar target/wu-lazy-cloud-heartbeat-server-start-1.3.1-JDK24.jar
# 启动客户端
cd ../wu-lazy-cloud-heartbeat-client-start
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.1-JDK24.jar</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Configuration -->
<section class="docs-section" id="configuration">
<div class="container">
<div class="section-header">
<h2 class="section-title">配置说明</h2>
<p class="section-subtitle">详细的配置参数说明和使用示例</p>
</div>
<div class="docs-content">
<div class="config-tabs">
<el-tabs v-model="activeConfigTab">
<el-tab-pane label="服务端配置" name="server">
<div class="config-content">
<h3>服务端配置</h3>
<div class="code-block">
<pre><code class="language-yaml">spring:
lazy:
netty:
server:
mode: standalone # 模式standalone/cluster
node-id: default # 节点ID
node-host: 127.0.0.1 # 节点主机
node-port: 7001 # 节点端口
enable-flow-control: true # 启用流量控制
enable-token-verification: false # 启用Token验证
tcp:
port: 7001 # TCP端口
udp:
port: 7001 # UDP端口</code></pre>
</div>
<h4>配置参数说明</h4>
<div class="param-table">
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>mode</td>
<td>运行模式支持 standalone cluster</td>
<td>standalone</td>
</tr>
<tr>
<td>node-id</td>
<td>节点唯一标识</td>
<td>default</td>
</tr>
<tr>
<td>node-host</td>
<td>节点主机地址</td>
<td>127.0.0.1</td>
</tr>
<tr>
<td>node-port</td>
<td>节点监听端口</td>
<td>7001</td>
</tr>
<tr>
<td>enable-flow-control</td>
<td>是否启用流量控制</td>
<td>true</td>
</tr>
</tbody>
</table>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="客户端配置" name="client">
<div class="config-content">
<h3>客户端配置</h3>
<div class="code-block">
<pre><code class="language-yaml">spring:
lazy:
netty:
client:
client-id: your-client-id # 客户端ID
inet-host: 127.0.0.1 # 服务端地址
inet-port: 7001 # 服务端端口
enable: true # 启用客户端连接</code></pre>
</div>
<h4>配置参数说明</h4>
<div class="param-table">
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>client-id</td>
<td>客户端唯一标识</td>
<td>-</td>
</tr>
<tr>
<td>inet-host</td>
<td>服务端 IP 地址</td>
<td>127.0.0.1</td>
</tr>
<tr>
<td>inet-port</td>
<td>服务端 TCP 端口</td>
<td>7001</td>
</tr>
<tr>
<td>enable</td>
<td>是否启用客户端连接</td>
<td>true</td>
</tr>
</tbody>
</table>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="代理配置" name="proxy">
<div class="config-content">
<h3>代理配置</h3>
<div class="code-block">
<pre><code class="language-yaml">spring:
lazy:
netty:
protocol:
proxy:
authentication: true # 启用代理认证
enable-proxy-log: false # 启用代理日志
socket-protocol-proxy:
port: 9001 # SOCKS代理端口
http-protocol-proxy:
port: 8001 # HTTP代理端口</code></pre>
</div>
<h4>配置参数说明</h4>
<div class="param-table">
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>authentication</td>
<td>是否启用代理认证</td>
<td>true</td>
</tr>
<tr>
<td>enable-proxy-log</td>
<td>是否启用代理日志</td>
<td>false</td>
</tr>
<tr>
<td>socket-protocol-proxy.port</td>
<td>SOCKS 代理端口</td>
<td>9001</td>
</tr>
<tr>
<td>http-protocol-proxy.port</td>
<td>HTTP 代理端口</td>
<td>8001</td>
</tr>
</tbody>
</table>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</section>
<!-- Usage Globe -->
<section class="docs-section" id="usage">
<div class="container">
<div class="section-header">
<h2 class="section-title">使用指南</h2>
<p class="section-subtitle">详细的功能使用说明和最佳实践</p>
</div>
<div class="docs-content">
<div class="usage-guides">
<div class="guide-card">
<h3>内网穿透配置</h3>
<div class="guide-content">
<h4>服务端渗透客户端</h4>
<ol>
<li>在服务端管理界面配置端口池</li>
<li>设置访客端口与客户端真实端口的映射关系</li>
<li>例如访客端口 19080 客户端本地端口 18080</li>
<li>通过 <code>http://服务端IP:19080</code> 访问客户端本地 18080 端口</li>
</ol>
<h4>客户端渗透服务端</h4>
<ol>
<li>在客户端管理界面配置本地端口池</li>
<li>设置本地端口到远程端口的映射关系</li>
<li>例如本地端口 13306 远程端口 3306</li>
<li>通过 <code>localhost:13306</code> 访问远程 3306 端口</li>
</ol>
</div>
</div>
<div class="guide-card">
<h3>代理功能使用</h3>
<div class="guide-content">
<h4>HTTP 代理</h4>
<ul>
<li><strong>服务端代理</strong>: 127.0.0.1:8001</li>
<li><strong>客户端代理</strong>: 127.0.0.1:8002</li>
</ul>
<h4>SOCKS 代理</h4>
<ul>
<li><strong>服务端代理</strong>: 127.0.0.1:9001</li>
<li><strong>客户端代理</strong>: 127.0.0.1:9002</li>
</ul>
<h4>代理配置示例</h4>
<div class="code-block">
<pre><code class="language-bash"># 系统代理设置 (Mac)
networksetup -setwebproxy "Wi-Fi" 127.0.0.1 8001
networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 8001
# 使用 curl 测试代理
curl -x http://127.0.0.1:8001 http://httpbin.org/ip</code></pre>
</div>
</div>
</div>
<div class="guide-card">
<h3>流量监控</h3>
<div class="guide-content">
<h4>监控功能</h4>
<ul>
<li>实时监控每个客户端的流量使用情况</li>
<li>按端口统计流量数据</li>
<li>提供流量趋势图表</li>
<li>支持流量告警设置</li>
</ul>
<h4>报表功能</h4>
<ul>
<li>日流量统计报表</li>
<li>客户端流量排行</li>
<li>端口使用情况分析</li>
<li>历史数据查询</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Troubleshooting -->
<section class="docs-section" id="troubleshooting">
<div class="container">
<div class="section-header">
<h2 class="section-title">故障排除</h2>
<p class="section-subtitle">常见问题解决方案和调试技巧</p>
</div>
<div class="docs-content">
<div class="troubleshooting-list">
<div class="trouble-item">
<h3>客户端连接失败</h3>
<div class="trouble-content">
<h4>检查项</h4>
<ul>
<li>服务端 IP 和端口是否正确</li>
<li>网络连接是否正常</li>
<li>防火墙是否阻止连接</li>
</ul>
<h4>解决方案</h4>
<div class="code-block">
<pre><code class="language-bash"># 检查网络连接
ping YOUR_SERVER_IP
# 检查端口是否开放
telnet YOUR_SERVER_IP 7001
# 查看客户端日志
docker logs wlcn-client</code></pre>
</div>
</div>
</div>
<div class="trouble-item">
<h3>内网穿透不工作</h3>
<div class="trouble-content">
<h4>检查项</h4>
<ul>
<li>端口映射配置是否正确</li>
<li>目标服务是否正在运行</li>
<li>防火墙规则是否允许</li>
</ul>
<h4>解决方案</h4>
<ul>
<li>确认映射配置: 访客端口 客户端端口</li>
<li>检查目标服务状态</li>
<li>测试本地端口访问</li>
</ul>
</div>
</div>
<div class="trouble-item">
<h3>代理连接失败</h3>
<div class="trouble-content">
<h4>检查项</h4>
<ul>
<li>代理地址和端口是否正确</li>
<li>代理认证信息是否正确</li>
<li>网络连接是否正常</li>
</ul>
<h4>解决方案</h4>
<ul>
<li>验证代理配置信息</li>
<li>检查认证设置</li>
<li>测试网络连接</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
Timer,
Download,
Setting,
Document,
Tools,
Box
} from '@element-plus/icons-vue'
const activeConfigTab = ref('server')
const scrollToSection = (sectionId) => {
const element = document.getElementById(sectionId)
if (element) {
element.scrollIntoView({ behavior: 'smooth' })
}
}
onMounted(() => {
// 高亮代码块
if (window.Prism) {
window.Prism.highlightAll()
}
})
</script>
<style lang="scss" scoped>
.docs-page {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
}
// Docs Navigation
.docs-navigation {
background: white;
border-bottom: 1px solid var(--border-light);
padding: 20px 0;
position: sticky;
top: 70px;
z-index: 100;
}
.docs-nav {
display: flex;
justify-content: center;
gap: 40px;
flex-wrap: wrap;
.nav-item {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
color: var(--text-regular);
&:hover {
background: var(--background-light);
color: var(--primary-color);
}
.el-icon {
font-size: 18px;
}
span {
font-weight: 500;
}
}
}
// Section Header
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
// Docs Section
.docs-section {
padding: 80px 0;
&:nth-child(even) {
background: var(--background-light);
}
}
.docs-content {
max-width: 1000px;
margin: 0 auto;
}
// Step Globe
.step-guide {
.step {
display: flex;
gap: 20px;
margin-bottom: 40px;
.step-number {
width: 40px;
height: 40px;
background: var(--primary-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.step-content {
flex: 1;
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
p {
color: var(--text-regular);
margin-bottom: 16px;
}
.step-notes {
margin-top: 16px;
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 4px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
}
}
}
}
// Deployment Methods
.deployment-methods {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.method-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
.method-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
.el-icon {
font-size: 24px;
color: var(--primary-color);
}
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
}
p {
color: var(--text-regular);
margin-bottom: 20px;
}
.method-steps {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
ul {
list-style: none;
margin-bottom: 16px;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
}
}
// Config Tabs
.config-tabs {
.config-content {
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 12px;
}
}
}
// Parameter Table
.param-table {
margin-top: 20px;
table {
width: 100%;
border-collapse: collapse;
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-light);
}
th {
background: var(--background-light);
font-weight: 600;
color: var(--text-primary);
}
td {
color: var(--text-regular);
}
}
}
// Usage Globes
.usage-guides {
display: grid;
gap: 30px;
}
.guide-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
}
.guide-content {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 16px 0 8px;
}
ol, ul {
color: var(--text-regular);
margin-bottom: 16px;
li {
margin-bottom: 6px;
line-height: 1.5;
}
}
code {
background: var(--background-light);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
}
}
}
// Troubleshooting
.troubleshooting-list {
display: grid;
gap: 30px;
}
.trouble-item {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
}
.trouble-content {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 16px 0 8px;
}
ul {
list-style: none;
margin-bottom: 16px;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,773 @@
<template>
<div class="download-page">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">下载安装</h1>
<p class="hero-subtitle">
选择适合您的部署方式快速开始使用 Wu-Lazy-Cloud-Network
</p>
</div>
</div>
</section>
<!-- Version Info -->
<section class="version-info">
<div class="container">
<div class="version-card">
<div class="version-header">
<h2>当前版本</h2>
<div class="version-badge">v1.3.1-JDK24</div>
</div>
<div class="version-details">
<div class="detail-item">
<span class="label">发布日期</span>
<span class="value">2024年12月</span>
</div>
<div class="detail-item">
<span class="label">JDK 版本</span>
<span class="value">JDK 24</span>
</div>
<div class="detail-item">
<span class="label">Spring Boot</span>
<span class="value">3.5.0</span>
</div>
<div class="detail-item">
<span class="label">许可证</span>
<span class="value">Apache License 2.0</span>
</div>
</div>
</div>
</div>
</section>
<!-- Download Options -->
<section class="download-options">
<div class="container">
<div class="section-header">
<h2 class="section-title">下载方式</h2>
<p class="section-subtitle">支持多种部署方式满足不同环境需求</p>
</div>
<div class="options-grid">
<div class="option-card">
<div class="option-header">
<div class="option-icon">
<el-icon><Box /></el-icon>
</div>
<h3>Docker 镜像</h3>
<p>推荐使用简单快速部署</p>
</div>
<div class="option-content">
<h4>服务端镜像</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24
# 启动容器
docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 \
--name wlcn-server \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24</code></pre>
</div>
<h4>客户端镜像</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
# 启动容器
docker run -d -it --privileged -p 6004:6004 \
--name wlcn-client \
--restart=always \
-e spring.lazy.netty.client.inet-host=YOUR_SERVER_IP \
-e spring.lazy.netty.client.inet-port=7001 \
-e spring.lazy.netty.client.client-id="your-client-id" \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24</code></pre>
</div>
<div class="option-actions">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="btn btn-primary">
<el-icon><Platform /></el-icon>
查看源码
</a>
</div>
</div>
</div>
<div class="option-card">
<div class="option-header">
<div class="option-icon">
<el-icon><Download /></el-icon>
</div>
<h3>源码编译</h3>
<p>从源码编译适合开发环境</p>
</div>
<div class="option-content">
<h4>环境要求</h4>
<ul>
<li>JDK 24 JDK 17+</li>
<li>Maven 3.6+</li>
<li>MySQL 8.0+ (可选)</li>
<li>Git 2.30+</li>
</ul>
<h4>编译步骤</h4>
<div class="code-block">
<pre><code class="language-bash"># 克隆项目
git clone https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network.git
cd wu-lazy-cloud-network
# 编译项目
mvn clean package -DskipTests
# 启动服务端
cd wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start
java -jar target/wu-lazy-cloud-heartbeat-server-start-1.3.1-JDK24.jar
# 启动客户端
cd ../wu-lazy-cloud-heartbeat-client-start
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.1-JDK24.jar</code></pre>
</div>
<div class="option-actions">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="btn btn-outline">
<el-icon><Platform /></el-icon>
下载源码
</a>
</div>
</div>
</div>
<div class="option-card">
<div class="option-header">
<div class="option-icon">
<el-icon><Setting /></el-icon>
</div>
<h3>预编译包</h3>
<p>直接下载预编译的 JAR </p>
</div>
<div class="option-content">
<h4>下载链接</h4>
<div class="download-links">
<div class="download-link">
<span class="link-label">服务端</span>
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/releases" target="_blank" class="link-url">
wu-lazy-cloud-heartbeat-server-start-1.3.1-JDK24.jar
</a>
</div>
<div class="download-link">
<span class="link-label">客户端</span>
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/releases" target="_blank" class="link-url">
wu-lazy-cloud-heartbeat-client-start-1.3.1-JDK24.jar
</a>
</div>
<div class="download-link">
<span class="link-label">集群版</span>
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/releases" target="_blank" class="link-url">
wu-lazy-cloud-heartbeat-server-cluster-start-1.3.1-JDK24.jar
</a>
</div>
</div>
<h4>启动命令</h4>
<div class="code-block">
<pre><code class="language-bash"># 服务端启动
java -jar wu-lazy-cloud-heartbeat-server-start-1.3.1-JDK24.jar
# 客户端启动
java -jar wu-lazy-cloud-heartbeat-client-start-1.3.1-JDK24.jar</code></pre>
</div>
<div class="option-actions">
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/releases" target="_blank" class="btn btn-outline">
<el-icon><Download /></el-icon>
下载 JAR
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Deployment Globe -->
<section class="deployment-guide">
<div class="container">
<div class="section-header">
<h2 class="section-title">部署指南</h2>
<p class="section-subtitle">详细的部署说明和最佳实践</p>
</div>
<div class="guide-content">
<div class="guide-tabs">
<el-tabs v-model="activeGlobeTab">
<el-tab-pane label="Docker 部署" name="docker">
<div class="guide-section">
<h3>Docker 部署指南</h3>
<h4>1. 环境准备</h4>
<ul>
<li>安装 Docker 20.10+</li>
<li>确保 Docker 服务正在运行</li>
<li>准备公网 IP 地址服务端</li>
</ul>
<h4>2. 启动服务端</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24
# 启动容器
docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 \
--name wlcn-server \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24</code></pre>
</div>
<h4>3. 启动客户端</h4>
<div class="code-block">
<pre><code class="language-bash"># 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24
# 启动容器替换 YOUR_SERVER_IP 为实际服务端 IP
docker run -d -it --privileged -p 6004:6004 \
--name wlcn-client \
--restart=always \
-e spring.lazy.netty.client.inet-host=YOUR_SERVER_IP \
-e spring.lazy.netty.client.inet-port=7001 \
-e spring.lazy.netty.client.client-id="your-client-id" \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24</code></pre>
</div>
<h4>4. 验证部署</h4>
<ul>
<li>访问服务端管理界面: <code>http://服务端IP:6001/netty-server-ui/index.html</code></li>
<li>访问客户端管理界面: <code>http://客户端IP:6004/netty-client-local-ui/index.html</code></li>
<li>默认登录信息: admin/admin</li>
</ul>
</div>
</el-tab-pane>
<el-tab-pane label="源码部署" name="source">
<div class="guide-section">
<h3>源码部署指南</h3>
<h4>1. 环境准备</h4>
<ul>
<li>安装 JDK 24 JDK 17+</li>
<li>安装 Maven 3.6+</li>
<li>安装 Git 2.30+</li>
<li>准备 MySQL 8.0+ (可选)</li>
</ul>
<h4>2. 获取源码</h4>
<div class="code-block">
<pre><code class="language-bash"># 克隆项目
git clone https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network.git
cd wu-lazy-cloud-network
# 切换到稳定版本
git checkout v1.3.1-JDK24</code></pre>
</div>
<h4>3. 编译项目</h4>
<div class="code-block">
<pre><code class="language-bash"># 编译所有模块
mvn clean package -DskipTests
# 或者编译特定模块
mvn clean package -pl wu-lazy-cloud-heartbeat-server-start -am -DskipTests
mvn clean package -pl wu-lazy-cloud-heartbeat-client-start -am -DskipTests</code></pre>
</div>
<h4>4. 启动应用</h4>
<div class="code-block">
<pre><code class="language-bash"># 启动服务端
cd wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start
java -jar target/wu-lazy-cloud-heartbeat-server-start-1.3.1-JDK24.jar
# 启动客户端
cd ../wu-lazy-cloud-heartbeat-client-start
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.1-JDK24.jar</code></pre>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="集群部署" name="cluster">
<div class="guide-section">
<h3>集群部署指南</h3>
<h4>1. 集群架构</h4>
<p>支持多节点集群部署提供高可用性和负载均衡</p>
<h4>2. 节点配置</h4>
<div class="code-block">
<pre><code class="language-yaml">spring:
lazy:
netty:
server:
mode: cluster
node-id: node-1
node-host: 192.168.1.100
node-port: 7001</code></pre>
</div>
<h4>3. 启动集群节点</h4>
<div class="code-block">
<pre><code class="language-bash"># 启动集群服务端
cd wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-cluster-start
java -jar target/wu-lazy-cloud-heartbeat-server-cluster-start-1.3.1-JDK24.jar</code></pre>
</div>
<h4>4. 负载均衡</h4>
<ul>
<li>使用 Nginx 进行负载均衡</li>
<li>配置健康检查和故障转移</li>
<li>支持会话保持</li>
</ul>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</section>
<!-- System Requirements -->
<section class="system-requirements">
<div class="container">
<div class="section-header">
<h2 class="section-title">系统要求</h2>
<p class="section-subtitle">确保您的环境满足运行要求</p>
</div>
<div class="requirements-grid">
<div class="requirement-card">
<div class="requirement-header">
<el-icon><Monitor /></el-icon>
<h3>操作系统</h3>
</div>
<ul>
<li>Linux (Ubuntu 18.04+, CentOS 7+)</li>
<li>Windows 10+</li>
<li>macOS 10.15+</li>
</ul>
</div>
<div class="requirement-card">
<div class="requirement-header">
<el-icon><Cpu /></el-icon>
<h3>Java 环境</h3>
</div>
<ul>
<li>JDK 24 (推荐)</li>
<li>JDK 17+ (最低要求)</li>
<li>JVM 内存: 512MB+</li>
</ul>
</div>
<div class="requirement-card">
<div class="requirement-header">
<el-icon><DataBase /></el-icon>
<h3>数据库</h3>
</div>
<ul>
<li>MySQL 8.0+ (生产环境)</li>
<li>H2 2.1+ (开发环境)</li>
<li>支持自动初始化</li>
</ul>
</div>
<div class="requirement-card">
<div class="requirement-header">
<el-icon><Connection /></el-icon>
<h3>网络要求</h3>
</div>
<ul>
<li>服务端需要公网 IP</li>
<li>端口 6001, 7001, 8001, 9001</li>
<li>客户端端口 6004, 8002, 9002</li>
</ul>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
Box,
Download,
Setting,
Platform,
Monitor,
Cpu,
// DataBase, // 替换 DataBase
Connection,
// Server, // 替换 Server
// Globe // 替换 Globe
} from '@element-plus/icons-vue'
const activeGlobeTab = ref('docker')
onMounted(() => {
// 高亮代码块
if (window.Prism) {
window.Prism.highlightAll()
}
})
</script>
<style lang="scss" scoped>
.download-page {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
}
// Version Info
.version-info {
padding: 60px 0;
background: var(--background-color);
}
.version-card {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 12px;
padding: 40px;
box-shadow: var(--shadow-light);
text-align: center;
.version-header {
margin-bottom: 30px;
h2 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
.version-badge {
display: inline-block;
background: var(--primary-color);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
font-size: 16px;
}
}
.version-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
.detail-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid var(--border-light);
.label {
color: var(--text-regular);
font-weight: 500;
}
.value {
color: var(--text-primary);
font-weight: 600;
}
}
}
}
// Section Header
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
// Download Options
.download-options {
padding: 80px 0;
background: var(--background-light);
}
.options-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.option-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
transition: transform 0.3s ease;
&:hover {
transform: translateY(-4px);
}
.option-header {
text-align: center;
margin-bottom: 30px;
.option-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
.el-icon {
font-size: 28px;
color: white;
}
}
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
p {
color: var(--text-regular);
font-size: 14px;
}
}
.option-content {
h4 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
ul {
list-style: none;
margin-bottom: 20px;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
.download-links {
margin-bottom: 20px;
.download-link {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--border-light);
.link-label {
font-weight: 500;
color: var(--text-primary);
}
.link-url {
color: var(--primary-color);
text-decoration: none;
font-size: 14px;
&:hover {
text-decoration: underline;
}
}
}
}
.option-actions {
text-align: center;
margin-top: 20px;
}
}
}
// Deployment Globe
.deployment-guide {
padding: 80px 0;
background: var(--background-color);
}
.guide-content {
max-width: 1000px;
margin: 0 auto;
}
.guide-section {
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
}
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 12px;
}
ul {
list-style: none;
margin-bottom: 20px;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '•';
position: absolute;
left: 0;
color: var(--primary-color);
}
}
}
code {
background: var(--background-light);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
}
}
// System Requirements
.system-requirements {
padding: 80px 0;
background: var(--background-light);
}
.requirements-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.requirement-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
.requirement-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
.el-icon {
font-size: 24px;
color: var(--primary-color);
}
h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 8px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
</style>

View File

@@ -0,0 +1,641 @@
<template>
<div class="features-page">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">功能特性</h1>
<p class="hero-subtitle">
提供完整的内网穿透和网络代理解决方案支持多种协议和部署模式
</p>
</div>
</div>
</section>
<!-- Features Overview -->
<section class="features-overview">
<div class="container">
<div class="features-grid">
<div class="feature-item" id="penetration">
<div class="feature-header">
<div class="feature-icon">
<el-icon><Connection /></el-icon>
</div>
<h2 class="feature-title">内网穿透</h2>
</div>
<p class="feature-description">
支持多种穿透模式将内网服务安全地暴露到公网实现远程访问和调试
</p>
<div class="feature-details">
<h3>穿透模式</h3>
<div class="mode-grid">
<div class="mode-card">
<h4>服务端渗透客户端</h4>
<p>将内网服务映射到公网实现远程访问</p>
<div class="code-block">
<pre><code class="language-yaml"># 配置示例
visitor_port: 19080
real_host: 192.168.1.100
real_port: 18080</code></pre>
</div>
</div>
<div class="mode-card">
<h4>客户端渗透服务端</h4>
<p>本地端口映射到远程服务端端口</p>
<div class="code-block">
<pre><code class="language-yaml"># 配置示例
local_port: 13306
remote_port: 3306</code></pre>
</div>
</div>
<div class="mode-card">
<h4>客户端渗透客户端</h4>
<p>不同网络间的端口映射实现异地组网</p>
<div class="code-block">
<pre><code class="language-yaml"># 配置示例
client_a_port: 8080
client_b_port: 9090</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="feature-item" id="proxy">
<div class="feature-header">
<div class="feature-icon">
<el-icon><Share /></el-icon>
</div>
<h2 class="feature-title">网络代理</h2>
</div>
<p class="feature-description">
提供 HTTP SOCKS 代理服务支持异地组网和网络加速
</p>
<div class="feature-details">
<h3>代理类型</h3>
<div class="proxy-types">
<div class="proxy-type">
<h4>HTTP 代理</h4>
<ul>
<li>支持 HTTP/HTTPS 协议</li>
<li>可配置代理认证</li>
<li>支持流量监控</li>
<li>端口服务端 8001客户端 8002</li>
</ul>
</div>
<div class="proxy-type">
<h4>SOCKS 代理</h4>
<ul>
<li>支持 SOCKS4/SOCKS5 协议</li>
<li>支持 TCP/UDP 代理</li>
<li>可配置认证机制</li>
<li>端口服务端 9001客户端 9002</li>
</ul>
</div>
</div>
<div class="proxy-config">
<h4>代理配置示例</h4>
<div class="code-block">
<pre><code class="language-yaml">spring:
lazy:
netty:
protocol:
proxy:
authentication: true
enable-proxy-log: false
socket-protocol-proxy:
port: 9001
http-protocol-proxy:
port: 8001</code></pre>
</div>
</div>
</div>
</div>
<div class="feature-item" id="monitor">
<div class="feature-header">
<div class="feature-icon">
<el-icon><DataAnalysis /></el-icon>
</div>
<h2 class="feature-title">流量监控</h2>
</div>
<p class="feature-description">
实时监控网络流量使用情况提供详细的统计报表和分析
</p>
<div class="feature-details">
<h3>监控功能</h3>
<div class="monitor-features">
<div class="monitor-feature">
<h4>实时监控</h4>
<ul>
<li>监控每个客户端的实时流量</li>
<li>按端口统计流量使用情况</li>
<li>提供流量趋势图表</li>
<li>支持流量告警设置</li>
</ul>
</div>
<div class="monitor-feature">
<h4>报表功能</h4>
<ul>
<li>日流量统计报表</li>
<li>客户端流量排行</li>
<li>端口使用情况分析</li>
<li>历史数据查询</li>
</ul>
</div>
</div>
<div class="monitor-stats">
<h4>监控指标</h4>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">10,000+</div>
<div class="stat-label">并发连接</div>
</div>
<div class="stat-card">
<div class="stat-value">&lt; 10ms</div>
<div class="stat-label">平均响应时间</div>
</div>
<div class="stat-card">
<div class="stat-value">99.9%</div>
<div class="stat-label">系统可用性</div>
</div>
</div>
</div>
</div>
</div>
<div class="feature-item" id="route">
<div class="feature-header">
<div class="feature-icon">
<el-icon><Setting /></el-icon>
</div>
<h2 class="feature-title">路由管理</h2>
</div>
<p class="feature-description">
支持虚拟路由和流量转发实现灵活的网络配置
</p>
<div class="feature-details">
<h3>路由功能</h3>
<div class="route-features">
<div class="route-feature">
<h4>虚拟 IP 路由</h4>
<p>创建虚拟 IP 地址将流量代理到目标地址</p>
<div class="code-block">
<pre><code class="language-yaml"># 虚拟路由配置
virtual_ip: 10.0.0.100
target_host: 192.168.1.100
target_port: 8080</code></pre>
</div>
</div>
<div class="route-feature">
<h4>流量转发规则</h4>
<ul>
<li>支持基于端口的转发</li>
<li>支持基于协议的转发</li>
<li>支持负载均衡</li>
<li>支持故障转移</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Security Features -->
<section class="security-features">
<div class="container">
<div class="section-header">
<h2 class="section-title">安全特性</h2>
<p class="section-subtitle">多重安全机制保障数据传输和访问安全</p>
</div>
<div class="security-grid">
<div class="security-card">
<div class="security-icon">
<el-icon><Lock /></el-icon>
</div>
<h3>认证机制</h3>
<ul>
<li>Token 认证</li>
<li>AppKey/AppSecret 认证</li>
<li>代理认证</li>
<li>动态密钥管理</li>
</ul>
</div>
<div class="security-card">
<div class="security-icon">
<el-icon><Key /></el-icon>
</div>
<h3>加密传输</h3>
<ul>
<li>SSL/TLS 加密</li>
<li>流量加密传输</li>
<li>敏感信息加密存储</li>
<li>支持自定义加密算法</li>
</ul>
</div>
<div class="security-card">
<div class="security-icon">
<el-icon><User /></el-icon>
</div>
<h3>访问控制</h3>
<ul>
<li>基于角色的权限控制</li>
<li>细粒度权限管理</li>
<li>操作审计日志</li>
<li>访问白名单</li>
</ul>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
Connection,
Share,
DataAnalysis,
Setting,
Lock,
Key,
User
} from '@element-plus/icons-vue'
onMounted(() => {
// 高亮代码块
if (window.Prism) {
window.Prism.highlightAll()
}
})
</script>
<style lang="scss" scoped>
.features-page {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.hero-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
}
// Features Overview
.features-overview {
padding: 80px 0;
background: var(--background-color);
}
.features-grid {
max-width: 1000px;
margin: 0 auto;
}
.feature-item {
margin-bottom: 80px;
.feature-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
.feature-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
.el-icon {
font-size: 28px;
color: white;
}
}
.feature-title {
font-size: 32px;
font-weight: 700;
color: var(--text-primary);
@media (max-width: 768px) {
font-size: 24px;
}
}
}
.feature-description {
font-size: 18px;
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 30px;
}
}
.feature-details {
h3 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
}
}
// Mode Grid
.mode-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.mode-card {
background: var(--background-light);
border-radius: 8px;
padding: 20px;
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
p {
color: var(--text-regular);
margin-bottom: 12px;
}
}
// Proxy Types
.proxy-types {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-bottom: 30px;
}
.proxy-type {
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
// Monitor Features
.monitor-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-bottom: 30px;
}
.monitor-feature {
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
// Stats Grid
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.stat-card {
text-align: center;
padding: 20px;
background: var(--background-light);
border-radius: 8px;
.stat-value {
font-size: 24px;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 8px;
}
.stat-label {
color: var(--text-regular);
font-size: 14px;
}
}
// Route Features
.route-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.route-feature {
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
p {
color: var(--text-regular);
margin-bottom: 12px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
// Security Features
.security-features {
padding: 80px 0;
background: var(--background-light);
}
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
.security-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.security-card {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: var(--shadow-light);
transition: transform 0.3s ease;
&:hover {
transform: translateY(-4px);
}
.security-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
.el-icon {
font-size: 28px;
color: white;
}
}
h3 {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
ul {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 8px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
</style>

649
website/src/views/Home.vue Normal file
View File

@@ -0,0 +1,649 @@
<template>
<div class="home">
<Header />
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<div class="hero-text">
<h1 class="hero-title">
高性能网络穿透和代理工具
</h1>
<p class="hero-subtitle">
基于 Spring Boot 3.5.0 JDK 24 开发支持 TCPHTTPSOCKS 协议
提供完整的内网穿透网络代理流量监控等功能
</p>
<div class="hero-actions">
<router-link to="/download" class="btn btn-primary btn-large">
<el-icon><Download /></el-icon>
立即下载
</router-link>
<router-link to="/docs" class="btn btn-outline btn-large">
<el-icon><Document /></el-icon>
查看文档
</router-link>
</div>
<div class="hero-stats">
<div class="stat-item">
<div class="stat-number">1.3.1</div>
<div class="stat-label">当前版本</div>
</div>
<div class="stat-item">
<div class="stat-number">JDK 24</div>
<div class="stat-label">技术栈</div>
</div>
<div class="stat-item">
<div class="stat-number">10K+</div>
<div class="stat-label">并发连接</div>
</div>
</div>
</div>
<div class="hero-visual">
<div class="network-diagram">
<div class="client-node">
<el-icon><Monitor /></el-icon>
<span>客户端</span>
</div>
<div class="connection-line"></div>
<div class="server-node">
<el-icon><Server /></el-icon>
<span>服务端</span>
</div>
<div class="connection-line"></div>
<div class="internet-node">
<el-icon><Globe /></el-icon>
<span>公网</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="features">
<div class="container">
<div class="section-header">
<h2 class="section-title">核心功能</h2>
<p class="section-subtitle">提供完整的内网穿透和网络代理解决方案</p>
</div>
<div class="features-grid">
<div class="feature-card card card-hover">
<div class="feature-icon">
<el-icon><Connection /></el-icon>
</div>
<h3 class="feature-title">内网穿透</h3>
<p class="feature-description">
支持多种穿透模式将内网服务安全地暴露到公网实现远程访问和调试
</p>
<ul class="feature-list">
<li>服务端渗透客户端</li>
<li>客户端渗透服务端</li>
<li>客户端渗透客户端</li>
<li>支持 TCP/HTTP 协议</li>
</ul>
</div>
<div class="feature-card card card-hover">
<div class="feature-icon">
<el-icon><Share /></el-icon>
</div>
<h3 class="feature-title">网络代理</h3>
<p class="feature-description">
提供 HTTP SOCKS 代理服务支持异地组网和网络加速
</p>
<ul class="feature-list">
<li>HTTP/HTTPS 代理</li>
<li>SOCKS4/SOCKS5 代理</li>
<li>代理认证支持</li>
<li>流量加密传输</li>
</ul>
</div>
<div class="feature-card card card-hover">
<div class="feature-icon">
<el-icon><DataAnalysis /></el-icon>
</div>
<h3 class="feature-title">流量监控</h3>
<p class="feature-description">
实时监控网络流量使用情况提供详细的统计报表和分析
</p>
<ul class="feature-list">
<li>实时流量统计</li>
<li>客户端流量排行</li>
<li>端口使用分析</li>
<li>流量趋势图表</li>
</ul>
</div>
<div class="feature-card card card-hover">
<div class="feature-icon">
<el-icon><Setting /></el-icon>
</div>
<h3 class="feature-title">路由管理</h3>
<p class="feature-description">
支持虚拟路由和流量转发实现灵活的网络配置
</p>
<ul class="feature-list">
<li>虚拟 IP 路由</li>
<li>流量转发规则</li>
<li>智能路由选择</li>
<li>负载均衡支持</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Technology Section -->
<section class="technology">
<div class="container">
<div class="section-header">
<h2 class="section-title">技术架构</h2>
<p class="section-subtitle">基于现代化的技术栈提供高性能和可靠性</p>
</div>
<div class="tech-grid">
<div class="tech-item">
<div class="tech-icon">
<el-icon><Monitor /></el-icon>
</div>
<h4>Spring Boot 3.5.0</h4>
<p>现代化的应用框架提供快速开发和部署能力</p>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Connection /></el-icon>
</div>
<h4>Netty</h4>
<p>高性能异步网络框架支持高并发连接处理</p>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><DataBase /></el-icon>
</div>
<h4>Lazy ORM</h4>
<p>轻量级 ORM 框架提供类型安全的数据库操作</p>
</div>
<div class="tech-item">
<div class="tech-icon">
<el-icon><Cpu /></el-icon>
</div>
<h4>JDK 24</h4>
<p>最新的 Java 开发工具包提供优秀的性能表现</p>
</div>
</div>
</div>
</section>
<!-- Quick Start Section -->
<section class="quick-start">
<div class="container">
<div class="section-header">
<h2 class="section-title">快速开始</h2>
<p class="section-subtitle">几分钟内完成部署和配置</p>
</div>
<div class="start-steps">
<div class="step-item">
<div class="step-number">1</div>
<div class="step-content">
<h4>启动服务端</h4>
<div class="code-block">
<pre><code class="language-bash">docker run -d -it -p 6001:6001 -p 7001:7001 -p 8001:8001 -p 9001:9001 \
--name wlcn-server \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.1-JDK24</code></pre>
</div>
</div>
</div>
<div class="step-item">
<div class="step-number">2</div>
<div class="step-content">
<h4>启动客户端</h4>
<div class="code-block">
<pre><code class="language-bash">docker run -d -it --privileged -p 6004:6004 \
--name wlcn-client \
--restart=always \
-e spring.lazy.netty.client.inet-host=YOUR_SERVER_IP \
-e spring.lazy.netty.client.inet-port=7001 \
-e spring.lazy.netty.client.client-id="your-client-id" \
registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-client-start:1.3.1-JDK24</code></pre>
</div>
</div>
</div>
<div class="step-item">
<div class="step-number">3</div>
<div class="step-content">
<h4>访问管理界面</h4>
<div class="code-block">
<pre><code class="language-bash"># 服务端管理界面
http://127.0.0.1:6001/netty-server-ui/index.html
# 客户端管理界面
http://127.0.0.1:6004/netty-client-local-ui/index.html</code></pre>
</div>
</div>
</div>
</div>
<div class="start-actions">
<router-link to="/docs" class="btn btn-primary btn-large">
<el-icon><Document /></el-icon>
查看详细文档
</router-link>
<a href="https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network" target="_blank" class="btn btn-outline btn-large">
<el-icon><Platform /></el-icon>
访问 GitHub
</a>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="cta">
<div class="container">
<div class="cta-content">
<h2 class="cta-title">开始使用 Wu-Lazy-Cloud-Network</h2>
<p class="cta-subtitle">
加入数千名开发者体验高性能的网络穿透和代理服务
</p>
<div class="cta-actions">
<router-link to="/download" class="btn btn-primary btn-large">
<el-icon><Download /></el-icon>
立即下载
</router-link>
<router-link to="/docs" class="btn btn-outline btn-large">
<el-icon><Document /></el-icon>
查看文档
</router-link>
</div>
</div>
</div>
</section>
<Footer />
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
import {
Download,
Document,
Connection,
Share,
DataAnalysis,
Setting,
Monitor,
// Server,
// Globe,
// DataBase,
Cpu,
Platform
} from '@element-plus/icons-vue'
onMounted(() => {
// 高亮代码块
if (window.Prism) {
window.Prism.highlightAll()
}
})
</script>
<style lang="scss" scoped>
.home {
min-height: 100vh;
}
// Hero Section
.hero {
padding: 120px 0 80px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
.hero-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
@media (max-width: 768px) {
grid-template-columns: 1fr;
gap: 40px;
}
}
.hero-title {
font-size: 48px;
font-weight: 700;
line-height: 1.2;
margin-bottom: 24px;
@media (max-width: 768px) {
font-size: 32px;
}
}
.hero-subtitle {
font-size: 18px;
line-height: 1.6;
margin-bottom: 32px;
opacity: 0.9;
}
.hero-actions {
display: flex;
gap: 16px;
margin-bottom: 40px;
@media (max-width: 768px) {
flex-direction: column;
}
}
.hero-stats {
display: flex;
gap: 40px;
@media (max-width: 768px) {
gap: 20px;
}
.stat-item {
text-align: center;
.stat-number {
font-size: 24px;
font-weight: 700;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
opacity: 0.8;
}
}
}
.hero-visual {
.network-diagram {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
.client-node,
.server-node,
.internet-node {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
backdrop-filter: blur(10px);
.el-icon {
font-size: 32px;
}
span {
font-size: 14px;
font-weight: 500;
}
}
.connection-line {
width: 40px;
height: 2px;
background: rgba(255, 255, 255, 0.3);
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: -3px;
width: 0;
height: 0;
border-left: 6px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
}
}
}
}
}
// Features Section
.features {
padding: 80px 0;
background: var(--background-color);
}
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: 36px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.section-subtitle {
font-size: 18px;
color: var(--text-regular);
max-width: 600px;
margin: 0 auto;
}
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 30px;
}
.feature-card {
.feature-icon {
width: 60px;
height: 60px;
background: var(--primary-color);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
.el-icon {
font-size: 28px;
color: white;
}
}
.feature-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
.feature-description {
color: var(--text-regular);
line-height: 1.6;
margin-bottom: 16px;
}
.feature-list {
list-style: none;
li {
color: var(--text-regular);
margin-bottom: 6px;
position: relative;
padding-left: 16px;
&::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
}
}
}
// Technology Section
.technology {
padding: 80px 0;
background: var(--background-light);
}
.tech-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
}
.tech-item {
text-align: center;
padding: 30px;
.tech-icon {
width: 80px;
height: 80px;
background: white;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
box-shadow: var(--shadow-light);
.el-icon {
font-size: 36px;
color: var(--primary-color);
}
}
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
p {
color: var(--text-regular);
line-height: 1.6;
}
}
// Quick Start Section
.quick-start {
padding: 80px 0;
background: var(--background-color);
}
.start-steps {
max-width: 800px;
margin: 0 auto 40px;
}
.step-item {
display: flex;
gap: 20px;
margin-bottom: 40px;
.step-number {
width: 40px;
height: 40px;
background: var(--primary-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.step-content {
flex: 1;
h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
}
}
.start-actions {
text-align: center;
display: flex;
gap: 16px;
justify-content: center;
@media (max-width: 768px) {
flex-direction: column;
align-items: center;
}
}
// CTA Section
.cta {
padding: 80px 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
.cta-title {
font-size: 36px;
font-weight: 700;
margin-bottom: 16px;
@media (max-width: 768px) {
font-size: 28px;
}
}
.cta-subtitle {
font-size: 18px;
opacity: 0.9;
margin-bottom: 32px;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
@media (max-width: 768px) {
flex-direction: column;
align-items: center;
}
}
}
</style>

30
website/vite.config.js Normal file
View File

@@ -0,0 +1,30 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import viteCompression from 'vite-plugin-compression'
export default defineConfig({
plugins: [vue(), viteCompression()],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
server: {
port: 3000,
open: true
},
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
elementPlus: ['element-plus', '@element-plus/icons-vue']
}
}
}
}
})

View File

@@ -5,13 +5,33 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wu-lazy-cloud-heartbeat-client</artifactId>
<description>云下心跳客户端</description>
<name>wlcn客户端模块</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -22,11 +42,12 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
<version>${wu-lazy-cloud-heartbeat-common.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-protocol-proxy</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>${wu-lazy-cloud-heartbeat-protocol-proxy.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
@@ -38,14 +59,17 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.3.232</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web-spring-starter</artifactId>
<version>${wu-framework-web-spring-starter.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-orm-plus-starter</artifactId>
<version>${wu-database-lazy-orm-plus-starter.version}</version>
</dependency>
</dependencies>

View File

@@ -7,6 +7,8 @@ import org.framework.lazy.cloud.network.heartbeat.client.config.NettyClientPrope
import org.framework.lazy.cloud.network.heartbeat.client.config.PropertiesType;
import org.framework.lazy.cloud.network.heartbeat.client.infrastructure.entity.LazyNettyServerPropertiesDO;
import org.framework.lazy.cloud.network.heartbeat.common.enums.ProtocolType;
import org.framework.lazy.cloud.network.heartbeat.protocol.token.AuthenticationToken;
import org.framework.lazy.cloud.network.heartbeat.protocol.token.AuthenticationTokenContext;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
@@ -51,6 +53,7 @@ public class NettyClientSocketApplicationListener implements ApplicationListener
log.warn("配置信息为空,请通过页面添加配置信息:{}", nettyClientProperties);
return;
}
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = new LazyNettyServerPropertiesDO();
lazyNettyServerPropertiesDO.setClientId(clientId);
@@ -73,6 +76,12 @@ public class NettyClientSocketApplicationListener implements ApplicationListener
if (!exists) {
lazyLambdaStream.insert(lazyNettyServerPropertiesDO);
}
// 添加本地token
AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setUsedByClientId(clientId);
authenticationToken.setAppKey(appKey);
authenticationToken.setAppSecret(appSecret);
AuthenticationTokenContext.setAuthenticationToken(clientId, appKey, appSecret);
}catch (Exception e){
e.printStackTrace();
}

View File

@@ -65,7 +65,7 @@ public class LazyNettyServerPropertiesDO {
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
@LazyTableFieldUnique(name="inet_host",comment="服务端host",columnType="varchar(25)")
@LazyTableFieldUnique(name="inet_host",comment="服务端host",columnType="varchar(255)")
private String inetHost;
/**

View File

@@ -6,11 +6,32 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
<description>wlcn项目通用模块</description>
<name>wlcn项目通用模块</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -20,19 +41,22 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.122.Final</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
<version>${wu-framework-web.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>${spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-queue</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@@ -11,6 +11,7 @@ import org.framework.lazy.cloud.network.heartbeat.common.NettyByteBuf;
public class ChannelAttributeKeyUtils {
private static final AttributeKey<String> VISITOR_ID = AttributeKey.newInstance("visitorId");
private static final AttributeKey<String> REQUEST_ID = AttributeKey.newInstance("request_id");
private static final AttributeKey<Integer> VISITOR_PORT = AttributeKey.newInstance("visitorPort");
private static final AttributeKey<String> CLIENT_ID = AttributeKey.newInstance("clientId");
private static final AttributeKey<String> APP_KEY = AttributeKey.newInstance("appKey");
@@ -58,6 +59,26 @@ public class ChannelAttributeKeyUtils {
}
/**
* 为通道绑定 访客Request属性
*
* @param channel 通道
* @param requestId requestId
*/
public static void buildRequestId(Channel channel, String requestId) {
channel.attr(REQUEST_ID).set(requestId);
}
/**
* 获取 通道中Request ID
*
* @param channel 通道
*/
public static String getRequestId(Channel channel) {
return channel.attr(REQUEST_ID).get();
}
/**
* 为通道绑定 访客属性
*
@@ -316,7 +337,7 @@ public class ChannelAttributeKeyUtils {
* 为通道绑定 目标端口
*
* @param channel 通道
* @param targetIp 目标端口
* @param targetPort 目标端口
*/
public static void buildTargetPort(Channel channel, Integer targetPort) {
channel.attr(TARGET_PORT).set(targetPort);

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -13,6 +13,26 @@
<artifactId>wu-lazy-cloud-heartbeat-dns</artifactId>
<description>云上心跳服务dns</description>
<name>wlcn项目DNS</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -22,11 +42,13 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
<version>${wu-framework-web.version}</version>
</dependency>
<!-- 通用心跳包 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
<version>${wu-lazy-cloud-heartbeat-common.version}</version>
</dependency>
<!-- 数据库 -->
<dependency>
@@ -38,6 +60,7 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-orm-plus-starter</artifactId>
<version>${wu-database-lazy-orm-plus-starter.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
@@ -52,6 +75,7 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-lazy-orm-spring-starter</artifactId>
<version>${wu-framework-lazy-orm-spring-starter.version}</version>
</dependency>
</dependencies>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -13,6 +13,27 @@
<artifactId>wu-lazy-cloud-heartbeat-protocol-proxy</artifactId>
<description>云上心跳服务协议代理</description>
<name>wlcn项目代理组件</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -22,11 +43,13 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
<version>${wu-framework-web.version}</version>
</dependency>
<!-- 通用心跳包 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
<version>${wu-lazy-cloud-heartbeat-common.version}</version>
</dependency>
<!-- 数据库 -->
<dependency>
@@ -38,6 +61,7 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-orm-plus-starter</artifactId>
<version>${wu-database-lazy-orm-plus-starter.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
@@ -52,6 +76,12 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-lazy-orm-spring-starter</artifactId>
<version>${wu-framework-lazy-orm-spring-starter.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-queue</artifactId>
<version>1.3.4-JDK24-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@@ -13,7 +13,10 @@ import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyC
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettySocketChannelContext;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.proxy.socks.AbstractHandleSocketLocalProxyTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.decoder.TransferDecoder;
import org.framework.lazy.cloud.network.heartbeat.common.encoder.TransferEncoder;
import org.framework.lazy.cloud.network.heartbeat.common.factory.EventLoopGroupFactory;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettyProxy2RealInboundHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettySocketBackendHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettySocks5CommandRequestHandler;
@@ -34,11 +37,11 @@ public class NettySocketProtocolHandleSocketLocalProxyTypeAdvanced
@Override
protected void doHandler(NettyChannelContext nettyChannelContext, NettyProxyMsg nettyProxyMsg) {
NettySocketChannelContext nettySocketChannelContext = (NettySocketChannelContext) nettyChannelContext;
Channel channel = nettySocketChannelContext.channel();
ChannelHandlerContext channelHandlerContext = nettySocketChannelContext.channelHandlerContext();
Channel proxyChannel = nettySocketChannelContext.channel();
EventLoopGroup group = EventLoopGroupFactory.createClientWorkGroup();
String host = nettyProxyMsg.targetIp();
Integer port = Integer.parseInt(nettyProxyMsg.targetPort());
String visitorId = nettyProxyMsg.visitorId();
Bootstrap b = new Bootstrap();
Socks5AddressType socks5AddressType = nettySocketChannelContext.getSocks5AddressType();
@@ -48,25 +51,38 @@ public class NettySocketProtocolHandleSocketLocalProxyTypeAdvanced
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettySocketBackendHandler(channelHandlerContext));
ch.pipeline().addLast(new TransferDecoder(Integer.MAX_VALUE, 1024 * 1024 * 10));
ch.pipeline().addLast(new TransferEncoder());
ch.pipeline().addLast(new NettySocketBackendHandler());
}
});
log.info("准备连接目标服务器ip={},port={}", host, port);
ChannelFuture f = b.connect(new InetSocketAddress(host, port));
f.addListener((ChannelFutureListener) future -> {
Channel realChannel = future.channel();
// 绑定real 通道对应的 目标host、port
ChannelAttributeKeyUtils.buildTargetIp(realChannel,host);
ChannelAttributeKeyUtils.buildTargetPort(realChannel,port);
if (future.isSuccess()) {
log.info("目标服务器连接成功");
// 绑定next通道
ChannelAttributeKeyUtils.buildNextChannel(realChannel, proxyChannel);
ChannelAttributeKeyUtils.buildNextChannel(proxyChannel, realChannel);
ChannelAttributeKeyUtils.buildVisitorId(realChannel, visitorId);
//添加客户端转发请求到服务端的Handler
channel.pipeline().addLast(new NettyProxy2RealInboundHandler(future));
// 解码、编码
proxyChannel.pipeline().addLast(new TransferDecoder(Integer.MAX_VALUE, 1024 * 1024 * 10));
proxyChannel.pipeline().addLast(new TransferEncoder());
proxyChannel.pipeline().addLast(new NettyProxy2RealInboundHandler());
DefaultSocks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, socks5AddressType);
channel.writeAndFlush(commandResponse);
channel.pipeline().remove(NettySocks5CommandRequestHandler.class);
channel.pipeline().remove(Socks5CommandRequestDecoder.class);
proxyChannel.writeAndFlush(commandResponse);
proxyChannel.pipeline().remove(NettySocks5CommandRequestHandler.class);
proxyChannel.pipeline().remove(Socks5CommandRequestDecoder.class);
} else {
log.error("连接目标服务器失败,address={},port={}", host, port);
DefaultSocks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, socks5AddressType);
channel.writeAndFlush(commandResponse);
future.channel().close();
proxyChannel.writeAndFlush(commandResponse);
realChannel.close();
}
});

View File

@@ -0,0 +1,19 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.auth;
public abstract class AbstractNettyPasswordAuth implements NettyPasswordAuth {
public abstract boolean doVerify(String username, String password);
/**
* 验证账号密码
*
* @param username 用户
* @param password 密码
* @return
*/
@Override
public boolean verify(String username, String password) {
return doVerify(username, password);
}
}

View File

@@ -0,0 +1,24 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.auth;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.framework.lazy.cloud.network.heartbeat.protocol.token.AuthenticationTokenContext;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class DefaultNettyPasswordAuth extends AbstractNettyPasswordAuth {
@Override
public boolean doVerify(String username, String password) {
boolean verify = AuthenticationTokenContext.verify(username, password);
if (!verify) {
log.error("授权失败");
}
return verify;
}
}

View File

@@ -0,0 +1,13 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.auth;
public interface NettyPasswordAuth {
/**
* 验证账号密码
*
* @param username 用户
* @param password 密码
* @return
*/
boolean verify(String username, String password);
}

View File

@@ -1,17 +1,15 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.filter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder;
import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder;
import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder;
import io.netty.handler.codec.socksx.SocksPortUnificationServerHandler;
import io.netty.handler.codec.socksx.v5.*;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.factory.EventLoopGroupFactory;
import org.framework.lazy.cloud.network.heartbeat.common.filter.DebugChannelInitializer;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettySocks5CommandRequestHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.NettySocks5InitialRequestHandler;
import org.framework.lazy.cloud.network.heartbeat.protocol.auth.NettyPasswordAuth;
import org.framework.lazy.cloud.network.heartbeat.protocol.handler.*;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -25,9 +23,13 @@ import java.util.List;
@Component
public class NettyTcpProxyFilter extends DebugChannelInitializer<SocketChannel> {
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
private final NettyPasswordAuth nettyPasswordAuth;
private final ProtocolProxyProperties protocolProxyProperties;
public NettyTcpProxyFilter(List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
public NettyTcpProxyFilter(List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList, NettyPasswordAuth nettyPasswordAuth, ProtocolProxyProperties protocolProxyProperties) {
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
this.nettyPasswordAuth = nettyPasswordAuth;
this.protocolProxyProperties = protocolProxyProperties;
}
@Override
@@ -35,20 +37,21 @@ public class NettyTcpProxyFilter extends DebugChannelInitializer<SocketChannel>
ChannelPipeline pipeline = ch.pipeline();
// 类型处理器适配器
ChannelTypeAdapter channelTypeAdapter = new ChannelTypeAdapter(handleChannelTypeAdvancedList);
//
// //添加 SOCKS 协议统一处理器
// pipeline.addLast(new SocksPortUnificationServerHandler());
//socks5响应最后一个encode
pipeline.addLast(Socks5ServerEncoder.DEFAULT);
// 初始化连接
pipeline.addLast(new Socks5InitialRequestDecoder());
pipeline.addLast(new NettySocks5InitialRequestHandler());
pipeline.addLast(new NettySocks5InitialRequestHandler(protocolProxyProperties, nettyPasswordAuth));
// 认证
// ch.pipeline().addLast(new Socks5PasswordAuthRequestDecoder());
// ch.pipeline().addLast(new Socks5PasswordAuthRequestInboundHandler());
EventLoopGroup clientWorkGroup = EventLoopGroupFactory.createClientWorkGroup();
if (protocolProxyProperties.getAuthentication()) {
// 认证
pipeline.addLast(new Socks5PasswordAuthRequestDecoder());
pipeline.addLast(new NettySocks5PasswordAuthRequestInboundHandler(nettyPasswordAuth));
}
// 连接请求
pipeline.addLast(new Socks5CommandRequestDecoder());

View File

@@ -1,23 +1,31 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.NettyByteBuf;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.framework.lazy.cloud.network.heartbeat.protocol.log.ProxyLog;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.framework.wu.framework.queue.Message;
import org.framework.wu.framework.queue.MessageQueue;
import org.framework.wu.framework.queue.MessageQueueFactory;
import org.wu.framework.spring.utils.SpringContextHolder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* 代理,真实通道发送数据
*/
@Slf4j
public class NettyProxy2RealInboundHandler extends ChannelInboundHandlerAdapter {
public class NettyProxy2RealInboundHandler extends SimpleChannelInboundHandler<NettyByteBuf> {
private final ChannelFuture dstChannelFuture;
public NettyProxy2RealInboundHandler(ChannelFuture dstChannelFuture) {
this.dstChannelFuture = dstChannelFuture;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
@@ -25,20 +33,63 @@ public class NettyProxy2RealInboundHandler extends ChannelInboundHandlerAdapter
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
public void channelRead0(ChannelHandlerContext ctx, NettyByteBuf nettyByteBuf) throws Exception {
log.debug("本地转发客户端的请求到代理服务器");
if (dstChannelFuture.channel().isActive()) {
dstChannelFuture.channel().writeAndFlush(msg);
Channel currentChannel = ctx.channel();
byte[] data = nettyByteBuf.getData();
// 把数据转到真实服务
ByteBuf buf = currentChannel.config().getAllocator().buffer(data.length);
buf.writeBytes(data);
log.info("发送请求到真实客户端:{}", new String(data, StandardCharsets.UTF_8));
Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(currentChannel);
String targetIp = ChannelAttributeKeyUtils.getTargetIp(nextChannel);
Integer targetPort = ChannelAttributeKeyUtils.getTargetPort(nextChannel);
// 设置请求ID
String requestId = UUID.randomUUID().toString();
ChannelAttributeKeyUtils.buildRequestId(currentChannel, requestId);
ChannelAttributeKeyUtils.buildRequestId(nextChannel, requestId);
if (nextChannel.isActive()) {
ProtocolProxyProperties protocolProxyProperties = SpringContextHolder.getBean(ProtocolProxyProperties.class);
if (protocolProxyProperties.getEnableProxyLog()) {
log.debug("记录代理发送日志开始");
try {
String visitorId = ChannelAttributeKeyUtils.getVisitorId(nextChannel);
String sendMsgQueue = protocolProxyProperties.getSendMsgQueue();
MessageQueue queue = MessageQueueFactory.getQueue(sendMsgQueue);
Message message = new Message();
message.setTopic(sendMsgQueue);
message.setId(visitorId);
ProxyLog proxyLog = new ProxyLog();
proxyLog.setVisitorId(visitorId);
proxyLog.setHost(targetIp);
proxyLog.setPort(targetPort);
proxyLog.setSend(data);
proxyLog.setRequestId(requestId);
message.setBody(proxyLog);
queue.send(message);
} catch (Exception e) {
log.error(e.getMessage());
}
log.debug("记录代理发送日志结束");
}
nextChannel.writeAndFlush(buf);
} else {
log.info("释放内存");
ReferenceCountUtil.release(msg);
ReferenceCountUtil.release(buf);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.debug("客户端与代理服务器的连接已经断开,即将断开代理服务器和目标服务器的连接");
if (dstChannelFuture.channel().isActive()) {
Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(ctx.channel());
if (nextChannel.isActive()) {
if (ctx.channel().isActive()) {
ctx.channel().writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}

View File

@@ -1,21 +1,27 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.NettyByteBuf;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.framework.lazy.cloud.network.heartbeat.protocol.log.ProxyLog;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.framework.wu.framework.queue.Message;
import org.framework.wu.framework.queue.MessageQueue;
import org.framework.wu.framework.queue.MessageQueueFactory;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.spring.utils.SpringContextHolder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@Slf4j
public class NettySocketBackendHandler extends ChannelInboundHandlerAdapter {
public class NettySocketBackendHandler extends SimpleChannelInboundHandler<NettyByteBuf> {
private final ChannelHandlerContext clientChannelHandlerContext;
public NettySocketBackendHandler(ChannelHandlerContext clientChannelHandlerContext) {
this.clientChannelHandlerContext = clientChannelHandlerContext;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
@@ -23,21 +29,59 @@ public class NettySocketBackendHandler extends ChannelInboundHandlerAdapter {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
public void channelRead0(ChannelHandlerContext ctx, NettyByteBuf nettyByteBuf) throws Exception {
log.trace("开始写回客户端数据");
if (clientChannelHandlerContext.channel().isActive()) {
clientChannelHandlerContext.writeAndFlush(msg);
Channel currentChannel = ctx.channel();
byte[] data = nettyByteBuf.getData();
// 把数据转到真实服务
ByteBuf buf = currentChannel.config().getAllocator().buffer(data.length);
buf.writeBytes(data);
log.info("将数据返回给客户端:{}", new String(data, StandardCharsets.UTF_8));
Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(currentChannel);
String targetIp = ChannelAttributeKeyUtils.getTargetIp(nextChannel);
Integer targetPort = ChannelAttributeKeyUtils.getTargetPort(nextChannel);
String requestId = ChannelAttributeKeyUtils.getRequestId(currentChannel);
if (nextChannel.isActive()) {
ProtocolProxyProperties protocolProxyProperties = SpringContextHolder.getBean(ProtocolProxyProperties.class);
if (protocolProxyProperties.getEnableProxyLog()) {
log.debug("记录代理返回日志开始");
try {
String visitorId = ChannelAttributeKeyUtils.getVisitorId(nextChannel);
String receiverMsgQueue = protocolProxyProperties.getReceiverMsgQueue();
MessageQueue queue = MessageQueueFactory.getQueue(receiverMsgQueue);
Message message = new Message();
message.setTopic(receiverMsgQueue);
message.setId(visitorId);
ProxyLog proxyLog = new ProxyLog();
proxyLog.setVisitorId(visitorId);
proxyLog.setHost(targetIp);
proxyLog.setPort(targetPort);
proxyLog.setReceiver(data);
proxyLog.setRequestId(requestId);
message.setBody(proxyLog);
queue.send(message);
} catch (Exception e) {
log.error(e.getMessage());
}
log.debug("记录代理返回日志结束");
}
nextChannel.writeAndFlush(buf);
} else {
log.info("释放内存");
ReferenceCountUtil.release(msg);
ReferenceCountUtil.release(buf);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.trace("代理服务器和目标服务器的连接已经断开,即将断开客户端和代理服务器的连接");
if (clientChannelHandlerContext.channel().isActive()) {
clientChannelHandlerContext.channel().writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(ctx.channel());
if (nextChannel.isActive()) {
nextChannel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}

View File

@@ -56,6 +56,9 @@ public class NettySocks5CommandRequestHandler extends SimpleChannelInboundHandle
int originPort = request.dstPort();
Socks5AddressType socks5AddressType = request.dstAddrType();
String visitorId = ChannelAttributeKeyUtils.getVisitorId(ctx.channel());
ChannelAttributeKeyUtils.buildTargetIp(ctx.channel(),originHost);
ChannelAttributeKeyUtils.buildTargetPort(ctx.channel(),originPort);
NettyProxyMsg proxyMsg = new NettyProxyMsg();
proxyMsg.setVisitorId(visitorId);

View File

@@ -4,9 +4,20 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socksx.v5.*;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.protocol.auth.NettyPasswordAuth;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
@Slf4j
public class NettySocks5InitialRequestHandler extends SimpleChannelInboundHandler<Socks5InitialRequest> {
private final ProtocolProxyProperties protocolProxyProperties;
private final NettyPasswordAuth nettyPasswordAuth;
public NettySocks5InitialRequestHandler(ProtocolProxyProperties protocolProxyProperties, NettyPasswordAuth nettyPasswordAuth) {
this.protocolProxyProperties = protocolProxyProperties;
this.nettyPasswordAuth = nettyPasswordAuth;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Socks5InitialRequest msg) throws Exception {
boolean failure = msg.decoderResult().isFailure();
@@ -18,9 +29,18 @@ public class NettySocks5InitialRequestHandler extends SimpleChannelInboundHandle
}
log.info("初始化socket连接");
// 不验证账号密码
Socks5InitialResponse socks5InitialResponse = new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH);
ctx.writeAndFlush(socks5InitialResponse);
if(protocolProxyProperties.getAuthentication()){
// 验证账号密码
Socks5InitialResponse socks5InitialResponse =
new DefaultSocks5InitialResponse(Socks5AuthMethod.PASSWORD);
ctx.writeAndFlush(socks5InitialResponse);
}else {
// 不验证账号密码
Socks5InitialResponse socks5InitialResponse =
new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH);
ctx.writeAndFlush(socks5InitialResponse);
}
ctx.pipeline().remove(this);
ctx.pipeline().remove(Socks5InitialRequestDecoder.class);

View File

@@ -0,0 +1,50 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.handler;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socksx.v5.*;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.protocol.auth.NettyPasswordAuth;
@Slf4j
public class NettySocks5PasswordAuthRequestInboundHandler extends SimpleChannelInboundHandler<DefaultSocks5PasswordAuthRequest> {
private final NettyPasswordAuth nettyPasswordAuth;
public NettySocks5PasswordAuthRequestInboundHandler(NettyPasswordAuth nettyPasswordAuth) {
this.nettyPasswordAuth = nettyPasswordAuth;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5PasswordAuthRequest msg) throws Exception {
//认证成功
String username = msg.username();
String password = msg.password();
if (nettyPasswordAuth.verify(username, password)) {
log.debug("login with username:{} password:{}", username, password);
log.debug("认证直接成功");
Socks5PasswordAuthResponse passwordAuthResponse = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS);
ctx.writeAndFlush(passwordAuthResponse);
ctx.pipeline().remove(this);
ctx.pipeline().remove(Socks5PasswordAuthRequestDecoder.class);
} else {
log.error("授权失败: with username:{} password:{}", username, password);
// 认证失败
Socks5PasswordAuthResponse passwordAuthResponse = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.FAILURE);
//发送鉴权失败消息完成后关闭channel
ctx.writeAndFlush(passwordAuthResponse).addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("处理 SOCKS5 密码认证时发生异常: " + cause.getMessage());
ctx.close();
}
}

View File

@@ -0,0 +1,16 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.log;
import lombok.Data;
@Data
public class ProxyLog {
private String requestId;
private String host;;
private Integer port;
private String visitorId;
private byte[] receiver;
private byte[] send;
}

View File

@@ -0,0 +1,72 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.log;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.flow.permeate.ChannelFlow;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.framework.wu.framework.queue.Message;
import org.framework.wu.framework.queue.MessageQueue;
import org.framework.wu.framework.queue.MessageQueueFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class ProxyReceiverLog implements CommandLineRunner {
private final ProtocolProxyProperties protocolProxyProperties;
private final LazyLambdaStream lazyLambdaStream;
ThreadPoolExecutor CHANNEL_LOG_EXECUTOR =
new ThreadPoolExecutor(10, 20, 3L, TimeUnit.MINUTES,
new LinkedBlockingDeque<>(50), new ThreadPoolExecutor.AbortPolicy());
public ProxyReceiverLog(ProtocolProxyProperties protocolProxyProperties, LazyLambdaStream lazyLambdaStream) {
this.protocolProxyProperties = protocolProxyProperties;
this.lazyLambdaStream = lazyLambdaStream;
}
@Override
public void run(String... args) throws Exception {
String receiverMsgQueue = protocolProxyProperties.getReceiverMsgQueue();
MessageQueue receiverQueue = MessageQueueFactory.getQueue(receiverMsgQueue);
// 创建监听线程
Thread thread = new Thread(() -> {
while (true) {
Message receive = receiverQueue.receive();
if (ObjectUtils.isNotEmpty(receive)) {
ProxyLog proxyLog = (ProxyLog) receive.getBody();
lazyLambdaStream.upsert(proxyLog);
}
}
});
CHANNEL_LOG_EXECUTOR.submit(thread::start);
String sendMsgQueue = protocolProxyProperties.getSendMsgQueue();
MessageQueue sendQueue = MessageQueueFactory.getQueue(sendMsgQueue);
// 创建监听线程
Thread sendThread = new Thread(() -> {
while (true) {
Message receive = sendQueue.receive();
if (ObjectUtils.isNotEmpty(receive)) {
ProxyLog proxyLog = (ProxyLog) receive.getBody();
lazyLambdaStream.upsert(proxyLog);
}
}
});
CHANNEL_LOG_EXECUTOR.submit(sendThread::start);
}
}

View File

@@ -1,5 +1,6 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.properties;
import jdk.jfr.Description;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -13,6 +14,29 @@ import org.springframework.context.annotation.Configuration;
public class ProtocolProxyProperties {
public static final String prefix = "spring.lazy.netty.protocol.proxy";
/**
* 是否验证权限账号
*/
private Boolean authentication = false;
/**
* 是否允许记录代理日志
*/
@Description("是否允许记录代理日志")
private Boolean enableProxyLog = true;
/**
* 发送数据对应通道
*/
@Description("发送数据对应通道")
private String sendMsgQueue="wlcn-send-queue";
/**
* 接收数据对应通道
*/
@Description("接收数据对应通道")
private String receiverMsgQueue="wlcn-receiver-queue";
/**
* http协议代理

View File

@@ -0,0 +1,39 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.token;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 授权信息
*/
@Data
public class AuthenticationToken {
/**
* 令牌key
*/
@Schema(description = "令牌key", name = "appKey", example = "")
private String appKey;
/**
* 令牌密钥
*/
@Schema(description = "令牌密钥", name = "appSecret", example = "")
private String appSecret;
/**
* 过期时间
*/
@Schema(description = "过期时间", name = "expireInTime", example = "")
private LocalDateTime expireInTime;
/**
* 被使用的客户端ID
*/
@Schema(description = "被使用的客户端ID", name = "usedByClientId", example = "")
private String usedByClientId;
}

View File

@@ -0,0 +1,83 @@
package org.framework.lazy.cloud.network.heartbeat.protocol.token;
import org.wu.framework.core.utils.ObjectUtils;
import java.util.concurrent.ConcurrentHashMap;
public class AuthenticationTokenContext {
// key appid value AuthenticationToken
private static final ConcurrentHashMap<String, AuthenticationToken> m = new ConcurrentHashMap<>();
/**
* 设置授权信息
*
* @param clientId 客户端ID
* @param appKey 令牌
* @param appSecret 迷药
*/
public static void setAuthenticationToken(String clientId, String appKey, String appSecret) {
AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setUsedByClientId(clientId);
authenticationToken.setAppKey(appKey);
authenticationToken.setAppSecret(appSecret);
AuthenticationTokenContext.setAuthenticationToken(authenticationToken);
}
/**
* 设置授权信息
*
* @param authenticationToken 授权信息
*/
public static void setAuthenticationToken(AuthenticationToken authenticationToken) {
String key = authenticationToken.getAppKey();
if (m.containsKey(key)) {
m.put(key, authenticationToken);
return;
}
m.put(key, authenticationToken);
}
/**
* 获取授权信息
*
* @param appKey 令牌key
*/
public static AuthenticationToken getAuthenticationToken(String appKey) {
AuthenticationToken p = m.values()
.stream()
.filter(authenticationToken -> authenticationToken.getAppKey().equals(appKey))
.findFirst()
.orElse(null);
return p;
}
/**
* 删除授权信息
*
* @param appKey 令牌key
*/
public static void removeAuthenticationToken(String appKey) {
m.remove(appKey);
}
/**
* 验证令牌
*
* @param appKey 令牌key
*/
public static Boolean verify(String appKey, String appSecret) {
AuthenticationToken p = m.values()
.stream()
.filter(authenticationToken -> authenticationToken.getAppKey().equals(appKey)
&& authenticationToken.getAppSecret().equals(appSecret)
)
.findFirst()
.orElse(null);
return ObjectUtils.isNotEmpty(p);
}
}

View File

@@ -5,13 +5,34 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wu-lazy-cloud-heartbeat-server-cluster</artifactId>
<description>云上心跳服务端集群</description>
<name>wlcn项目集群方式</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -22,11 +43,13 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-server</artifactId>
<version>${wu-lazy-cloud-heartbeat-server.version}</version>
</dependency>
<!-- 客户端 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-client</artifactId>
<version>${wu-lazy-cloud-heartbeat-client.version}</version>
</dependency>
</dependencies>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-network</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>1.3.4-JDK24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -13,6 +13,27 @@
<artifactId>wu-lazy-cloud-heartbeat-server</artifactId>
<description>云上心跳服务端</description>
<name>wlcn项目服务端组件</name>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
<!-->开发者的信息<-->
<developers>
<developer>
<name>Jia Wei Wu</name>
<email>1207537021@qq.com</email>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-->项目的版本管理地址<-->
<scm>
<url>https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network</url>
</scm>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
@@ -22,16 +43,18 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
<version>${wu-framework-web.version}</version>
</dependency>
<!-- 通用心跳包 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-common</artifactId>
<version>${wu-lazy-cloud-heartbeat-common.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-protocol-proxy</artifactId>
<version>1.3.1-JDK24-SNAPSHOT</version>
<version>${wu-lazy-cloud-heartbeat-protocol-proxy.version}</version>
</dependency>
<!-- 数据库 -->
<dependency>
@@ -43,16 +66,19 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-orm-plus-starter</artifactId>
<version>${wu-database-lazy-orm-plus-starter.version}</version>
</dependency>
<!-- 授权平台 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-authorization-server-platform-starter</artifactId>
<version>${wu-authorization-server-platform-starter.version}</version>
</dependency>
<!-- jvm -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-jvm-server-platform-starter</artifactId>
<version>${wu-jvm-server-platform-starter.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
@@ -67,15 +93,18 @@
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-lazy-orm-spring-starter</artifactId>
<version>${wu-framework-lazy-orm-spring-starter.version}</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-log-server-platform-starter</artifactId>
<version>${wu-log-server-platform-starter.version}</version>
</dependency>
<!-- 内网穿透客户端 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-lazy-cloud-heartbeat-client</artifactId>
<version>${wu-lazy-cloud-heartbeat-client.version}</version>
</dependency>
</dependencies>

View File

@@ -1,14 +1,10 @@
package org.framework.lazy.cloud.network.heartbeat.server.cluster.application.assembler;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.*;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.dto.LazyNettyClusterNodeDTO;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.domain.model.cluster.node.LazyNettyClusterNode;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.LazyNettyClusterNodeRemoveCommand;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.LazyNettyClusterNodeStoryCommand;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.LazyNettyClusterNodeUpdateCommand;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.LazyNettyClusterNodeQueryListCommand;
import org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node.LazyNettyClusterNodeQueryOneCommand;
import org.wu.framework.core.mapper.LazyStructMappers;
import org.wu.framework.core.mapper.LazyStructMapper;
import org.wu.framework.core.mapper.LazyStructMappers;
/**
* describe 集群配置信息

View File

@@ -1,14 +1,11 @@
package org.framework.lazy.cloud.network.heartbeat.server.cluster.application.command.lazy.netty.cluster.node;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.lazy.cloud.network.heartbeat.common.enums.NettyClientStatus;
import java.lang.String;
import java.lang.Integer;
import java.time.LocalDateTime;
import java.lang.Boolean;
/**
* describe 集群配置信息

View File

@@ -15,7 +15,7 @@ import org.framework.lazy.cloud.network.heartbeat.server.properties.ServerNodePr
import org.springframework.stereotype.Component;
@Slf4j
@Component
//@Component
public class NettyUdpServerSocketApplicationListener implements SocketApplicationListener {
private final EventLoopGroup bossGroup = "linux".equalsIgnoreCase(SystemPropertyUtil.get("os.name")) ? new EpollEventLoopGroup() : new NioEventLoopGroup();

View File

@@ -0,0 +1,43 @@
package org.framework.lazy.cloud.network.heartbeat.server.init;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.protocol.token.AuthenticationTokenContext;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.token.bucket.LazyNettyClientTokenBucket;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.token.bucket.LazyNettyClientTokenBucketRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class InitAuthenticationToken implements CommandLineRunner {
private final LazyNettyClientTokenBucketRepository lazyNettyClientTokenBucketRepository;
public InitAuthenticationToken(LazyNettyClientTokenBucketRepository lazyNettyClientTokenBucketRepository) {
this.lazyNettyClientTokenBucketRepository = lazyNettyClientTokenBucketRepository;
}
@Override
public void run(String... args) throws Exception {
try {
LazyNettyClientTokenBucket lazyNettyClientTokenBucket = new LazyNettyClientTokenBucket();
lazyNettyClientTokenBucket.setIsDeleted(false);
for (LazyNettyClientTokenBucket nettyClientTokenBucket : lazyNettyClientTokenBucketRepository.findList(lazyNettyClientTokenBucket)
.getData()) {
String clientId = nettyClientTokenBucket.getUsedByClientId();
if (clientId != null) {
String appKey = nettyClientTokenBucket.getAppKey();
String appSecret = nettyClientTokenBucket.getAppSecret();
AuthenticationTokenContext.setAuthenticationToken(clientId, appKey, appSecret);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("初始化令牌桶失败:{}", e.getMessage());
}
}
}

View File

@@ -16,7 +16,8 @@ import org.springframework.context.annotation.Import;
*/
@Slf4j
@Configuration
@Import({NettyTcpServerSocketApplicationListener.class, NettyUdpServerSocketApplicationListener.class})
@Import({NettyTcpServerSocketApplicationListener.class})
//@Import({NettyTcpServerSocketApplicationListener.class, NettyUdpServerSocketApplicationListener.class})
public class InitServerSocket {

View File

@@ -119,12 +119,16 @@ public class NettyTcpServerHandler extends SimpleChannelInboundHandler<NettyProx
log.warn("client: {} channel:{}, disconnect with visitorId:{}", clientId, channel.id().toString(), visitorId);
// 访客通道 关闭访客通道
NettyCommunicationIdContext.clear(visitorId);
// 关闭通信通道
Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(channel);
Channel transferNextChannel = ChannelAttributeKeyUtils.getTransferNextChannel(channel);
// 关闭通信通道
channel.close();
nextChannel.close();
transferNextChannel.close();
if (ObjectUtils.isNotEmpty(nextChannel)) {
nextChannel.close();
}
if (ObjectUtils.isNotEmpty(transferNextChannel)) {
transferNextChannel.close();
}
super.channelInactive(ctx);
} else if (!ObjectUtils.isEmpty(clientId)) {
// 断开客户端的连接:{}

View File

@@ -1,19 +1,13 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.application;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.state.record.LazyNettyClientStateRecord;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.LazyNettyClientStateRecordRemoveCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.LazyNettyClientStateRecordStoryCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.LazyNettyClientStateRecordUpdateCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.LazyNettyClientStateRecordQueryListCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.LazyNettyClientStateRecordQueryOneCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.state.record.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyNettyClientStateRecordDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.state.record.LazyNettyClientStateRecord;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.web.response.Result;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 客户端状态变更记录
*

View File

@@ -1,20 +1,15 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.application;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyClientFlowPerDayEchartsDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyClientPortFlowPerDayEchartsDTO;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyVisitorPortPerDayFlowDTO;
import org.wu.framework.web.response.Result;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.visitor.port.per.day.flow.LazyVisitorPortPerDayFlow;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowRemoveCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowStoryCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowUpdateCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowQueryListCommand;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.visitor.port.per.day.flow.LazyVisitorPortPerDayFlowQueryOneCommand;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.web.response.Result;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 每日统计流量
*

View File

@@ -1,13 +1,10 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.route;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Long;
import java.lang.Boolean;
/**
* describe 客户端代理路由规则

View File

@@ -1,13 +1,10 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.route;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Long;
import java.lang.Boolean;
/**
* describe 客户端代理路由规则

View File

@@ -1,13 +1,10 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.lazy.netty.client.route;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Long;
import java.lang.Boolean;
/**
* describe 客户端代理路由规则

View File

@@ -18,7 +18,7 @@ import org.springframework.stereotype.Repository;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -183,7 +183,7 @@ public class LazyNettyClientRouteRepositoryImpl implements LazyNettyClientRouteR
@Override
public Result<LazyNettyClientRoute> remove(LazyNettyClientRoute lazyNettyClientRoute) {
lazyLambdaStream.update(LazyUpdateSetValueWrappers.<LazyNettyClientRouteDO>lambdaWrapper()
lazyLambdaStream.update(LazySetValueWrappers.<LazyNettyClientRouteDO>lambdaWrapper()
.set(LazyNettyClientRouteDO::getIsDeleted, true),
LazyWrappers
.<LazyNettyClientRouteDO>lambdaWrapper()

View File

@@ -10,7 +10,7 @@ import org.springframework.stereotype.Repository;
import org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyInfrastructurePersistence;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -57,7 +57,7 @@ public class LazyNettyClientStateRepositoryImpl implements LazyNettyClientStateR
@Override
public Result<LazyNettyClientState> updateOne(LazyNettyClientState lazyNettyClientState) {
LazyNettyClientStateDO lazyNettyClientStateDO = LazyNettyClientStateConverter.INSTANCE.fromNettyClientState(lazyNettyClientState);
lazyLambdaStream.update(LazyUpdateSetValueWrappers.
lazyLambdaStream.update(LazySetValueWrappers.
lambdaWrapperBeanIgnoreEmpty(lazyNettyClientStateDO)
,

View File

@@ -1,6 +1,7 @@
package org.framework.lazy.cloud.network.heartbeat.server.standalone.infrastructure.persistence;
import jakarta.annotation.Resource;
import org.framework.lazy.cloud.network.heartbeat.protocol.token.AuthenticationTokenContext;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.token.bucket.LazyNettyClientTokenBucket;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.lazy.netty.client.token.bucket.LazyNettyClientTokenBucketRepository;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.infrastructure.converter.LazyNettyClientTokenBucketConverter;
@@ -8,7 +9,7 @@ import org.framework.lazy.cloud.network.heartbeat.server.standalone.infrastructu
import org.springframework.stereotype.Repository;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -177,7 +178,7 @@ public class LazyNettyClientTokenBucketRepositoryImpl implements LazyNettyClient
// 绑定客户端ID
lazyLambdaStream.update(
LazyUpdateSetValueWrappers.<LazyNettyClientTokenBucketDO>lambdaWrapper()
LazySetValueWrappers.<LazyNettyClientTokenBucketDO>lambdaWrapper()
.set(LazyNettyClientTokenBucketDO::getUsedByClientId, clientId)
.set(LazyNettyClientTokenBucketDO::getUpdateTime, LocalDateTime.now())
,
@@ -186,6 +187,9 @@ public class LazyNettyClientTokenBucketRepositoryImpl implements LazyNettyClient
.eq(LazyNettyClientTokenBucketDO::getAppKey, appKey)
.eq(LazyNettyClientTokenBucketDO::getAppSecret, appSecret)
);
AuthenticationTokenContext.setAuthenticationToken(clientId,appKey,appSecret);
return ResultFactory.successOf(true);
}

View File

@@ -16,7 +16,7 @@ import org.framework.lazy.cloud.network.heartbeat.server.standalone.infrastructu
import org.springframework.stereotype.Repository;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -171,7 +171,7 @@ public class LazyNettyClientVirtualRouteRepositoryImpl implements LazyNettyClien
@Override
public Result<LazyNettyClientVirtualRoute> remove(LazyNettyClientVirtualRoute lazyNettyClientVirtualRoute) {
LazyNettyClientVirtualRouteDO lazyNettyClientVirtualRouteDO = LazyNettyClientVirtualRouteConverter.INSTANCE.fromLazyNettyClientVirtualRoute(lazyNettyClientVirtualRoute);
lazyLambdaStream.update(LazyUpdateSetValueWrappers.<LazyNettyClientVirtualRouteDO>lambdaWrapper()
lazyLambdaStream.update(LazySetValueWrappers.<LazyNettyClientVirtualRouteDO>lambdaWrapper()
.set(LazyNettyClientVirtualRouteDO::getIsDeleted, true),
LazyWrappers
.<LazyNettyClientVirtualRouteDO>lambdaWrapper()

View File

@@ -18,7 +18,7 @@ import org.springframework.stereotype.Repository;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -176,7 +176,7 @@ public class LazyNettyServerRouteRepositoryImpl implements LazyNettyServerRouteR
@Override
public Result<LazyNettyServerRoute> remove(LazyNettyServerRoute lazyNettyServerRoute) {
lazyLambdaStream.update(LazyUpdateSetValueWrappers.<LazyNettyServerRouteDO>lambdaWrapper()
lazyLambdaStream.update(LazySetValueWrappers.<LazyNettyServerRouteDO>lambdaWrapper()
.set(LazyNettyServerRouteDO::getIsDeleted, true),
LazyWrappers
.<LazyNettyServerRouteDO>lambdaWrapper()

View File

@@ -16,7 +16,7 @@ import org.framework.lazy.cloud.network.heartbeat.server.standalone.infrastructu
import org.springframework.stereotype.Repository;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyUpdateSetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazySetValueWrappers;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
@@ -171,7 +171,7 @@ public class LazyNettyServerVirtualRouteRepositoryImpl implements LazyNettyServe
@Override
public Result<LazyNettyServerVirtualRoute> remove(LazyNettyServerVirtualRoute lazyNettyServerVirtualRoute) {
LazyNettyServerVirtualRouteDO lazyNettyServerVirtualRouteDO = LazyNettyServerVirtualRouteConverter.INSTANCE.fromLazyNettyServerVirtualRoute(lazyNettyServerVirtualRoute);
lazyLambdaStream.update(LazyUpdateSetValueWrappers.<LazyNettyServerVirtualRouteDO>lambdaWrapper()
lazyLambdaStream.update(LazySetValueWrappers.<LazyNettyServerVirtualRouteDO>lambdaWrapper()
.set(LazyNettyServerVirtualRouteDO::getIsDeleted, true),
LazyWrappers
.<LazyNettyServerVirtualRouteDO>lambdaWrapper()

View File

@@ -10,7 +10,7 @@ import org.wu.framework.web.ui.LazyUI;
public class NettyServerLazyUI implements LazyUI {
public static final String UI_URL = "/netty-server-ui/**";
public static final String UI_URL_INDEX = "/netty-server-ui/index.html";
public static final String CLASSPATH = "classpath:/netty-server-ui/v1/";
public static final String CLASSPATH = "classpath:/wlcn-server-ui/v1/";
/**
* 是否支持 default false

View File

@@ -10,7 +10,7 @@ import org.wu.framework.web.ui.LazyUI;
public class WLCNUI implements LazyUI {
public static final String UI_URL = "/wlcn/**";
public static final String UI_URL_INDEX = "/wlcn/index.html";
public static final String CLASSPATH = "classpath:/netty-server-ui/v1/";
public static final String CLASSPATH = "classpath:/wlcn-server-ui/v1/";
/**
* 是否支持 default false

View File

@@ -1 +0,0 @@
.el-form-item[data-v-259b6f0a]{width:100%}.fl{float:left}.p0{padding:0!important}.application_item_class_[data-v-5a34f483],.el-form-item[data-v-8c51848e],.el-form-item[data-v-af3b3260],.menu_edit_item[data-v-0823986f],.role_edit_item{width:100%}.ve_table .ve_table_page[data-v-11019130],.ve_table[data-v-11019130]{flex:1;display:flex;flex-direction:column}.ve_table .ve_table_page .ve_table_content[data-v-11019130]{flex:1}

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[1079],{11079:function(e,l,t){t.r(l),t.d(l,{default:function(){return a}});var o=t(47038),r=t(71802);var a={__name:"ServerPermeateClientPortPoolEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const t={visitorPort:[{required:!0,message:"请输入访客端口",trigger:"blur"}]},a=e,u=l,{title:i,rowData:d}=(0,r.BK)(a),n=()=>{u("closeDialog",!1)},s=(0,r.iH)(null),v=(0,r.qj)({visitorPort:"",describe:""}),{visitorPort:c,describe:m}=(0,r.BK)(v);d.value&&(c.value=d.value.visitorPort,m.value=d.value.describe);return(0,o.bv)((async()=>{})),(l,a)=>{const u=(0,o.up)("el-input"),p=(0,o.up)("el-form-item"),_=(0,o.up)("el-form"),f=(0,o.up)("el-button"),w=(0,o.up)("el-dialog");return(0,o.wg)(),(0,o.j4)(w,{title:(0,r.SU)(i),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:a[4]||(a[4]=e=>n())},{footer:(0,o.w5)((()=>[(0,o._)("span",null,[(0,o.Wm)(f,{onClick:a[2]||(a[2]=e=>n())},{default:(0,o.w5)((()=>a[5]||(a[5]=[(0,o.Uk)("取消")]))),_:1,__:[5]}),(0,o.Wm)(f,{type:"primary",onClick:a[3]||(a[3]=e=>{s.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===i.value?await VE_API.cloudNetworkServerPerMeate.lazyNettyServerPermeatePortPoolSave(v):await VE_API.cloudNetworkServerPerMeate.lazyNettyServerPermeatePortPoolUpdate({visitorPort:d.value.visitorPort,...v});const{code:l}=e;0===l&&n()}}))})},{default:(0,o.w5)((()=>a[6]||(a[6]=[(0,o.Uk)("确定")]))),_:1,__:[6]})])])),default:(0,o.w5)((()=>[(0,o.Wm)(_,{model:v,ref_key:"formRef",ref:s,rules:t,"label-width":"80px",inline:!1},{default:(0,o.w5)((()=>[(0,o.Wm)(p,{label:"访客端口",prop:"visitorPort"},{default:(0,o.w5)((()=>[(0,o.Wm)(u,{modelValue:(0,r.SU)(c),"onUpdate:modelValue":a[0]||(a[0]=e=>(0,r.dq)(c)?c.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(p,{label:"描述",prop:"describe"},{default:(0,o.w5)((()=>[(0,o.Wm)(u,{modelValue:(0,r.SU)(m),"onUpdate:modelValue":a[1]||(a[1]=e=>(0,r.dq)(m)?m.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[1572],{71572:function(e,l,a){a.r(l),a.d(l,{default:function(){return d}});a(35239),a(16738);var t=a(47038),o=a(71802),u=a(46537);const r={style:{float:"left"}};var d={__name:"ServerPermeateClientNetWorkMappingEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a={clientId:[{required:!0,message:"请输入选择客户端",trigger:"blur"}],clientTargetIp:[{required:!0,message:"请输入客户端目标IP",trigger:"blur"}],clientTargetPort:[{required:!0,message:"请输入客户端目标端口",trigger:"blur"}],visitorPort:[{required:!0,message:"请选择访客端口",trigger:"blur"}]},d=e,i=l,{title:n,rowData:c}=(0,o.BK)(d),p=()=>{i("closeDialog",!1)},s=(0,o.iH)(null),v=(0,o.iH)(null),m=(0,o.iH)(null),g=(0,o.qj)({visitorPort:"",targetClientId:"",clientTargetIp:"0.0.0.0",clientTargetPort:"",describe:""}),{visitorPort:w,targetClientId:b,clientTargetIp:_,clientTargetPort:f,describe:P}=(0,o.BK)(g);c.value&&(b.value=c.value.targetClientId,_.value=c.value.clientTargetIp,f.value=c.value.clientTargetPort,w.value=c.value.visitorPort,P.value=c.value.describe);return(0,t.bv)((async()=>{await(async()=>{let e=await VE_API.cloudNetworkServerPerMeate.lazyNettyServerPermeatePortPoolList({});const{code:l}=e;0===l&&(e.data.map((e=>{e.label=e.describe,e.value=e.visitorPort})),m.value=e.data?e.data:[])})(),await(async()=>{VE_API.cloudNetwork.cloudClientFindListGroupByClient({}).then((e=>{0===e.code&&e.data&&(e.data.map((e=>{e.label=e.clientId,e.value=e.clientId})),v.value=e.data?e.data:[])}))})()})),(l,d)=>{const i=(0,t.up)("el-option"),y=(0,t.up)("el-select"),I=(0,t.up)("el-form-item"),V=(0,t.up)("el-input"),k=(0,t.up)("el-form"),U=(0,t.up)("el-button"),W=(0,t.up)("el-dialog");return(0,t.wg)(),(0,t.j4)(W,{title:(0,o.SU)(n),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:d[7]||(d[7]=e=>p())},{footer:(0,t.w5)((()=>[(0,t._)("span",null,[(0,t.Wm)(U,{onClick:d[5]||(d[5]=e=>p())},{default:(0,t.w5)((()=>d[8]||(d[8]=[(0,t.Uk)("取消")]))),_:1,__:[8]}),(0,t.Wm)(U,{type:"primary",onClick:d[6]||(d[6]=e=>{s.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===n.value?await VE_API.cloudNetwork.networkMappingSave(g):await VE_API.cloudNetwork.networkMappingUpdate({clientTargetPort:c.value.clientTargetPort,...g});const{code:l}=e;0===l&&p()}}))})},{default:(0,t.w5)((()=>d[9]||(d[9]=[(0,t.Uk)("确定")]))),_:1,__:[9]})])])),default:(0,t.w5)((()=>[(0,t.Wm)(k,{model:g,ref_key:"formRef",ref:s,rules:a,"label-width":"80px",inline:!1},{default:(0,t.w5)((()=>[(0,t.Wm)(I,{label:"访问端口",prop:"visitorPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(y,{modelValue:(0,o.SU)(w),"onUpdate:modelValue":d[0]||(d[0]=e=>(0,o.dq)(w)?w.value=e:null),placeholder:"访问端口",clearable:""},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(m.value,(e=>((0,t.wg)(),(0,t.j4)(i,{key:e.value,label:e.label,value:e.value},{default:(0,t.w5)((()=>[(0,t._)("span",r," 端口:【"+(0,u.zw)(e.value)+"】"+(0,u.zw)(e.label),1)])),_:2},1032,["label","value"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"目标客户端ID",prop:"targetClientId"},{default:(0,t.w5)((()=>[(0,t.Wm)(y,{modelValue:(0,o.SU)(b),"onUpdate:modelValue":d[1]||(d[1]=e=>(0,o.dq)(b)?b.value=e:null),placeholder:"目标客户端ID",clearable:""},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(v.value,(e=>((0,t.wg)(),(0,t.j4)(i,{key:e.value,label:e.label,value:e.value,namespace:e},null,8,["label","value","namespace"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"客户端目标地址",prop:"clientTargetIp"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,o.SU)(_),"onUpdate:modelValue":d[2]||(d[2]=e=>(0,o.dq)(_)?_.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"客户端目标端口",prop:"clientTargetPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,o.SU)(f),"onUpdate:modelValue":d[3]||(d[3]=e=>(0,o.dq)(f)?f.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"描述",prop:"describe"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,o.SU)(P),"onUpdate:modelValue":d[4]||(d[4]=e=>(0,o.dq)(P)?P.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2021],{72021:function(e,n,u){u.r(n),u.d(n,{default:function(){return _}});var t=u(47038),o=u(7280),a=u(69125),l={__name:"Hamburger",setup(e){const n=(0,a.oR)(),u=(0,t.Fl)((()=>n.getters.opened)),l=()=>{n.dispatch(`app/${o.qL}`)};return(e,n)=>{const o=(0,t.up)("el-icon");return(0,t.wg)(),(0,t.iD)("div",{class:"ve_slider_menu",onClick:l},[(0,t.Wm)(o,null,{default:(0,t.w5)((()=>[((0,t.wg)(),(0,t.j4)((0,t.LL)(u.value?"expand":"fold")))])),_:1})])}}};var _=(0,u(60199).Z)(l,[["__scopeId","data-v-6e3be609"]])}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2149],{42149:function(e,l,a){a.r(l),a.d(l,{default:function(){return o}});a(35239),a(16738);var t=a(47038),u=a(71802);var o={__name:"ClientRouteEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a={clientId:[{required:!0,message:"请选择客户端",trigger:"blur"}],routeIp:[{required:!0,message:"请输入路由IP",trigger:"blur"}]},o=e,d=l,{title:n,rowData:r}=(0,u.BK)(o),i=(0,u.iH)(null),c=()=>{d("closeDialog",!1)},p=(0,u.iH)(null),s=(0,u.qj)({clientId:"",routeIp:"",describe:""}),{clientId:m,routeIp:v,describe:w}=(0,u.BK)(s);r.value&&(m.value=r.value.clientId,v.value=r.value.routeIp,w.value=r.value.describe);return(0,t.bv)((async()=>{await(async()=>{VE_API.cloudNetwork.cloudClientFindListGroupByClient({}).then((e=>{0===e.code&&e.data&&(e.data.map((e=>{e.label=e.clientId,e.value=e.clientId})),i.value=e.data?e.data:[])}))})()})),(l,o)=>{const d=(0,t.up)("el-option"),_=(0,t.up)("el-select"),b=(0,t.up)("el-form-item"),f=(0,t.up)("el-input"),y=(0,t.up)("el-form"),I=(0,t.up)("el-button"),g=(0,t.up)("el-dialog");return(0,t.wg)(),(0,t.j4)(g,{title:(0,u.SU)(n),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:o[5]||(o[5]=e=>c())},{footer:(0,t.w5)((()=>[(0,t._)("span",null,[(0,t.Wm)(I,{onClick:o[3]||(o[3]=e=>c())},{default:(0,t.w5)((()=>o[6]||(o[6]=[(0,t.Uk)("取消")]))),_:1,__:[6]}),(0,t.Wm)(I,{type:"primary",onClick:o[4]||(o[4]=e=>{p.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===n.value?await VE_API.cloudNetworkRoute.lazyNettyClientRouteSave(s):await VE_API.cloudNetworkRoute.lazyNettyClientRouteUpdate({id:r.value.id,...s});const{code:l}=e;0===l&&c()}}))})},{default:(0,t.w5)((()=>o[7]||(o[7]=[(0,t.Uk)("确定")]))),_:1,__:[7]})])])),default:(0,t.w5)((()=>[(0,t.Wm)(y,{model:s,ref_key:"formRef",ref:p,rules:a,"label-width":"80px",inline:!1},{default:(0,t.w5)((()=>[(0,t.Wm)(b,{label:"客户端ID",prop:"clientId"},{default:(0,t.w5)((()=>[(0,t.Wm)(_,{modelValue:(0,u.SU)(m),"onUpdate:modelValue":o[0]||(o[0]=e=>(0,u.dq)(m)?m.value=e:null),placeholder:"客户端ID",clearable:""},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(i.value,(e=>((0,t.wg)(),(0,t.j4)(d,{key:e.value,label:e.label,value:e.value,namespace:e},null,8,["label","value","namespace"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(b,{label:"路由IP",prop:"routeIp"},{default:(0,t.w5)((()=>[(0,t.Wm)(f,{modelValue:(0,u.SU)(v),"onUpdate:modelValue":o[1]||(o[1]=e=>(0,u.dq)(v)?v.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(b,{label:"描述",prop:"describe"},{default:(0,t.w5)((()=>[(0,t.Wm)(f,{modelValue:(0,u.SU)(w),"onUpdate:modelValue":o[2]||(o[2]=e=>(0,u.dq)(w)?w.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2160,6238],{14975:function(e,a,l){l.r(a),l.d(a,{default:function(){return i}});l(47619);var r=l(47038),s=l(71802),u=l(27538),o=l(7280),d=l(71770),t=l(69125),n=l(37911);const p={class:"ve_container"};var m={__name:"Register",setup(e){const a={username:[{required:!0,message:"请输入用户名",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}],checkPassword:[{required:!0,message:"密码确认",trigger:"blur"}]},{proxy:l}=(0,r.FN)(),m=(0,t.oR)(),i=(0,n.tv)(),_=(0,s.qj)({username:"",password:"",checkPassword:""}),{username:c,password:w,checkPassword:f}=(0,s.BK)(_),g=(0,s.iH)(null),h=(0,s.iH)(!1);sessionStorage.clear(),m.dispatch(`app/${o.L4}`,""),i.options.isAddDynamicMenuRoutes=!1;const v=()=>{_.password===_.checkPassword?g.value.validate((async e=>{if(e){const e=await VE_API.system.register(_);0===e.code&&(m.dispatch(`app/${o.L4}`,e.data),m.dispatch(`app/${o.TR}`,_.username),h.value=!0,i.push({name:"AppMain"}))}})):l.$message({type:"error",message:"请确认,两次输入密码是否一致!"})};return(e,l)=>{const o=(0,r.up)("router-link"),t=(0,r.up)("Avatar"),n=(0,r.up)("el-icon"),m=(0,r.up)("el-input"),i=(0,r.up)("el-form-item"),W=(0,r.up)("Key"),y=(0,r.up)("el-button"),k=(0,r.up)("el-form"),b=(0,r.up)("el-card");return(0,r.wg)(),(0,r.j4)((0,s.SU)(d.default),null,{default:(0,r.w5)((()=>[(0,r._)("div",p,[(0,r.Wm)(b,{"body-style":{background:"rgba(0,0,0,0.15)"}},{default:(0,r.w5)((()=>[(0,r.Wm)(o,{style:{float:"right"},to:{path:"login"}},{default:(0,r.w5)((()=>l[3]||(l[3]=[(0,r.Uk)("登录")]))),_:1,__:[3]}),l[5]||(l[5]=(0,r._)("h1",null,"网络渗透",-1)),(0,r.Wm)(u.uT,{name:"el-fade-in-linear"},{default:(0,r.w5)((()=>[(0,r.wy)((0,r.Wm)(k,{model:_,rules:a,class:"ve_form",ref_key:"ref_form",ref:g,inline:!1,onKeyup:(0,u.D2)(v,["enter"])},{default:(0,r.w5)((()=>[(0,r.Wm)(i,{prop:"username",label:"注册账号"},{default:(0,r.w5)((()=>[(0,r.Wm)(m,{modelValue:(0,s.SU)(c),"onUpdate:modelValue":l[0]||(l[0]=e=>(0,s.dq)(c)?c.value=e:null),modelModifiers:{trim:!0},placeholder:"注册账号"},{prepend:(0,r.w5)((()=>[(0,r.Wm)(n,{size:20},{default:(0,r.w5)((()=>[(0,r.Wm)(t)])),_:1})])),_:1},8,["modelValue"])])),_:1}),(0,r.Wm)(i,{prop:"password",label:"账号密码"},{default:(0,r.w5)((()=>[(0,r.Wm)(m,{modelValue:(0,s.SU)(w),"onUpdate:modelValue":l[1]||(l[1]=e=>(0,s.dq)(w)?w.value=e:null),modelModifiers:{trim:!0},"show-password":"",placeholder:"账号密码"},{prepend:(0,r.w5)((()=>[(0,r.Wm)(n,{size:20},{default:(0,r.w5)((()=>[(0,r.Wm)(W)])),_:1})])),_:1},8,["modelValue"])])),_:1}),(0,r.Wm)(i,{prop:"checkPassword",label:"密码确认"},{default:(0,r.w5)((()=>[(0,r.Wm)(m,{modelValue:(0,s.SU)(f),"onUpdate:modelValue":l[2]||(l[2]=e=>(0,s.dq)(f)?f.value=e:null),modelModifiers:{trim:!0},"show-password":"",placeholder:"密码确认"},{prepend:(0,r.w5)((()=>[(0,r.Wm)(n,{size:20},{default:(0,r.w5)((()=>[(0,r.Wm)(W)])),_:1})])),_:1},8,["modelValue"])])),_:1}),(0,r.Wm)(i,null,{default:(0,r.w5)((()=>[(0,r.Wm)(y,{class:"ve_submit",type:"primary",onClick:v},{default:(0,r.w5)((()=>l[4]||(l[4]=[(0,r.Uk)(" 注册 ")]))),_:1,__:[4]})])),_:1})])),_:1},8,["model"]),[[u.F8,!h.value]])])),_:1})])),_:1,__:[5]})])])),_:1})}}};var i=(0,l(60199).Z)(m,[["__scopeId","data-v-216052e6"]])}}]);

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2226],{68469:function(e){e.exports={main_bg_color:"#f5f5f5",base_color:"#409EFF",nav_height:"50px",nav_menu_height:"50px",side_close_width:"65px",side_open_width:"160px",sideBgColor:"#545c64",sideTextColor:"#fff",sideActiveTextColor:"#ffd04b"}},92226:function(e,n,o){"use strict";o.r(n),o.d(n,{default:function(){return s}});var t=o(27538),a=o(71802),r=o(47038),l=o(68469);const i={"element-loading-text":"拼命加载中","element-loading-spinner":"el-icon-loading"},u=["src"];var _={__name:"IFrame",props:{url:{type:String}},setup(e){(0,t.sj)((e=>({"4916a745":(0,a.SU)(l.nav_height)})));const n=e,o=(0,a.iH)(!0),_=(0,a.iH)(null);return(0,r.bv)((()=>{(()=>{const e=()=>{o.value=!1};_.value.attachEvent?_.value.attachEvent("onload",e):_.value.onload=e})()})),(e,t)=>{const a=(0,r.Q2)("loading");return(0,r.wy)(((0,r.wg)(),(0,r.iD)("div",i,[(0,r._)("iframe",{ref_key:"iframe",ref:_,src:n.url,frameborder:"0"},null,8,u)])),[[a,o.value]])}}};var s=(0,o(60199).Z)(_,[["__scopeId","data-v-03589f92"]])}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2236],{42236:function(e,l,a){a.r(l),a.d(l,{default:function(){return t}});var o=a(47038),u=a(71802);var t={__name:"ApplicationEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a=e,t=l,{title:d,rowData:n}=(0,u.BK)(a),i=()=>{t("closeDialog",!1)},r=(0,u.qj)({code:"",icon:"",name:"",isStation:!0,openNewWindow:!0,url:""}),{code:p,icon:m,name:_,isStation:c,openNewWindow:s,url:w}=(0,u.BK)(r),f=(0,u.iH)(null),v={name:[{required:!0,message:"请输入应用名称",trigger:"blur"}],code:[{required:!0,message:"请输入应用编码",trigger:"blur"}],isStation:[{required:!0,message:"请选择是否站内",trigger:"blur"}],openNewWindow:[{required:!0,message:"请选择是否新窗口打开",trigger:"blur"}]};n.value&&(_.value=n.value.name,p.value=n.value.code,m.value=n.value.icon,c.value=n.value.isStation,s.value=n.value.openNewWindow,w.value=n.value.url);return(0,o.bv)((async()=>{})),(l,a)=>{const t=(0,o.up)("el-input"),b=(0,o.up)("el-form-item"),W=(0,o.up)("el-radio-button"),g=(0,o.up)("el-radio-group"),V=(0,o.up)("el-form"),U=(0,o.up)("el-button"),S=(0,o.up)("el-dialog");return(0,o.wg)(),(0,o.j4)(S,{title:(0,u.SU)(d),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:a[8]||(a[8]=e=>i())},{footer:(0,o.w5)((()=>[(0,o._)("span",null,[(0,o.Wm)(U,{onClick:a[6]||(a[6]=e=>i())},{default:(0,o.w5)((()=>a[13]||(a[13]=[(0,o.Uk)("取消")]))),_:1,__:[13]}),(0,o.Wm)(U,{type:"primary",onClick:a[7]||(a[7]=e=>{f.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e,l=new FormData;l.append("name",r.name),l.append("code",r.code),l.append("icon",r.icon),l.append("isStation",r.isStation),l.append("openNewWindow",r.openNewWindow),l.append("url",r.url),e="添加"===d.value?await VE_API.application.applicationStory(r):await VE_API.application.applicationStory({id:n.value.id,...r});const{code:a}=e;0===a&&i()}}))})},{default:(0,o.w5)((()=>a[14]||(a[14]=[(0,o.Uk)("确定")]))),_:1,__:[14]})])])),default:(0,o.w5)((()=>[(0,o.Wm)(V,{model:r,ref_key:"formRef",ref:f,rules:v,inline:!1,"label-width":"120px"},{default:(0,o.w5)((()=>[(0,o.Wm)(b,{label:"应用名称",prop:"name",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(_),"onUpdate:modelValue":a[0]||(a[0]=e=>(0,u.dq)(_)?_.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(b,{label:"应用编码",prop:"code",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(p),"onUpdate:modelValue":a[1]||(a[1]=e=>(0,u.dq)(p)?p.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(b,{label:"应用图标",prop:"icon",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(m),"onUpdate:modelValue":a[2]||(a[2]=e=>(0,u.dq)(m)?m.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(b,{label:"是否站内",prop:"isStation",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(g,{modelValue:(0,u.SU)(c),"onUpdate:modelValue":a[3]||(a[3]=e=>(0,u.dq)(c)?c.value=e:null)},{default:(0,o.w5)((()=>[(0,o.Wm)(W,{label:!0},{default:(0,o.w5)((()=>a[9]||(a[9]=[(0,o.Uk)("站内")]))),_:1,__:[9]}),(0,o.Wm)(W,{label:!1},{default:(0,o.w5)((()=>a[10]||(a[10]=[(0,o.Uk)("站外")]))),_:1,__:[10]})])),_:1},8,["modelValue"])])),_:1}),(0,o.Wm)(b,{label:"应用地址",prop:"url",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(w),"onUpdate:modelValue":a[4]||(a[4]=e=>(0,u.dq)(w)?w.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(b,{label:"是否新窗口打开",prop:"openNewWindow",class:"role_edit_item"},{default:(0,o.w5)((()=>[(0,o.Wm)(g,{modelValue:(0,u.SU)(s),"onUpdate:modelValue":a[5]||(a[5]=e=>(0,u.dq)(s)?s.value=e:null)},{default:(0,o.w5)((()=>[(0,o.Wm)(W,{label:!0},{default:(0,o.w5)((()=>a[11]||(a[11]=[(0,o.Uk)("新窗口打开")]))),_:1,__:[11]}),(0,o.Wm)(W,{label:!1},{default:(0,o.w5)((()=>a[12]||(a[12]=[(0,o.Uk)("当前页面跳转")]))),_:1,__:[12]})])),_:1},8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2244],{52244:function(e,l,a){a.r(l),a.d(l,{default:function(){return d}});a(35239),a(16738);var t=a(47038),r=a(71802),u=a(46537);const o={style:{float:"left"}};var d={__name:"ClientPermeateServerNetworkMappingEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a={clientId:[{required:!0,message:"请选择客户端",trigger:"blur"}],visitorPort:[{required:!0,message:"请选择访客端口",trigger:"blur"}],permeateTargetIp:[{required:!0,message:"请输入客户端目标IP",trigger:"blur"}],permeateTargetPort:[{required:!0,message:"请输入客户端目标端口",trigger:"blur"}]},d=e,n=l,{title:i,rowData:p}=(0,r.BK)(d),m=()=>{n("closeDialog",!1)},c=(0,r.iH)(null),v=(0,r.iH)(null),s=(0,r.iH)(null),g=(0,r.qj)({permeateTargetIp:"0.0.0.0",permeateTargetPort:"",visitorPort:"",clientId:"",describe:"",id:""}),{permeateTargetIp:w,permeateTargetPort:b,visitorPort:_,clientId:f,describe:P,id:y}=(0,r.BK)(g);p.value&&(w.value=p.value.permeateTargetIp,f.value=p.value.clientId,b.value=p.value.permeateTargetPort,_.value=p.value.visitorPort,P.value=p.value.describe,y.value=p.value.id);return(0,t.bv)((async()=>{await(async()=>{VE_API.cloudNetwork.cloudClientFindListGroupByClient({}).then((e=>{0===e.code&&e.data&&(e.data.map((e=>{e.label=e.clientId,e.value=e.clientId})),s.value=e.data?e.data:[])}))})()})),(l,d)=>{const n=(0,t.up)("el-option"),y=(0,t.up)("el-select"),I=(0,t.up)("el-form-item"),V=(0,t.up)("el-input"),k=(0,t.up)("el-form"),S=(0,t.up)("el-button"),U=(0,t.up)("el-dialog");return(0,t.wg)(),(0,t.j4)(U,{title:(0,r.SU)(i),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:d[8]||(d[8]=e=>m())},{footer:(0,t.w5)((()=>[(0,t._)("span",null,[(0,t.Wm)(S,{onClick:d[6]||(d[6]=e=>m())},{default:(0,t.w5)((()=>d[9]||(d[9]=[(0,t.Uk)("取消")]))),_:1,__:[9]}),(0,t.Wm)(S,{type:"primary",onClick:d[7]||(d[7]=e=>{c.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===i.value?await VE_API.cloudNetworkServerPerMeate.lazyInternalNetworkClientPermeateServerMappingSave(g):await VE_API.cloudNetworkServerPerMeate.lazyInternalNetworkClientPermeateServerMappingUpdate({permeateTargetPort:p.value.permeateTargetPort,...g});const{code:l}=e;0===l&&m()}}))})},{default:(0,t.w5)((()=>d[10]||(d[10]=[(0,t.Uk)("确定")]))),_:1,__:[10]})])])),default:(0,t.w5)((()=>[(0,t.Wm)(k,{model:g,ref_key:"formRef",ref:c,rules:a,"label-width":"80px",inline:!1},{default:(0,t.w5)((()=>[(0,t.Wm)(I,{label:"客户端ID",prop:"clientId"},{default:(0,t.w5)((()=>[(0,t.Wm)(y,{modelValue:(0,r.SU)(f),"onUpdate:modelValue":d[0]||(d[0]=e=>(0,r.dq)(f)?f.value=e:null),placeholder:"客户端ID",clearable:"",onChange:d[1]||(d[1]=e=>(async e=>{let l=await VE_API.cloudNetworkServerPerMeate.lazyNettyClientPermeatePortPoolList({clientId:e});const{code:a}=l;0===a&&l.data&&(l.data.map((e=>{e.label=e.describe,e.value=e.visitorPort})),v.value=l.data?l.data:[])})((0,r.SU)(f)))},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(s.value,(e=>((0,t.wg)(),(0,t.j4)(n,{key:e.value,label:e.label,value:e.value,namespace:e},null,8,["label","value","namespace"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"客户端访问端口",prop:"visitorPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(y,{modelValue:(0,r.SU)(_),"onUpdate:modelValue":d[2]||(d[2]=e=>(0,r.dq)(_)?_.value=e:null),placeholder:"客户端访问端口",clearable:""},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(v.value,(e=>((0,t.wg)(),(0,t.j4)(n,{key:e.value,label:e.label,value:e.value},{default:(0,t.w5)((()=>[(0,t._)("span",o," 端口:【"+(0,u.zw)(e.value)+"】"+(0,u.zw)(e.label),1)])),_:2},1032,["label","value"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"服务端目标地址",prop:"permeateTargetIp"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,r.SU)(w),"onUpdate:modelValue":d[3]||(d[3]=e=>(0,r.dq)(w)?w.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"服务端目标端口",prop:"permeateTargetPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,r.SU)(b),"onUpdate:modelValue":d[4]||(d[4]=e=>(0,r.dq)(b)?b.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"描述",prop:"describe"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,r.SU)(P),"onUpdate:modelValue":d[5]||(d[5]=e=>(0,r.dq)(P)?P.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[2278],{72278:function(e,l,a){a.r(l),a.d(l,{default:function(){return r}});a(35239),a(16738);var t=a(47038),u=a(71802);var r={__name:"ClientVirtualRouteEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a={targetClientId:[{required:!0,message:"请选择客户端",trigger:"blur"}],virtualIp:[{required:!0,message:"请输入虚拟IP",trigger:"blur"}],virtualPort:[{required:!0,message:"请输入虚拟端口",trigger:"blur"}],targetIp:[{required:!0,message:"请输入目标IP",trigger:"blur"}],targetPort:[{required:!0,message:"请输入目标端口",trigger:"blur"}]},r=e,o=l,{title:d,rowData:i}=(0,u.BK)(r),n=(0,u.iH)(null),p=()=>{o("closeDialog",!1)},m=(0,u.iH)(null),v=(0,u.qj)({targetClientId:"",virtualIp:"",virtualPort:"*",targetIp:"",targetPort:"*",describe:""}),{targetClientId:c,virtualIp:s,virtualPort:g,targetIp:b,targetPort:w,describe:_}=(0,u.BK)(v);i.value&&(c.value=i.value.targetClientId,s.value=i.value.virtualIp,g.value=i.value.virtualPort,b.value=i.value.targetIp,w.value=i.value.targetPort,_.value=i.value.describe);return(0,t.bv)((async()=>{await(async()=>{VE_API.cloudNetwork.cloudClientFindListGroupByClient({}).then((e=>{0===e.code&&e.data&&(e.data.map((e=>{e.label=e.clientId,e.value=e.clientId})),n.value=e.data?e.data:[])}))})()})),(l,r)=>{const o=(0,t.up)("el-option"),f=(0,t.up)("el-select"),I=(0,t.up)("el-form-item"),V=(0,t.up)("el-input"),y=(0,t.up)("el-form"),P=(0,t.up)("el-button"),U=(0,t.up)("el-dialog");return(0,t.wg)(),(0,t.j4)(U,{title:(0,u.SU)(d),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:r[8]||(r[8]=e=>p())},{footer:(0,t.w5)((()=>[(0,t._)("span",null,[(0,t.Wm)(P,{onClick:r[6]||(r[6]=e=>p())},{default:(0,t.w5)((()=>r[9]||(r[9]=[(0,t.Uk)("取消")]))),_:1,__:[9]}),(0,t.Wm)(P,{type:"primary",onClick:r[7]||(r[7]=e=>{m.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===d.value?await VE_API.cloudNetworkRoute.lazyNettyClientVirtualRouteSave(v):await VE_API.cloudNetworkRoute.lazyNettyClientVirtualRouteUpdate({id:i.value.id,...v});const{code:l}=e;0===l&&p()}}))})},{default:(0,t.w5)((()=>r[10]||(r[10]=[(0,t.Uk)("确定")]))),_:1,__:[10]})])])),default:(0,t.w5)((()=>[(0,t.Wm)(y,{model:v,ref_key:"formRef",ref:m,rules:a,"label-width":"80px",inline:!1},{default:(0,t.w5)((()=>[(0,t.Wm)(I,{label:"客户端ID",prop:"targetClientId"},{default:(0,t.w5)((()=>[(0,t.Wm)(f,{modelValue:(0,u.SU)(c),"onUpdate:modelValue":r[0]||(r[0]=e=>(0,u.dq)(c)?c.value=e:null),placeholder:"客户端ID",clearable:""},{default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)(n.value,(e=>((0,t.wg)(),(0,t.j4)(o,{key:e.value,label:e.label,value:e.value,namespace:e},null,8,["label","value","namespace"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"虚拟IP",prop:"virtualIp"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,u.SU)(s),"onUpdate:modelValue":r[1]||(r[1]=e=>(0,u.dq)(s)?s.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"虚拟端口",prop:"virtualPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,u.SU)(g),"onUpdate:modelValue":r[2]||(r[2]=e=>(0,u.dq)(g)?g.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"目标IP",prop:"targetIp"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,u.SU)(b),"onUpdate:modelValue":r[3]||(r[3]=e=>(0,u.dq)(b)?b.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"目标端口",prop:"targetPort"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,u.SU)(w),"onUpdate:modelValue":r[4]||(r[4]=e=>(0,u.dq)(w)?w.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,t.Wm)(I,{label:"描述",prop:"describe"},{default:(0,t.w5)((()=>[(0,t.Wm)(V,{modelValue:(0,u.SU)(_),"onUpdate:modelValue":r[5]||(r[5]=e=>(0,u.dq)(_)?_.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[275],{20275:function(e,n,r){r.r(n),r.d(n,{default:function(){return m}});r(47619),r(35239),r(58958);var t=r(47038),a=r(71802),i=r(46537),u=r(37911),l=r(14791),c=r(21470),o=r(5003),s=r.n(o);const d={class:"el-menu-item-text"},p={class:"el-menu-item-text"};var m={__name:"SlideMenu",props:["menu"],setup(e){const n=e,{menu:r}=(0,a.BK)(n),o=(0,u.tv)(),m=e=>{let n=`/${e.url.replace(/\//g,"-")}-${e.code}`;return 1===e.iframe&&(n=`/i-${e.code}`),n};return(e,n)=>{const u=(0,t.up)("el-icon"),y=(0,t.up)("slide-menu",!0),f=(0,t.up)("el-sub-menu"),h=(0,t.up)("el-menu-item");return 0===(0,a.SU)(r).type&&(e=>{if(e&&e.length>0)return s().toTreeArray(e).some((e=>1===e.type));return!1})((0,a.SU)(r).children)?((0,t.wg)(),(0,t.j4)(f,{key:0,index:(0,a.SU)(r).code+""},{title:(0,t.w5)((()=>[(0,t.Wm)(u,{size:16,style:{"margin-right":"6px"}},{default:(0,t.w5)((()=>[((0,t.wg)(),(0,t.j4)((0,t.LL)((0,a.SU)(r).icon)))])),_:1}),(0,t._)("span",d,(0,i.zw)((0,a.SU)(r).name),1)])),default:(0,t.w5)((()=>[((0,t.wg)(!0),(0,t.iD)(t.HY,null,(0,t.Ko)((0,a.SU)(r).children,(e=>((0,t.wg)(),(0,t.j4)(y,{key:e.code,menu:e},null,8,["menu"])))),128))])),_:1},8,["index"])):1===(0,a.SU)(r).type?((0,t.wg)(),(0,t.j4)(h,{key:1,index:m((0,a.SU)(r)),onClick:n[0]||(n[0]=e=>(e=>{let n=e.url.replace(/\//g,"-")+`-${e.code}`;1===e.iframe&&(n=`i-${e.code}`),l.Z.dispatch(`tabbar/${c.cn}`,{barName:e.name,barCode:n}),o.push({name:n})})((0,a.SU)(r)))},{title:(0,t.w5)((()=>[(0,t.Wm)(u,{size:16,style:{"margin-right":"6px"}},{default:(0,t.w5)((()=>[((0,t.wg)(),(0,t.j4)((0,t.LL)((0,a.SU)(r).icon)))])),_:1}),(0,t._)("span",p,(0,i.zw)((0,a.SU)(r).name),1)])),_:1},8,["index"])):(0,t.kq)("",!0)}}}},5003:function(e,n,r){var t=r(3410),a=r(33144),i=r(48297),u=r(77916),l=r(81837),c=r(65252),o=r(55614),s=r(66571),d=r(81669),p=r(60550),m=r(95462),y=r(34321),f=r(75790),h=r(13801),w=r(14026),_=r(93079),g=r(13556),k=r(45739),S=r(96021),x=r(28065),U=r(72474),z=r(44206),b=r(24133),v=r(87454),T=r(29413),j=r(74428),A=r(77793),L=r(28235),$=r(11991),C=r(95511),B=r(49745),K=r(85167),O=r(48577),W=r(44125),q=r(64817),E={uniq:o,union:s,sortBy:p,orderBy:m,shuffle:y,sample:f,some:a,every:i,slice:h,filter:w,find:k,findLast:S,findKey:_,includes:g,arrayIndexOf:r(87397),arrayLastIndexOf:r(74985),map:t,reduce:x,copyWithin:U,chunk:z,zip:b,unzip:v,zipObject:T,flatten:j,toArray:d,includeArrays:u,pluck:A,invoke:L,arrayEach:l,lastArrayEach:c,toArrayTree:$,toTreeArray:C,findTree:B,eachTree:K,mapTree:O,filterTree:W,searchTree:q};e.exports=E}}]);

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[3995],{63995:function(e,l,t){t.r(l),t.d(l,{default:function(){return u}});t(35239),t(16738);var a=t(47038),o=t(71802);var u={__name:"ServerRouteEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const t={routeIp:[{required:!0,message:"请输入路由IP",trigger:"blur"}]},u=e,r=l,{title:d,rowData:n}=(0,o.BK)(u),i=(0,o.iH)(null),p=()=>{r("closeDialog",!1)},c=(0,o.iH)(null),s=(0,o.qj)({routeIp:"",describe:""}),{routeIp:m,describe:_}=(0,o.BK)(s);n.value&&(m.value=n.value.routeIp,_.value=n.value.describe);return(0,a.bv)((async()=>{VE_API.cloudNetwork.cloudClientFindListGroupByClient().then((e=>{0===e.code&&e.data&&(e.data.map((e=>{e.label=e.clientId,e.value=e.clientId})),i.value=e.data?e.data:[])}))})),(l,u)=>{const r=(0,a.up)("el-input"),i=(0,a.up)("el-form-item"),v=(0,a.up)("el-form"),w=(0,a.up)("el-button"),f=(0,a.up)("el-dialog");return(0,a.wg)(),(0,a.j4)(f,{title:(0,o.SU)(d),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:u[4]||(u[4]=e=>p())},{footer:(0,a.w5)((()=>[(0,a._)("span",null,[(0,a.Wm)(w,{onClick:u[2]||(u[2]=e=>p())},{default:(0,a.w5)((()=>u[5]||(u[5]=[(0,a.Uk)("取消")]))),_:1,__:[5]}),(0,a.Wm)(w,{type:"primary",onClick:u[3]||(u[3]=e=>{c.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===d.value?await VE_API.cloudNetworkRoute.lazyNettyServerRouteSave(s):await VE_API.cloudNetworkRoute.lazyNettyServerRouteUpdate({id:n.value.id,...s});const{code:l}=e;0===l&&p()}}))})},{default:(0,a.w5)((()=>u[6]||(u[6]=[(0,a.Uk)("确定")]))),_:1,__:[6]})])])),default:(0,a.w5)((()=>[(0,a.Wm)(v,{model:s,ref_key:"formRef",ref:c,rules:t,"label-width":"80px",inline:!1},{default:(0,a.w5)((()=>[(0,a.Wm)(i,{label:"路由IP","label-width":"140px",prop:"routeIp"},{default:(0,a.w5)((()=>[(0,a.Wm)(r,{modelValue:(0,o.SU)(m),"onUpdate:modelValue":u[0]||(u[0]=e=>(0,o.dq)(m)?m.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,a.Wm)(i,{label:"描述",prop:"describe"},{default:(0,a.w5)((()=>[(0,a.Wm)(r,{modelValue:(0,o.SU)(_),"onUpdate:modelValue":u[1]||(u[1]=e=>(0,o.dq)(_)?_.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[4050],{34050:function(e,l,a){a.r(l),a.d(l,{default:function(){return d}});var u=a(47038),o=a(71802),t={__name:"UsersEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a={username:[{required:!0,message:"请输入用户名",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}],roleIds:[{required:!0,message:"请选择角色",trigger:"change"}]},t=e,d=l,{title:r,rowData:s}=(0,o.BK)(t),n=()=>{d("closeDialog",!1)},i=(0,o.iH)(null),m=(0,o.qj)({username:"",password:"",roleIds:[],status:!0}),{username:p,password:w,roleIds:_,status:c}=(0,o.BK)(m),f=(0,o.iH)([]);s.value&&(p.value=s.value.username,w.value=s.value.password,_.value=s.value.roleIds,c.value=s.value.status);(0,u.bv)((async()=>{await(async()=>{const{code:e,data:l}=await VE_API.system.roleList();0===e&&(f.value=l)})()}));return(l,t)=>{const d=(0,u.up)("el-input"),v=(0,u.up)("el-form-item"),b=(0,u.up)("el-option"),g=(0,u.up)("el-select"),y=(0,u.up)("el-radio-button"),V=(0,u.up)("el-radio-group"),U=(0,u.up)("el-form"),W=(0,u.up)("el-button"),h=(0,u.up)("el-dialog");return(0,u.wg)(),(0,u.j4)(h,{title:(0,o.SU)(r),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:t[6]||(t[6]=e=>n())},{footer:(0,u.w5)((()=>[(0,u._)("span",null,[(0,u.Wm)(W,{onClick:t[4]||(t[4]=e=>n())},{default:(0,u.w5)((()=>t[9]||(t[9]=[(0,u.Uk)("取消")]))),_:1,__:[9]}),(0,u.Wm)(W,{type:"primary",onClick:t[5]||(t[5]=e=>{i.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===r.value?await VE_API.system.userAdd(m):await VE_API.system.userEdit({id:s.value.id,...m});const{code:l}=e;0===l&&n()}}))})},{default:(0,u.w5)((()=>t[10]||(t[10]=[(0,u.Uk)("确定")]))),_:1,__:[10]})])])),default:(0,u.w5)((()=>[(0,u.Wm)(U,{model:m,ref_key:"formRef",ref:i,rules:a,"label-width":"80px",inline:!1},{default:(0,u.w5)((()=>[(0,u.Wm)(v,{label:"用户名",prop:"username"},{default:(0,u.w5)((()=>[(0,u.Wm)(d,{modelValue:(0,o.SU)(p),"onUpdate:modelValue":t[0]||(t[0]=e=>(0,o.dq)(p)?p.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,u.Wm)(v,{label:"密码",prop:"password"},{default:(0,u.w5)((()=>[(0,u.Wm)(d,{modelValue:(0,o.SU)(w),"onUpdate:modelValue":t[1]||(t[1]=e=>(0,o.dq)(w)?w.value=e:null),"show-password":"",placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,u.Wm)(v,{label:"角色",prop:"roleIds"},{default:(0,u.w5)((()=>[(0,u.Wm)(g,{style:{width:"100%"},modelValue:(0,o.SU)(_),"onUpdate:modelValue":t[2]||(t[2]=e=>(0,o.dq)(_)?_.value=e:null),multiple:"",placeholder:"",clearable:""},{default:(0,u.w5)((()=>[((0,u.wg)(!0),(0,u.iD)(u.HY,null,(0,u.Ko)(f.value,(e=>((0,u.wg)(),(0,u.j4)(b,{key:e.id,label:e.name,value:e.id},null,8,["label","value"])))),128))])),_:1},8,["modelValue"])])),_:1}),(0,u.Wm)(v,{label:"状态"},{default:(0,u.w5)((()=>[(0,u.Wm)(V,{modelValue:(0,o.SU)(c),"onUpdate:modelValue":t[3]||(t[3]=e=>(0,o.dq)(c)?c.value=e:null)},{default:(0,u.w5)((()=>[(0,u.Wm)(y,{label:!0},{default:(0,u.w5)((()=>t[7]||(t[7]=[(0,u.Uk)("启用")]))),_:1,__:[7]}),(0,u.Wm)(y,{label:!1},{default:(0,u.w5)((()=>t[8]||(t[8]=[(0,u.Uk)("停用")]))),_:1,__:[8]})])),_:1},8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}};var d=(0,a(60199).Z)(t,[["__scopeId","data-v-259b6f0a"]])}}]);

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunkwu_vue_zone_ecology_net=self.webpackChunkwu_vue_zone_ecology_net||[]).push([[4938],{54938:function(e,l,a){a.r(l),a.d(l,{default:function(){return t}});var o=a(47038),u=a(71802);var t={__name:"DictionaryDataEdit",props:{showDialog:{type:Boolean,default:!0},title:{type:String,default:"添加"},rowData:{type:Object,default:null}},emits:["closeDialog"],setup(e,{emit:l}){const a=e,t=l,{title:d,rowData:n}=(0,u.BK)(a),r=()=>{t("closeDialog",!1)},i=(0,u.qj)({code:"",name:"",description:"",dictionaryCode:"",type:1}),{code:m,name:p,description:c,dictionaryCode:s,type:_}=(0,u.BK)(i),f=(0,u.iH)(null),v={name:[{required:!0,message:"请输入字典项名称",trigger:"blur"}],code:[{required:!0,message:"请输入字典项编码",trigger:"blur"}]};n.value&&(p.value=n.value.name,m.value=n.value.code,c.value=n.value.description,s.value=n.value.dictionaryCode,_.value=n.value.type);return(0,o.bv)((async()=>{})),(l,a)=>{const t=(0,o.up)("el-input"),w=(0,o.up)("el-form-item"),y=(0,o.up)("el-radio-button"),b=(0,o.up)("el-radio-group"),g=(0,o.up)("el-form"),V=(0,o.up)("el-button"),U=(0,o.up)("el-dialog");return(0,o.wg)(),(0,o.j4)(U,{title:(0,u.SU)(d),"append-to-body":"","destroy-on-close":"","model-value":e.showDialog,onClose:a[7]||(a[7]=e=>r())},{footer:(0,o.w5)((()=>[(0,o._)("span",null,[(0,o.Wm)(V,{onClick:a[5]||(a[5]=e=>r())},{default:(0,o.w5)((()=>a[10]||(a[10]=[(0,o.Uk)("取消")]))),_:1,__:[10]}),(0,o.Wm)(V,{type:"primary",onClick:a[6]||(a[6]=e=>{f.value.validate((async e=>{if(!e)return console.log("error submit!!"),!1;{let e;e="添加"===d.value?await VE_API.system.dictionaryDataAdd(i):await VE_API.system.dictionaryDataEdit({id:n.value.id,...i});const{code:l}=e;"00"==l&&r()}}))})},{default:(0,o.w5)((()=>a[11]||(a[11]=[(0,o.Uk)("确定")]))),_:1,__:[11]})])])),default:(0,o.w5)((()=>[(0,o.Wm)(g,{model:i,ref_key:"formRef",ref:f,rules:v,"label-width":"80px",inline:!1},{default:(0,o.w5)((()=>[(0,o.Wm)(w,{label:"字典项名称",prop:"name"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(p),"onUpdate:modelValue":a[0]||(a[0]=e=>(0,u.dq)(p)?p.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(w,{label:"字典项编码",prop:"code"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(m),"onUpdate:modelValue":a[1]||(a[1]=e=>(0,u.dq)(m)?m.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(w,{label:"字典项描述",prop:"description"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(c),"onUpdate:modelValue":a[2]||(a[2]=e=>(0,u.dq)(c)?c.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(w,{label:"字典编码",prop:"dictionaryCode"},{default:(0,o.w5)((()=>[(0,o.Wm)(t,{modelValue:(0,u.SU)(s),"onUpdate:modelValue":a[3]||(a[3]=e=>(0,u.dq)(s)?s.value=e:null),placeholder:"",clearable:""},null,8,["modelValue"])])),_:1}),(0,o.Wm)(w,{label:"字典类型",prop:"status"},{default:(0,o.w5)((()=>[(0,o.Wm)(b,{modelValue:(0,u.SU)(_),"onUpdate:modelValue":a[4]||(a[4]=e=>(0,u.dq)(_)?_.value=e:null)},{default:(0,o.w5)((()=>[(0,o.Wm)(y,{label:1},{default:(0,o.w5)((()=>a[8]||(a[8]=[(0,o.Uk)("树状")]))),_:1,__:[8]}),(0,o.Wm)(y,{label:0},{default:(0,o.w5)((()=>a[9]||(a[9]=[(0,o.Uk)("列表")]))),_:1,__:[9]})])),_:1},8,["modelValue"])])),_:1})])),_:1},8,["model"])])),_:1},8,["title","model-value"])}}}}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More