mirror of
https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network.git
synced 2026-02-04 23:15:52 +08:00
Compare commits
59 Commits
61f639c51d
...
1.3.6-JDK2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f4a05ba5c | ||
|
|
a42694af58 | ||
|
|
eee7b9e511 | ||
|
|
3b231083fe | ||
|
|
8938bfca6a | ||
|
|
55ca1974da | ||
|
|
f28efb5fe8 | ||
|
|
fc05d9a09b | ||
|
|
0aaf8a47ec | ||
|
|
eab0dacaa9 | ||
|
|
ae962c7393 | ||
|
|
4e4dc0dc78 | ||
|
|
85a44c1665 | ||
|
|
5aa9ca4b85 | ||
|
|
43193138cd | ||
|
|
abaea6c2ca | ||
|
|
d8285fd074 | ||
|
|
8d845c64c1 | ||
|
|
b8bfe15368 | ||
|
|
6c496ce7da | ||
|
|
d5561b341c | ||
|
|
7abf25c5b8 | ||
|
|
b8ebf39fff | ||
|
|
6690e65ee2 | ||
|
|
c4cb55f6dc | ||
|
|
eb73dc1b87 | ||
|
|
98e3e6d218 | ||
|
|
e377b49244 | ||
|
|
8516f8c854 | ||
|
|
b834241859 | ||
|
|
d01c905752 | ||
|
|
aaf22634bd | ||
|
|
38ca56dbba | ||
|
|
ec35d1c296 | ||
|
|
3754ee8701 | ||
|
|
49f9e96c29 | ||
|
|
ac38d8dfd7 | ||
|
|
6142105ac3 | ||
|
|
ff3681fc46 | ||
|
|
9e1a47f4e9 | ||
|
|
13858a0b7c | ||
|
|
caff71be87 | ||
|
|
04c683cb9a | ||
|
|
e5f53b520b | ||
|
|
50b2b1866c | ||
|
|
841c24a6ed | ||
|
|
e387a43437 | ||
|
|
438198bcc5 | ||
|
|
afe1a1417f | ||
|
|
d9d82a99d5 | ||
|
|
c5aeb6f0f7 | ||
|
|
f8f485a14a | ||
|
|
7178974abb | ||
|
|
f2fede4a96 | ||
|
|
74eab26478 | ||
|
|
faabae3d19 | ||
|
|
6c86a41359 | ||
|
|
3353e4bd83 | ||
|
|
2f0f4270d3 |
@@ -7,15 +7,15 @@
|
||||
|
||||
#### 操作步骤:拥有公网ip的服务器开发6001(web)、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.4-JDK24-SNAPSHOT
|
||||
```
|
||||
|
||||
#### 操作步骤:杭州本地机房所在网络服务器启动客户端、你老家所在网络中启动客户端,命令如下
|
||||
```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.4-JDK24-SNAPSHOT
|
||||
```
|
||||
```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.4-JDK24-SNAPSHOT
|
||||
```
|
||||
#### 操作步骤:配置端口
|
||||

|
||||
|
||||
45
README.md
45
README.md
@@ -14,6 +14,9 @@
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-17-green.svg" alt="jdk-17" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk24-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-24-green.svg" alt="jdk-24" />
|
||||
</a>
|
||||
<br />
|
||||
<img src="https://img.shields.io/badge/SpringBoot-v3.x-blue">
|
||||
<br />
|
||||
@@ -30,10 +33,12 @@
|
||||
</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
|
||||
ORM操作数据库,使用环境JDK17 Spring Boot 3.0.2。的网络穿透、渗透工具支持Tcp、Http、Socks
|
||||
ORM操作数据库,使用环境JDK24 Spring Boot 3.5.0。的网络穿透、渗透工具支持Tcp、Http、Socks
|
||||
主要功能:
|
||||
- 服务端渗透客户端(网络穿透),对于没有公网IP的服务进行公网IP映射
|
||||
- 
|
||||
@@ -48,6 +53,7 @@ ORM操作数据库,使用环境JDK17 Spring Boot 3.0.2。的网络穿透、渗
|
||||
|
||||
[UI](https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network-server-ui)
|
||||
|
||||
|
||||
### 内网穿透
|
||||
|
||||
| 模块 | 所属层级 | 描述 | 快照版本 | 发布版本 |
|
||||
@@ -86,36 +92,36 @@ ORM操作数据库,使用环境JDK17 Spring Boot 3.0.2。的网络穿透、渗
|
||||
|
||||
### 项目结构
|
||||
|
||||
| 模块 | 版本 | 描述 |
|
||||
|------------------------------------------------------------------------------------------------------------|----------------------|------------------------------|
|
||||
| [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.4-JDK24-SNAPSHOT | 内网穿透公共模块(声明接口、枚举、常量、适配器、解析器) |
|
||||
| [wu-lazy-cloud-heartbeat-client](wu-lazy-cloud-heartbeat-client) | 1.3.4-JDK24-SNAPSHOT | 客户端(支持二次开发) |
|
||||
| [wu-lazy-cloud-heartbeat-server](wu-lazy-cloud-heartbeat-server) | 1.3.4-JDK24-SNAPSHOT | 服务端(支持二次开发) |
|
||||
| [wu-lazy-cloud-heartbeat-client-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start) | 1.3.4-JDK24-SNAPSHOT | 客户端样例 |
|
||||
| [wu-lazy-cloud-heartbeat-server-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-client-start) | 1.3.4-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-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.4-JDK24-SNAPSHOT | web容器 |
|
||||
| [Lazy -ORM](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-inner-intergration/wu-database-parent) | 1.3.4-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.4-JDK24-SNAPSHOT | 用户授权体系 |
|
||||
|
||||
### 使用环境
|
||||
|
||||
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.4-JDK24-SNAPSHOT
|
||||
|
||||
http://127.0.0.1:18080/swagger-ui/index.html
|
||||
|
||||
@@ -183,4 +189,7 @@ ORM操作数据库,使用环境JDK17 Spring Boot 3.0.2。的网络穿透、渗
|
||||

|
||||
- 服务端虚拟路由管理
|
||||
- 创建一个虚拟的不存在的IP,代理到指定的IP
|
||||

|
||||

|
||||
-
|
||||
- 代理流量管理
|
||||

|
||||
@@ -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.4-JDK24-SNAPSHOT
|
||||
env:
|
||||
- name: spring.lazy.netty.client.inet-host
|
||||
value: 124.222.48.62
|
||||
|
||||
BIN
client_proxy_flow_manager.png
Normal file
BIN
client_proxy_flow_manager.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
@@ -1,4 +1,5 @@
|
||||
#### docker环境安装wlcn
|
||||
|
||||
##### 启动服务端
|
||||
- 6001端口 webui
|
||||
- 7001端口 tcp连接端口
|
||||
@@ -7,11 +8,13 @@
|
||||
- 1001端口 自定义需要代理到客户端端口
|
||||
- 打开浏览器访问界面 http://127.0.0.1:6001/netty-server-ui/index.html
|
||||
|
||||
|
||||
```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.4-JDK24-SNAPSHOT
|
||||
|
||||
```
|
||||
|
||||
##### 启动客户端
|
||||
|
||||
- 6004端口 webui
|
||||
@@ -24,5 +27,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.4-JDK24-SNAPSHOT
|
||||
```
|
||||
|
||||
241
pom.xml
241
pom.xml
@@ -8,14 +8,33 @@
|
||||
<parent>
|
||||
<artifactId>wu-framework-parent</artifactId>
|
||||
<groupId>top.wu2020</groupId>
|
||||
<version>1.3.1-JDK24-SNAPSHOT</version>
|
||||
<version>1.3.6-JDK24</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>wu-lazy-cloud-network</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.3.1-JDK24-SNAPSHOT</version>
|
||||
<version>1.3.6-JDK24</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>
|
||||
<!-- 云上服务组件 -->
|
||||
@@ -26,7 +45,7 @@
|
||||
<module>wu-lazy-cloud-heartbeat-dns</module>
|
||||
<module>wu-lazy-cloud-heartbeat-protocol-proxy</module>
|
||||
|
||||
<!-- 样例 -->
|
||||
<!-- 启动类 -->
|
||||
<module>wu-lazy-cloud-heartbeat-start</module>
|
||||
</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.6-JDK24</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -79,200 +101,33 @@
|
||||
</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>
|
||||
<id>windows</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<packaging.type>msi</packaging.type>
|
||||
</properties>
|
||||
</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>
|
||||
<id>mac</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<packaging.type>dmg</packaging.type>
|
||||
</properties>
|
||||
</profile>
|
||||
<!-- 可以添加更多针对不同操作系统的 profile -->
|
||||
</profiles>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
28
version.md
28
version.md
@@ -1,6 +1,6 @@
|
||||
### 版本更新
|
||||
#### 1.2.3-JDK17-SNAPSHOT
|
||||
【fix】修正流量计算保存两位小数
|
||||
[fix]修正流量计算保存两位小数
|
||||
[fix] 添加配置spring.lazy.netty.enable 控制是否开启客户端默认是自动连接服务端的 需要手动关闭
|
||||
|
||||
|
||||
@@ -30,10 +30,32 @@
|
||||
[change] 修复通道关闭导致调度线程池submit异常问题
|
||||
[change] 添加记录客户端IP
|
||||
[change] 1.2.9为大版本,报文中添加数据无法向下兼容,建议服务端与客户端保持版本一致
|
||||
#### 1.3.1-JDK24-SNAPSHOT
|
||||
#### 1.3.4-JDK24-SNAPSHOT
|
||||
[add] 新增http代理(客户端代理服务端、客户端代理客户杜安、服务端代理客户端、服务端代理服务端)
|
||||
[add] 新增socks代理(客户端代理服务端、客户端代理客户杜安、服务端代理客户端、服务端代理服务端)
|
||||
[add] 新增客户端路由管理、新增服务端路由管理
|
||||
[add] 支持新增虚拟IP,代理到指定的IP
|
||||
[add] 通过页面配置,代理支持控制端口代理控制
|
||||
#### 下一版本计划
|
||||
#### 1.3.4-JDK24
|
||||
[add] 新增代理流量监控
|
||||
[add] docker仓库上架dockerhub,搜索https://hub.docker.com/search?q=wlcn
|
||||
|
||||
#### 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
|
||||
|
||||
#### 1.3.4-JDK24
|
||||
[fix]支持Mac平台应用安装,默认wlcn-client、wlcn-server 使用本地h2作为数据存储
|
||||
[fix]UI优化
|
||||
[add]添加区域管理
|
||||
[add]默认客户端ID为当前设备唯一标识
|
||||
|
||||
|
||||
#### 下一版本计划
|
||||
[add] 流媒体抓取
|
||||
[add] 监听本地网卡进行代理
|
||||
12
wlcn-website/BuildDocker.md
Normal file
12
wlcn-website/BuildDocker.md
Normal 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.4-JDK24-SNAPSHOT .
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/wu-lazy/wlcn-website:1.3.4-JDK24-SNAPSHOT
|
||||
|
||||
```
|
||||
2
wlcn-website/Dockerfile
Normal file
2
wlcn-website/Dockerfile
Normal 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
wlcn-website/README.md
Normal file
241
wlcn-website/README.md
Normal 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
|
||||
**更新时间**: 2025年8月
|
||||
**维护者**: 吴佳伟
|
||||
16
wlcn-website/index.html
Normal file
16
wlcn-website/index.html
Normal 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
wlcn-website/package.json
Normal file
42
wlcn-website/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "wu-lazy-cloud-network-website",
|
||||
"version": "1.3.3",
|
||||
"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"
|
||||
}
|
||||
1
wlcn-website/public/favicon.svg
Normal file
1
wlcn-website/public/favicon.svg
Normal 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
wlcn-website/src/App.vue
Normal file
58
wlcn-website/src/App.vue
Normal 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>
|
||||
417
wlcn-website/src/assets/styles/components.scss
Normal file
417
wlcn-website/src/assets/styles/components.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
232
wlcn-website/src/assets/styles/index.scss
Normal file
232
wlcn-website/src/assets/styles/index.scss
Normal 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;
|
||||
}
|
||||
189
wlcn-website/src/components/Footer.vue
Normal file
189
wlcn-website/src/components/Footer.vue
Normal 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 开发的高性能网络穿透和代理工具,
|
||||
支持 TCP、HTTP、SOCKS 协议,提供完整的内网穿透、网络代理、流量监控等功能。
|
||||
</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>© 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>
|
||||
219
wlcn-website/src/components/Header.vue
Normal file
219
wlcn-website/src/components/Header.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<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="http://demo-wlcn.wu-framework.cn/wlcn/index.html#/login" target="_blank" class="btn btn-outline">
|
||||
<el-icon><Platform /></el-icon>
|
||||
体验地址
|
||||
</a>
|
||||
<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
wlcn-website/src/favicon.ico
Normal file
BIN
wlcn-website/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
22
wlcn-website/src/main.js
Normal file
22
wlcn-website/src/main.js
Normal 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')
|
||||
63
wlcn-website/src/router/index.js
Normal file
63
wlcn-website/src/router/index.js
Normal 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
wlcn-website/src/utils/index.js
Normal file
227
wlcn-website/src/utils/index.js
Normal 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
wlcn-website/src/views/About.vue
Normal file
863
wlcn-website/src/views/About.vue
Normal 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>支持多种协议:TCP、HTTP、SOCKS</li>
|
||||
<li>提供完整的流量监控和统计功能</li>
|
||||
<li>支持集群部署和负载均衡</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="intro-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">1.3.3</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">< 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.3 (当前版本)</h3>
|
||||
<p class="timeline-date">2025年8月</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>
|
||||
798
wlcn-website/src/views/Architecture.vue
Normal file
798
wlcn-website/src/views/Architecture.vue
Normal 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
wlcn-website/src/views/Docs.vue
Normal file
967
wlcn-website/src/views/Docs.vue
Normal 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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器
|
||||
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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器
|
||||
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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT.jar
|
||||
|
||||
# 启动客户端
|
||||
cd ../wu-lazy-cloud-heartbeat-client-start
|
||||
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.4-JDK24-SNAPSHOT.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>
|
||||
773
wlcn-website/src/views/Download.vue
Normal file
773
wlcn-website/src/views/Download.vue
Normal 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.4-JDK24-SNAPSHOT</div>
|
||||
</div>
|
||||
<div class="version-details">
|
||||
<div class="detail-item">
|
||||
<span class="label">发布日期</span>
|
||||
<span class="value">2025年8月</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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器
|
||||
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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器
|
||||
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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT.jar
|
||||
|
||||
# 启动客户端
|
||||
cd ../wu-lazy-cloud-heartbeat-client-start
|
||||
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT.jar
|
||||
|
||||
# 客户端启动
|
||||
java -jar wu-lazy-cloud-heartbeat-client-start-1.3.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器
|
||||
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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT
|
||||
|
||||
# 启动容器(替换 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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT.jar
|
||||
|
||||
# 启动客户端
|
||||
cd ../wu-lazy-cloud-heartbeat-client-start
|
||||
java -jar target/wu-lazy-cloud-heartbeat-client-start-1.3.4-JDK24-SNAPSHOT.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.4-JDK24-SNAPSHOT.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>
|
||||
641
wlcn-website/src/views/Features.vue
Normal file
641
wlcn-website/src/views/Features.vue
Normal 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">< 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
wlcn-website/src/views/Home.vue
Normal file
649
wlcn-website/src/views/Home.vue
Normal 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 开发,支持 TCP、HTTP、SOCKS 协议,
|
||||
提供完整的内网穿透、网络代理、流量监控等功能。
|
||||
</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.3</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.4-JDK24-SNAPSHOT</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.4-JDK24-SNAPSHOT</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
wlcn-website/vite.config.js
Normal file
30
wlcn-website/vite.config.js
Normal 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']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -5,16 +5,37 @@
|
||||
<parent>
|
||||
<groupId>top.wu2020</groupId>
|
||||
<artifactId>wu-lazy-cloud-network</artifactId>
|
||||
<version>1.3.1-JDK24-SNAPSHOT</version>
|
||||
<version>1.3.6-JDK24</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>
|
||||
<javafx.version>24</javafx.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -22,11 +43,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 +60,26 @@
|
||||
<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>
|
||||
|
||||
|
||||
<!-- JavaFX Web 模块(包含 javafx.scene.web.JSObject) -->
|
||||
<!-- 窗口 -->
|
||||
<dependency>
|
||||
<groupId>top.wu2020</groupId>
|
||||
<artifactId>wu-framework-javafx-spring-starter</artifactId>
|
||||
<version>1.3.6-JDK24</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.framework.lazy.cloud.network.heartbeat.client.config;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import lombok.Data;
|
||||
import org.framework.lazy.cloud.network.heartbeat.common.enums.ProtocolType;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.wu.framework.core.NormalUsedString;
|
||||
import org.wu.framework.core.utils.ComputerUniqueIdUtil;
|
||||
|
||||
/**
|
||||
* netty 客户服务端地址配置属性
|
||||
@@ -30,20 +32,19 @@ public class NettyClientProperties {
|
||||
/**
|
||||
* 客户端ID 如:1024
|
||||
*/
|
||||
private String clientId;
|
||||
@Description("客户端ID,默认当前机器唯一序列号")
|
||||
private String clientId = ComputerUniqueIdUtil.generateUniqueId();
|
||||
|
||||
/**
|
||||
* 协议类型
|
||||
*/
|
||||
private ProtocolType protocolType = ProtocolType.TCP;
|
||||
/**
|
||||
*
|
||||
* 令牌key
|
||||
*/
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* 令牌密钥
|
||||
*/
|
||||
private String appSecret;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -9,69 +9,64 @@ import org.framework.lazy.cloud.network.heartbeat.common.enums.ProtocolType;
|
||||
import org.wu.framework.core.NormalUsedString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* describe 服务端配置信息
|
||||
* describe 服务端配置信息
|
||||
*
|
||||
* @author Jia wei Wu
|
||||
* @date 2024/04/03 03:00 下午
|
||||
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyDomain
|
||||
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyDomain
|
||||
**/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "lazy_netty_server_properties",description = "服务端配置信息")
|
||||
@Schema(title = "lazy_netty_server_properties", description = "服务端配置信息")
|
||||
public class LazyNettyServerProperties {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 客户身份ID
|
||||
*/
|
||||
@Schema(description ="客户身份ID",name ="clientId",example = "")
|
||||
@Schema(description = "客户身份ID", name = "clientId", example = "")
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
*
|
||||
* 状态(on_line、off_line)
|
||||
*/
|
||||
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
|
||||
@Schema(description = "状态(on_line、off_line)", name = "connectStatus", example = "")
|
||||
private NettyClientStatus connectStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description ="创建时间",name ="createTime",example = "")
|
||||
@Schema(description = "创建时间", name = "createTime", example = "")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 服务端host
|
||||
*/
|
||||
@Schema(description ="服务端host",name ="inetHost",example = "")
|
||||
@Schema(description = "服务端host", name = "inetHost", example = "")
|
||||
private String inetHost;
|
||||
|
||||
/**
|
||||
*
|
||||
* 服务端端口
|
||||
*/
|
||||
@Schema(description ="服务端端口",name ="inetPort",example = "")
|
||||
@Schema(description = "服务端端口", name = "inetPort", example = "")
|
||||
private Integer inetPort;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 类型(配置、DB)
|
||||
*/
|
||||
@Schema(description ="类型(配置、DB)",name ="type",example = "")
|
||||
@Schema(description = "类型(配置、DB)", name = "type", example = "")
|
||||
private PropertiesType type;
|
||||
|
||||
/**
|
||||
* 协议类型
|
||||
* 默认tcp
|
||||
*/
|
||||
@Schema(description = "协议类型", name = "protocol_type", example = "")
|
||||
private ProtocolType protocolType;
|
||||
private ProtocolType protocolType = ProtocolType.TCP;
|
||||
/**
|
||||
* 令牌key
|
||||
*/
|
||||
@@ -84,10 +79,9 @@ public class LazyNettyServerProperties {
|
||||
@Schema(description = "令牌密钥", name = "appSecret", example = "")
|
||||
private String appSecret;
|
||||
/**
|
||||
*
|
||||
* 更新时间
|
||||
*/
|
||||
@Schema(description ="更新时间",name ="updateTime",example = "")
|
||||
@Schema(description = "更新时间", name = "updateTime", example = "")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.6-JDK24</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,20 +41,30 @@
|
||||
<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.6-JDK24</version>
|
||||
</dependency>
|
||||
<!-- log -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>top.wu2020</groupId>-->
|
||||
<!-- <artifactId>wu-framework-log-spring-starter</artifactId>-->
|
||||
<!-- <version>1.3.6-JDK24</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -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);
|
||||
|
||||
@@ -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.6-JDK24</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>
|
||||
|
||||
|
||||
@@ -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.6-JDK24</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.6-JDK24</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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协议代理
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package org.framework.lazy.cloud.network.heartbeat.protocol.test1;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* 流量拦截处理器:执行拦截策略,处理流量转发/丢弃/修改
|
||||
*/
|
||||
public class TrafficInterceptHandler extends ChannelDuplexHandler {
|
||||
private final TrafficInterceptor.InterceptStrategy strategy;
|
||||
private Channel forwardChannel; // 转发通道(连接原目标服务器)
|
||||
private InetSocketAddress originalTarget; // 流量的原目标地址(需解析获取)
|
||||
|
||||
public TrafficInterceptHandler(TrafficInterceptor.InterceptStrategy strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端连接建立时,解析原目标地址(关键:透明代理需知道流量要发往的原目标)
|
||||
* 注:原目标地址的解析方式需根据场景调整(如通过路由表、ARP 缓存、或 RAW socket 抓包解析 IP 头部)
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
InetSocketAddress clientAddr = (InetSocketAddress) ctx.channel().remoteAddress();
|
||||
System.out.println("拦截到新连接:客户端 " + clientAddr);
|
||||
|
||||
// 关键步骤:解析流量的原目标地址(此处为模拟,实际需通过以下方式获取)
|
||||
// 方式 1:若拦截器作为网关,原目标地址即客户端请求的目标 IP:Port(需解析应用层协议,如 HTTP Host 头)
|
||||
// 方式 2:用 RAW socket 抓包,解析 IP 头部的「目的地址」字段(推荐,适用于所有 TCP 流量)
|
||||
// 方式 3:通过 iptables 端口转发,原目标地址由 iptables 传递(Linux 环境)
|
||||
// 此处模拟:假设原目标是百度服务器(实际需动态解析)
|
||||
originalTarget = new InetSocketAddress("220.181.38.251", 80);
|
||||
|
||||
// 根据策略初始化转发通道(若需要转发)
|
||||
if (strategy == TrafficInterceptor.InterceptStrategy.FORWARD || strategy == TrafficInterceptor.InterceptStrategy.MODIFY) {
|
||||
initForwardChannel(ctx);
|
||||
}
|
||||
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化转发通道:连接原目标服务器,实现透明转发
|
||||
*/
|
||||
private void initForwardChannel(ChannelHandlerContext ctx) {
|
||||
Bootstrap bootstrap = new Bootstrap()
|
||||
.group(ctx.channel().eventLoop()) // 复用客户端事件循环组,性能更优
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(io.netty.channel.ChannelOption.SO_KEEPALIVE, true)
|
||||
.handler(new ChannelDuplexHandler() {
|
||||
// 接收原目标服务器的响应,转发回客户端
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext forwardCtx, Object msg) throws Exception {
|
||||
ByteBuf response = (ByteBuf) msg;
|
||||
System.out.printf("收到原目标 %s 响应,长度:%d 字节%n", originalTarget, response.readableBytes());
|
||||
|
||||
// 若策略是「修改流量」,则修改响应内容
|
||||
if (strategy == TrafficInterceptor.InterceptStrategy.MODIFY) {
|
||||
modifyData(response, false); // false 表示修改响应流量
|
||||
}
|
||||
|
||||
// 转发响应到客户端
|
||||
ctx.channel().writeAndFlush(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext forwardCtx) throws Exception {
|
||||
System.out.println("转发通道断开:原目标 " + originalTarget);
|
||||
ctx.channel().close(); // 转发通道断开,关闭客户端连接
|
||||
}
|
||||
});
|
||||
|
||||
// 连接原目标服务器
|
||||
bootstrap.connect(originalTarget).addListener((ChannelFutureListener) future -> {
|
||||
if (future.isSuccess()) {
|
||||
forwardChannel = future.channel();
|
||||
System.out.println("成功连接原目标:" + originalTarget);
|
||||
} else {
|
||||
System.err.println("连接原目标失败:" + originalTarget + ",原因:" + future.cause().getMessage());
|
||||
ctx.channel().close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截客户端发送的流量(出站流量)
|
||||
*/
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
ByteBuf request = (ByteBuf) msg;
|
||||
InetSocketAddress clientAddr = (InetSocketAddress) ctx.channel().remoteAddress();
|
||||
System.out.printf("拦截到客户端 %s 发送的流量,长度:%d 字节%n", clientAddr, request.readableBytes());
|
||||
|
||||
switch (strategy) {
|
||||
case DROP:
|
||||
// 丢弃流量,释放缓冲区
|
||||
request.release();
|
||||
System.out.println("已丢弃该流量(策略:DROP)");
|
||||
break;
|
||||
|
||||
case MODIFY:
|
||||
// 修改流量内容(示例:在 HTTP 请求头添加自定义字段)
|
||||
modifyData(request, true); // true 表示修改请求流量
|
||||
System.out.println("已修改流量,准备转发");
|
||||
// fall through 到 FORWARD 逻辑
|
||||
case FORWARD:
|
||||
// 转发流量到原目标服务器
|
||||
if (forwardChannel != null && forwardChannel.isActive()) {
|
||||
forwardChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
|
||||
if (!future.isSuccess()) {
|
||||
System.err.println("流量转发失败:" + future.cause().getMessage());
|
||||
ctx.channel().close();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
request.release();
|
||||
System.err.println("转发通道未就绪,丢弃流量");
|
||||
}
|
||||
break;
|
||||
|
||||
case LOG_ONLY:
|
||||
// 仅记录日志,不拦截,直接放行(透传流量)
|
||||
super.write(ctx, msg, promise);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改流量内容(示例:修改 HTTP 请求/响应)
|
||||
* @param data 要修改的 ByteBuf 数据
|
||||
* @param isRequest 是否为请求流量(true:请求,false:响应)
|
||||
*/
|
||||
private void modifyData(ByteBuf data, boolean isRequest) {
|
||||
// 示例:在 HTTP 请求头添加 X-Intercepted: true 字段
|
||||
if (isRequest) {
|
||||
// 切换为读模式,读取 HTTP 头
|
||||
data.markReaderIndex();
|
||||
byte[] temp = new byte[data.readableBytes()];
|
||||
data.readBytes(temp);
|
||||
String content = new String(temp);
|
||||
|
||||
// 找到 HTTP 头的结束位置("\r\n\r\n"),插入自定义字段
|
||||
if (content.contains("\r\n\r\n")) {
|
||||
content = content.replace("\r\n\r\n", "\r\nX-Intercepted: true\r\n\r\n");
|
||||
// 重置 ByteBuf,写入修改后的数据
|
||||
data.resetReaderIndex();
|
||||
data.clear();
|
||||
data.writeBytes(content.getBytes());
|
||||
System.out.println("已修改请求流量:添加 X-Intercepted 头");
|
||||
}
|
||||
} else {
|
||||
// 示例:修改 HTTP 响应的内容
|
||||
data.markReaderIndex();
|
||||
byte[] temp = new byte[data.readableBytes()];
|
||||
data.readBytes(temp);
|
||||
String content = new String(temp);
|
||||
if (content.contains("<body>")) {
|
||||
content = content.replace("<body>", "<body><h1>流量已被 Netty 拦截并修改</h1>");
|
||||
data.resetReaderIndex();
|
||||
data.clear();
|
||||
data.writeBytes(content.getBytes());
|
||||
System.out.println("已修改响应流量:添加拦截提示");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截客户端接收的流量(响应流量)
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
// 若策略为「仅记录日志」,直接放行;其他策略已在转发通道中处理
|
||||
if (strategy == TrafficInterceptor.InterceptStrategy.LOG_ONLY) {
|
||||
ByteBuf data = (ByteBuf) msg;
|
||||
System.out.printf("记录流量:客户端接收数据,长度:%d 字节%n", data.readableBytes());
|
||||
super.channelRead(ctx, msg);
|
||||
} else {
|
||||
((ByteBuf) msg).release(); // 已在转发通道中处理,此处释放缓冲区
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接断开时,关闭转发通道
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
System.out.println("连接断开:客户端 " + ctx.channel().remoteAddress());
|
||||
if (forwardChannel != null && forwardChannel.isActive()) {
|
||||
forwardChannel.close();
|
||||
}
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理:关闭所有连接
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
System.err.println("流量拦截异常:" + cause.getMessage());
|
||||
ctx.close();
|
||||
if (forwardChannel != null && forwardChannel.isActive()) {
|
||||
forwardChannel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package org.framework.lazy.cloud.network.heartbeat.protocol.test1;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* 网络流量拦截器:绑定指定网卡,拦截该网卡的所有 TCP 流量,支持转发/丢弃/修改
|
||||
*/
|
||||
public class TrafficInterceptor {
|
||||
// 绑定的网卡 IP(必须是本地网卡已配置的 IP)
|
||||
private final String bindIp;
|
||||
// 拦截策略(可自定义:转发、丢弃、修改等)
|
||||
private final InterceptStrategy strategy;
|
||||
|
||||
// Netty 事件循环组
|
||||
private EventLoopGroup bossGroup;
|
||||
private EventLoopGroup workerGroup;
|
||||
|
||||
/**
|
||||
* 构造拦截器
|
||||
* @param bindIp 绑定的网卡 IP(如 192.168.1.100)
|
||||
* @param strategy 拦截策略
|
||||
*/
|
||||
public TrafficInterceptor(String bindIp, InterceptStrategy strategy) {
|
||||
this.bindIp = bindIp;
|
||||
this.strategy = strategy;
|
||||
validateBindIp(bindIp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动拦截器(监听绑定网卡的所有 TCP 端口,实际通过端口复用实现)
|
||||
* 注:全端口监听需操作系统支持,或通过「端口范围监听」模拟(此处用 1-65535 端口范围)
|
||||
*/
|
||||
public void start() throws InterruptedException {
|
||||
bossGroup = new NioEventLoopGroup(1);
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
// 核心:启动服务端,绑定网卡 IP,监听所有 TCP 端口(模拟全端口拦截)
|
||||
// 实际生产中可优化为:监听常用端口 + 动态端口,或用 RAW socket 直接抓包
|
||||
ServerBootstrap bootstrap = new ServerBootstrap()
|
||||
.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.localAddress(new InetSocketAddress(bindIp, 0)) // 端口 0 表示随机端口,实际通过端口复用扩展
|
||||
.option(ChannelOption.SO_REUSEADDR, true) // 允许端口复用(关键)
|
||||
.option(ChannelOption.SO_BACKLOG, 1024)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.handler(new LoggingHandler(LogLevel.INFO)) // 日志打印(可选)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline()
|
||||
.addLast(new LoggingHandler(LogLevel.DEBUG)) // 打印流量日志
|
||||
.addLast(new TrafficInterceptHandler(strategy)); // 核心拦截处理器
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定网卡 IP,监听所有端口(模拟:实际需遍历端口或用 RAW socket,此处以常用端口为例)
|
||||
System.out.printf("流量拦截器启动成功!绑定网卡:%s,拦截策略:%s%n", bindIp, strategy);
|
||||
System.out.println("开始拦截该网卡的所有 TCP 流量...");
|
||||
|
||||
// 阻塞等待服务端关闭
|
||||
bootstrap.bind().sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
System.out.println("流量拦截器已关闭");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验绑定的网卡是否存在
|
||||
*/
|
||||
private void validateBindIp(String bindIp) {
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
boolean exists = false;
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = interfaces.nextElement();
|
||||
Enumeration<java.net.InetAddress> addresses = ni.getInetAddresses();
|
||||
while (addresses.hasMoreElements()) {
|
||||
java.net.InetAddress addr = addresses.nextElement();
|
||||
if (addr.getHostAddress().equals(bindIp)) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exists) break;
|
||||
}
|
||||
if (!exists) {
|
||||
throw new IllegalArgumentException("绑定的网卡 IP " + bindIp + " 不存在于本地网卡");
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException("获取本地网卡信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截策略枚举(可扩展)
|
||||
*/
|
||||
public enum InterceptStrategy {
|
||||
FORWARD("转发流量到原目标"),
|
||||
DROP("丢弃流量"),
|
||||
MODIFY("修改流量内容后转发"),
|
||||
LOG_ONLY("仅记录日志,不拦截");
|
||||
|
||||
private final String desc;
|
||||
|
||||
InterceptStrategy(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// 示例:绑定网卡 192.168.1.100,策略为「记录日志并转发」
|
||||
String bindIp = "192.168.3.6";
|
||||
TrafficInterceptor.InterceptStrategy strategy = TrafficInterceptor.InterceptStrategy.LOG_ONLY;
|
||||
new TrafficInterceptor(bindIp, strategy).start();
|
||||
}
|
||||
}
|
||||
@@ -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.6-JDK24</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>
|
||||
|
||||
@@ -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.6-JDK24</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,30 @@
|
||||
<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>
|
||||
<!-- saas -->
|
||||
<dependency>
|
||||
<groupId>top.wu2020</groupId>
|
||||
<artifactId>wu-saas-share-platform-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- 窗口 -->
|
||||
<dependency>
|
||||
<groupId>top.wu2020</groupId>
|
||||
<artifactId>wu-framework-javafx-spring-starter</artifactId>
|
||||
<version>1.3.6-JDK24</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -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 集群配置信息
|
||||
|
||||
@@ -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 集群配置信息
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
// 断开客户端的连接:{}
|
||||
|
||||
@@ -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 客户端状态变更记录
|
||||
*
|
||||
|
||||
@@ -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 每日统计流量
|
||||
*
|
||||
|
||||
@@ -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 客户端代理路由规则
|
||||
|
||||
@@ -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 客户端代理路由规则
|
||||
|
||||
@@ -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 客户端代理路由规则
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
,
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.ve_menu_logo[data-v-52d93925]{width:100%;height:50px;white-space:nowrap;overflow:hidden}.ve_menu_logo .ve_logo_img[data-v-52d93925]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_menu_logo .ve_logo_title[data-v-52d93925]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_menu_logo .ve_logo_title[data-v-52d93925]:hover{color:#409eff}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_slider_menu[data-v-6e3be609]{cursor:pointer;margin-right:10px}.ve_slider_menu i[data-v-6e3be609]{font-size:40px}.ve_slider_menu:hover i[data-v-6e3be609]{color:#409eff}.ve_zone_logo[data-v-39b56a89]{width:100%;white-space:nowrap;overflow:hidden;display:flex;align-items:center}.ve_zone_logo .ve_zone_img[data-v-39b56a89]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_zone_logo .ve_zone_select[data-v-39b56a89]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_zone_logo .ve_zone_select[data-v-39b56a89]:hover{color:#409eff}.ve_personal[data-v-a0485328]{flex:1;text-align:right}.ve_personal .ve_nav_dropdown[data-v-a0485328]{font-weight:700}.ve_nav_bar[data-v-1236108a],.ve_nav_bar[data-v-3ca12f2e]{display:flex;align-items:center;height:100%}.ve_menu_logo[data-v-52d93925]{width:100%;height:50px;white-space:nowrap;overflow:hidden}.ve_menu_logo .ve_logo_img[data-v-52d93925]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_menu_logo .ve_logo_title[data-v-52d93925]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_menu_logo .ve_logo_title[data-v-52d93925]:hover{color:#409eff}li.el-menu-item.is-active{background-color:#31363a!important}.el-menu-item .el-icon svg,.el-sub-menu__title .el-icon svg{vertical-align:unset}a[data-v-e49a1e12]:active{color:#409eff}.ve_el_menu[data-v-e49a1e12]{background:#545c64;height:calc(100vh - var(--093d59b6))}.el-main[data-v-3c99ec1a]{height:calc(100vh - var(--9463ec52) - var(--16bdf557) - 18px);background:#f5f5f5}.el-main[data-v-3c99ec1a] .el-scrollbar__bar.is-horizontal{visibility:hidden}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_menu_logo[data-v-52d93925]{width:100%;height:50px;white-space:nowrap;overflow:hidden}.ve_menu_logo .ve_logo_img[data-v-52d93925]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_menu_logo .ve_logo_title[data-v-52d93925]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_menu_logo .ve_logo_title[data-v-52d93925]:hover{color:#409eff}li.el-menu-item.is-active{background-color:#31363a!important}.el-menu-item .el-icon svg,.el-sub-menu__title .el-icon svg{vertical-align:unset}a[data-v-e49a1e12]:active{color:#409eff}.ve_el_menu[data-v-e49a1e12]{background:#545c64;height:calc(100vh - var(--093d59b6))}
|
||||
@@ -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}
|
||||
@@ -1 +0,0 @@
|
||||
.el-form-item[data-v-af3b3260]{width:100%}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_zone_logo[data-v-39b56a89]{width:100%;white-space:nowrap;overflow:hidden;display:flex;align-items:center}.ve_zone_logo .ve_zone_img[data-v-39b56a89]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_zone_logo .ve_zone_select[data-v-39b56a89]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_zone_logo .ve_zone_select[data-v-39b56a89]:hover{color:#409eff}
|
||||
@@ -1 +0,0 @@
|
||||
.el-form-item[data-v-8c51848e]{width:100%}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_nav_bar[data-v-3ca12f2e]{display:flex;align-items:center;height:100%}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_slider_menu[data-v-6e3be609]{cursor:pointer;margin-right:10px}.ve_slider_menu i[data-v-6e3be609]{font-size:40px}.ve_slider_menu:hover i[data-v-6e3be609]{color:#409eff}.ve_zone_logo[data-v-39b56a89]{width:100%;white-space:nowrap;overflow:hidden;display:flex;align-items:center}.ve_zone_logo .ve_zone_img[data-v-39b56a89]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_zone_logo .ve_zone_select[data-v-39b56a89]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_zone_logo .ve_zone_select[data-v-39b56a89]:hover{color:#409eff}.ve_personal[data-v-a0485328]{flex:1;text-align:right}.ve_personal .ve_nav_dropdown[data-v-a0485328]{font-weight:700}.ve_nav_bar[data-v-1236108a]{display:flex;align-items:center;height:100%}
|
||||
@@ -1 +0,0 @@
|
||||
.ve_zone_logo[data-v-39b56a89]{width:100%;white-space:nowrap;overflow:hidden;display:flex;align-items:center}.ve_zone_logo .ve_zone_img[data-v-39b56a89]{height:100%;text-align:center;width:65px;display:inline-block;box-sizing:border-box;vertical-align:middle}.ve_zone_logo .ve_zone_select[data-v-39b56a89]{width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;margin:0;vertical-align:middle;color:#000}.ve_zone_logo .ve_zone_select[data-v-39b56a89]:hover{color:#409eff}.ve_personal[data-v-a0485328]{flex:1;text-align:right}.ve_personal .ve_nav_dropdown[data-v-a0485328]{font-weight:700}
|
||||
@@ -1,2 +0,0 @@
|
||||
.ve_table .ve_table_page[data-v-c40e394a],.ve_table[data-v-c40e394a]{flex:1;display:flex;flex-direction:column}.ve_table .ve_table_page .ve_table_content[data-v-c40e394a]{flex:1}.ve_table .ve_table_page[data-v-51533de8],.ve_table[data-v-51533de8]{flex:1;display:flex;flex-direction:column}.ve_table .ve_table_page .ve_table_content[data-v-51533de8]{flex:1}
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}html{background:#fff;transition:color .3s,background-color .3s}html.dark{filter:contrast(100%) invert(100%)}html.dark img{filter:hue-rotate(180deg)}.el-table__fixed-right-patch,.ve_header_cell_class_name,.ve_header_row_class_name{background:#f5f5f5!important}.ve_cell_class_name{border-color:#409eff!important}.ve_cell_class_name,.ve_row_class_name{background:#409eff!important}.ve_p_10{padding:10px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-thumb{background-color:hsla(220,4%,58%,.3);border-radius:3px}.ve_select_option_slot{font-size:12px;float:left;line-height:normal;padding-bottom:10px;opacity:.7}.ve_option_box{width:calc(50% - 132px)}.size-watch{width:100%;height:100%;position:absolute;top:0;z-index:-1;visibility:hidden;margin:0;padding:0;border:0}.ve_flex_col{display:flex;flex-direction:column;height:calc(100vh - 198px)}.el-select{width:120px}.el-form-item__label{text-align:left!important;width:auto!important}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><link rel="icon" href="favicon.png"/><title>网络渗透</title><script defer="defer" src="js/chunk-elementPlusIcon.a43625dd.js"></script><script defer="defer" src="js/chunk-elementPlus.672f27a8.js"></script><script defer="defer" src="js/chunk-mockjs.ce849090.js"></script><script defer="defer" src="js/chunk-vendors.e881d423.js"></script><script defer="defer" src="js/app.f86d3fe7.js"></script><link href="css/chunk-elementPlus.3e429b97.css" rel="stylesheet"><link href="css/app.4836014d.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but wu-vue-zone-ecology-net doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
@@ -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
Binary file not shown.
@@ -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
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -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"]])}}]);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user