mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-17 12:53:02 +00:00
Compare commits
1 Commits
0.1.8.prox
...
main-jdk11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ded137539 |
@@ -1,28 +0,0 @@
|
|||||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
||||||
// README at: https://github.com/devcontainers/templates/tree/main/src/java
|
|
||||||
{
|
|
||||||
"name": "Java",
|
|
||||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/java:0-17",
|
|
||||||
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers/features/java:1": {
|
|
||||||
"version": "none",
|
|
||||||
"installMaven": "true",
|
|
||||||
"installGradle": "false"
|
|
||||||
},
|
|
||||||
"ghcr.io/devcontainers-contrib/features/ant-sdkman:2": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
// "forwardPorts": [],
|
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
|
||||||
// "postCreateCommand": "java -version",
|
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
|
||||||
// "customizations": {},
|
|
||||||
|
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
||||||
// "remoteUser": "root"
|
|
||||||
}
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,4 +39,3 @@ gradlew.bat
|
|||||||
unused.txt
|
unused.txt
|
||||||
/web-service/src/main/generated/
|
/web-service/src/main/generated/
|
||||||
/db
|
/db
|
||||||
/webroot/nfd-front/
|
|
||||||
|
|||||||
70
.vscode/launch.json
vendored
70
.vscode/launch.json
vendored
@@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
// 使用 IntelliSense 了解相关属性。
|
|
||||||
// 悬停以查看现有属性的描述。
|
|
||||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "Current File",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "${file}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "StringCase",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.vx.core.util.StringCase",
|
|
||||||
"projectName": "core"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "FCURLParser",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.parser.FCURLParser",
|
|
||||||
"projectName": "parser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "QkTool",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.parser.impl.QkTool",
|
|
||||||
"projectName": "parser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "WebClientExample",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "qaiu.web.test.WebClientExample",
|
|
||||||
"projectName": "parser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "AppMain",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.lz.AppMain",
|
|
||||||
"projectName": "web-service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "TestJs",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.web.test.TestJs",
|
|
||||||
"projectName": "web-service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "TestOS",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.web.test.TestOS",
|
|
||||||
"projectName": "web-service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "WebProxyExamples",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "cn.qaiu.web.test.WebProxyExamples",
|
|
||||||
"projectName": "web-service"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,4 +1,3 @@
|
|||||||
{
|
{
|
||||||
"java.compile.nullAnalysis.mode": "automatic",
|
"java.compile.nullAnalysis.mode": "automatic"
|
||||||
"java.configuration.updateBuildConfiguration": "interactive"
|
|
||||||
}
|
}
|
||||||
84
README.md
84
README.md
@@ -1,20 +1,18 @@
|
|||||||
云盘解析服务 (nfd云解析)
|
云盘解析服务 (nfd云解析)
|
||||||
预览地址 https://lz.qaiu.top
|
预览地址 https://lz.qaiu.top
|
||||||
main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/netdisk-fast-download/tree/main-jdk11)
|
|
||||||
**注意: 请不要过度依赖lz.qaiu.top预览地址服务,建议本地搭建或者云服务器自行搭建。
|
**注意: 请不要过度依赖lz.qaiu.top预览地址服务,建议本地搭建或者云服务器自行搭建。
|
||||||
解析次数过多IP会被部分网盘厂商限制,不推荐做公共解析。**
|
解析次数过多IP会被部分网盘厂商限制,不推荐做公共解析。**
|
||||||
|
|
||||||
[](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml)
|
[](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml)
|
||||||
[](https://www.oracle.com/cn/java/technologies/downloads/)
|
[](https://www.oracle.com/cn/java/technologies/downloads/)
|
||||||
[](https://vertx-china.github.io/)
|
[](https://vertx-china.github.io/)
|
||||||
[](https://github.com/qaiu/netdisk-fast-download/releases/latest)
|
[](https://github.com/qaiu/netdisk-fast-download/releases/tag/0.1.6-releases)
|
||||||
|
|
||||||
## 项目介绍
|
## 项目介绍
|
||||||
网盘直链解析工具能把网盘分享下载链接转化为直链,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享。
|
网盘直链解析工具能把网盘分享下载链接转化为直链,已支持蓝奏云/蓝奏云优享/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘/Cloudreve等,支持加密分享。
|
||||||
**20240929 1k Star留念,0.1.8版本json接口格式有调整,尤其依赖lz.qaiu.top做下载服务的朋友们记得修改, 参考json返回数据格式示例**
|
|
||||||
|
|
||||||
|
|
||||||
*重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。转发/分享该项目请注明来源*
|
*重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。*
|
||||||
|
|
||||||
## 网盘支持情况:
|
## 网盘支持情况:
|
||||||
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
||||||
@@ -24,17 +22,38 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
|||||||
`网盘名称(网盘标识):`
|
`网盘名称(网盘标识):`
|
||||||
|
|
||||||
- [蓝奏云 (lz)](https://pc.woozooo.com/)
|
- [蓝奏云 (lz)](https://pc.woozooo.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [蓝奏云优享 (iz)](https://www.ilanzou.com/)
|
- [蓝奏云优享 (iz)](https://www.ilanzou.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [奶牛快传 (cow)](https://cowtransfer.com/)
|
- [奶牛快传 (cow)](https://cowtransfer.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [移动云云空间 (ec)](https://www.ecpan.cn/web)
|
- [移动云云空间 (ec)](https://www.ecpan.cn/web)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [小飞机网盘 (fj)](https://www.feijipan.com/)
|
- [小飞机网盘 (fj)](https://www.feijipan.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [亿方云 (fc)](https://www.fangcloud.com/)
|
- [亿方云 (fc)](https://www.fangcloud.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [123云盘 (ye)](https://www.123pan.com/)
|
- [123云盘 (ye)](https://www.123pan.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
- [文叔叔 (ws)](https://www.wenshushu.cn/)
|
- [文叔叔 (ws)](https://www.wenshushu.cn/)
|
||||||
- [联想乐云 (le)](https://lecloud.lenovo.com/)
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
- [QQ邮箱文件中转站 (qq)](https://mail.qq.com/)
|
- [X] 直链解析
|
||||||
- [超星网盘-开发中 (cx)](https://passport2.chaoxing.com/login?newversion=true&refer=https%3A%2F%2Fpan-yz.chaoxing.com%2F)
|
- [Cloudreve自建网盘 (ce)](https://github.com/cloudreve/Cloudreve)
|
||||||
- [Cloudreve自建网盘--正在优化 (ce)](https://github.com/cloudreve/Cloudreve)
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析
|
||||||
|
- [QQ邮箱 (qq) 暂不可用-存在cookie问题](https://wx.mail.qq.com/)
|
||||||
|
- [ ] 登录, 上传, 下载, 分享
|
||||||
|
- [X] 直链解析(用户无法直接使用直链)
|
||||||
|
- [夸克网盘 (qk) 寄了](https://pan.quark.cn/)
|
||||||
|
- [UC网盘 (uc) 寄了](https://fast.uc.cn/)
|
||||||
|
|
||||||
**TODO:**
|
**TODO:**
|
||||||
- 登录接口, 文件上传/下载/分享后端接口
|
- 登录接口, 文件上传/下载/分享后端接口
|
||||||
- 短地址服务
|
- 短地址服务
|
||||||
@@ -42,24 +61,38 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
|||||||
|
|
||||||
### API接口说明
|
### API接口说明
|
||||||
your_host指的是您的域名或者IP,实际使用时替换为实际域名或者IP,端口默认6400,可以使用nginx代理来做域名访问。
|
your_host指的是您的域名或者IP,实际使用时替换为实际域名或者IP,端口默认6400,可以使用nginx代理来做域名访问。
|
||||||
解析方式分为两种类型直接跳转下载文件和获取下载链接,每一种都提供了两种接口形式parser和网盘标志/分享key拼接的短地址(标志短链),所有规则参考示例。
|
解析方式分为两种类型直接跳转下载链接和获取下载链接(JSON),每一种都提供了两种接口形式parser和网盘标志/分享key拼接的短地址(标志短链),所有规则参考示例。
|
||||||
- 通用接口: `/parser?url=分享链接`,加密分享需要加上参数pwd=密码;
|
- 通用接口: `/parser?url=分享链接`,加密分享需要加上参数pwd=密码;
|
||||||
- 标志短链: `/网盘标识/分享key` 在分享Key后面加上@密码;
|
- 标志短链: `/网盘标识/分享key` 在分享Key后面加上@密码;
|
||||||
- 直链JSON: `通用接口`和`标志短链`前加上`/json` 加密分享的密码规则同上;
|
- 直链JSON: `通用接口`和`标志短链`前加上`/json` 加密分享的密码规则同上;
|
||||||
- 网盘标识参考上面网盘支持情况
|
- 网盘标识参考上面网盘支持情况
|
||||||
- 当带有分享密码时需要加上密码参数(pwd)
|
- 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
|
||||||
- 移动云云空间,小飞机网盘的加密分享的密码可以忽略
|
- 移动云云空间,小飞机网盘的加密分享的密码可以忽略
|
||||||
- 移动云空间分享key取分享链接中的data参数,比如`&data=xxx`的参数就是xxx
|
- 移动云空间分享key取分享链接中的data参数,比如`&data=xxx`的参数就是xxx
|
||||||
|
|
||||||
API规则:
|
规则示例:
|
||||||
```
|
```
|
||||||
|
|
||||||
1. 解析并自动302跳转 :
|
1. 解析并自动302跳转 :
|
||||||
http://your_host/parser?url=分享链接&pwd=xxx
|
http://your_host/parser?url=分享链接(&pwd=xxx)
|
||||||
http://your_host/网盘标识/分享key@分享密码
|
http://your_host/网盘标识/分享key(@分享密码)
|
||||||
2. 获取解析后的直链--JSON格式
|
2. 获取解析后的直链--JSON格式
|
||||||
http://your_host/json/parser?url=分享链接&pwd=xxx
|
http://your_host/json/parser?url=分享链接(&pwd=xxx)
|
||||||
http://your_host/json/网盘标识/分享key@分享密码
|
http://your_host/json/网盘标识/分享key(@分享密码)
|
||||||
|
3. 需要特殊处理的网盘分享:
|
||||||
|
1. 移动云云空间(ec)使用parser?url= 解析时因为分享链接比较特殊(链接带有参数且含有#符号)所以要么对#进行转义%23要么直接去掉# 或者URL直接是主机名+'/'跟一个data参数
|
||||||
|
比如 http://your_host/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
|
http://your_host/parser?url=https://www.ecpan.cn/web/%23/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
|
http://your_host/parser?url=https://www.ecpan.cn/&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
|
|
||||||
|
2. Cloudreve自建网盘解析规则:
|
||||||
|
1. 标志短链: 根据网盘使用https和http选择 http://your_host/ce/https_网盘域名_s_wDz5TK 或 http://your_host/ce/http_网盘域名_s_wDz5TK
|
||||||
|
网盘域名指的是Cloudreve搭建网盘的主域名比如pan.huang1111.cn,如果存在子路径需要将/替换为_,是否存在子路径看分享链接格式是否是://网盘域名/子路径/s/xxx,一般不存在子路径:网盘域名/s/xxx,
|
||||||
|
比如: http://127.0.0.1:6400/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||||
|
2. parser接口 -> http://your_host/parser?url=分享链接(&pwd=xxx)
|
||||||
|
比如: http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
json返回数据格式示例:
|
json返回数据格式示例:
|
||||||
@@ -69,14 +102,8 @@ json返回数据格式示例:
|
|||||||
"msg": "success",
|
"msg": "success",
|
||||||
"success": true,
|
"success": true,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"data": {
|
"data": "https://下载链接",
|
||||||
"shareKey": "lz:xxx",
|
"timestamp": 1690733953927
|
||||||
"directLink": "下载直链",
|
|
||||||
"cacheHit": true,
|
|
||||||
"expires": "2024-09-18 01:48:02",
|
|
||||||
"expiration": 1726638482825
|
|
||||||
},
|
|
||||||
"timestamp": 1726637151902
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -149,11 +176,10 @@ mvn package
|
|||||||
```
|
```
|
||||||
打包好的文件位于 web-service/target/netdisk-fast-download-bin.zip
|
打包好的文件位于 web-service/target/netdisk-fast-download-bin.zip
|
||||||
## Linux服务部署
|
## Linux服务部署
|
||||||
### [宝塔安装参考](https://blog.qaiu.top/archives/netdisk-fast-download-bao-ta-an-zhuang-jiao-cheng)
|
|
||||||
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
|
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
|
||||||
```shell
|
```shell
|
||||||
cd ~
|
cd ~
|
||||||
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.8-release-fixed2/netdisk-fast-download-bin-fixed2.zip
|
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.7-release-fixed2/netdisk-fast-download-bin-fixed2.zip
|
||||||
unzip netdisk-fast-download-bin.zip
|
unzip netdisk-fast-download-bin.zip
|
||||||
cd netdisk-fast-download
|
cd netdisk-fast-download
|
||||||
bash service-install.sh
|
bash service-install.sh
|
||||||
@@ -187,9 +213,9 @@ bash service-install.sh
|
|||||||
|
|
||||||
## 0.1.8 开发计划
|
## 0.1.8 开发计划
|
||||||
- Docker部署
|
- Docker部署
|
||||||
- 联想乐云解析 √
|
- 联想乐云解析
|
||||||
- 直链缓存 √
|
- 直链缓存
|
||||||
- 日志优化 √
|
- 日志优化
|
||||||
|
|
||||||
|
|
||||||
**技术栈:**
|
**技术栈:**
|
||||||
@@ -203,7 +229,7 @@ Core模块集成Vert.x实现类似spring的注解式路由API
|
|||||||
|
|
||||||
|
|
||||||
## 支持该项目
|
## 支持该项目
|
||||||
开源不易,用爱发电,本项目长期维护如果觉得有帮助, 可以请作者喝杯咖啡, 感谢支持
|
本项目长期维护如果觉得有帮助, 可以请作者喝杯咖啡, 感谢支持
|
||||||

|

|
||||||
|
|
||||||
[手机端支付宝打赏跳转链接](https://qr.alipay.com/fkx01882dnoxxtjenhlxt53)
|
[手机端支付宝打赏跳转链接](https://qr.alipay.com/fkx01882dnoxxtjenhlxt53)
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
|
|
||||||
rem 获取当前 Java 版本信息并搜索是否包含 "17."
|
rem 获取当前 Java 版本信息并搜索是否包含 "11."
|
||||||
java -version 2>&1 | find "17." >nul
|
java -version 2>&1 | find "11." >nul
|
||||||
|
|
||||||
rem 如果找不到 JDK 17.x,则下载并安装
|
rem 如果找不到 JDK 17.x,则下载并安装
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo JDK 17.x not found. Downloading and installing...
|
echo JDK 11.x not found. Downloading and installing...
|
||||||
|
|
||||||
REM 这里添加下载和安装 JDK 的代码
|
REM 这里添加下载和安装 JDK 的代码
|
||||||
|
|
||||||
rem 验证安装
|
rem 验证安装
|
||||||
java -version
|
java -version
|
||||||
|
|
||||||
echo JDK 17.x installation complete.
|
echo JDK 11.x installation complete.
|
||||||
) else (
|
) else (
|
||||||
echo JDK 17.x is already installed.
|
echo JDK 11.x is already installed.
|
||||||
)
|
)
|
||||||
|
|
||||||
endlocal
|
endlocal
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Wants=network-online.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
# User=USER
|
# User=USER
|
||||||
# 需要JDK17及以上版本 注意修改为自己的路径
|
# 需要JDK11及以上版本 注意修改为自己的路径
|
||||||
ExecStart=/root/java/jdk-17.0.2/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download/netdisk-fast-download.jar
|
ExecStart=/root/java/jdk-17.0.2/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download/netdisk-fast-download.jar
|
||||||
ExecStop=/bin/kill -s QUIT $MAINPID
|
ExecStop=/bin/kill -s QUIT $MAINPID
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
@echo off && @chcp 65001 > nul
|
@echo off && @chcp 65001 > nul
|
||||||
|
|
||||||
|
:: 需要JDK11及以上版本和Windows环境变量已配置jdk的路径
|
||||||
pushd %~dp0
|
pushd %~dp0
|
||||||
set LIB_DIR=%~dp0
|
set LIB_DIR=%~dp0
|
||||||
for /f "delims=X" %%i in ('dir /b %LIB_DIR%\netdisk-fast-download.jar') do (
|
for /f "delims=X" %%i in ('dir /b %LIB_DIR%\netdisk-fast-download.jar') do (
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<artifactId>core-database</artifactId>
|
<artifactId>core-database</artifactId>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>17</java.version>
|
<java.version>11</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.5</slf4j.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class JDBCPoolInit {
|
|||||||
JsonObject dbConfig;
|
JsonObject dbConfig;
|
||||||
Vertx vertx = VertxHolder.getVertxInstance();
|
Vertx vertx = VertxHolder.getVertxInstance();
|
||||||
String url;
|
String url;
|
||||||
private final JDBCType type;
|
private JDBCType type;
|
||||||
|
|
||||||
private static JDBCPoolInit instance;
|
private static JDBCPoolInit instance;
|
||||||
|
|
||||||
@@ -64,17 +64,22 @@ public class JDBCPoolInit {
|
|||||||
* init h2db<br>
|
* init h2db<br>
|
||||||
* 这个方法只允许调用一次
|
* 这个方法只允许调用一次
|
||||||
*/
|
*/
|
||||||
synchronized public void initPool() {
|
public void initPool() {
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
LOGGER.error("pool 重复初始化");
|
LOGGER.error("pool 重复初始化");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化数据库连接
|
// 初始化数据库连接
|
||||||
|
vertx.createSharedWorkerExecutor("sql-pool-init")
|
||||||
|
.executeBlocking(() -> {
|
||||||
// 初始化连接池
|
// 初始化连接池
|
||||||
pool = JDBCPool.pool(vertx, dbConfig);
|
pool = JDBCPool.pool(vertx, dbConfig);
|
||||||
CreateTable.createTable(pool, type);
|
CreateTable.createTable(pool, type);
|
||||||
LOGGER.info("数据库连接初始化: URL=" + url);
|
return "数据库连接初始化: URL=" + url;
|
||||||
|
})
|
||||||
|
.onSuccess(LOGGER::info)
|
||||||
|
.onFailure(Throwable::printStackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +87,7 @@ public class JDBCPoolInit {
|
|||||||
*
|
*
|
||||||
* @return pool
|
* @return pool
|
||||||
*/
|
*/
|
||||||
synchronized public JDBCPool getPool() {
|
public JDBCPool getPool() {
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,19 +87,15 @@ public final class Deploy {
|
|||||||
var calendar = Calendar.getInstance();
|
var calendar = Calendar.getInstance();
|
||||||
calendar.setTime(new Date());
|
calendar.setTime(new Date());
|
||||||
var year = calendar.get(Calendar.YEAR);
|
var year = calendar.get(Calendar.YEAR);
|
||||||
var logoTemplate = """
|
|
||||||
|
|
||||||
Web Server powered by:\s
|
|
||||||
____ ____ _ _ _ \s
|
|
||||||
|_^^_| |_^^_| / |_ | | | | \s
|
|
||||||
\\ \\ / /.---. _ .--.`| |-' _ __ | |__| |_ \s
|
|
||||||
\\ \\ / // /__\\\\[ `/'`\\]| | [ \\ [ ]|____ _|\s
|
|
||||||
\\ V / | \\__., | | | |, _ > ' < _| |_ \s
|
|
||||||
\\_/ '.__.'[___] \\__/(_)[__]`\\_] |_____|\s
|
|
||||||
Version: %s; Framework version: %s; %s©%d.
|
|
||||||
|
|
||||||
""";
|
|
||||||
|
|
||||||
|
String logoTemplate = "Web Server powered by: \n" +
|
||||||
|
" ____ ____ _ _ _ \n" +
|
||||||
|
"|_^^_| |_^^_| / |_ | | | | \n" +
|
||||||
|
" \\ \\ / /.---. _ .--.`| |-' _ __ | |__| |_ \n" +
|
||||||
|
" \\ \\ / // /__\\\\[ `/'`\\]| | [ \\ [ ]|____ _|\n" +
|
||||||
|
" \\ V / | \\__., | | | |, _ > ' < _| |_ \n" +
|
||||||
|
" \\_/ '.__.'[___] \\__/(_)[__]`\\_] |_____|\n" +
|
||||||
|
" Version: %s; Framework version: %s; JDK11; %s©%d.\n\n";
|
||||||
System.out.printf(logoTemplate,
|
System.out.printf(logoTemplate,
|
||||||
conf.getString("version_app"),
|
conf.getString("version_app"),
|
||||||
VersionCommand.getVersion(),
|
VersionCommand.getVersion(),
|
||||||
@@ -132,23 +128,20 @@ public final class Deploy {
|
|||||||
localMap.put(GLOBAL_CONFIG, globalConfig);
|
localMap.put(GLOBAL_CONFIG, globalConfig);
|
||||||
localMap.put(CUSTOM_CONFIG, customConfig);
|
localMap.put(CUSTOM_CONFIG, customConfig);
|
||||||
localMap.put(SERVER, globalConfig.getJsonObject(SERVER));
|
localMap.put(SERVER, globalConfig.getJsonObject(SERVER));
|
||||||
var future0 = vertx.createSharedWorkerExecutor("other-handle")
|
var future0 = vertx.createSharedWorkerExecutor("other-handle").executeBlocking(() -> {
|
||||||
.executeBlocking(() -> {
|
|
||||||
handle.handle(globalConfig);
|
handle.handle(globalConfig);
|
||||||
return "Other handle complete";
|
LOGGER.info("other handle complete");
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
future0.onSuccess(res -> {
|
|
||||||
LOGGER.info(res);
|
|
||||||
// 部署 路由、异步service、反向代理 服务
|
// 部署 路由、异步service、反向代理 服务
|
||||||
var future1 = vertx.deployVerticle(RouterVerticle.class, getWorkDeploymentOptions("Router"));
|
var future1 = vertx.deployVerticle(RouterVerticle.class, getWorkDeploymentOptions("Router"));
|
||||||
var future2 = vertx.deployVerticle(ServiceVerticle.class, getWorkDeploymentOptions("Service"));
|
var future2 = vertx.deployVerticle(ServiceVerticle.class, getWorkDeploymentOptions("Service"));
|
||||||
var future3 = vertx.deployVerticle(ReverseProxyVerticle.class, getWorkDeploymentOptions("proxy"));
|
var future3 = vertx.deployVerticle(ReverseProxyVerticle.class, getWorkDeploymentOptions("proxy"));
|
||||||
|
|
||||||
Future.all(future1, future2, future3)
|
Future.all(future1, future2, future3, future0)
|
||||||
.onSuccess(this::deployWorkVerticalSuccess)
|
.onSuccess(this::deployWorkVerticalSuccess)
|
||||||
.onFailure(this::deployVerticalFailed);
|
.onFailure(this::deployVerticalFailed);
|
||||||
}).onFailure(e -> LOGGER.error("Other handle error", e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,7 +178,7 @@ public final class Deploy {
|
|||||||
private DeploymentOptions getWorkDeploymentOptions(String name, int ins) {
|
private DeploymentOptions getWorkDeploymentOptions(String name, int ins) {
|
||||||
return new DeploymentOptions()
|
return new DeploymentOptions()
|
||||||
.setWorkerPoolName(name)
|
.setWorkerPoolName(name)
|
||||||
.setWorker(true)
|
.setThreadingModel(ThreadingModel.WORKER)
|
||||||
.setInstances(ins);
|
.setInstances(ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,15 +32,12 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static cn.qaiu.vx.core.util.ConfigConstant.ROUTE_TIME_OUT;
|
import static cn.qaiu.vx.core.util.ConfigConstant.ROUTE_TIME_OUT;
|
||||||
import static cn.qaiu.vx.core.verticle.ReverseProxyVerticle.REROUTE_PATH_PREFIX;
|
|
||||||
import static io.vertx.core.http.HttpHeaders.*;
|
import static io.vertx.core.http.HttpHeaders.*;
|
||||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路由映射, 参数绑定
|
* 路由映射, 参数绑定
|
||||||
@@ -73,19 +70,23 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
|||||||
public Router createRouter() {
|
public Router createRouter() {
|
||||||
// 主路由
|
// 主路由
|
||||||
Router mainRouter = Router.router(VertxHolder.getVertxInstance());
|
Router mainRouter = Router.router(VertxHolder.getVertxInstance());
|
||||||
mainRouter.route().handler(ctx -> {
|
|
||||||
String realPath = ctx.request().uri();;
|
// 静态资源
|
||||||
if (realPath.startsWith(REROUTE_PATH_PREFIX)) {
|
String path = SharedDataUtil.getJsonConfig("server")
|
||||||
// vertx web proxy暂不支持rewrite, 所以这里进行手动替换, 请求地址中的请求path前缀替换为originPath
|
.getString("staticResourcePath");
|
||||||
String rePath = realPath.substring(REROUTE_PATH_PREFIX.length());
|
if (!StringUtils.isEmpty(path)) {
|
||||||
ctx.reroute(rePath);
|
// 静态资源
|
||||||
return;
|
mainRouter.route("/*").handler(StaticHandler
|
||||||
|
.create(path)
|
||||||
|
.setCachingEnabled(true)
|
||||||
|
.setDefaultContentEncoding("UTF-8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mainRouter.route().handler(ctx -> {
|
||||||
LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}",
|
LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}",
|
||||||
ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method());
|
ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method());
|
||||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||||
ctx.response().headers().add(DATE, LocalDateTime.now().format(ISO_LOCAL_DATE_TIME));
|
|
||||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS, PUT, DELETE, HEAD");
|
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS, PUT, DELETE, HEAD");
|
||||||
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, " +
|
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, " +
|
||||||
"X-Requested-With, Dev, Authorization, Version, Token");
|
"X-Requested-With, Dev, Authorization, Version, Token");
|
||||||
@@ -111,7 +112,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
|||||||
return Integer.compare(routeHandler2.order(), routeHandler1.order());
|
return Integer.compare(routeHandler2.order(), routeHandler1.order());
|
||||||
};
|
};
|
||||||
// 获取处理器类列表
|
// 获取处理器类列表
|
||||||
List<Class<?>> sortedHandlers = handlers.stream().sorted(comparator).toList();
|
List<Class<?>> sortedHandlers = handlers.stream().sorted(comparator).collect(Collectors.toList());
|
||||||
for (Class<?> handler : sortedHandlers) {
|
for (Class<?> handler : sortedHandlers) {
|
||||||
try {
|
try {
|
||||||
// 注册请求处理方法
|
// 注册请求处理方法
|
||||||
@@ -152,7 +153,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
|
|||||||
|
|
||||||
methodList.addAll(Stream.of(methods).filter(
|
methodList.addAll(Stream.of(methods).filter(
|
||||||
method -> method.isAnnotationPresent(SockRouteMapper.class)
|
method -> method.isAnnotationPresent(SockRouteMapper.class)
|
||||||
).toList());
|
).collect(Collectors.toList()));
|
||||||
|
|
||||||
// 依次注册处理方法
|
// 依次注册处理方法
|
||||||
for (Method method : methodList) {
|
for (Method method : methodList) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,7 +16,6 @@ import java.io.Serializable;
|
|||||||
*/
|
*/
|
||||||
public class JsonResult<T> implements Serializable {
|
public class JsonResult<T> implements Serializable {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final int SUCCESS_CODE = 200;
|
private static final int SUCCESS_CODE = 200;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ public interface ConfigConstant {
|
|||||||
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
|
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
|
||||||
String LOCAL = "local";
|
String LOCAL = "local";
|
||||||
String SERVER = "server";
|
String SERVER = "server";
|
||||||
String CACHE = "cache";
|
|
||||||
String GLOBAL_CONFIG = "globalConfig";
|
String GLOBAL_CONFIG = "globalConfig";
|
||||||
String CUSTOM_CONFIG = "customConfig";
|
String CUSTOM_CONFIG = "customConfig";
|
||||||
String ASYNC_SERVICE_INSTANCES = "asyncServiceInstances";
|
String ASYNC_SERVICE_INSTANCES = "asyncServiceInstances";
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import io.vertx.core.Future;
|
|||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
import io.vertx.core.http.HttpClient;
|
import io.vertx.core.http.HttpClient;
|
||||||
import io.vertx.core.http.HttpServer;
|
import io.vertx.core.http.HttpServer;
|
||||||
import io.vertx.core.http.HttpServerOptions;
|
import io.vertx.core.http.WebSocket;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.core.net.PemKeyCertOptions;
|
|
||||||
import io.vertx.ext.web.Route;
|
|
||||||
import io.vertx.ext.web.Router;
|
import io.vertx.ext.web.Router;
|
||||||
import io.vertx.ext.web.handler.StaticHandler;
|
import io.vertx.ext.web.handler.StaticHandler;
|
||||||
|
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||||
|
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
|
||||||
import io.vertx.ext.web.proxy.handler.ProxyHandler;
|
import io.vertx.ext.web.proxy.handler.ProxyHandler;
|
||||||
import io.vertx.httpproxy.HttpProxy;
|
import io.vertx.httpproxy.HttpProxy;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -40,12 +40,10 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
.getJsonConfig(ConfigConstant.GLOBAL_CONFIG)
|
.getJsonConfig(ConfigConstant.GLOBAL_CONFIG)
|
||||||
.getString("proxyConf");
|
.getString("proxyConf");
|
||||||
private static final Future<JsonObject> CONFIG = ConfigUtil.readYamlConfig(PATH_PROXY_CONFIG);
|
private static final Future<JsonObject> CONFIG = ConfigUtil.readYamlConfig(PATH_PROXY_CONFIG);
|
||||||
private static final String DEFAULT_PATH_404 = "webroot/err/page404.html";
|
private static final String DEFAULT_PATH_404 = "webroot/err/404.html";
|
||||||
|
|
||||||
private static String serverName = "Vert.x-proxy-server"; //Server name in Http response header
|
private static String serverName = "Vert.x-proxy-server"; //Server name in Http response header
|
||||||
|
|
||||||
public static String REROUTE_PATH_PREFIX = "/__rrvpspp"; //re_route_vert_proxy_server_path_prefix 硬编码
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Promise<Void> startPromise) throws Exception {
|
public void start(Promise<Void> startPromise) throws Exception {
|
||||||
@@ -76,24 +74,22 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
* @param proxyConf 代理配置
|
* @param proxyConf 代理配置
|
||||||
*/
|
*/
|
||||||
private void handleProxyConf(JsonObject proxyConf) {
|
private void handleProxyConf(JsonObject proxyConf) {
|
||||||
// page404 path
|
// 404 path
|
||||||
if (proxyConf.containsKey(
|
if (proxyConf.containsKey("404")) {
|
||||||
|
|
||||||
"page404")) {
|
|
||||||
System.getProperty("user.dir");
|
System.getProperty("user.dir");
|
||||||
String path = proxyConf.getString("page404");
|
String path = proxyConf.getString("404");
|
||||||
if (StringUtils.isEmpty(path)) {
|
if (StringUtils.isEmpty(path)) {
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
proxyConf.put("404", DEFAULT_PATH_404);
|
||||||
} else {
|
} else {
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
path = "/" + path;
|
path = "/" + path;
|
||||||
}
|
}
|
||||||
if (!new File(System.getProperty("user.dir") + path).exists()) {
|
if (!new File(System.getProperty("user.dir") + path).exists()) {
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
proxyConf.put("404", DEFAULT_PATH_404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
proxyConf.put("404", DEFAULT_PATH_404);
|
||||||
}
|
}
|
||||||
|
|
||||||
final HttpClient httpClient = VertxHolder.getVertxInstance().createHttpClient();
|
final HttpClient httpClient = VertxHolder.getVertxInstance().createHttpClient();
|
||||||
@@ -115,12 +111,17 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
handleStatic(proxyConf.getJsonObject("static"), proxyRouter);
|
handleStatic(proxyConf.getJsonObject("static"), proxyRouter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send page404 page
|
// static server
|
||||||
|
if (proxyConf.containsKey("sock")) {
|
||||||
|
handleSock(proxyConf.getJsonArray("sock"), httpClient, proxyRouter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send 404 page
|
||||||
proxyRouter.errorHandler(404, ctx -> {
|
proxyRouter.errorHandler(404, ctx -> {
|
||||||
ctx.response().sendFile(proxyConf.getString("page404"));
|
ctx.response().sendFile(proxyConf.getString("404"));
|
||||||
});
|
});
|
||||||
|
|
||||||
HttpServer server = getHttpsServer(proxyConf);
|
HttpServer server = vertx.createHttpServer();
|
||||||
server.requestHandler(proxyRouter);
|
server.requestHandler(proxyRouter);
|
||||||
|
|
||||||
Integer port = proxyConf.getInteger("listen");
|
Integer port = proxyConf.getInteger("listen");
|
||||||
@@ -128,38 +129,6 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
server.listen(port);
|
server.listen(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpServer getHttpsServer(JsonObject proxyConf) {
|
|
||||||
HttpServerOptions httpServerOptions = new HttpServerOptions();
|
|
||||||
if (proxyConf.containsKey("ssl")) {
|
|
||||||
JsonObject sslConfig = proxyConf.getJsonObject("ssl");
|
|
||||||
|
|
||||||
URL sslUrl = this.getClass().getClassLoader().getResource("");
|
|
||||||
if (sslUrl == null) {
|
|
||||||
throw new RuntimeException("SSL url not exist...");
|
|
||||||
}
|
|
||||||
if (sslConfig.containsKey("enable") && sslConfig.getBoolean("enable")) {
|
|
||||||
String sslCertificatePath = sslUrl.getPath() + sslConfig.getString("ssl_certificate");
|
|
||||||
String sslCertificateKeyPath = sslUrl.getPath() + sslConfig.getString("ssl_certificate_key");
|
|
||||||
LOGGER.info("enable ssl config. ");
|
|
||||||
httpServerOptions
|
|
||||||
.setSsl(true)
|
|
||||||
.setKeyCertOptions(
|
|
||||||
new PemKeyCertOptions()
|
|
||||||
.setKeyPath(sslCertificateKeyPath)
|
|
||||||
.setCertPath(sslCertificatePath)
|
|
||||||
).addEnabledSecureTransportProtocol(sslConfig.getString("ssl_protocols"));
|
|
||||||
String sslCiphers = sslConfig.getString("ssl_ciphers");
|
|
||||||
if (sslCiphers != null && !sslCiphers.isEmpty()) {
|
|
||||||
for (String s : sslCiphers.split(":")) {
|
|
||||||
httpServerOptions.addEnabledCipherSuite(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return vertx.createHttpServer(httpServerOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理静态资源配置
|
* 处理静态资源配置
|
||||||
*
|
*
|
||||||
@@ -176,12 +145,9 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
ctx.next();
|
ctx.next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final StaticHandler staticHandler = StaticHandler.create();
|
||||||
StaticHandler staticHandler;
|
|
||||||
if (staticConf.containsKey("root")) {
|
if (staticConf.containsKey("root")) {
|
||||||
staticHandler = StaticHandler.create(staticConf.getString("root"));
|
staticHandler.setWebRoot(staticConf.getString("root"));
|
||||||
} else {
|
|
||||||
staticHandler = StaticHandler.create();
|
|
||||||
}
|
}
|
||||||
if (staticConf.containsKey("directory-listing")) {
|
if (staticConf.containsKey("directory-listing")) {
|
||||||
staticHandler.setDirectoryListing(staticConf.getBoolean("directory-listing"));
|
staticHandler.setDirectoryListing(staticConf.getBoolean("directory-listing"));
|
||||||
@@ -212,7 +178,7 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
port = 80;
|
port = 80;
|
||||||
}
|
}
|
||||||
String originPath = url.getPath();
|
String originPath = url.getPath();
|
||||||
LOGGER.info("Conf(path, originPath, host, port) ----> {},{},{},{}", path, originPath, host, port);
|
LOGGER.debug("Conf(path, originPath, host, port) ----> {},{},{},{}", path, originPath, host, port);
|
||||||
|
|
||||||
// 注意这里不能origin多个代理地址, 一个实例只能代理一个origin
|
// 注意这里不能origin多个代理地址, 一个实例只能代理一个origin
|
||||||
final HttpProxy httpProxy = HttpProxy.reverseProxy(httpClient);
|
final HttpProxy httpProxy = HttpProxy.reverseProxy(httpClient);
|
||||||
@@ -223,21 +189,14 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
|
|
||||||
// 代理目标路径为空 就像nginx一样路径穿透 (相对路径)
|
// 代理目标路径为空 就像nginx一样路径穿透 (相对路径)
|
||||||
if (StringUtils.isEmpty(originPath) || path.equals(originPath)) {
|
if (StringUtils.isEmpty(originPath) || path.equals(originPath)) {
|
||||||
Route route = path.startsWith("~") ? proxyRouter.routeWithRegex(path.substring(1))
|
proxyRouter.route(path + "*").handler(ProxyHandler.create(httpProxy));
|
||||||
: proxyRouter.route(path);
|
|
||||||
route.handler(ProxyHandler.create(httpProxy));
|
|
||||||
} else {
|
} else {
|
||||||
// 配置 /api/, / => 请求 /api/test 代理后 /test
|
proxyRouter.route(originPath + "*").handler(ProxyHandler.create(httpProxy));
|
||||||
// 配置 /api/, /xxx => 请求 /api/test 代理后 /xxx/test
|
proxyRouter.route(path + "*").handler(ctx -> {
|
||||||
final String path0 = path;
|
String realPath = ctx.request().path();
|
||||||
final String originPath0 = REROUTE_PATH_PREFIX + originPath;
|
if (realPath.startsWith(path)) {
|
||||||
|
|
||||||
proxyRouter.route(originPath0 + "*").handler(ProxyHandler.create(httpProxy));
|
|
||||||
proxyRouter.route(path0 + "*").handler(ctx -> {
|
|
||||||
String realPath = ctx.request().uri();
|
|
||||||
if (realPath.startsWith(path0)) {
|
|
||||||
// vertx web proxy暂不支持rewrite, 所以这里进行手动替换, 请求地址中的请求path前缀替换为originPath
|
// vertx web proxy暂不支持rewrite, 所以这里进行手动替换, 请求地址中的请求path前缀替换为originPath
|
||||||
String rePath = realPath.replaceAll("^" + path0, originPath0);
|
String rePath = realPath.replaceAll("^" + path, originPath);
|
||||||
ctx.reroute(rePath);
|
ctx.reroute(rePath);
|
||||||
} else {
|
} else {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
@@ -251,4 +210,54 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理websocket
|
||||||
|
*
|
||||||
|
* @param confList sock配置
|
||||||
|
* @param httpClient 客户端
|
||||||
|
* @param proxyRouter 代理路由
|
||||||
|
*/
|
||||||
|
private void handleSock(JsonArray confList, HttpClient httpClient, Router proxyRouter) {
|
||||||
|
// 代理规则
|
||||||
|
confList.stream().map(e -> (JsonObject) e).forEach(conf -> {
|
||||||
|
|
||||||
|
String origin = conf.getString("origin");
|
||||||
|
String path = conf.getString("path");
|
||||||
|
LOGGER.info("websocket proxy: {}, {}",origin,path);
|
||||||
|
|
||||||
|
SockJSHandlerOptions options = new SockJSHandlerOptions()
|
||||||
|
.setHeartbeatInterval(2000)
|
||||||
|
.setRegisterWriteHandler(true);
|
||||||
|
|
||||||
|
SockJSHandler sockJSHandler = SockJSHandler.create(VertxHolder.getVertxInstance(), options);
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Router route = sockJSHandler.socketHandler(sock -> {
|
||||||
|
sock.handler(buffer -> {
|
||||||
|
Future<WebSocket> webSocketFuture = httpClient.webSocket(8086,"127.0.0.1",sock.uri());
|
||||||
|
webSocketFuture.onSuccess(s -> {
|
||||||
|
System.out.println(buffer.toString());
|
||||||
|
s.write(buffer).onSuccess(v -> {
|
||||||
|
s.handler(w->{
|
||||||
|
System.out.println("--------"+w.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
sock.endHandler(v -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
sock.closeHandler(v -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
proxyRouter.mountSubRouter("/real/serverApi/test", route);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
note.txt
8
note.txt
@@ -23,12 +23,8 @@ Cloudreve自建网盘 (ce) {origin}/s/{shareKey}
|
|||||||
|
|
||||||
https://f.ws59.cn/f/e3peohu6192
|
https://f.ws59.cn/f/e3peohu6192
|
||||||
|
|
||||||
开源版 TODO
|
|
||||||
1. 缓存优化, 配置自动重载
|
短链接设计
|
||||||
|
|
||||||
|
|
||||||
专属版 功能设计
|
|
||||||
1. 支持绑定域名, 后台管理-账号管理, token管理, 账号解析次数限制
|
|
||||||
2. 流量统计, 文件分享信息, 目录解析, 文件云下载
|
|
||||||
3. IP代理池
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
package cn.qaiu.entity;
|
|
||||||
|
|
||||||
public class ShareLinkInfo {
|
|
||||||
|
|
||||||
private String shareKey; // 分享键
|
|
||||||
|
|
||||||
private String panName; // 网盘名称
|
|
||||||
private String type; // 分享类型
|
|
||||||
private String sharePassword; // 分享密码(如果存在)
|
|
||||||
private String shareUrl; // 原始分享链接
|
|
||||||
private String standardUrl; // 规范化的标准链接
|
|
||||||
|
|
||||||
private ShareLinkInfo(Builder builder) {
|
|
||||||
this.shareKey = builder.shareKey;
|
|
||||||
this.panName = builder.panName;
|
|
||||||
this.type = builder.type;
|
|
||||||
this.sharePassword = builder.sharePassword;
|
|
||||||
this.shareUrl = builder.shareUrl;
|
|
||||||
this.standardUrl = builder.standardUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter和Setter方法
|
|
||||||
|
|
||||||
public String getShareKey() {
|
|
||||||
return shareKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPanName() {
|
|
||||||
return panName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShareKey(String shareKey) {
|
|
||||||
this.shareKey = shareKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPanName(String panName) {
|
|
||||||
this.panName = panName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSharePassword() {
|
|
||||||
return sharePassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSharePassword(String sharePassword) {
|
|
||||||
this.sharePassword = sharePassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getShareUrl() {
|
|
||||||
return shareUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShareUrl(String shareUrl) {
|
|
||||||
this.shareUrl = shareUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStandardUrl() {
|
|
||||||
return standardUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStandardUrl(String standardUrl) {
|
|
||||||
this.standardUrl = standardUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCacheKey() {
|
|
||||||
// 将type和shareKey组合成一个字符串作为缓存key
|
|
||||||
return type + ":" + shareKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 静态方法创建建造者对象
|
|
||||||
public static ShareLinkInfo.Builder newBuilder() {
|
|
||||||
return new ShareLinkInfo.Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 建造者类
|
|
||||||
public static class Builder {
|
|
||||||
public String panName; // 分享网盘名称
|
|
||||||
private String shareKey; // 分享键
|
|
||||||
private String type; // 分享类型 (网盘模板枚举的小写)
|
|
||||||
private String sharePassword = ""; // 分享密码(如果存在)
|
|
||||||
private String shareUrl; // 原始分享链接
|
|
||||||
private String standardUrl; // 规范化的标准链接
|
|
||||||
|
|
||||||
public Builder shareKey(String shareKey) {
|
|
||||||
this.shareKey = shareKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder panName(String panName) {
|
|
||||||
this.panName = panName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder type(String type) {
|
|
||||||
this.type = type;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder sharePassword(String sharePassword) {
|
|
||||||
this.sharePassword = sharePassword;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder shareUrl(String shareUrl) {
|
|
||||||
this.shareUrl = shareUrl;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder standardUrl(String standardUrl) {
|
|
||||||
this.standardUrl = standardUrl;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShareLinkInfo build() {
|
|
||||||
return new ShareLinkInfo(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ShareLinkInfo{" +
|
|
||||||
"shareKey='" + shareKey + '\'' +
|
|
||||||
", panName='" + panName + '\'' +
|
|
||||||
", type='" + type + '\'' +
|
|
||||||
", sharePassword='" + sharePassword + '\'' +
|
|
||||||
", shareUrl='" + shareUrl + '\'' +
|
|
||||||
", standardUrl='" + standardUrl + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,60 @@
|
|||||||
package cn.qaiu.parser;//package cn.qaiu.lz.common.parser;
|
package cn.qaiu.parser;//package cn.qaiu.lz.common.parser;
|
||||||
|
|
||||||
|
import cn.qaiu.parser.impl.*;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
|
|
||||||
public interface IPanTool {
|
public interface IPanTool {
|
||||||
Future<String> parse();
|
Future<String> parse();
|
||||||
|
|
||||||
|
static IPanTool typeMatching(String type, String key, String pwd) {
|
||||||
|
switch (type) {
|
||||||
|
case "lz": return new LzTool(key, pwd);
|
||||||
|
case "cow": return new CowTool(key, pwd);
|
||||||
|
case "ec": return new EcTool(key, pwd);
|
||||||
|
case "fc": return new FcTool(key, pwd);
|
||||||
|
case "uc": return new UcTool(key, pwd);
|
||||||
|
case "ye": return new YeTool(key, pwd);
|
||||||
|
case "fj": return new FjTool(key, pwd);
|
||||||
|
case "qk": return new QkTool(key, pwd);
|
||||||
|
case "le": return new LeTool(key, pwd);
|
||||||
|
case "ws": return new WsTool(key, pwd);
|
||||||
|
case "qq": return new QQTool(key, pwd);
|
||||||
|
case "iz": return new IzTool(key, pwd);
|
||||||
|
case "ce": return new CeTool(key, pwd);
|
||||||
|
default: throw new UnsupportedOperationException("未知分享类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IPanTool shareURLPrefixMatching(String url, String pwd) {
|
||||||
|
|
||||||
|
if (url.contains(CowTool.LINK_KEY)) {
|
||||||
|
return new CowTool(url, pwd);
|
||||||
|
} else if (url.startsWith(EcTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new EcTool(url, pwd);
|
||||||
|
} else if (url.startsWith(FcTool.SHARE_URL_PREFIX0)) {
|
||||||
|
return new FcTool(url, pwd);
|
||||||
|
} else if (url.startsWith(UcTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new UcTool(url, pwd);
|
||||||
|
} else if (url.startsWith(YeTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new YeTool(url, pwd);
|
||||||
|
} else if (url.startsWith(FjTool.SHARE_URL_PREFIX) || url.startsWith(FjTool.SHARE_URL_PREFIX2)) {
|
||||||
|
return new FjTool(url, pwd);
|
||||||
|
} else if (url.startsWith(IzTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new IzTool(url, pwd);
|
||||||
|
} else if (url.contains(LzTool.LINK_KEY)) {
|
||||||
|
return new LzTool(url, pwd);
|
||||||
|
} else if (url.startsWith(LeTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new LeTool(url, pwd);
|
||||||
|
} else if (url.contains(WsTool.SHARE_URL_PREFIX) || url.contains(WsTool.SHARE_URL_PREFIX2)) {
|
||||||
|
return new WsTool(url, pwd);
|
||||||
|
} else if (url.contains(QQTool.SHARE_URL_PREFIX)) {
|
||||||
|
return new QQTool(url, pwd);
|
||||||
|
} else if (url.contains("/s/")) {
|
||||||
|
// Cloudreve 网盘通用解析
|
||||||
|
return new CeTool(url, pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("未知分享类型");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package cn.qaiu.parser;
|
package cn.qaiu.parser;
|
||||||
|
|
||||||
import cn.qaiu.WebClientVertxInit;
|
import cn.qaiu.WebClientVertxInit;
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Handler;
|
import io.vertx.core.Handler;
|
||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
import io.vertx.core.json.DecodeException;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.ext.web.client.HttpResponse;
|
|
||||||
import io.vertx.ext.web.client.WebClient;
|
import io.vertx.ext.web.client.WebClient;
|
||||||
import io.vertx.ext.web.client.WebClientOptions;
|
import io.vertx.ext.web.client.WebClientOptions;
|
||||||
import io.vertx.ext.web.client.WebClientSession;
|
import io.vertx.ext.web.client.WebClientSession;
|
||||||
@@ -38,7 +35,16 @@ public abstract class PanBase {
|
|||||||
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
|
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
|
||||||
new WebClientOptions().setFollowRedirects(false));
|
new WebClientOptions().setFollowRedirects(false));
|
||||||
|
|
||||||
protected ShareLinkInfo shareLinkInfo;
|
/**
|
||||||
|
* 分享key 可以是整个URL; 如果是URL实现该类时要
|
||||||
|
* 使用{@link CommonUtils#adaptShortPaths(String urlPrefix, String key)}获取真实的分享key
|
||||||
|
*/
|
||||||
|
protected String key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享密码
|
||||||
|
*/
|
||||||
|
protected String pwd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 子类重写此构造方法不需要添加额外逻辑
|
* 子类重写此构造方法不需要添加额外逻辑
|
||||||
@@ -49,9 +55,12 @@ public abstract class PanBase {
|
|||||||
* }
|
* }
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
*
|
*
|
||||||
|
* @param key 分享key/url
|
||||||
|
* @param pwd 分享密码
|
||||||
*/
|
*/
|
||||||
public PanBase(ShareLinkInfo shareLinkInfo) {
|
protected PanBase(String key, String pwd) {
|
||||||
this.shareLinkInfo = shareLinkInfo;
|
this.key = key;
|
||||||
|
this.pwd = pwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,19 +110,4 @@ public abstract class PanBase {
|
|||||||
return t -> fail(this.getClass().getSimpleName() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace());
|
return t -> fail(this.getClass().getSimpleName() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bodyAsJsonObject的封装, 会自动处理异常
|
|
||||||
* @param res HttpResponse
|
|
||||||
* @return JsonObject
|
|
||||||
*/
|
|
||||||
protected JsonObject asJson(HttpResponse<?> res) {
|
|
||||||
try {
|
|
||||||
return res.bodyAsJsonObject();
|
|
||||||
} catch (DecodeException e) {
|
|
||||||
fail("解析失败: json格式异常: {}", res.bodyAsString());
|
|
||||||
throw new RuntimeException("解析失败: json格式异常");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
package cn.qaiu.parser;
|
|
||||||
|
|
||||||
import cn.qaiu.parser.impl.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 枚举类 PanDomainTemplate 定义了不同网盘服务的模板信息,包括:
|
|
||||||
* <ul>
|
|
||||||
* <li>displayName: 网盘服务的显示名称,用于用户界面展示。</li>
|
|
||||||
* <li>regexPattern: 用于匹配和解析分享链接的正则表达式。</li>
|
|
||||||
* <li>standardUrlTemplate: 网盘服务的标准URL模板,用于规范化分享链接。</li>
|
|
||||||
* <li>toolClass: 网盘解析工具实现类。</li>
|
|
||||||
* </ul>
|
|
||||||
* 请注意:增添网盘时,保证正则表达式最后一个捕捉组能匹配到分享key
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* at 2023/6/13 4:26
|
|
||||||
*/
|
|
||||||
public enum PanDomainTemplate {
|
|
||||||
|
|
||||||
// 网盘定义
|
|
||||||
LZ("蓝奏云",
|
|
||||||
"https://([a-z0-9-]+)?\\.?lanzou[a-z]\\.com/(.+/)?(.+)",
|
|
||||||
"https://lanzoux.com/{shareKey}",
|
|
||||||
LzTool.class),
|
|
||||||
|
|
||||||
// https://www.feijix.com/s/
|
|
||||||
// https://share.feijipan.com/s/
|
|
||||||
FJ("小飞机网盘",
|
|
||||||
"https://(share\\.feijipan\\.com|www\\.feijix\\.com)/s/(.+)",
|
|
||||||
"https://www.feijix.com/s/{shareKey}",
|
|
||||||
FjTool.class),
|
|
||||||
|
|
||||||
// https://lecloud.lenovo.com/share/
|
|
||||||
LE("联想乐云",
|
|
||||||
"https://lecloud?\\.lenovo\\.com/share/(.+)",
|
|
||||||
"https://lecloud.lenovo.com/share/{shareKey}",
|
|
||||||
LeTool.class),
|
|
||||||
|
|
||||||
// https://v2.fangcloud.com/s/
|
|
||||||
FC("亿方云",
|
|
||||||
"https://v2\\.fangcloud\\.(com|cn)/(s|sharing)/([^/]+)",
|
|
||||||
"https://v2.fangcloud.com/s/{shareKey}",
|
|
||||||
FcTool.class),
|
|
||||||
// https://www.ilanzou.com/s/
|
|
||||||
IZ("蓝奏云优享",
|
|
||||||
"https://www\\.ilanzou\\.com/s/(.+)",
|
|
||||||
"https://www.ilanzou.com/s/{shareKey}",
|
|
||||||
IzTool.class),
|
|
||||||
// https://wx.mail.qq.com/ftn/download?
|
|
||||||
QQ("QQ邮箱中转站",
|
|
||||||
"https://i?wx\\.mail\\.qq\\.com/ftn/download\\?(.+)",
|
|
||||||
"https://iwx.mail.qq.com/ftn/download/{shareKey}",
|
|
||||||
QQTool.class),
|
|
||||||
// https://f.ws59.cn/f/或者https://www.wenshushu.cn/f/
|
|
||||||
WS("文叔叔",
|
|
||||||
"https://(f\\.ws([0-9]{2})\\.cn|www\\.wenshushu\\.cn)/f/(.+)",
|
|
||||||
"https://www.wenshushu.cn/f/{shareKey}",
|
|
||||||
WsTool.class),
|
|
||||||
// https://www.123pan.com/s/
|
|
||||||
YE("123网盘",
|
|
||||||
"https://www\\.(123pan|123865|123684)\\.com/s/(.+)",
|
|
||||||
"https://www.123pan.com/s/{shareKey}",
|
|
||||||
YeTool.class),
|
|
||||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={code}&isShare=1
|
|
||||||
EC("移动云空间",
|
|
||||||
"https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
|
||||||
"([^&]+)&isShare=1",
|
|
||||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data={shareKey}&isShare=1",
|
|
||||||
EcTool.class),
|
|
||||||
// https://cowtransfer.com/s/
|
|
||||||
COW("奶牛快传",
|
|
||||||
"https://(.*)cowtransfer\\.com/s/(.+)",
|
|
||||||
"https://cowtransfer.com/s/{shareKey}",
|
|
||||||
CowTool.class),
|
|
||||||
// https://pan.huang1111.cn/s/xxx
|
|
||||||
// 通用域名([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}
|
|
||||||
CE("Cloudreve",
|
|
||||||
"https://([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}/s/(.+)",
|
|
||||||
"https://{CloudreveDomainName}/s/{shareKey}",
|
|
||||||
CeTool.class);
|
|
||||||
|
|
||||||
|
|
||||||
// 网盘的显示名称,用于用户界面显示
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
// 用于匹配和解析分享链接的正则表达式,保证最后一个捕捉组能匹配到分享key
|
|
||||||
private final String regexPattern;
|
|
||||||
|
|
||||||
// 网盘的标准链接模板,不含占位符,用于规范化分享链接
|
|
||||||
private final String standardUrlTemplate;
|
|
||||||
|
|
||||||
// 指向解析工具IPanTool实现类
|
|
||||||
private final Class<? extends IPanTool> toolClass;
|
|
||||||
|
|
||||||
PanDomainTemplate(String displayName, String regexPattern, String standardUrlTemplate,
|
|
||||||
Class<? extends IPanTool> toolClass) {
|
|
||||||
this.displayName = displayName;
|
|
||||||
this.regexPattern = regexPattern;
|
|
||||||
this.standardUrlTemplate = standardUrlTemplate;
|
|
||||||
this.toolClass = toolClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRegexPattern() {
|
|
||||||
return regexPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStandardUrlTemplate() {
|
|
||||||
return standardUrlTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends IPanTool> getToolClass() {
|
|
||||||
return toolClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
package cn.qaiu.parser;
|
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 该类提供方法来解析和规范化不同来源的分享链接,确保它们可以转换为统一的标准链接格式。
|
|
||||||
* 通过这种方式,应用程序可以更容易地处理和识别不同网盘服务的分享链接。
|
|
||||||
*
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/15 14:10
|
|
||||||
*/
|
|
||||||
public class ParserCreate {
|
|
||||||
private final PanDomainTemplate panDomainTemplate;
|
|
||||||
private final ShareLinkInfo shareLinkInfo;
|
|
||||||
|
|
||||||
public ParserCreate(PanDomainTemplate panDomainTemplate, ShareLinkInfo shareLinkInfo) {
|
|
||||||
this.panDomainTemplate = panDomainTemplate;
|
|
||||||
this.shareLinkInfo = shareLinkInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 解析并规范化分享链接
|
|
||||||
public ParserCreate normalizeShareLink() {
|
|
||||||
if (shareLinkInfo == null) {
|
|
||||||
throw new IllegalArgumentException("ShareLinkInfo not init");
|
|
||||||
}
|
|
||||||
// 匹配并提取shareKey
|
|
||||||
String shareUrl = shareLinkInfo.getShareUrl();
|
|
||||||
if (StringUtils.isEmpty(shareUrl)) {
|
|
||||||
throw new IllegalArgumentException("ShareLinkInfo shareUrl is empty");
|
|
||||||
}
|
|
||||||
Pattern pattern = Pattern.compile(this.panDomainTemplate.getRegexPattern());
|
|
||||||
Matcher matcher = pattern.matcher(shareUrl);
|
|
||||||
if (matcher.find()) {
|
|
||||||
String shareKey = matcher.group(matcher.groupCount());
|
|
||||||
// 返回规范化的标准链接
|
|
||||||
String standardUrl = getStandardUrlTemplate().replace("{shareKey}", shareKey);
|
|
||||||
shareLinkInfo.setShareUrl(shareUrl);
|
|
||||||
shareLinkInfo.setShareKey(shareKey);
|
|
||||||
if (!(panDomainTemplate == PanDomainTemplate.CE)) {
|
|
||||||
shareLinkInfo.setStandardUrl(standardUrl);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid share URL for " + this.panDomainTemplate.getDisplayName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPanTool createTool() {
|
|
||||||
if (shareLinkInfo == null || StringUtils.isEmpty(shareLinkInfo.getType())) {
|
|
||||||
throw new IllegalArgumentException("ShareLinkInfo not init or type is empty");
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(shareLinkInfo.getShareKey())) {
|
|
||||||
this.normalizeShareLink();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return this.panDomainTemplate.getToolClass()
|
|
||||||
.getDeclaredConstructor(ShareLinkInfo.class)
|
|
||||||
.newInstance(shareLinkInfo);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("无法创建工具实例: " + panDomainTemplate.getToolClass().getName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set share key
|
|
||||||
public ParserCreate shareKey(String shareKey) {
|
|
||||||
if (panDomainTemplate == PanDomainTemplate.CE) {
|
|
||||||
// 处理Cloudreve(ce)类: pan.huang1111.cn_s_wDz5TK _ -> /
|
|
||||||
String[] s = shareKey.split("_");
|
|
||||||
String standardUrl = "https://" + String.join("/", s);
|
|
||||||
shareLinkInfo.setShareKey(s[s.length - 1]);
|
|
||||||
shareLinkInfo.setStandardUrl(standardUrl);
|
|
||||||
shareLinkInfo.setShareUrl(standardUrl);
|
|
||||||
} else {
|
|
||||||
shareLinkInfo.setShareKey(shareKey);
|
|
||||||
shareLinkInfo.setStandardUrl(panDomainTemplate.getStandardUrlTemplate().replace("{shareKey}", shareKey));
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStandardUrlTemplate() {
|
|
||||||
return this.panDomainTemplate.getStandardUrlTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShareLinkInfo getShareLinkInfo() {
|
|
||||||
return shareLinkInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParserCreate setShareLinkInfoPwd(String pwd) {
|
|
||||||
shareLinkInfo.setSharePassword(pwd);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据分享链接获取PanDomainTemplate实例
|
|
||||||
public synchronized static ParserCreate fromShareUrl(String shareUrl) {
|
|
||||||
for (PanDomainTemplate panDomainTemplate : PanDomainTemplate.values()) {
|
|
||||||
if (shareUrl.matches(panDomainTemplate.getRegexPattern())) {
|
|
||||||
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
|
||||||
.type(panDomainTemplate.name().toLowerCase())
|
|
||||||
.panName(panDomainTemplate.getDisplayName())
|
|
||||||
.shareUrl(shareUrl).build();
|
|
||||||
if (panDomainTemplate == PanDomainTemplate.CE) {
|
|
||||||
shareLinkInfo.setStandardUrl(shareUrl);
|
|
||||||
}
|
|
||||||
ParserCreate parserCreate = new ParserCreate(panDomainTemplate, shareLinkInfo);
|
|
||||||
return parserCreate.normalizeShareLink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Unsupported share URL");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据type获取枚举实例
|
|
||||||
public synchronized static ParserCreate fromType(String type) {
|
|
||||||
try {
|
|
||||||
PanDomainTemplate panDomainTemplate = Enum.valueOf(PanDomainTemplate.class, type.toUpperCase());
|
|
||||||
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
|
|
||||||
.type(type.toLowerCase()).build();
|
|
||||||
return new ParserCreate(panDomainTemplate, shareLinkInfo);
|
|
||||||
} catch (IllegalArgumentException ignore) {
|
|
||||||
// 如果没有找到对应的枚举实例,抛出异常
|
|
||||||
throw new IllegalArgumentException("No enum constant for type name: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成parser短链path(不包含domainName)
|
|
||||||
public String genPathSuffix() {
|
|
||||||
|
|
||||||
String path;
|
|
||||||
if (panDomainTemplate == PanDomainTemplate.CE) {
|
|
||||||
// 处理Cloudreve(ce)类: pan.huang1111.cn_s_wDz5TK _ -> /
|
|
||||||
path = this.shareLinkInfo.getType() + "/"
|
|
||||||
+ this.shareLinkInfo.getShareUrl()
|
|
||||||
.substring("https://".length()).replace("/", "_");
|
|
||||||
} else {
|
|
||||||
path = this.shareLinkInfo.getType() + "/" + this.shareLinkInfo.getShareKey();
|
|
||||||
}
|
|
||||||
String sharePassword = this.shareLinkInfo.getSharePassword();
|
|
||||||
return path + (StringUtils.isBlank(sharePassword) ? "" : ("@" + sharePassword));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
@@ -8,14 +7,13 @@ import io.vertx.core.buffer.Buffer;
|
|||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.client.HttpRequest;
|
import io.vertx.ext.web.client.HttpRequest;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://github.com/cloudreve/Cloudreve">Cloudreve自建网盘解析</a> <br>
|
* <a href="https://github.com/cloudreve/Cloudreve">Cloudreve网盘解析</a> <br>
|
||||||
* <a href="https://pan.xiaomuxi.cn">暮希云盘</a> <br>
|
* <a href="https://pan.xiaomuxi.cn">暮希云盘</a> <br>
|
||||||
* <a href="https://pan.huang1111.cn">huang1111</a> <br>
|
* <a href="https://pan.huang1111.cn">huang1111</a> <br>
|
||||||
* <a href="https://pan.seeoss.com">看见存储</a> <br>
|
|
||||||
* <a href="https://dav.yiandrive.com">亿安云盘</a> <br>
|
|
||||||
*/
|
*/
|
||||||
public class CeTool extends PanBase implements IPanTool {
|
public class CeTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
@@ -24,25 +22,29 @@ public class CeTool extends PanBase implements IPanTool {
|
|||||||
// api/v3/share/info/g31PcQ?password=qaiu
|
// api/v3/share/info/g31PcQ?password=qaiu
|
||||||
private static final String SHARE_API_PATH = "/api/v3/share/info/";
|
private static final String SHARE_API_PATH = "/api/v3/share/info/";
|
||||||
|
|
||||||
public CeTool(ShareLinkInfo shareLinkInfo) {
|
public CeTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
String key = shareLinkInfo.getShareKey();
|
|
||||||
String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
// https://pan.huang1111.cn/s/wDz5TK
|
// https://pan.huang1111.cn/s/wDz5TK
|
||||||
// https://pan.huang1111.cn/s/y12bI6 -> https://pan.huang1111
|
// https://pan.huang1111.cn/s/y12bI6 -> https://pan.huang1111
|
||||||
// .cn/api/v3/share/download/y12bI6?path=undefined%2Fundefined;
|
// .cn/api/v3/share/download/y12bI6?path=undefined%2Fundefined;
|
||||||
// 类型解析 -> /ce/pan.huang1111.cn_s_wDz5TK
|
// 类型解析 -> /ce/https_pan.huang1111.cn_s_wDz5TK
|
||||||
// parser接口 -> /parser?url=https://pan.huang1111.cn/s/wDz5TK
|
// parser接口 -> /parser?url=https://pan.huang1111.cn/s/wDz5TK
|
||||||
try {
|
try {
|
||||||
// // 处理URL
|
if (key.startsWith("https_") || key.startsWith("http_")) {
|
||||||
URL url = new URL(shareLinkInfo.getShareUrl());
|
key = key.replace("https_", "https://")
|
||||||
String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + key + "?path" +
|
.replace("http_", "http://")
|
||||||
|
.replace("_", "/");
|
||||||
|
}
|
||||||
|
// 处理URL
|
||||||
|
URL url = new URL(key);
|
||||||
|
String path = url.getPath();
|
||||||
|
String shareKey = path.substring(3);
|
||||||
|
String downloadApiUrl = url.getProtocol() + "://" + url.getHost() + DOWNLOAD_API_PATH + shareKey + "?path" +
|
||||||
"=undefined/undefined;";
|
"=undefined/undefined;";
|
||||||
String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + key;
|
String shareApiUrl = url.getProtocol() + "://" + url.getHost() + SHARE_API_PATH + shareKey;
|
||||||
|
|
||||||
// 设置cookie
|
// 设置cookie
|
||||||
HttpRequest<Buffer> httpRequest = clientSession.getAbs(shareApiUrl);
|
HttpRequest<Buffer> httpRequest = clientSession.getAbs(shareApiUrl);
|
||||||
@@ -51,7 +53,7 @@ public class CeTool extends PanBase implements IPanTool {
|
|||||||
}
|
}
|
||||||
// 获取下载链接
|
// 获取下载链接
|
||||||
httpRequest.send().onSuccess(res -> getDownURL(downloadApiUrl)).onFailure(handleFail(shareApiUrl));
|
httpRequest.send().onSuccess(res -> getDownURL(downloadApiUrl)).onFailure(handleFail(shareApiUrl));
|
||||||
} catch (Exception e) {
|
} catch (MalformedURLException e) {
|
||||||
fail(e, "URL解析错误");
|
fail(e, "URL解析错误");
|
||||||
}
|
}
|
||||||
return promise.future();
|
return promise.future();
|
||||||
@@ -59,7 +61,7 @@ public class CeTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
private void getDownURL(String apiUrl) {
|
private void getDownURL(String apiUrl) {
|
||||||
clientSession.putAbs(apiUrl).send().onSuccess(res -> {
|
clientSession.putAbs(apiUrl).send().onSuccess(res -> {
|
||||||
JsonObject jsonObject = asJson(res);
|
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||||
System.out.println(jsonObject.encodePrettily());
|
System.out.println(jsonObject.encodePrettily());
|
||||||
if (jsonObject.containsKey("code") && jsonObject.getInteger("code") == 0) {
|
if (jsonObject.containsKey("code") && jsonObject.getInteger("code") == 0) {
|
||||||
promise.complete(jsonObject.getString("data"));
|
promise.complete(jsonObject.getString("data"));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -16,17 +16,19 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
public class CowTool extends PanBase implements IPanTool {
|
public class CowTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
private static final String API_REQUEST_URL = "https://cowtransfer.com/core/api/transfer/share";
|
private static final String API_REQUEST_URL = "https://cowtransfer.com/core/api/transfer/share";
|
||||||
|
public static final String SHARE_URL_PREFIX = "https://cowtransfer.com/s/";
|
||||||
|
|
||||||
public CowTool(ShareLinkInfo shareLinkInfo) {
|
public static final String LINK_KEY = "cowtransfer.com/s/";
|
||||||
super(shareLinkInfo);
|
|
||||||
|
public CowTool(String key, String pwd) {
|
||||||
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String key = shareLinkInfo.getShareKey();
|
key = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
String url = API_REQUEST_URL + "?uniqueUrl=" + key;
|
String url = API_REQUEST_URL + "?uniqueUrl=" + key;
|
||||||
client.getAbs(url).send().onSuccess(res -> {
|
client.getAbs(url).send().onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
if ("success".equals(resJson.getString("message")) && resJson.containsKey("data")) {
|
if ("success".equals(resJson.getString("message")) && resJson.containsKey("data")) {
|
||||||
JsonObject dataJson = resJson.getJsonObject("data");
|
JsonObject dataJson = resJson.getJsonObject("data");
|
||||||
String guid = dataJson.getString("guid");
|
String guid = dataJson.getString("guid");
|
||||||
@@ -41,7 +43,7 @@ public class CowTool extends PanBase implements IPanTool {
|
|||||||
}
|
}
|
||||||
String url2 = url2Build.toString();
|
String url2 = url2Build.toString();
|
||||||
client.getAbs(url2).send().onSuccess(res2 -> {
|
client.getAbs(url2).send().onSuccess(res2 -> {
|
||||||
JsonObject res2Json = asJson(res2);
|
JsonObject res2Json = res2.bodyAsJsonObject();
|
||||||
if ("success".equals(res2Json.getString("message")) && res2Json.containsKey("data")) {
|
if ("success".equals(res2Json.getString("message")) && res2Json.containsKey("data")) {
|
||||||
JsonObject data2 = res2Json.getJsonObject("data");
|
JsonObject data2 = res2Json.getJsonObject("data");
|
||||||
String downloadUrl = data2.getString("downloadUrl");
|
String downloadUrl = data2.getString("downloadUrl");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
@@ -12,29 +12,27 @@ import io.vertx.uritemplate.UriTemplate;
|
|||||||
* 移动云空间解析
|
* 移动云空间解析
|
||||||
*/
|
*/
|
||||||
public class EcTool extends PanBase implements IPanTool {
|
public class EcTool extends PanBase implements IPanTool {
|
||||||
// https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1
|
|
||||||
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
|
private static final String FIRST_REQUEST_URL = "https://www.ecpan.cn/drive/fileextoverrid" +
|
||||||
".do?extractionCode={extractionCode}&chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
".do?extractionCode={extractionCode}&chainUrlTemplate=https:%2F%2Fwww.ecpan" +
|
||||||
".cn%2Fweb%2F%23%2FyunpanProxy%3Fpath%3D%252F%2523%252Fdrive%252Foutside&parentId=-1&data={dataKey}";
|
".cn%2Fweb%2F%23%2FyunpanProxy%3Fpath%3D%252F%2523%252Fdrive%252Foutside&parentId=-1&data={dataKey}";
|
||||||
|
|
||||||
private static final String DOWNLOAD_REQUEST_URL = "https://www.ecpan.cn/drive/sharedownload.do";
|
private static final String DOWNLOAD_REQUEST_URL = "https://www.ecpan.cn/drive/sharedownload.do";
|
||||||
|
|
||||||
public EcTool(ShareLinkInfo shareLinkInfo) {
|
public static final String SHARE_URL_PREFIX = "www.ecpan.cn/";
|
||||||
super(shareLinkInfo);
|
|
||||||
|
public EcTool(String key, String pwd) {
|
||||||
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
|
|
||||||
// 第一次请求 获取文件信息
|
// 第一次请求 获取文件信息
|
||||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL))
|
||||||
.setTemplateParam("dataKey", dataKey)
|
.setTemplateParam("dataKey", dataKey)
|
||||||
.setTemplateParam("extractionCode", pwd == null ? "" : pwd)
|
.setTemplateParam("extractionCode", pwd == null ? "" : pwd)
|
||||||
.send()
|
.send()
|
||||||
.onSuccess(res -> {
|
.onSuccess(res -> {
|
||||||
JsonObject jsonObject = asJson(res);
|
JsonObject jsonObject = res.bodyAsJsonObject();
|
||||||
log.debug("ecPan get file info -> {}", jsonObject);
|
log.debug("ecPan get file info -> {}", jsonObject);
|
||||||
JsonObject fileInfo = jsonObject
|
JsonObject fileInfo = jsonObject
|
||||||
.getJsonObject("var")
|
.getJsonObject("var")
|
||||||
@@ -59,7 +57,7 @@ public class EcTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
// 第二次请求 获取下载链接
|
// 第二次请求 获取下载链接
|
||||||
client.postAbs(DOWNLOAD_REQUEST_URL).sendJsonObject(requestBodyJson).onSuccess(res2 -> {
|
client.postAbs(DOWNLOAD_REQUEST_URL).sendJsonObject(requestBodyJson).onSuccess(res2 -> {
|
||||||
JsonObject jsonRes = asJson(res2);
|
JsonObject jsonRes = res2.bodyAsJsonObject();
|
||||||
log.debug("ecPan get download url -> {}", res2.body().toString());
|
log.debug("ecPan get download url -> {}", res2.body().toString());
|
||||||
promise.complete(jsonRes.getJsonObject("var").getString("downloadUrl"));
|
promise.complete(jsonRes.getJsonObject("var").getString("downloadUrl"));
|
||||||
}).onFailure(handleFail(""));
|
}).onFailure(handleFail(""));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
import io.vertx.core.MultiMap;
|
||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
@@ -21,19 +21,19 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public class FcTool extends PanBase implements IPanTool {
|
public class FcTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
|
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
||||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
||||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
||||||
private static final String DOWN_REQUEST_URL = "https://v2.fangcloud.cn/apps/files/download?file_id={fid}" +
|
private static final String DOWN_REQUEST_URL = "https://v2.fangcloud.cn/apps/files/download?file_id={fid}" +
|
||||||
"&scenario=share&unique_name={uname}";
|
"&scenario=share&unique_name={uname}";
|
||||||
|
|
||||||
public FcTool(ShareLinkInfo shareLinkInfo) {
|
public FcTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
String data = key.replace("share","sharing");
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, data);
|
||||||
WebClientSession sClient = WebClientSession.create(client);
|
WebClientSession sClient = WebClientSession.create(client);
|
||||||
// 第一次请求 自动重定向
|
// 第一次请求 自动重定向
|
||||||
sClient.getAbs(SHARE_URL_PREFIX + dataKey).send().onSuccess(res -> {
|
sClient.getAbs(SHARE_URL_PREFIX + dataKey).send().onSuccess(res -> {
|
||||||
@@ -88,7 +88,7 @@ public class FcTool extends PanBase implements IPanTool {
|
|||||||
.setTemplateParam("unique_name", dataKey).send().onSuccess(res2 -> {
|
.setTemplateParam("unique_name", dataKey).send().onSuccess(res2 -> {
|
||||||
JsonObject resJson;
|
JsonObject resJson;
|
||||||
try {
|
try {
|
||||||
resJson = asJson(res2);
|
resJson = res2.bodyAsJsonObject();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
fail(e, DOWN_REQUEST_URL + " 第二次请求没有返回JSON, 可能下载受限");
|
fail(e, DOWN_REQUEST_URL + " 第二次请求没有返回JSON, 可能下载受限");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.AESUtils;
|
import cn.qaiu.util.AESUtils;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import cn.qaiu.util.UUIDUtil;
|
import cn.qaiu.util.UUIDUtil;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
import io.vertx.core.MultiMap;
|
||||||
@@ -18,7 +18,10 @@ import io.vertx.uritemplate.UriTemplate;
|
|||||||
* @version V016_230609
|
* @version V016_230609
|
||||||
*/
|
*/
|
||||||
public class FjTool extends PanBase implements IPanTool {
|
public class FjTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
|
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
|
||||||
public static final String REFERER_URL = "https://share.feijipan.com/";
|
public static final String REFERER_URL = "https://share.feijipan.com/";
|
||||||
|
public static final String SHARE_URL_PREFIX2 = REFERER_URL + "s/";
|
||||||
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
private static final String API_URL_PREFIX = "https://api.feijipan.com/ws/";
|
||||||
|
|
||||||
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "recommend/list?devType=6&devModel=Chrome" +
|
||||||
@@ -35,14 +38,19 @@ public class FjTool extends PanBase implements IPanTool {
|
|||||||
"={uuid}&extra=2×tamp={ts}";
|
"={uuid}&extra=2×tamp={ts}";
|
||||||
// https://api.feijipan.com/ws/buy/vip/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=E2C53155F6D09417A27981561134CB73
|
// https://api.feijipan.com/ws/buy/vip/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=E2C53155F6D09417A27981561134CB73
|
||||||
|
|
||||||
public FjTool(ShareLinkInfo shareLinkInfo) {
|
public FjTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
String dataKey;
|
||||||
|
if (key.startsWith(SHARE_URL_PREFIX2)) {
|
||||||
|
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX2, key);
|
||||||
|
} else {
|
||||||
|
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
|
}
|
||||||
|
|
||||||
// 240530 此处shareId又改为了原始的shareId
|
// 240530 此处shareId又改为了原始的shareId, nm玩呢?
|
||||||
String shareId = dataKey; // String.valueOf(AESUtils.idEncrypt(dataKey));
|
String shareId = dataKey; // String.valueOf(AESUtils.idEncrypt(dataKey));
|
||||||
long nowTs = System.currentTimeMillis();
|
long nowTs = System.currentTimeMillis();
|
||||||
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
String tsEncode = AESUtils.encrypt2Hex(Long.toString(nowTs));
|
||||||
@@ -63,7 +71,7 @@ public class FjTool extends PanBase implements IPanTool {
|
|||||||
.setTemplateParam("uuid", uuid)
|
.setTemplateParam("uuid", uuid)
|
||||||
.setTemplateParam("ts", tsEncode0)
|
.setTemplateParam("ts", tsEncode0)
|
||||||
.send().onSuccess(res -> {
|
.send().onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
if (resJson.getInteger("code") != 200) {
|
if (resJson.getInteger("code") != 200) {
|
||||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.AESUtils;
|
import cn.qaiu.util.AESUtils;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
import io.vertx.core.MultiMap;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
@@ -26,12 +26,12 @@ public class IzTool extends PanBase implements IPanTool {
|
|||||||
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "file/redirect?downloadId={fidEncode}&enable=1" +
|
||||||
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
"&devType=6&uuid={uuid}×tamp={ts}&auth={auth}";
|
||||||
|
|
||||||
public IzTool(ShareLinkInfo shareLinkInfo) {
|
public IzTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
String dataKey = shareLinkInfo.getShareKey();
|
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
|
|
||||||
// 24.5.12 ilanzou改规则无需计算shareId
|
// 24.5.12 ilanzou改规则无需计算shareId
|
||||||
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
// String shareId = String.valueOf(AESUtils.idEncryptIz(dataKey));
|
||||||
@@ -39,7 +39,7 @@ public class IzTool extends PanBase implements IPanTool {
|
|||||||
// 第一次请求 获取文件信息
|
// 第一次请求 获取文件信息
|
||||||
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
// POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&extra=2&shareId=146731&type=0&offset=1&limit=60
|
||||||
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", dataKey).send().onSuccess(res -> {
|
client.postAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("shareId", dataKey).send().onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
if (resJson.getInteger("code") != 200) {
|
if (resJson.getInteger("code") != 200) {
|
||||||
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
@@ -13,21 +13,22 @@ import java.util.UUID;
|
|||||||
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
|
||||||
*/
|
*/
|
||||||
public class LeTool extends PanBase implements IPanTool {
|
public class LeTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
|
public static final String SHARE_URL_PREFIX = "https://lecloud.lenovo.com/share/";
|
||||||
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/";
|
private static final String API_URL_PREFIX = "https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/";
|
||||||
|
|
||||||
public LeTool(ShareLinkInfo shareLinkInfo) {
|
public LeTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
|
||||||
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
String apiUrl1 = API_URL_PREFIX + "shareInfo";
|
||||||
client.postAbs(apiUrl1)
|
client.postAbs(apiUrl1)
|
||||||
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", -1))
|
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", -1))
|
||||||
.onSuccess(res -> {
|
.onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
if (resJson.containsKey("result")) {
|
if (resJson.containsKey("result")) {
|
||||||
if (resJson.getBoolean("result")) {
|
if (resJson.getBoolean("result")) {
|
||||||
JsonObject dataJson = resJson.getJsonObject("data");
|
JsonObject dataJson = resJson.getJsonObject("data");
|
||||||
@@ -46,7 +47,7 @@ public class LeTool extends PanBase implements IPanTool {
|
|||||||
JsonObject fileInfoJson = files.getJsonObject(0);
|
JsonObject fileInfoJson = files.getJsonObject(0);
|
||||||
if (fileInfoJson != null) {
|
if (fileInfoJson != null) {
|
||||||
// TODO 文件大小fileSize和文件名fileName
|
// TODO 文件大小fileSize和文件名fileName
|
||||||
String fileId = fileInfoJson.getString("fileId");
|
Long fileId = fileInfoJson.getLong("fileId");
|
||||||
// 根据文件ID获取跳转链接
|
// 根据文件ID获取跳转链接
|
||||||
getDownURL(dataKey, fileId);
|
getDownURL(dataKey, fileId);
|
||||||
}
|
}
|
||||||
@@ -60,7 +61,7 @@ public class LeTool extends PanBase implements IPanTool {
|
|||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getDownURL(String key, String fileId) {
|
private void getDownURL(String key, Long fileId) {
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
JsonArray fileIds = JsonArray.of(fileId);
|
JsonArray fileIds = JsonArray.of(fileId);
|
||||||
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
|
||||||
@@ -68,7 +69,7 @@ public class LeTool extends PanBase implements IPanTool {
|
|||||||
client.postAbs(apiUrl2)
|
client.postAbs(apiUrl2)
|
||||||
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
|
||||||
.onSuccess(res -> {
|
.onSuccess(res -> {
|
||||||
JsonObject resJson = asJson(res);
|
JsonObject resJson = res.bodyAsJsonObject();
|
||||||
if (resJson.containsKey("result")) {
|
if (resJson.containsKey("result")) {
|
||||||
if (resJson.getBoolean("result")) {
|
if (resJson.getBoolean("result")) {
|
||||||
JsonObject dataJson = resJson.getJsonObject("data");
|
JsonObject dataJson = resJson.getJsonObject("data");
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.JsExecUtils;
|
import cn.qaiu.util.JsExecUtils;
|
||||||
@@ -24,15 +23,15 @@ public class LzTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoux.com";
|
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoux.com";
|
||||||
|
|
||||||
|
public static final String LINK_KEY = "lanzou";
|
||||||
|
|
||||||
public LzTool(ShareLinkInfo shareLinkInfo) {
|
public LzTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
String sUrl = shareLinkInfo.getStandardUrl();
|
String sUrl = key.startsWith("https://") ? key : SHARE_URL_PREFIX + "/" + key;
|
||||||
String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
|
|
||||||
WebClient client = clientNoRedirects;
|
WebClient client = clientNoRedirects;
|
||||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
client.getAbs(sUrl).send().onSuccess(res -> {
|
||||||
@@ -51,18 +50,16 @@ public class LzTool extends PanBase implements IPanTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
|
||||||
int i = jsText.indexOf("document.getElementById('rpt')");
|
jsText = jsText.substring(0, jsText.indexOf("document.getElementById('rpt')"));
|
||||||
if (i > 0) {
|
|
||||||
jsText = jsText.substring(0, i);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
|
||||||
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
|
||||||
} catch (ScriptException | NoSuchMethodException e) {
|
} catch (ScriptException | NoSuchMethodException e) {
|
||||||
fail(e, "js引擎执行失败");
|
fail(e, "js引擎执行失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 没有密码
|
|
||||||
String iframePath = matcher.group(1);
|
String iframePath = matcher.group(1);
|
||||||
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
|
||||||
String html2 = res2.bodyAsString();
|
String html2 = res2.bodyAsString();
|
||||||
@@ -81,7 +78,6 @@ public class LzTool extends PanBase implements IPanTool {
|
|||||||
fail(e, "js引擎执行失败");
|
fail(e, "js引擎执行失败");
|
||||||
}
|
}
|
||||||
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
}).onFailure(handleFail(SHARE_URL_PREFIX));
|
||||||
}
|
|
||||||
}).onFailure(handleFail(sUrl));
|
}).onFailure(handleFail(sUrl));
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
@@ -115,7 +111,7 @@ public class LzTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
String url = SHARE_URL_PREFIX + "/ajaxm.php";
|
String url = SHARE_URL_PREFIX + "/ajaxm.php";
|
||||||
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||||
JsonObject urlJson = asJson(res2);
|
JsonObject urlJson = res2.bodyAsJsonObject();
|
||||||
if (urlJson.getInteger("zt") != 1) {
|
if (urlJson.getInteger("zt") != 1) {
|
||||||
fail(urlJson.getString("inf"));
|
fail(urlJson.getString("inf"));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,48 +1,43 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.StringUtils;
|
|
||||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
import io.vertx.core.MultiMap;
|
||||||
|
import io.vertx.ext.web.client.WebClient;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://wx.mail.qq.com/">QQ邮箱</a>
|
* <a href="https://wx.mail.qq.com/">QQ邮箱</a>
|
||||||
*/
|
*/
|
||||||
public class QQTool extends PanBase implements IPanTool {
|
public class QQTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
public static final String REDIRECT_URL_TEMP = "https://iwx.mail.qq.com/ftn/download?func=4&key={key}&code={code}";
|
public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?";
|
||||||
|
|
||||||
public QQTool(ShareLinkInfo shareLinkInfo) {
|
public QQTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
// QQ mail 直接替换为302链接 无需请求
|
|
||||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(shareLinkInfo.getShareUrl(), StandardCharsets.UTF_8);
|
WebClient httpClient = this.client;
|
||||||
Map<String, List<String>> prms = queryStringDecoder.parameters();
|
|
||||||
if (prms.containsKey("key") && prms.containsKey("code") && prms.containsKey("func")) {
|
// 补全链接
|
||||||
log.info(prms.get("func").get(0));
|
if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) {
|
||||||
promise.complete(REDIRECT_URL_TEMP.replace("{key}",
|
if (this.key.startsWith(SHARE_URL_PREFIX)) {
|
||||||
prms.get("key").get(0)).replace("{code}", prms.get("code").get(0)));
|
this.key = "https://" + this.key;
|
||||||
|
} else if (this.key.startsWith("func=")) {
|
||||||
|
this.key = "https://" + SHARE_URL_PREFIX + this.key;
|
||||||
} else {
|
} else {
|
||||||
fail("key 不合法");
|
throw new UnsupportedOperationException("未知分享类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 通过请求URL获取文件信息和直链 暂时不需要
|
|
||||||
// getFileInfo(key);
|
|
||||||
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getFileInfo(String key) {
|
|
||||||
// 设置基础HTTP头部
|
// 设置基础HTTP头部
|
||||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
||||||
"like " +
|
"like " +
|
||||||
@@ -55,7 +50,7 @@ public class QQTool extends PanBase implements IPanTool {
|
|||||||
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
|
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
|
||||||
|
|
||||||
// 获取下载中转站页面
|
// 获取下载中转站页面
|
||||||
client.getAbs(key).putHeaders(headers).send().onSuccess(res -> {
|
httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> {
|
||||||
if (res.statusCode() == 200) {
|
if (res.statusCode() == 200) {
|
||||||
String html = res.bodyAsString();
|
String html = res.bodyAsString();
|
||||||
|
|
||||||
@@ -66,10 +61,10 @@ public class QQTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
if (filename != null && filesize != null && fileurl != null) {
|
if (filename != null && filesize != null && fileurl != null) {
|
||||||
// 设置所需HTTP头部
|
// 设置所需HTTP头部
|
||||||
headers.set("Referer", "https://" + StringUtils.StringCutNot(key, "https://", "/") + "/");
|
headers.set("Referer", "https://" + StringUtils.StringCutNot(this.key, "https://", "/") + "/");
|
||||||
headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/"));
|
headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/"));
|
||||||
res.headers().forEach((k, v) -> {
|
res.headers().forEach((k, v) -> {
|
||||||
if (k.equalsIgnoreCase("set-cookie")) {
|
if (k.toLowerCase().equals("set-cookie")) {
|
||||||
headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";");
|
headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,7 +82,9 @@ public class QQTool extends PanBase implements IPanTool {
|
|||||||
} else {
|
} else {
|
||||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||||
}
|
}
|
||||||
}).onFailure(this.handleFail(key));
|
}).onFailure(this.handleFail(this.key));
|
||||||
|
|
||||||
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
@@ -10,14 +9,11 @@ import java.util.stream.IntStream;
|
|||||||
|
|
||||||
public class QkTool extends PanBase implements IPanTool {
|
public class QkTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
public QkTool(ShareLinkInfo shareLinkInfo) {
|
public QkTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
final String key = shareLinkInfo.getShareKey();
|
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
|
|
||||||
promise.complete("https://lz.qaiu.top");
|
promise.complete("https://lz.qaiu.top");
|
||||||
IntStream.range(0, 1000).forEach(num -> {
|
IntStream.range(0, 1000).forEach(num -> {
|
||||||
clientNoRedirects.getAbs(key).send()
|
clientNoRedirects.getAbs(key).send()
|
||||||
@@ -37,6 +33,14 @@ public class QkTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
new QkTool("https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12298704&dk" +
|
||||||
|
"=19ab590770399d4438ea885446e27186cc668cdfa559f5fcc063a1ecf78008e5&pk" +
|
||||||
|
"=ef45aa4d25c1dcecb631b3394f51539d59cb35c6a40c3911df8ba431ba2a3244&pc=true&ot=ali&ob=sync-cloud-disk" +
|
||||||
|
"&ok=649593714557087744.dex&fn=classes" +
|
||||||
|
".dex&ds=8909208&dc=1&bi=asdddsad&ri=&ts=1701235051759&sn" +
|
||||||
|
"=13dc33749bd9cc108009fa505b3ecca9f358d70874352858475956ba4240e4c3", "")
|
||||||
|
.parse().onSuccess((res) -> {
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
parser/src/main/java/cn/qaiu/parser/impl/UcTool.java
Normal file
82
parser/src/main/java/cn/qaiu/parser/impl/UcTool.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
|
import cn.qaiu.parser.IPanTool;
|
||||||
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.CommonUtils;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.json.JsonArray;
|
||||||
|
import io.vertx.core.json.JsonObject;
|
||||||
|
import io.vertx.uritemplate.UriTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UC网盘解析
|
||||||
|
*/
|
||||||
|
public class UcTool extends PanBase implements IPanTool {
|
||||||
|
private static final String API_URL_PREFIX = "https://pc-api.uc.cn/1/clouddrive/";
|
||||||
|
|
||||||
|
public static final String SHARE_URL_PREFIX = "https://fast.uc.cn/s/";
|
||||||
|
|
||||||
|
private static final String FIRST_REQUEST_URL = API_URL_PREFIX + "share/sharepage/token?entry=ft&fr=pc&pr" +
|
||||||
|
"=UCBrowser";
|
||||||
|
|
||||||
|
private static final String SECOND_REQUEST_URL = API_URL_PREFIX + "transfer_share/detail?pwd_id={pwd_id}&passcode" +
|
||||||
|
"={passcode}&stoken={stoken}";
|
||||||
|
|
||||||
|
private static final String THIRD_REQUEST_URL = API_URL_PREFIX + "file/download?entry=ft&fr=pc&pr=UCBrowser";
|
||||||
|
|
||||||
|
public UcTool(String key, String pwd) {
|
||||||
|
super(key, pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<String> parse() {
|
||||||
|
var dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
|
var passcode = (pwd == null) ? "" : pwd;
|
||||||
|
var jsonObject = JsonObject.of("share_for_transfer", true);
|
||||||
|
jsonObject.put("pwd_id", dataKey);
|
||||||
|
jsonObject.put("passcode", passcode);
|
||||||
|
// 第一次请求 获取文件信息
|
||||||
|
client.postAbs(FIRST_REQUEST_URL).sendJsonObject(jsonObject).onSuccess(res -> {
|
||||||
|
log.debug("第一阶段 {}", res.body());
|
||||||
|
var resJson = res.bodyAsJsonObject();
|
||||||
|
if (resJson.getInteger("code") != 0) {
|
||||||
|
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var stoken = resJson.getJsonObject("data").getString("stoken");
|
||||||
|
// 第二次请求
|
||||||
|
client.getAbs(UriTemplate.of(SECOND_REQUEST_URL))
|
||||||
|
.setTemplateParam("pwd_id", dataKey)
|
||||||
|
.setTemplateParam("passcode", passcode)
|
||||||
|
.setTemplateParam("stoken", stoken)
|
||||||
|
.send().onSuccess(res2 -> {
|
||||||
|
log.debug("第二阶段 {}", res2.body());
|
||||||
|
JsonObject resJson2 = res2.bodyAsJsonObject();
|
||||||
|
if (resJson2.getInteger("code") != 0) {
|
||||||
|
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 文件信息
|
||||||
|
var info = resJson2.getJsonObject("data").getJsonArray("list").getJsonObject(0);
|
||||||
|
// 第二次请求
|
||||||
|
var bodyJson = JsonObject.of()
|
||||||
|
.put("fids", JsonArray.of(info.getString("fid")))
|
||||||
|
.put("pwd_id", dataKey)
|
||||||
|
.put("stoken", stoken)
|
||||||
|
.put("fids_token", JsonArray.of(info.getString("share_fid_token")));
|
||||||
|
client.postAbs(THIRD_REQUEST_URL).sendJsonObject(bodyJson)
|
||||||
|
.onSuccess(res3 -> {
|
||||||
|
log.debug("第三阶段 {}", res3.body());
|
||||||
|
var resJson3 = res3.bodyAsJsonObject();
|
||||||
|
if (resJson3.getInteger("code") != 0) {
|
||||||
|
fail(FIRST_REQUEST_URL + " 返回异常: " + resJson2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise.complete(resJson3.getJsonArray("data").getJsonObject(0).getString("download_url"));
|
||||||
|
}).onFailure(handleFail(THIRD_REQUEST_URL));
|
||||||
|
|
||||||
|
}).onFailure(handleFail(SECOND_REQUEST_URL));
|
||||||
|
}
|
||||||
|
).onFailure(handleFail(FIRST_REQUEST_URL));
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
@@ -9,27 +13,33 @@ import io.vertx.core.json.DecodeException;
|
|||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.client.WebClient;
|
import io.vertx.ext.web.client.WebClient;
|
||||||
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://www.wenshushu.cn/">文叔叔</a>
|
* <a href="https://www.wenshushu.cn/">文叔叔</a>
|
||||||
*/
|
*/
|
||||||
public class WsTool extends PanBase implements IPanTool {
|
public class WsTool extends PanBase implements IPanTool {
|
||||||
|
|
||||||
public static final String SHARE_URL_PREFIX = "www.wenshushu.cn/f/";
|
public static final String SHARE_URL_PREFIX = "www.wenshushu.cn/f/";
|
||||||
|
public static final String SHARE_URL_PREFIX2 = "f.ws59.cn/f/";
|
||||||
public static final String SHARE_URL_API = "https://www.wenshushu.cn/ap/";
|
public static final String SHARE_URL_API = "https://www.wenshushu.cn/ap/";
|
||||||
|
|
||||||
public WsTool(ShareLinkInfo shareLinkInfo) {
|
public WsTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
|
|
||||||
WebClient httpClient = this.client;
|
WebClient httpClient = this.client;
|
||||||
final String key = shareLinkInfo.getShareKey();
|
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
|
|
||||||
|
// 补全链接
|
||||||
|
if (!this.key.startsWith("https://" + SHARE_URL_PREFIX) && !this.key.startsWith("https://" + SHARE_URL_PREFIX2)) {
|
||||||
|
if (this.key.startsWith(SHARE_URL_PREFIX) || this.key.startsWith(SHARE_URL_PREFIX2)) {
|
||||||
|
this.key = "https://" + this.key;
|
||||||
|
} else if (this.key.matches("^[A-Za-z0-9]+$")) {
|
||||||
|
this.key = "https://" + SHARE_URL_PREFIX + this.key;
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("未知分享类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置基础HTTP头部
|
// 设置基础HTTP头部
|
||||||
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
|
||||||
@@ -50,23 +60,23 @@ public class WsTool extends PanBase implements IPanTool {
|
|||||||
if (res.statusCode() == 200) {
|
if (res.statusCode() == 200) {
|
||||||
try {
|
try {
|
||||||
// 设置匿名登录token
|
// 设置匿名登录token
|
||||||
String token = asJson(res).getJsonObject("data").getString("token");
|
String token = res.bodyAsJsonObject().getJsonObject("data").getString("token");
|
||||||
headers.set("X-Token", token);
|
headers.set("X-Token", token);
|
||||||
|
|
||||||
// 获取文件夹信息
|
// 获取文件夹信息
|
||||||
httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers)
|
httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers)
|
||||||
.sendJsonObject(JsonObject.of(
|
.sendJsonObject(JsonObject.of(
|
||||||
"tid", key,
|
"tid", StringUtils.StringCutNot(key, this.key.startsWith(SHARE_URL_PREFIX) ? SHARE_URL_PREFIX : SHARE_URL_PREFIX2),
|
||||||
"password", pwd
|
"password", ""
|
||||||
)).onSuccess(res2 -> {
|
)).onSuccess(res2 -> {
|
||||||
|
|
||||||
if (res2.statusCode() == 200) {
|
if (res2.statusCode() == 200) {
|
||||||
try {
|
try {
|
||||||
// 获取文件夹信息
|
// 获取文件夹信息
|
||||||
String filetime = asJson(res2).getJsonObject("data").getString("expire"); // 文件夹剩余时间
|
String filetime = res2.bodyAsJsonObject().getJsonObject("data").getString("expire"); // 文件夹剩余时间
|
||||||
String filesize = asJson(res2).getJsonObject("data").getString("file_size"); // 文件夹大小
|
String filesize = res2.bodyAsJsonObject().getJsonObject("data").getString("file_size"); // 文件夹大小
|
||||||
String filepid = asJson(res2).getJsonObject("data").getString("ufileid"); // 文件夹pid
|
String filepid = res2.bodyAsJsonObject().getJsonObject("data").getString("ufileid"); // 文件夹pid
|
||||||
String filebid = asJson(res2).getJsonObject("data").getString("boxid"); // 文件夹bid
|
String filebid = res2.bodyAsJsonObject().getJsonObject("data").getString("boxid"); // 文件夹bid
|
||||||
|
|
||||||
// 调试输出文件夹信息
|
// 调试输出文件夹信息
|
||||||
System.out.println("文件夹期限: " + filetime);
|
System.out.println("文件夹期限: " + filetime);
|
||||||
@@ -93,9 +103,9 @@ public class WsTool extends PanBase implements IPanTool {
|
|||||||
if (res3.statusCode() == 200) {
|
if (res3.statusCode() == 200) {
|
||||||
try {
|
try {
|
||||||
// 获取文件信息
|
// 获取文件信息
|
||||||
String filename = asJson(res3).getJsonObject("data")
|
String filename = res3.bodyAsJsonObject().getJsonObject("data")
|
||||||
.getJsonArray("fileList").getJsonObject(0).getString("fname"); // 文件名称
|
.getJsonArray("fileList").getJsonObject(0).getString("fname"); // 文件名称
|
||||||
String filefid = asJson(res3).getJsonObject("data")
|
String filefid = res3.bodyAsJsonObject().getJsonObject("data")
|
||||||
.getJsonArray("fileList").getJsonObject(0).getString("fid"); // 文件fid
|
.getJsonArray("fileList").getJsonObject(0).getString("fid"); // 文件fid
|
||||||
|
|
||||||
// 调试输出文件信息
|
// 调试输出文件信息
|
||||||
@@ -113,14 +123,21 @@ public class WsTool extends PanBase implements IPanTool {
|
|||||||
if (res4.statusCode() == 200) {
|
if (res4.statusCode() == 200) {
|
||||||
try {
|
try {
|
||||||
// 获取直链
|
// 获取直链
|
||||||
String fileurl = asJson(res4).getJsonObject("data").getString("url");
|
String fileurl = res4.bodyAsJsonObject().getJsonObject("data").getString("url");
|
||||||
|
|
||||||
// 调试输出文件直链
|
// 调试输出文件直链
|
||||||
System.out.println("文件直链: " + fileurl);
|
System.out.println("文件直链: " + fileurl);
|
||||||
|
|
||||||
if (!fileurl.equals("")) {
|
if (!fileurl.equals(""))
|
||||||
promise.complete(URLDecoder.decode(fileurl, StandardCharsets.UTF_8));
|
{
|
||||||
} else {
|
try {
|
||||||
|
promise.complete(URLDecoder.decode(fileurl, "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
promise.complete(fileurl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
this.fail("文件已失效");
|
this.fail("文件已失效");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +166,7 @@ public class WsTool extends PanBase implements IPanTool {
|
|||||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||||
}
|
}
|
||||||
|
|
||||||
}).onFailure(this.handleFail(key));
|
}).onFailure(this.handleFail(this.key));
|
||||||
|
|
||||||
} catch (DecodeException | NullPointerException e) {
|
} catch (DecodeException | NullPointerException e) {
|
||||||
this.fail("token获取失败,可能是分享链接的方式已更新");
|
this.fail("token获取失败,可能是分享链接的方式已更新");
|
||||||
@@ -158,7 +175,7 @@ public class WsTool extends PanBase implements IPanTool {
|
|||||||
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
this.fail("HTTP状态不正确,可能是分享链接的方式已更新");
|
||||||
}
|
}
|
||||||
|
|
||||||
}).onFailure(this.handleFail(key));
|
}).onFailure(this.handleFail(this.key));
|
||||||
|
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.parser.IPanTool;
|
import cn.qaiu.parser.IPanTool;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
import cn.qaiu.util.CommonUtils;
|
import cn.qaiu.util.CommonUtils;
|
||||||
@@ -31,14 +30,13 @@ public class YeTool extends PanBase implements IPanTool {
|
|||||||
"&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event=homeListFile&operateType=1";
|
"&shareKey={shareKey}&SharePwd={pwd}&ParentFileId=0&Page=1&event=homeListFile&operateType=1";
|
||||||
private static final String DOWNLOAD_API_URL = "https://www.123pan.com/a/api/share/download/info?{authK}={authV}";
|
private static final String DOWNLOAD_API_URL = "https://www.123pan.com/a/api/share/download/info?{authK}={authV}";
|
||||||
|
|
||||||
public YeTool(ShareLinkInfo shareLinkInfo) {
|
public YeTool(String key, String pwd) {
|
||||||
super(shareLinkInfo);
|
super(key, pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
|
|
||||||
final String dataKey = shareLinkInfo.getShareKey();
|
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
|
||||||
final String pwd = shareLinkInfo.getSharePassword();
|
|
||||||
|
|
||||||
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("key", dataKey).send().onSuccess(res -> {
|
client.getAbs(UriTemplate.of(FIRST_REQUEST_URL)).setTemplateParam("key", dataKey).send().onSuccess(res -> {
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ public class YeTool extends PanBase implements IPanTool {
|
|||||||
.putHeader("Platform", "web")
|
.putHeader("Platform", "web")
|
||||||
.putHeader("App-Version", "3")
|
.putHeader("App-Version", "3")
|
||||||
.send().onSuccess(res2 -> {
|
.send().onSuccess(res2 -> {
|
||||||
JsonObject infoJson = asJson(res2);
|
JsonObject infoJson = res2.bodyAsJsonObject();
|
||||||
if (infoJson.getInteger("code") != 0) {
|
if (infoJson.getInteger("code") != 0) {
|
||||||
fail("{} 状态码异常 {}", dataKey, infoJson);
|
fail("{} 状态码异常 {}", dataKey, infoJson);
|
||||||
return;
|
return;
|
||||||
@@ -121,7 +119,7 @@ public class YeTool extends PanBase implements IPanTool {
|
|||||||
.putHeader("Platform", "web")
|
.putHeader("Platform", "web")
|
||||||
.putHeader("App-Version", "3")
|
.putHeader("App-Version", "3")
|
||||||
.sendJsonObject(jsonObject).onSuccess(res2 -> {
|
.sendJsonObject(jsonObject).onSuccess(res2 -> {
|
||||||
JsonObject downURLJson = asJson(res2);
|
JsonObject downURLJson = res2.bodyAsJsonObject();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (downURLJson.getInteger("code") != 0) {
|
if (downURLJson.getInteger("code") != 0) {
|
||||||
@@ -141,7 +139,7 @@ public class YeTool extends PanBase implements IPanTool {
|
|||||||
|
|
||||||
// 获取直链
|
// 获取直链
|
||||||
client.getAbs(downUrl2).send().onSuccess(res3 -> {
|
client.getAbs(downUrl2).send().onSuccess(res3 -> {
|
||||||
JsonObject res3Json = asJson(res3);
|
JsonObject res3Json = res3.bodyAsJsonObject();
|
||||||
try {
|
try {
|
||||||
if (res3Json.getInteger("code") != 0) {
|
if (res3Json.getInteger("code") != 0) {
|
||||||
fail("Ye: downUrl2返回值异常->" + res3Json);
|
fail("Ye: downUrl2返回值异常->" + res3Json);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.qaiu.util;
|
package cn.qaiu.util;
|
||||||
|
|
||||||
|
import cn.qaiu.util.jdk17halper.HexFormat;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.crypto.*;
|
import javax.crypto.*;
|
||||||
@@ -12,7 +13,6 @@ import java.security.MessageDigest;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package cn.qaiu.util;
|
|
||||||
|
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.Vertx;
|
|
||||||
import io.vertx.core.http.impl.headers.HeadersMultiMap;
|
|
||||||
import io.vertx.ext.web.client.WebClient;
|
|
||||||
import io.vertx.ext.web.client.WebClientSession;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class IpExtractor {
|
|
||||||
public static void main(String[] args) throws InterruptedException {
|
|
||||||
|
|
||||||
|
|
||||||
// 创建请求头Map
|
|
||||||
MultiMap headers = new HeadersMultiMap();
|
|
||||||
headers.add("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
|
|
||||||
headers.add("accept-language", "zh-CN,zh;q=0.9,en;q=0.8");
|
|
||||||
headers.add("cache-control", "no-cache");
|
|
||||||
headers.add("dnt", "1");
|
|
||||||
headers.add("origin", "https://ip.ihuan.me");
|
|
||||||
headers.add("pragma", "no-cache");
|
|
||||||
headers.add("priority", "u=0, i");
|
|
||||||
headers.add("referer", "https://ip.ihuan.me/ti.html");
|
|
||||||
headers.add("sec-ch-ua", "\"Google Chrome\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"");
|
|
||||||
headers.add("sec-ch-ua-mobile", "?0");
|
|
||||||
headers.add("sec-ch-ua-platform", "\"Windows\"");
|
|
||||||
headers.add("sec-fetch-dest", "document");
|
|
||||||
headers.add("sec-fetch-mode", "navigate");
|
|
||||||
headers.add("sec-fetch-site", "same-origin");
|
|
||||||
headers.add("sec-fetch-user", "?1");
|
|
||||||
headers.add("upgrade-insecure-requests", "1");
|
|
||||||
headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36");
|
|
||||||
headers.add("Content-Type", "application/x-www-form-urlencoded");
|
|
||||||
|
|
||||||
WebClient client = WebClient.create(Vertx.vertx());
|
|
||||||
WebClientSession webClientSession = WebClientSession.create(client);
|
|
||||||
webClientSession.getAbs("https://ip.ihuan.me").putHeaders(headers).send().onSuccess(res->{
|
|
||||||
System.out.println(res.toString());
|
|
||||||
webClientSession.getAbs("https://ip.ihuan.me").putHeaders(headers).send().onSuccess(res2->{
|
|
||||||
System.out.println(res2.toString());
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//
|
|
||||||
// String htmlContent = "<div class=\"panel-heading\">提取结果</div>\n" +
|
|
||||||
// "<div class=\"panel-body\">111.1.27.85:80<br>42.63.65.46:80<br>118.195.242.20:8080<br>42.63.65.119:80<br>117.50.108.90:7890<br>116.62.50.250:7890<br>114.231.8.177:8089<br>190.43.92.90:999<br>221.178.85.68:8080<br><br>61.160.202.86:80<br>42.63.65.86:80<br>42.63.65.7:80<br>42.63.65.41:80<br>159.226.227.119:80<br>61.160.202.52:80<br>42.63.65.15:80<br>112.17.16.204:80<br>61.160.202.53:80<br>42.63.65.9:80<br>42.63.65.60:80<br>42.63.65.18:80<br>203.190.115.177:8071<br>42.63.65.38:80<br>42.63.65.31:80<br>91.185.3.126:8080<br>139.9.119.20:80<br>1.15.47.213:443<br>183.164.243.108:8089<br>165.225.208.177:80<br>194.163.132.232:3128<br>91.235.220.122:80<br>39.100.120.200:7890<br>141.147.33.121:80<br>183.164.243.138:8089<br>104.129.205.94:54321<br>117.160.250.138:81<br>180.120.213.208:8089<br>61.130.9.37:443<br>182.34.18.206:9999<br>117.86.12.150:8089<br>27.192.173.108:9000<br>183.164.243.11:8089<br>114.231.41.205:8089<br>103.104.233.78:8080<br>183.164.243.174:8089<br>36.6.144.230:8089<br>111.224.213.239:8089<br>182.92.73.106:80<br>36.6.145.81:8089<br>117.69.232.125:8089<br>36.6.144.64:8089<br>117.57.92.20:8089<br>47.100.69.29:8888<br>117.57.93.246:8089<br>120.234.203.171:9002<br>114.231.46.231:8089<br>183.164.242.199:8089<br>117.69.237.179:8089<br>182.44.32.239:7890<br>47.100.91.57:8080<br>117.69.236.127:8089<br>114.231.8.18:8089<br>117.69.232.183:8089<br>117.69.237.29:8089<br>183.164.242.67:8089<br>183.164.242.35:8089<br>183.164.243.71:8089<br>113.223.214.155:8089<br>36.6.145.132:8089<br>182.106.220.252:9091<br>113.223.212.176:8089<br>62.152.53.186:8909<br>117.57.92.16:8089<br>183.164.243.186:8089<br>36.6.144.210:8089<br>183.164.242.189:8089<br>213.178.39.170:8080<br>121.52.145.163:8080<br>36.6.144.240:8089<br>60.188.5.234:80<br>113.223.213.48:8089<br>183.164.243.149:8089<br>200.58.87.195:8080<br>36.6.144.153:8089<br>36.6.144.67:8089<br>36.6.145.182:8089<br>117.57.93.226:8089<br>42.112.24.127:8888<br>43.138.20.156:80<br>117.57.92.79:8089<br>65.109.111.238:3128<br>183.166.137.201:41122<br>113.223.213.150:8089<br>36.6.145.154:8089<br>185.5.209.101:80<br>36.6.144.17:8089<br>114.231.8.244:8089<br>117.69.237.24:8089<br>117.69.236.232:8089<br>117.69.236.127:8089<br>114.231.8.18:8089<br>117.69.232.183:8089<br>117.69.237.29:8089<br>183.164.242.67:8089<br>183.164.242.35:8089<br>183.164.243.71:8089<br>113.223.214.155:8089<br>36.6.145.132:8089<br>182.106.220.252:9091<br>113.223.212.176:8089<br>62.152.53.186:8909<br>117.57.92.16:8089<br>183.164.243.186:8089<br>36.6.144.210:8089<br>183.164.242.189:8089<br>213.178.39.170:8080<br>121.52.145.163:8080<br>36.6.144.240:8089<br>60.188.5.234:80<br>113.223.213.48:8089<br>183.164.243.149:8089<br>200.58.87.195:8080<br>36.6.144.153:8089<br>36.6.144.67:8089<br>36.6.145.182:8089<br>117.57.93.226:8089<br>42.112.24.127:8888<br>43.138.20.156:80<br>117.57.92.79:8089<br>65.109.111.238:3128<br>183.166.137.201:41122<br>113.223.213.150:8089<br>36.6.145.154:8089<br>185.5.209.101:80<br>36.6.144.17:8089<br>114.231.8.244:8089<br>117.69.237.24:8089<br>117.69.236.232:8089</div>";
|
|
||||||
//
|
|
||||||
// // 正则表达式匹配提取结果关键字下面的IP地址
|
|
||||||
// Pattern pattern = Pattern.compile("<div class=\"panel-heading\">提取结果</div>\\s*<div class=\"panel-body\">([\\s\\S]*?)(?=</div>)", Pattern.DOTALL);
|
|
||||||
// Matcher matcher = pattern.matcher(htmlContent);
|
|
||||||
//
|
|
||||||
// if (matcher.find()) {
|
|
||||||
// String ipText = matcher.group(1); // 获取匹配的IP地址部分
|
|
||||||
// Pattern ipPattern = Pattern.compile("(?:[0-9]{1,3}\\.){3}[0-9]{1,3}(?::\\d+)?");
|
|
||||||
// Matcher ipMatcher = ipPattern.matcher(ipText);
|
|
||||||
//
|
|
||||||
// List<String> ips = new ArrayList<>();
|
|
||||||
// while (ipMatcher.find()) {
|
|
||||||
// ips.add(ipMatcher.group());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// System.out.println("提取到的IP地址:");
|
|
||||||
// for (String ip : ips) {
|
|
||||||
//// System.out.println(ip);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// System.out.println("没有找到匹配的IP地址");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// TimeUnit.SECONDS.sleep(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,152 +1,160 @@
|
|||||||
package cn.qaiu.util;
|
package cn.qaiu.util;
|
||||||
|
|
||||||
public interface JsContent {
|
public interface JsContent {
|
||||||
String ye123 = """
|
String ye123 = "/*\n" +
|
||||||
/*
|
" https://statics.123pan.com/share-static/dist/umi.fb72555e.js\n" +
|
||||||
https://statics.123pan.com/share-static/dist/umi.fb72555e.js
|
" eaefamemdead\n" +
|
||||||
eaefamemdead
|
" eaefameidldy\n" +
|
||||||
eaefameidldy
|
" _0x4f141a(1690439821|5790548|/b/api/share/download/info|web|3|1946841013) = 秘钥\n" +
|
||||||
_0x4f141a(1690439821|5790548|/b/api/share/download/info|web|3|1946841013) = 秘钥
|
" \n" +
|
||||||
|
" _0x1e2592 1690439821 时间戳\n" +
|
||||||
_0x1e2592 1690439821 时间戳
|
" _0x48562f 5790548 随机码\n" +
|
||||||
_0x48562f 5790548 随机码
|
" _0x1e37d5 /b/api/share/download/info\n" +
|
||||||
_0x1e37d5 /b/api/share/download/info
|
" _0x4e2d74 web\n" +
|
||||||
_0x4e2d74 web
|
" _0x56f040 3\n" +
|
||||||
_0x56f040 3
|
" _0x43bdc6 1946841013 加密时间HASH戳\n" +
|
||||||
_0x43bdc6 1946841013 加密时间HASH戳
|
" \n" +
|
||||||
|
" >>>>\n" +
|
||||||
>>>>
|
" _0x43bdc6=''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)\n" +
|
||||||
_0x43bdc6=''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)
|
" 加密时间HASH戳 = 时间戳-随机码-秘钥\n" +
|
||||||
加密时间HASH戳 = 时间戳-随机码-秘钥
|
" */\n" +
|
||||||
*/
|
" \n" +
|
||||||
|
" function _0x1b5d95(_0x278d1a) {\n" +
|
||||||
function _0x1b5d95(_0x278d1a) {
|
" var _0x839b57,\n" +
|
||||||
var _0x839b57,
|
" _0x4ed4dc = arguments['length'] > 0x2 && void 0x0 !== arguments[0x2] ? arguments[0x2] : " +
|
||||||
_0x4ed4dc = arguments['length'] > 0x2 && void 0x0 !== arguments[0x2] ? arguments[0x2] : 0x8;
|
"0x8;\n" +
|
||||||
if (0x0 === arguments['length'])
|
" if (0x0 === arguments['length'])\n" +
|
||||||
return null;
|
" return null;\n" +
|
||||||
'object' === typeof _0x278d1a ? _0x839b57 = _0x278d1a : (0xa === ('' + _0x278d1a)['length'] && (_0x278d1a = 0x3e8 * parseInt(_0x278d1a)),
|
" 'object' === typeof _0x278d1a ? _0x839b57 = _0x278d1a : (0xa === ('' + _0x278d1a)" +
|
||||||
_0x839b57 = new Date(_0x278d1a));
|
"['length'] && (_0x278d1a = 0x3e8 * parseInt(_0x278d1a)),\n" +
|
||||||
var _0xc5c54a = _0x278d1a + 0xea60 * new Date(_0x278d1a)['getTimezoneOffset']()
|
" _0x839b57 = new Date(_0x278d1a));\n" +
|
||||||
, _0x3732dc = _0xc5c54a + 0x36ee80 * _0x4ed4dc;
|
" var _0xc5c54a = _0x278d1a + 0xea60 * new Date(_0x278d1a)['getTimezoneOffset']()\n" +
|
||||||
return _0x839b57 = new Date(_0x3732dc),
|
" , _0x3732dc = _0xc5c54a + 0x36ee80 * _0x4ed4dc;\n" +
|
||||||
{
|
" return _0x839b57 = new Date(_0x3732dc),\n" +
|
||||||
'y': _0x839b57['getFullYear'](),
|
" {\n" +
|
||||||
'm': _0x839b57['getMonth']() + 0x1 < 0xa ? '0' + (_0x839b57['getMonth']() + 0x1) : _0x839b57['getMonth']() + 0x1,
|
" 'y': _0x839b57['getFullYear'](),\n" +
|
||||||
'd': _0x839b57['getDate']() < 0xa ? '0' + _0x839b57['getDate']() : _0x839b57['getDate'](),
|
" 'm': _0x839b57['getMonth']() + 0x1 < 0xa ? '0' + (_0x839b57['getMonth']() + 0x1) : " +
|
||||||
'h': _0x839b57['getHours']() < 0xa ? '0' + _0x839b57['getHours']() : _0x839b57['getHours'](),
|
"_0x839b57['getMonth']() + 0x1,\n" +
|
||||||
'f': _0x839b57['getMinutes']() < 0xa ? '0' + _0x839b57['getMinutes']() : _0x839b57['getMinutes']()
|
" 'd': _0x839b57['getDate']() < 0xa ? '0' + _0x839b57['getDate']() : " +
|
||||||
};
|
"_0x839b57['getDate'](),\n" +
|
||||||
}
|
" 'h': _0x839b57['getHours']() < 0xa ? '0' + _0x839b57['getHours']() : " +
|
||||||
|
"_0x839b57['getHours'](),\n" +
|
||||||
|
" 'f': _0x839b57['getMinutes']() < 0xa ? '0' + _0x839b57['getMinutes']() : " +
|
||||||
function _0x4f141a(_0x4075b1) {
|
"_0x839b57['getMinutes']()\n" +
|
||||||
|
" };\n" +
|
||||||
for (var _0x4eddcb = arguments['length'] > 0x1 && void 0x0 !== arguments[0x1] ? arguments[0x1] : 0xa,
|
" }\n" +
|
||||||
_0x2fc680 = function() {
|
" \n" +
|
||||||
for (var _0x515c63, _0x361314 = [], _0x4cbdba = 0x0; _0x4cbdba < 0x100; _0x4cbdba++) {
|
" \n" +
|
||||||
_0x515c63 = _0x4cbdba;
|
" function _0x4f141a(_0x4075b1) {\n" +
|
||||||
for (var _0x460960 = 0x0; _0x460960 < 0x8; _0x460960++)
|
" \n" +
|
||||||
_0x515c63 = 0x1 & _0x515c63 ? 0xedb88320 ^ _0x515c63 >>> 0x1 : _0x515c63 >>> 0x1;
|
" for (var _0x4eddcb = arguments['length'] > 0x1 && void 0x0 !== arguments[0x1] ? " +
|
||||||
_0x361314[_0x4cbdba] = _0x515c63;
|
"arguments[0x1] : 0xa,\n" +
|
||||||
}
|
" _0x2fc680 = function() {\n" +
|
||||||
return _0x361314;
|
" for (var _0x515c63, _0x361314 = [], _0x4cbdba = 0x0; _0x4cbdba < 0x100; _0x4cbdba++) " +
|
||||||
},
|
"{\n" +
|
||||||
_0x4aed86 = _0x2fc680(),
|
" _0x515c63 = _0x4cbdba;\n" +
|
||||||
_0x5880f0 = _0x4075b1,
|
" for (var _0x460960 = 0x0; _0x460960 < 0x8; _0x460960++)\n" +
|
||||||
_0x492393 = -0x1, _0x25d82c = 0x0;
|
" _0x515c63 = 0x1 & _0x515c63 ? 0xedb88320 ^ _0x515c63 >>> 0x1 : _0x515c63 >>> 0x1;" +
|
||||||
_0x25d82c < _0x5880f0['length'];
|
"\n" +
|
||||||
_0x25d82c++)
|
" _0x361314[_0x4cbdba] = _0x515c63;\n" +
|
||||||
|
" }\n" +
|
||||||
_0x492393 = _0x492393 >>> 0x8 ^ _0x4aed86[0xff & (_0x492393 ^ _0x5880f0.charCodeAt(_0x25d82c))];
|
" return _0x361314;\n" +
|
||||||
return _0x492393 = (-0x1 ^ _0x492393) >>> 0x0,
|
" },\n" +
|
||||||
_0x492393.toString(_0x4eddcb);
|
" _0x4aed86 = _0x2fc680(),\n" +
|
||||||
}
|
" _0x5880f0 = _0x4075b1,\n" +
|
||||||
|
" _0x492393 = -0x1, _0x25d82c = 0x0;\n" +
|
||||||
|
" _0x25d82c < _0x5880f0['length'];\n" +
|
||||||
function getSign(_0x1e37d5) {
|
" _0x25d82c++)\n" +
|
||||||
var _0x4e2d74 = 'web';
|
" \n" +
|
||||||
var _0x56f040 = 3;
|
" _0x492393 = _0x492393 >>> 0x8 ^ _0x4aed86[0xff & (_0x492393 ^ _0x5880f0.charCodeAt" +
|
||||||
var _0x1e2592 = Math.round((new Date().getTime() + 0x3c * new Date().getTimezoneOffset() * 0x3e8 + 28800000) / 0x3e8).toString();
|
"(_0x25d82c))];\n" +
|
||||||
var key = 'a,d,e,f,g,h,l,m,y,i,j,n,o,p,k,q,r,s,t,u,b,c,v,w,s,z';
|
" return _0x492393 = (-0x1 ^ _0x492393) >>> 0x0,\n" +
|
||||||
var _0x48562f = Math['round'](0x989680 * Math['random']());
|
" _0x492393.toString(_0x4eddcb);\n" +
|
||||||
|
" }\n" +
|
||||||
var _0x2f7dfc;
|
" \n" +
|
||||||
var _0x35a889;
|
" \n" +
|
||||||
var _0x36f983;
|
" function getSign(_0x1e37d5) {\n" +
|
||||||
var _0x3b043d;
|
" var _0x4e2d74 = 'web';\n" +
|
||||||
var _0x5bc73b;
|
" var _0x56f040 = 3;\n" +
|
||||||
var _0x4b30b2;
|
" var _0x1e2592 = Math.round((new Date().getTime() + 0x3c * new Date().getTimezoneOffset() *" +
|
||||||
var _0x32399e;
|
" 0x3e8 + 28800000) / 0x3e8).toString();\n" +
|
||||||
var _0x25d94e;
|
" var key = 'a,d,e,f,g,h,l,m,y,i,j,n,o,p,k,q,r,s,t,u,b,c,v,w,s,z';\n" +
|
||||||
var _0x373490;
|
" var _0x48562f = Math['round'](0x989680 * Math['random']());\n" +
|
||||||
for (var _0x1c540f in (_0x2f7dfc = key.split(','),
|
" \n" +
|
||||||
_0x35a889 = _0x1b5d95(_0x1e2592),
|
" var _0x2f7dfc;\n" +
|
||||||
_0x36f983 = _0x35a889['y'],
|
" var _0x35a889;\n" +
|
||||||
_0x3b043d = _0x35a889['m'],
|
" var _0x36f983;\n" +
|
||||||
_0x5bc73b = _0x35a889['d'],
|
" var _0x3b043d;\n" +
|
||||||
_0x4b30b2 = _0x35a889['h'],
|
" var _0x5bc73b;\n" +
|
||||||
_0x32399e = _0x35a889['f'],
|
" var _0x4b30b2;\n" +
|
||||||
_0x25d94e = [_0x36f983, _0x3b043d, _0x5bc73b, _0x4b30b2, _0x32399e].join(''),
|
" var _0x32399e;\n" +
|
||||||
_0x373490 = [],
|
" var _0x25d94e;\n" +
|
||||||
_0x25d94e))
|
" var _0x373490;\n" +
|
||||||
_0x373490['push'](_0x2f7dfc[Number(_0x25d94e[_0x1c540f])]);
|
" for (var _0x1c540f in (_0x2f7dfc = key.split(','),\n" +
|
||||||
var _0x43bdc6;
|
" _0x35a889 = _0x1b5d95(_0x1e2592),\n" +
|
||||||
var _0x406c4e;
|
" _0x36f983 = _0x35a889['y'],\n" +
|
||||||
return _0x43bdc6 = _0x4f141a(_0x373490['join']('')),
|
" _0x3b043d = _0x35a889['m'],\n" +
|
||||||
_0x406c4e = _0x4f141a(''['concat'](_0x1e2592, '|')['concat'](_0x48562f, '|')['concat'](_0x1e37d5, '|')['concat'](_0x4e2d74, '|')['concat'](_0x56f040, '|')['concat'](_0x43bdc6)),
|
" _0x5bc73b = _0x35a889['d'],\n" +
|
||||||
[_0x43bdc6, ''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)];
|
" _0x4b30b2 = _0x35a889['h'],\n" +
|
||||||
}
|
" _0x32399e = _0x35a889['f'],\n" +
|
||||||
|
" _0x25d94e = [_0x36f983, _0x3b043d, _0x5bc73b, _0x4b30b2, _0x32399e].join(''),\n" +
|
||||||
""";
|
" _0x373490 = [],\n" +
|
||||||
String lz = """
|
" _0x25d94e))\n" +
|
||||||
/**
|
" _0x373490['push'](_0x2f7dfc[Number(_0x25d94e[_0x1c540f])]);\n" +
|
||||||
* 蓝奏云解析器js签名获取工具
|
" var _0x43bdc6;\n" +
|
||||||
*/
|
" var _0x406c4e;\n" +
|
||||||
|
" return _0x43bdc6 = _0x4f141a(_0x373490['join']('')),\n" +
|
||||||
var signObj;
|
" _0x406c4e = _0x4f141a(''['concat'](_0x1e2592, '|')['concat'](_0x48562f, '|')['concat']" +
|
||||||
|
"(_0x1e37d5, '|')['concat'](_0x4e2d74, '|')['concat'](_0x56f040, '|')['concat'](_0x43bdc6)),\n" +
|
||||||
|
" [_0x43bdc6, ''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)" +
|
||||||
var $, jQuery;
|
"];\n" +
|
||||||
|
" }\n" +
|
||||||
$ = jQuery = function () {
|
" ";
|
||||||
return new jQuery.fn.init();
|
String lz = "/**\n" +
|
||||||
}
|
" * 蓝奏云解析器js签名获取工具\n" +
|
||||||
|
" */\n" +
|
||||||
jQuery.fn = jQuery.prototype = {
|
" \n" +
|
||||||
init: function () {
|
" var signObj;\n" +
|
||||||
return {
|
" \n" +
|
||||||
focus: function (a) {
|
" \n" +
|
||||||
|
" var $, jQuery;\n" +
|
||||||
},
|
" \n" +
|
||||||
keyup: function(a) {
|
" $ = jQuery = function () {\n" +
|
||||||
|
" return new jQuery.fn.init();\n" +
|
||||||
},
|
" }\n" +
|
||||||
ajax: function (obj) {
|
" \n" +
|
||||||
signObj = obj
|
" jQuery.fn = jQuery.prototype = {\n" +
|
||||||
}
|
" init: function () {\n" +
|
||||||
|
" return {\n" +
|
||||||
}
|
" focus: function (a) {\n" +
|
||||||
},
|
" \n" +
|
||||||
|
" },\n" +
|
||||||
}
|
" keyup: function(a) {\n" +
|
||||||
|
" \n" +
|
||||||
jQuery.fn.init.prototype = jQuery.fn;
|
" },\n" +
|
||||||
|
" ajax: function (obj) {\n" +
|
||||||
|
" signObj = obj\n" +
|
||||||
// 伪装jquery.ajax函数获取关键数据
|
" }\n" +
|
||||||
$.ajax = function (obj) {
|
" \n" +
|
||||||
signObj = obj
|
" }\n" +
|
||||||
}
|
" },\n" +
|
||||||
|
" \n" +
|
||||||
var document = {
|
" }\n" +
|
||||||
getElementById: function (v) {
|
" \n" +
|
||||||
return {
|
" jQuery.fn.init.prototype = jQuery.fn;\n" +
|
||||||
value: 'v'
|
" \n" +
|
||||||
}
|
" \n" +
|
||||||
},
|
" // 伪装jquery.ajax函数获取关键数据\n" +
|
||||||
}
|
" $.ajax = function (obj) {\n" +
|
||||||
|
" signObj = obj\n" +
|
||||||
""";
|
" }\n" +
|
||||||
|
" \n" +
|
||||||
|
" var document = {\n" +
|
||||||
|
" getElementById: function (v) {\n" +
|
||||||
|
" return {\n" +
|
||||||
|
" value: 'v'\n" +
|
||||||
|
" }\n" +
|
||||||
|
" },\n" +
|
||||||
|
" }";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package cn.qaiu.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2023/7/16 1:53
|
|
||||||
*/
|
|
||||||
public class PanExceptionUtils {
|
|
||||||
|
|
||||||
public static RuntimeException fillRunTimeException(String name, String dataKey, Throwable t) {
|
|
||||||
return new RuntimeException(name + ": 请求异常: key = " + dataKey, t.fillInStackTrace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package cn.qaiu.util;
|
|
||||||
|
|
||||||
import io.vertx.core.AsyncResult;
|
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.Vertx;
|
|
||||||
import io.vertx.core.buffer.Buffer;
|
|
||||||
import io.vertx.core.http.impl.headers.HeadersMultiMap;
|
|
||||||
import io.vertx.ext.web.client.HttpResponse;
|
|
||||||
import io.vertx.ext.web.client.WebClient;
|
|
||||||
import io.vertx.ext.web.client.WebClientSession;
|
|
||||||
|
|
||||||
public class ReqIpUtil {
|
|
||||||
public static String BASE_URL = "https://ip.ihuan.me";
|
|
||||||
public static String BASE_URL_TEMPLATE = BASE_URL + "/{path}";
|
|
||||||
|
|
||||||
// GET https://ip.ihuan.me/mouse.do -> $("input[name='key']").val("30b4975b5547fed806bd2b9caa18485a");
|
|
||||||
public static String PATH1 = "mouse.do";
|
|
||||||
|
|
||||||
public static String PATH2 = "tqdl.html";
|
|
||||||
|
|
||||||
// 创建请求头Map
|
|
||||||
static MultiMap headers = new HeadersMultiMap();
|
|
||||||
|
|
||||||
static {
|
|
||||||
|
|
||||||
headers.set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
|
|
||||||
headers.set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8");
|
|
||||||
headers.set("cache-control", "no-cache");
|
|
||||||
headers.set("dnt", "1");
|
|
||||||
headers.set("origin", "https://ip.ihuan.me");
|
|
||||||
headers.set("pragma", "no-cache");
|
|
||||||
headers.set("priority", "u=0, i");
|
|
||||||
headers.set("referer", "https://ip.ihuan.me");
|
|
||||||
headers.set("sec-ch-ua", "\"Google Chrome\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"");
|
|
||||||
headers.set("sec-ch-ua-mobile", "?0");
|
|
||||||
headers.set("sec-ch-ua-platform", "\"Windows\"");
|
|
||||||
headers.set("sec-fetch-dest", "document");
|
|
||||||
headers.set("sec-fetch-mode", "navigate");
|
|
||||||
headers.set("sec-fetch-site", "same-origin");
|
|
||||||
headers.set("sec-fetch-user", "?1");
|
|
||||||
headers.set("upgrade-insecure-requests", "1");
|
|
||||||
// headers.set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Vertx vertx = Vertx.vertx();
|
|
||||||
WebClient webClient = WebClient.create(vertx);
|
|
||||||
// 发送GET请求
|
|
||||||
WebClientSession webClientSession = WebClientSession.create(webClient);
|
|
||||||
|
|
||||||
|
|
||||||
public void exec() {
|
|
||||||
webClientSession.getAbs(BASE_URL)
|
|
||||||
.putHeaders(headers) // 将请求头Map添加到请求中
|
|
||||||
.send(this::next);
|
|
||||||
}
|
|
||||||
|
|
||||||
void next(AsyncResult<HttpResponse<Buffer>> response) {
|
|
||||||
if (response.failed()) {
|
|
||||||
response.cause().printStackTrace();
|
|
||||||
} else {
|
|
||||||
HttpResponse<Buffer> res = response.result();
|
|
||||||
System.out.println("Received response with status code " + res.statusCode());
|
|
||||||
System.out.println("Body: " + res.body());
|
|
||||||
webClientSession.getAbs(BASE_URL_TEMPLATE).setTemplateParam("path", PATH1)
|
|
||||||
.putHeaders(headers) // 将请求头Map添加到请求中
|
|
||||||
.send(response2 -> {
|
|
||||||
System.out.println(response2.result().bodyAsString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1081
parser/src/main/java/cn/qaiu/util/jdk17halper/HexFormat.java
Normal file
1081
parser/src/main/java/cn/qaiu/util/jdk17halper/HexFormat.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
|||||||
package cn.qaiu.parser;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class FCURLParser {// 定义前缀
|
|
||||||
public static final String SHARE_URL_PREFIX0 = "https://v2.fangcloud.com/s";
|
|
||||||
public static final String SHARE_URL_PREFIX = "https://v2.fangcloud.com/sharing/";
|
|
||||||
public static final String SHARE_URL_PREFIX2 = "https://v2.fangcloud.cn/sharing/";
|
|
||||||
|
|
||||||
// 定义正则表达式,适用于所有前缀
|
|
||||||
private static final String SHARING_REGEX = "https://www\\.ecpan\\.cn/web(/%23|/#)?/yunpanProxy\\?path=.*&data=" +
|
|
||||||
"([^&]+)&isShare=1";
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// 测试 URL
|
|
||||||
String[] urls = {
|
|
||||||
"https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
|
||||||
"https://www.ecpan.cn/web/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1",
|
|
||||||
"https://v2.fangcloud.cn/sharing/xyz789"
|
|
||||||
};
|
|
||||||
|
|
||||||
// 编译正则表达式
|
|
||||||
Pattern pattern = Pattern.compile(SHARING_REGEX);
|
|
||||||
|
|
||||||
for (String url : urls) {
|
|
||||||
Matcher matcher = pattern.matcher(url);
|
|
||||||
if (matcher.find()) {
|
|
||||||
System.out.println(matcher.groupCount());
|
|
||||||
String shareKey = matcher.group(matcher.groupCount()); // 捕捉组 3
|
|
||||||
System.out.println("Captured part: " + shareKey);
|
|
||||||
} else {
|
|
||||||
System.out.println("No match found.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package cn.qaiu.parser;
|
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/8/8 2:39
|
|
||||||
*/
|
|
||||||
public class PanDomainTemplateTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void normalizeShareLink() {
|
|
||||||
// 准备测试数据
|
|
||||||
String testShareUrl = "https://test.lanzoux.com/s/someShareKey";
|
|
||||||
|
|
||||||
ParserCreate parserCreate = ParserCreate.fromShareUrl(testShareUrl); // 假设使用LZ网盘模板
|
|
||||||
|
|
||||||
// 调用normalizeShareLink方法
|
|
||||||
ShareLinkInfo result = parserCreate.getShareLinkInfo();
|
|
||||||
System.out.println(result);
|
|
||||||
// 断言结果是否符合预期
|
|
||||||
assertNotNull("Result should not be null", result);
|
|
||||||
assertEquals("Share key should match", "someShareKey", result.getShareKey());
|
|
||||||
assertEquals("Standard URL should be generated correctly", parserCreate.getStandardUrlTemplate().replace("{shareKey}", "someShareKey"), result.getStandardUrl());
|
|
||||||
// 可以添加更多的断言来验证其他字段
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fromShareUrl() throws InterruptedException {
|
|
||||||
// 准备测试数据
|
|
||||||
String lzUrl = "https://wwn.lanzouy.com/ihLkw1gezutg";
|
|
||||||
String cowUrl = "https://cowtransfer.com/s/9a644fe3e3a748";
|
|
||||||
String ceUrl = "https://pan.huang1111.cn/s/g31PcQ";
|
|
||||||
String wsUrl = "https://f.ws59.cn/f/f25625rv6p6";
|
|
||||||
// ParserCreate.fromShareUrl(wsUrl).createTool()
|
|
||||||
// .parse().onSuccess(System.out::println);
|
|
||||||
// ParserCreate.fromShareUrl(lzUrl).createTool()
|
|
||||||
// .parse().onSuccess(System.out::println);
|
|
||||||
// ParserCreate.fromShareUrl(cowUrl).createTool()
|
|
||||||
// .parse().onSuccess(System.out::println);
|
|
||||||
ParserCreate.fromShareUrl(lzUrl).createTool()
|
|
||||||
.parse().onSuccess(System.out::println);
|
|
||||||
|
|
||||||
// ParserCreate.fromType("lz").shareKey("ihLkw1gezutg")
|
|
||||||
// .createTool().parse().onSuccess(System.out::println);
|
|
||||||
// ParserCreate.LZ.shareKey("ihLkw1gezutg")
|
|
||||||
// .createTool().parse().onSuccess(System.out::println);
|
|
||||||
|
|
||||||
|
|
||||||
// 调用fromShareUrl方法
|
|
||||||
// PanDomainTemplate resultTemplate = ParserCreate.fromShareUrl(testShareUrl);
|
|
||||||
// System.out.println(resultTemplate.normalizeShareLink(testShareUrl));
|
|
||||||
// System.out.println(resultTemplate.shareKey("xxx"));
|
|
||||||
// System.out.println(resultTemplate.createTool("xxx",null).parse()
|
|
||||||
// .onSuccess(System.out::println));
|
|
||||||
// System.out.println(resultTemplate.getDisplayName());
|
|
||||||
// System.out.println(resultTemplate.getStandardUrlTemplate());
|
|
||||||
// System.out.println(resultTemplate.getRegexPattern());
|
|
||||||
//
|
|
||||||
// // 断言结果是否符合预期
|
|
||||||
// assertNotNull("Result should not be null", resultTemplate);
|
|
||||||
// assertEquals("Should return the correct template", ParserCreate.LZ, resultTemplate);
|
|
||||||
// // 可以添加更多的断言来验证正则表达式匹配逻辑
|
|
||||||
// new Scanner(System.in).nextLine();
|
|
||||||
TimeUnit.SECONDS.sleep(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package qaiu.web.test;
|
package qaiu.web.test;
|
||||||
|
|
||||||
import cn.qaiu.util.AESUtils;
|
import cn.qaiu.util.AESUtils;
|
||||||
|
import cn.qaiu.util.jdk17halper.HexFormat;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -9,7 +10,6 @@ import javax.crypto.IllegalBlockSizeException;
|
|||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.HexFormat;
|
|
||||||
|
|
||||||
public class TestAESUtil {
|
public class TestAESUtil {
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ public class TestRegex {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void regexYFC() {
|
public void regexYFC() {
|
||||||
String html = """
|
String html = "\n" +
|
||||||
<input type="hidden" id="typed_id" value="file_559003251828">
|
" <input type=\"hidden\" id=\"typed_id\" value=\"file_559003251828\">\n" +
|
||||||
<input type="hidden" id="share_link_token" value="9cbe4b73521ba4d65a8cd38a8c">
|
" <input type=\"hidden\" id=\"share_link_token\" " +
|
||||||
""";
|
"value=\"9cbe4b73521ba4d65a8cd38a8c\">";
|
||||||
|
|
||||||
Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\"");
|
Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\"");
|
||||||
Matcher matcher = compile.matcher(html);
|
Matcher matcher = compile.matcher(html);
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ public class TestWebClient2 {
|
|||||||
public void matcherHtml() {
|
public void matcherHtml() {
|
||||||
|
|
||||||
Pattern compile = Pattern.compile("class=\"ifr2\" name=.+src=\"(/fn\\?[a-zA-Z0-9_+/=]{16,})\"");
|
Pattern compile = Pattern.compile("class=\"ifr2\" name=.+src=\"(/fn\\?[a-zA-Z0-9_+/=]{16,})\"");
|
||||||
var text = """
|
var text = "<div class=\"ifr\"><!--<iframe class=\"ifr2\" name=\"1\" src=\"/fn?v2\" frameborder=\"0\" " +
|
||||||
<div class="ifr"><!--<iframe class="ifr2" name="1" src="/fn?v2" frameborder="0" scrolling="no"></iframe>-->
|
"scrolling=\"no\"></iframe>-->\n" +
|
||||||
<iframe class="ifr2" name="1685001208" src="/fn?UzUBa1oxBmUAYgNsUDUFNVI6BjJfJlchV21TZFU_aVWwANVQzXTBXMlUxUTcLZ1dwUn8DYwQ5AHFVOwdmBjRUPlM2AS9aOgY3AGIDMFA2" frameborder="0" scrolling="no"></iframe>""";
|
"<iframe class=\"ifr2\" name=\"1685001208\" " +
|
||||||
|
"src=\"/fn" +
|
||||||
|
"?UzUBa1oxBmUAYgNsUDUFNVI6BjJfJlchV21TZFU_aVWwANVQzXTBXMlUxUTcLZ1dwUn8DYwQ5AHFVOwdmBjRUPlM2AS9aOgY3AGIDMFA2\" frameborder=\"0\" scrolling=\"no\"></iframe>";
|
||||||
System.out.println(text);
|
System.out.println(text);
|
||||||
Matcher matcher = compile.matcher(text);
|
Matcher matcher = compile.matcher(text);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
|
|||||||
11
pom.xml
11
pom.xml
@@ -17,17 +17,18 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.1.8</revision>
|
<revision>0.1.7</revision>
|
||||||
<java.version>17</java.version>
|
<java.version>11</java.version>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
||||||
|
|
||||||
|
<slf4j.version>2.0.5</slf4j.version>
|
||||||
<vertx.version>4.5.6</vertx.version>
|
<vertx.version>4.5.6</vertx.version>
|
||||||
<org.reflections.version>0.10.2</org.reflections.version>
|
<org.reflections.version>0.10.2</org.reflections.version>
|
||||||
<lombok.version>1.18.30</lombok.version>
|
<lombok.version>1.18.12</lombok.version>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.5</slf4j.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
# nfd-web
|
# nfd-web
|
||||||
解析服务的前端页面, 提供API测试, 统计查询, 二维码生成等
|
当前页面修改自开源项目 https://github.com/HurryBy/CloudDiskAnalysis
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
## 关于如何将前端项目和java一块打包:
|
|
||||||
1. 先打包前端模块
|
|
||||||
2. 打包后请将当前目录下的nfd-front目录放置在项目下webroot目录, 然后使用maven打包java模块即可
|
|
||||||
3. 项目部署后演示页面的代理端口是6401默认使用http, 如需https可以加nginx代理, 也可以使用本项目自带的代理服务和配置证书路径
|
|
||||||
|
|
||||||
|

|
||||||
## Project setup
|
## Project setup
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB |
675
web-front/package-lock.json
generated
675
web-front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,11 +8,10 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.6.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"element-ui": "^2.15.14",
|
"element-ui": "^2.15.12",
|
||||||
"qrcode": "^1.5.4",
|
"vue": "^2.6.14",
|
||||||
"vue": "^2.7.16",
|
|
||||||
"vue-clipboard2": "^0.3.3",
|
"vue-clipboard2": "^0.3.3",
|
||||||
"vue-json-viewer": "^2.2.22"
|
"vue-json-viewer": "^2.2.22"
|
||||||
},
|
},
|
||||||
@@ -22,11 +21,12 @@
|
|||||||
"@vue/cli-plugin-babel": "~5.0.0",
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
"@vue/cli-service": "~5.0.0",
|
"@vue/cli-service": "~5.0.0",
|
||||||
"compression-webpack-plugin": "^6.1.1",
|
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-plugin-vue": "^8.0.3",
|
"eslint-plugin-vue": "^8.0.3",
|
||||||
"filemanager-webpack-plugin": "8.0.0",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"vue-template-compiler": "^2.7.16"
|
|
||||||
|
"compression-webpack-plugin": "^6.1.1",
|
||||||
|
"filemanager-webpack-plugin": "8.0.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|||||||
BIN
web-front/public/avator.png
Normal file
BIN
web-front/public/avator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB |
@@ -9,6 +9,9 @@
|
|||||||
content="Netdisk fast download,网盘直链解析工具">
|
content="Netdisk fast download,网盘直链解析工具">
|
||||||
<meta name="description"
|
<meta name="description"
|
||||||
content="Netdisk fast download 网盘直链解析工具">
|
content="Netdisk fast download 网盘直链解析工具">
|
||||||
|
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
|
||||||
|
<script>LA.init({id:"K8zkCkZMgFA6ShZK",ck:"K8zkCkZMgFA6ShZK"})</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.page-loading-wrap {
|
.page-loading-wrap {
|
||||||
padding: 120px;
|
padding: 120px;
|
||||||
|
|||||||
@@ -4,55 +4,39 @@
|
|||||||
<el-card class="box-card">
|
<el-card class="box-card">
|
||||||
<div class="demo-basic--circle">
|
<div class="demo-basic--circle">
|
||||||
<div class="block" style="text-align: center;">
|
<div class="block" style="text-align: center;">
|
||||||
<img :height="150" src="../public/images/lanzou111.png" alt="lz"></img>
|
<el-avatar :size="150" :src="avatar"></el-avatar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 style="text-align: center;">NFD网盘直链解析0.1.8_bate2(API演示)</h3>
|
<h3 style="text-align: center;">NFD网盘直链解析(API演示)</h3>
|
||||||
<div class="typo">
|
<div class="typo">
|
||||||
<p style="text-align: center;">
|
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
|
||||||
<span>
|
rel="nofollow"><u>netdisk-fast-download</u></a></p>
|
||||||
<el-link href="https://github.com/qaiu/netdisk-fast-download" target="_blank" rel="nofollow">
|
<p><strong>当前页面修改自开源项目</strong><a href="https://github.com/HurryBy/CloudDiskAnalysis"
|
||||||
<u>GitHub</u></el-link>
|
target="_blank"
|
||||||
</span>
|
rel="nofollow"><u>CloudDiskAnalysis</u></a></p>
|
||||||
<span style="margin-left: 30px">
|
<p><strong>目前支持 </strong>已支持蓝奏云/奶牛快传/移动云云空间/UC网盘(暂时失效)/小飞机盘/亿方云/123云盘</p>
|
||||||
<el-link href="https://blog.qaiu.top/archives/netdisk-fast-download-bao-ta-an-zhuang-jiao-cheng" target="_blank"
|
<p>
|
||||||
rel="nofollow"><u>宝塔部署安装教程</u>
|
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
|
||||||
</el-link>
|
</p>
|
||||||
</span>
|
<p>节点1: 成功:{{ node1Info.success }},失败:{{ node1Info.fail }},总数:{{ node1Info.total }}</p>
|
||||||
<span style="margin-left: 30px">
|
<p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>
|
||||||
<el-link href="https://blog.qaiu.top" target="_blank"
|
|
||||||
rel="nofollow"><u>QAIU博客</u>
|
|
||||||
</el-link>
|
|
||||||
</span></p>
|
|
||||||
<p><strong>目前支持 </strong>蓝奏云/蓝奏云优享/小飞机盘/123云盘/奶牛快传/移动云云空间/亿方云/文叔叔/QQ邮箱文件中转站</p>
|
|
||||||
<p>已加入缓存机制, 如果遇到解析出的下载链接失效的情况请及时到<a href="https://github.com/qaiu/netdisk-fast-download/issues">
|
|
||||||
<u><strong>项目GitHub反馈</strong></u></a></p>
|
|
||||||
<p>节点1: 回源请求数:{{ node1Info.parserTotal }}, 缓存请求数:{{ node1Info.cacheTotal }}, 总数:{{ node1Info.total }}</p>
|
|
||||||
<!-- <p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>-->
|
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="main" v-loading="isLoading">
|
<div class="main" v-loading="isLoading">
|
||||||
<div class="grid-content">
|
<div class="grid-content">
|
||||||
<el-input placeholder="请粘贴分享链接" v-model="link" id="url" lass="input-with-select">
|
<el-input placeholder="请粘贴分享链接" v-model="link" id="url" lass="input-with-select">
|
||||||
<strong slot="prepend">分享链接</strong>
|
<strong slot="prepend">分享链接</strong>
|
||||||
|
<el-button slot="append" @click="onSubmit">解析</el-button>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-input placeholder="请输入密码" v-model="password" id="url" lass="input-with-select">
|
<el-input placeholder="请输入密码" v-model="password" id="url" lass="input-with-select"></el-input>
|
||||||
<strong slot="prepend">分享密码</strong>
|
|
||||||
</el-input>
|
|
||||||
<el-input v-show="respData.data" placeholder="解析地址" :value="getLink2" id="url" lass="input-with-select">
|
<el-input v-show="respData.data" placeholder="解析地址" :value="getLink2" id="url" lass="input-with-select">
|
||||||
<el-button slot="append" v-clipboard:copy="getLink2"
|
<el-button slot="append" v-clipboard:copy="getLink2"
|
||||||
v-clipboard:success="onCopy"
|
v-clipboard:success="onCopy"
|
||||||
v-clipboard:error="onError">点我复制
|
v-clipboard:error="onError">点我复制
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-input>
|
</el-input>
|
||||||
<p style="text-align: center">
|
|
||||||
<el-button style="" @click="onSubmit">解析测试</el-button>
|
|
||||||
<el-button style="margin-left: 20px" @click="genMd">生成Markdown链接</el-button>
|
|
||||||
<el-button style="margin-left: 20px" @click="generateQRCode">生成二维码</el-button>
|
|
||||||
<el-button style="margin-left: 20px" @click="getTj">链接信息统计</el-button>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="respData.code" style="margin-top: 10px">
|
<div v-show="respData.code" style="margin-top: 10px">
|
||||||
<strong>解析结果: </strong>
|
<strong>解析结果: </strong>
|
||||||
<json-viewer
|
<json-viewer
|
||||||
:value="respData"
|
:value="respData"
|
||||||
@@ -63,44 +47,6 @@
|
|||||||
/>
|
/>
|
||||||
<a :href="downUrl" v-show="downUrl">点击下载</a>
|
<a :href="downUrl" v-show="downUrl">点击下载</a>
|
||||||
</div>
|
</div>
|
||||||
<el-row v-if="mdText" style="text-align: center">
|
|
||||||
<el-col span="22">
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 4, maxRows: 8}"
|
|
||||||
placeholder="请输入内容"
|
|
||||||
v-model="mdText">
|
|
||||||
</el-input>
|
|
||||||
</el-col>
|
|
||||||
<el-col span="2">
|
|
||||||
<el-button v-clipboard:copy="mdText"
|
|
||||||
v-clipboard:success="onCopy"
|
|
||||||
v-clipboard:error="onError">复制
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<div style="text-align: center" v-show="showQrc">
|
|
||||||
<div style="text-align: center"><el-link target="_blank" :href="codeUrl">{{ codeUrl }}</el-link></div>
|
|
||||||
<canvas ref="qrcodeCanvas"></canvas>
|
|
||||||
<div style="text-align: center"> 扫码下载 </div>
|
|
||||||
</div>
|
|
||||||
<div v-if="tjData.shareLinkInfo">
|
|
||||||
<el-descriptions class="margin-top" title="分享详情" :column="1" border>
|
|
||||||
<template slot="extra">
|
|
||||||
<el-button type="primary" size="small">操作</el-button>
|
|
||||||
</template>
|
|
||||||
<el-descriptions-item label="网盘名称">{{ tjData.shareLinkInfo.panName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="网盘标识">{{ tjData.shareLinkInfo.type }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="分享Key">{{ tjData.shareLinkInfo.shareKey }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="分享链接"> <el-link target="_blank" :href="tjData.shareLinkInfo.shareUrl">{{ tjData.shareLinkInfo.shareUrl }}</el-link></el-descriptions-item>
|
|
||||||
<el-descriptions-item label="jsonApi链接"> <el-link target="_blank" :href="tjData.apiLink">{{ tjData.apiLink }}</el-link></el-descriptions-item>
|
|
||||||
<el-descriptions-item label="302下载链接"> <el-link target="_blank" :href="tjData.downLink">{{ tjData.downLink }}</el-link></el-descriptions-item>
|
|
||||||
<el-descriptions-item label="解析次数">{{ tjData.parserTotal }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="缓存命中次数">{{ tjData.cacheHitTotal }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="总请求次数">{{ tjData.sumTotal }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -109,7 +55,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import QRCode from 'qrcode'
|
|
||||||
/*
|
/*
|
||||||
蓝奏云 (lz)
|
蓝奏云 (lz)
|
||||||
登录, 上传, 下载, 分享
|
登录, 上传, 下载, 分享
|
||||||
@@ -136,19 +81,13 @@ export default {
|
|||||||
name: 'App',
|
name: 'App',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// baseAPI: `${location.protocol}//${location.hostname}:6400`,
|
|
||||||
baseAPI: `${location.protocol}//${location.host}`,
|
|
||||||
current: {}, // 当前分享
|
|
||||||
showQrc: false,
|
|
||||||
codeUrl: '',
|
|
||||||
mdText: '',
|
|
||||||
link: "",
|
link: "",
|
||||||
password: "",
|
password: "",
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
downUrl: null,
|
downUrl: null,
|
||||||
|
avatar: "https://q2.qlogo.cn/headimg_dl?dst_uin=736226400&spec=640",
|
||||||
select: "lz",
|
select: "lz",
|
||||||
respData: {},
|
respData: {},
|
||||||
tjData: {},
|
|
||||||
panList: [
|
panList: [
|
||||||
{
|
{
|
||||||
name: "蓝奏云",
|
name: "蓝奏云",
|
||||||
@@ -180,36 +119,28 @@ export default {
|
|||||||
value: 'ye'
|
value: 'ye'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
getLink: null,
|
getLink: '',
|
||||||
getLink2: '',
|
getLink2: '',
|
||||||
getLinkInfo: null,
|
|
||||||
node1Info: {},
|
node1Info: {},
|
||||||
node2Info: {},
|
node2Info: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
check() {
|
onSubmit() {
|
||||||
this.mdText = ''
|
|
||||||
this.showQrc = false
|
|
||||||
this.respData = {}
|
|
||||||
this.tjData = {}
|
|
||||||
if (!this.link.startsWith("https://")) {
|
if (!this.link.startsWith("https://")) {
|
||||||
this.$message.error("请输入有效链接!")
|
this.$message.error("请输入有效链接!")
|
||||||
throw new Error('请输入有效链接')
|
return
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onSubmit() {
|
|
||||||
this.check()
|
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
this.downUrl = ''
|
this.downUrl = ''
|
||||||
this.respData = {}
|
this.respData = {}
|
||||||
this.getLink2 = `${this.baseAPI}/parser?url=${this.link}`
|
this.getLink2 = `${location.protocol}//${location.host}/parser?url=${this.link}`
|
||||||
// this.getLink = `${location.protocol}//${location.host}/api/json/parser?url=${this.link}`
|
// this.getLink = `${location.protocol}//${location.host}/api/json/parser?url=${this.link}`
|
||||||
// this.getLink = `${location.protocol}//${location.host}/json/parser`
|
this.getLink = `${location.protocol}//${location.host}/json/parser?url=${this.link}`
|
||||||
if (this.password) {
|
if (this.password) {
|
||||||
this.getLink2 += `&pwd=${this.password}`
|
this.getLink += `&pwd=${this.password}`
|
||||||
}
|
}
|
||||||
axios.get(this.getLink, {params: {url: this.link, pwd: this.password}}).then(
|
axios.get(this.getLink).then(
|
||||||
response => {
|
response => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
this.respData = response.data
|
this.respData = response.data
|
||||||
@@ -218,11 +149,10 @@ export default {
|
|||||||
message: response.data.msg,
|
message: response.data.msg,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
this.downUrl = response.data.data.directLink
|
this.downUrl = response.data.data
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(response.data.msg)
|
this.$message.error(response.data.msg)
|
||||||
}
|
}
|
||||||
this.getInfo()
|
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
@@ -244,79 +174,15 @@ export default {
|
|||||||
this.node1Info = response.data.data
|
this.node1Info = response.data.data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// axios.get('/n2/statisticsInfo').then(
|
axios.get('/n2/statisticsInfo').then(
|
||||||
// response => {
|
|
||||||
// if (response.data.success) {
|
|
||||||
// this.node2Info = response.data.data
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
},
|
|
||||||
genMd() {
|
|
||||||
this.check()
|
|
||||||
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
|
|
||||||
response => {
|
response => {
|
||||||
this.isLoading = false
|
if (response.data.success) {
|
||||||
if (response.data.code === 200) {
|
this.node2Info = response.data.data
|
||||||
this.$message({
|
|
||||||
message: response.data.msg,
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
this.mdText = this.buildMd('快速下载地址',response.data.data.downLink)
|
|
||||||
} else {
|
|
||||||
this.$message.error(response.data.msg)
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
buildMd(title, url) {
|
|
||||||
return `[${title}](${url})`
|
|
||||||
},
|
|
||||||
generateQRCode() {
|
|
||||||
this.check()
|
|
||||||
const options = { // 设置二维码的参数,例如大小、边距等
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
margin: 2,
|
|
||||||
colorDark : "#8e0fb7",
|
|
||||||
colorLight : "#fff2ad",
|
|
||||||
};
|
|
||||||
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
|
|
||||||
response => {
|
|
||||||
this.isLoading = false
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
this.$message({
|
|
||||||
message: response.data.msg,
|
|
||||||
type: 'success'
|
|
||||||
})
|
})
|
||||||
this.codeUrl = response.data.data.downLink
|
|
||||||
QRCode.toCanvas(this.$refs.qrcodeCanvas, this.codeUrl, options, error => {
|
|
||||||
if (error) console.error(error);
|
|
||||||
});
|
|
||||||
this.showQrc = true
|
|
||||||
} else {
|
|
||||||
this.$message.error(response.data.msg)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTj() {
|
|
||||||
this.check()
|
|
||||||
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
|
|
||||||
response => {
|
|
||||||
this.isLoading = false
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
this.$message({
|
|
||||||
message: response.data.msg,
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
this.tjData = response.data.data
|
|
||||||
} else {
|
|
||||||
this.$message.error(response.data.msg)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getLinkInfo = `${this.baseAPI}/v2/linkInfo`
|
|
||||||
this.getLink = `${this.baseAPI}/json/parser`
|
|
||||||
this.getInfo()
|
this.getInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +256,8 @@ body:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.typo a {
|
.typo a {
|
||||||
color: #0077ff;
|
color: #2c3e50;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
|
|||||||
@@ -40,17 +40,17 @@ module.exports = {
|
|||||||
test: /\.js$|\.html$|\.css/, // 匹配文件
|
test: /\.js$|\.html$|\.css/, // 匹配文件
|
||||||
threshold: 10240 // 对超过10k文件压缩
|
threshold: 10240 // 对超过10k文件压缩
|
||||||
}),
|
}),
|
||||||
// new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
|
new FileManagerPlugin({ //初始化 filemanager-webpack-plugin 插件实例
|
||||||
// onEnd: {
|
onEnd: {
|
||||||
// mkdir: ['./nfd-front'],
|
mkdir: ['./nfd-front'],
|
||||||
// delete: [ //首先需要删除项目根目录下的dist.zip
|
delete: [ //首先需要删除项目根目录下的dist.zip
|
||||||
// './nfd-front.zip',
|
'./nfd-front.zip',
|
||||||
// ],
|
],
|
||||||
// archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
||||||
// {source: './nfd-front', destination: './nfd-front.zip'},
|
{source: './nfd-front', destination: './nfd-front.zip'},
|
||||||
// ]
|
]
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
6895
web-front/yarn.lock
6895
web-front/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<packageDirectory>${project.basedir}/target/package</packageDirectory>
|
<packageDirectory>${project.basedir}/target/package</packageDirectory>
|
||||||
<java.version>17</java.version>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<finalName>netdisk-fast-download</finalName>
|
<finalName>netdisk-fast-download</finalName>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -33,7 +32,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>${lombok.version}</version>
|
<version>1.18.26</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--logback日志实现-->
|
<!--logback日志实现-->
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package cn.qaiu.lz;
|
|||||||
|
|
||||||
import cn.qaiu.WebClientVertxInit;
|
import cn.qaiu.WebClientVertxInit;
|
||||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
import cn.qaiu.db.pool.JDBCPoolInit;
|
||||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
|
||||||
import cn.qaiu.vx.core.Deploy;
|
import cn.qaiu.vx.core.Deploy;
|
||||||
import cn.qaiu.vx.core.util.ConfigConstant;
|
import cn.qaiu.vx.core.util.ConfigConstant;
|
||||||
import cn.qaiu.vx.core.util.VertxHolder;
|
import cn.qaiu.vx.core.util.VertxHolder;
|
||||||
@@ -24,20 +23,17 @@ public class AppMain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化数据库/缓存等
|
* 初始化数据库
|
||||||
*
|
*
|
||||||
* @param jsonObject 配置
|
* @param jsonObject 配置
|
||||||
*/
|
*/
|
||||||
private static void exec(JsonObject jsonObject) {
|
private static void exec(JsonObject jsonObject) {
|
||||||
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
||||||
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
||||||
// 数据库
|
|
||||||
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
|
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
|
||||||
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")).build().initPool();
|
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")).build().initPool();
|
||||||
}
|
}
|
||||||
// 缓存
|
|
||||||
if (jsonObject.containsKey(ConfigConstant.CACHE)) {
|
|
||||||
CacheConfigLoader.init(jsonObject.getJsonObject(ConfigConstant.CACHE));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package cn.qaiu.lz.common.cache;
|
|
||||||
|
|
||||||
import cn.qaiu.parser.PanDomainTemplate;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/12 7:38
|
|
||||||
*/
|
|
||||||
public class CacheConfigLoader {
|
|
||||||
private static final Map<String, Integer> CONFIGS = new HashMap<>();
|
|
||||||
public static String TYPE;
|
|
||||||
public static Integer DEFAULT_DURATION;
|
|
||||||
|
|
||||||
public static void init(JsonObject config) {
|
|
||||||
TYPE = config.getString("type");
|
|
||||||
Integer defaultDuration = config.getInteger("defaultDuration");
|
|
||||||
DEFAULT_DURATION = defaultDuration == null ? 60 : defaultDuration;
|
|
||||||
config.getJsonObject("duration").getMap().forEach((k,v) -> {
|
|
||||||
if (v == null) {
|
|
||||||
CONFIGS.put(k, DEFAULT_DURATION);
|
|
||||||
} else {
|
|
||||||
CONFIGS.put(k, (Integer) v);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Integer getDuration(PanDomainTemplate pdt) {
|
|
||||||
return CONFIGS.get(pdt.name().toLowerCase());
|
|
||||||
}
|
|
||||||
public static Integer getDuration(String type) {
|
|
||||||
return CONFIGS.get(type.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package cn.qaiu.lz.common.cache;
|
|
||||||
|
|
||||||
import cn.qaiu.db.pool.JDBCPoolInit;
|
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
|
||||||
import io.vertx.core.Future;
|
|
||||||
import io.vertx.core.Promise;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.jdbcclient.JDBCPool;
|
|
||||||
import io.vertx.sqlclient.Row;
|
|
||||||
import io.vertx.sqlclient.templates.SqlTemplate;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class CacheManager {
|
|
||||||
private final JDBCPool jdbcPool = JDBCPoolInit.instance().getPool();
|
|
||||||
|
|
||||||
|
|
||||||
public Future<CacheLinkInfo> get(String cacheKey) {
|
|
||||||
String sql = "SELECT share_key as shareKey, direct_link as directLink, expiration FROM cache_link_info WHERE share_key = #{share_key}";
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
|
||||||
params.put("share_key", cacheKey);
|
|
||||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
|
||||||
SqlTemplate.forQuery(jdbcPool, sql)
|
|
||||||
.mapTo(CacheLinkInfo.class)
|
|
||||||
.execute(params)
|
|
||||||
.onSuccess(rows->{
|
|
||||||
CacheLinkInfo cacheHit;
|
|
||||||
if (rows.size() > 0) {
|
|
||||||
cacheHit = rows.iterator().next();
|
|
||||||
cacheHit.setCacheHit(true);
|
|
||||||
} else {
|
|
||||||
cacheHit = new CacheLinkInfo(JsonObject.of("cacheHit", false, "shareKey", cacheKey));
|
|
||||||
}
|
|
||||||
promise.complete(cacheHit);
|
|
||||||
}).onFailure(Throwable::printStackTrace);
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 插入或更新缓存数据
|
|
||||||
public Future<Void> cacheShareLink(CacheLinkInfo cacheLinkInfo) {
|
|
||||||
String sql = "MERGE INTO cache_link_info (share_key, direct_link, expiration) " +
|
|
||||||
"KEY (share_key) " +
|
|
||||||
"VALUES (#{shareKey}, #{directLink}, #{expiration})";
|
|
||||||
|
|
||||||
// 直接传递 CacheLinkInfo 实体类
|
|
||||||
return SqlTemplate.forUpdate(jdbcPool, sql)
|
|
||||||
.mapFrom(CacheLinkInfo.class) // 将实体类映射为 Tuple 参数
|
|
||||||
.execute(cacheLinkInfo)
|
|
||||||
.mapEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入网盘厂商API解析次数
|
|
||||||
public Future<Integer> updateTotalByCached(String shareKey) {
|
|
||||||
Promise<Integer> promise = Promise.promise();
|
|
||||||
String sql = """
|
|
||||||
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `cache_hit_total`, `update_ts`)
|
|
||||||
KEY (`share_key`)
|
|
||||||
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
|
||||||
""";
|
|
||||||
|
|
||||||
getShareKeyTotal(shareKey, "cache_hit_total").onSuccess(total -> {
|
|
||||||
Integer newTotal = (total == null ? 0 : total) + 1;
|
|
||||||
SqlTemplate.forUpdate(jdbcPool, sql)
|
|
||||||
.execute(new HashMap<>() {{
|
|
||||||
put("panType", getShareType(shareKey));
|
|
||||||
put("shareKey", shareKey);
|
|
||||||
put("total", newTotal);
|
|
||||||
put("ts", System.currentTimeMillis());
|
|
||||||
}})
|
|
||||||
.onSuccess(res -> promise.complete(res.rowCount()))
|
|
||||||
.onFailure(Throwable::printStackTrace);
|
|
||||||
});
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String getShareType(String fullShareKey) {
|
|
||||||
// 将type和shareKey组合成一个字符串作为缓存key
|
|
||||||
return fullShareKey.split(":")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入网盘厂商API解析次数
|
|
||||||
public Future<Integer> updateTotalByParser(String shareKey) {
|
|
||||||
Promise<Integer> promise = Promise.promise();
|
|
||||||
String sql = """
|
|
||||||
MERGE INTO `api_statistics_info` (`pan_type`, `share_key`, `api_parser_total`, `update_ts`)
|
|
||||||
KEY (`share_key`)
|
|
||||||
VALUES (#{panType}, #{shareKey}, #{total}, #{ts})
|
|
||||||
""";
|
|
||||||
|
|
||||||
getShareKeyTotal(shareKey, "api_parser_total").onSuccess(total -> {
|
|
||||||
Integer newTotal = (total == null ? 0 : total) + 1;
|
|
||||||
SqlTemplate.forUpdate(jdbcPool, sql)
|
|
||||||
.execute(new HashMap<>() {{
|
|
||||||
put("panType", getShareType(shareKey));
|
|
||||||
put("shareKey", shareKey);
|
|
||||||
put("total", newTotal);
|
|
||||||
put("ts", System.currentTimeMillis());
|
|
||||||
}})
|
|
||||||
.onSuccess(res -> promise.complete(res.rowCount()))
|
|
||||||
.onFailure(Throwable::printStackTrace);
|
|
||||||
});
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<Integer> getShareKeyTotal(String shareKey, String name) {
|
|
||||||
String sql = """
|
|
||||||
select `share_key`, sum({total_name}) sum_num
|
|
||||||
from `api_statistics_info`
|
|
||||||
group by `share_key` having `share_key` = #{shareKey};
|
|
||||||
""".replace("{total_name}", name);
|
|
||||||
Promise<Integer> promise = Promise.promise();
|
|
||||||
Map<String, Object> paramMap = new HashMap<>();
|
|
||||||
paramMap.put("shareKey", shareKey);
|
|
||||||
SqlTemplate.forQuery(jdbcPool, sql)
|
|
||||||
.mapTo(Row::toJson)
|
|
||||||
.execute(paramMap)
|
|
||||||
.onSuccess(res -> {
|
|
||||||
Integer total = res.iterator().hasNext() ?
|
|
||||||
res.iterator().next().getInteger("sum_num") : null;
|
|
||||||
promise.complete(total);
|
|
||||||
});
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<Map<String, Integer>> getShareKeyTotal(String shareKey) {
|
|
||||||
String sql = """
|
|
||||||
select `share_key`, sum(cache_hit_total) hit_total, sum(api_parser_total) parser_total,
|
|
||||||
from `api_statistics_info`
|
|
||||||
group by `share_key` having `share_key` = #{shareKey}
|
|
||||||
""";
|
|
||||||
Promise<Map<String, Integer>> promise = Promise.promise();
|
|
||||||
Map<String, Object> paramMap = new HashMap<>();
|
|
||||||
paramMap.put("shareKey", shareKey);
|
|
||||||
SqlTemplate.forQuery(jdbcPool, sql)
|
|
||||||
.mapTo(Row::toJson)
|
|
||||||
.execute(paramMap)
|
|
||||||
.onSuccess(res -> {
|
|
||||||
if(res.iterator().hasNext()) {
|
|
||||||
JsonObject next = res.iterator().next();
|
|
||||||
Map<String, Integer> resp = new HashMap<>(){{
|
|
||||||
put("hit_total" ,next.getInteger("hit_total"));
|
|
||||||
put("parser_total" ,next.getInteger("parser_total"));
|
|
||||||
}};
|
|
||||||
promise.complete(resp);
|
|
||||||
} else {
|
|
||||||
promise.complete();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ public class DefaultInterceptor implements BeforeInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(RoutingContext ctx) {
|
public void handle(RoutingContext ctx) {
|
||||||
// System.out.println("进入前置拦截器1->" + ctx.request().path());
|
System.out.println("进入前置拦截器1->" + ctx.request().path());
|
||||||
doNext(ctx);
|
doNext(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.qaiu.db.pool.JDBCPoolInit;
|
|||||||
import cn.qaiu.lz.common.model.ParserLogInfo;
|
import cn.qaiu.lz.common.model.ParserLogInfo;
|
||||||
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
|
||||||
import cn.qaiu.vx.core.interceptor.AfterInterceptor;
|
import cn.qaiu.vx.core.interceptor.AfterInterceptor;
|
||||||
|
import cn.qaiu.vx.core.model.JsonResult;
|
||||||
import cn.qaiu.vx.core.util.CommonUtil;
|
import cn.qaiu.vx.core.util.CommonUtil;
|
||||||
import cn.qaiu.vx.core.util.SharedDataUtil;
|
import cn.qaiu.vx.core.util.SharedDataUtil;
|
||||||
import io.vertx.core.json.JsonArray;
|
import io.vertx.core.json.JsonArray;
|
||||||
@@ -35,9 +36,23 @@ public class LogStatistics implements AfterInterceptor {
|
|||||||
|
|
||||||
ParserLogInfo parserLogInfo = new ParserLogInfo();
|
ParserLogInfo parserLogInfo = new ParserLogInfo();
|
||||||
parserLogInfo.setPath(ctx.request().uri());
|
parserLogInfo.setPath(ctx.request().uri());
|
||||||
if (responseData == null) return;
|
if (responseData == null) {
|
||||||
if (responseData.containsKey("code") && responseData.getInteger("code") == 500) {
|
String location = ctx.response().headers().get("location");
|
||||||
log.error("code 500: {} {}", ctx.request().path(), responseData.getString("msg"));
|
if (location != null) {
|
||||||
|
parserLogInfo.setCode(200);
|
||||||
|
parserLogInfo.setData(location);
|
||||||
|
} else {
|
||||||
|
log.error("location不存在且responseData为空, path={}", ctx.request().path());
|
||||||
|
}
|
||||||
|
insert(parserLogInfo);
|
||||||
|
|
||||||
|
} else if (responseData.containsKey("code")) {
|
||||||
|
JsonResult<?> result = JsonResult.toJsonResult(responseData);
|
||||||
|
parserLogInfo.setCode(result.getCode());
|
||||||
|
parserLogInfo.setData(result.getCode() == 500 ? result.getMsg() : result.getData().toString());
|
||||||
|
insert(parserLogInfo);
|
||||||
|
} else {
|
||||||
|
log.error("未知json日志: {}, path: {}", responseData.encode(), ctx.request().path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ public class ParserLogInfo {
|
|||||||
String id = SnowflakeIdWorker.getStringId();
|
String id = SnowflakeIdWorker.getStringId();
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
|
||||||
Date logTime = new Date();
|
Date logTime = new Date();
|
||||||
|
|
||||||
@Length(varcharSize = 4096)
|
|
||||||
String path;
|
String path;
|
||||||
Integer code;
|
Integer code;
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package cn.qaiu.lz.common.util;
|
|
||||||
|
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.http.HttpServerRequest;
|
|
||||||
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理URL截断问题,拼接被截断的参数,特殊处理pwd参数。
|
|
||||||
*
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/13
|
|
||||||
*/
|
|
||||||
public class URLParamUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析并处理截断的URL参数
|
|
||||||
*
|
|
||||||
* @param request HttpServerRequest对象
|
|
||||||
* @return 完整的URL字符串
|
|
||||||
*/
|
|
||||||
public static String parserParams(HttpServerRequest request) {
|
|
||||||
|
|
||||||
String url = request.absoluteURI();
|
|
||||||
MultiMap params = request.params();
|
|
||||||
// 处理URL截断的情况,例如: url='https://...&key=...&code=...'
|
|
||||||
if (params.contains("url")) {
|
|
||||||
String encodedUrl = params.get("url");
|
|
||||||
url = handleTruncatedUrl(encodedUrl, params);
|
|
||||||
if (url.endsWith(".html")) {
|
|
||||||
// 123云盘的后缀处理
|
|
||||||
url = url.replace(".html", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理被截断的URL,拼接所有参数,特殊处理pwd参数。
|
|
||||||
*
|
|
||||||
* @param encodedUrl 被截断的url参数
|
|
||||||
* @param params 请求的其他参数
|
|
||||||
* @return 重新拼接后的完整URL
|
|
||||||
*/
|
|
||||||
private static String handleTruncatedUrl(String encodedUrl, MultiMap params) {
|
|
||||||
// 对URL进行解码,以便获取完整的URL
|
|
||||||
String decodedUrl = URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
// 如果URL已经包含查询参数,不需要额外拼接
|
|
||||||
if (params.contains("pwd")) {
|
|
||||||
if (params.size() == 2) {
|
|
||||||
return decodedUrl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (params.size() == 1) {
|
|
||||||
return decodedUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拼接被截断的URL参数,忽略pwd参数
|
|
||||||
StringBuilder urlBuilder = new StringBuilder(decodedUrl);
|
|
||||||
boolean firstParam = !decodedUrl.contains("?");
|
|
||||||
|
|
||||||
for (String paramName : params.names()) {
|
|
||||||
if (!paramName.equals("url") && !paramName.equals("pwd")) { // 忽略 "url" 和 "pwd" 参数
|
|
||||||
if (firstParam) {
|
|
||||||
urlBuilder.append("?");
|
|
||||||
firstParam = false;
|
|
||||||
} else {
|
|
||||||
urlBuilder.append("&");
|
|
||||||
}
|
|
||||||
urlBuilder.append(paramName).append("=").append(params.get(paramName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlBuilder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.controller;
|
|
||||||
|
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.lz.common.cache.CacheManager;
|
|
||||||
import cn.qaiu.lz.common.util.URLParamUtil;
|
|
||||||
import cn.qaiu.lz.web.model.LinkInfoResp;
|
|
||||||
import cn.qaiu.lz.web.model.SysUser;
|
|
||||||
import cn.qaiu.lz.web.service.DbService;
|
|
||||||
import cn.qaiu.lz.web.service.UserService;
|
|
||||||
import cn.qaiu.lz.web.model.StatisticsInfo;
|
|
||||||
import cn.qaiu.parser.ParserCreate;
|
|
||||||
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
|
||||||
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
|
||||||
import cn.qaiu.vx.core.enums.RouteMethod;
|
|
||||||
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
|
||||||
import cn.qaiu.vx.core.util.SharedDataUtil;
|
|
||||||
import io.vertx.core.Future;
|
|
||||||
import io.vertx.core.Promise;
|
|
||||||
import io.vertx.core.http.HttpServerRequest;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
@RouteHandler(value = "/v2", order = 10)
|
|
||||||
@Slf4j
|
|
||||||
public class ParserApi {
|
|
||||||
|
|
||||||
private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
|
|
||||||
private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
|
|
||||||
|
|
||||||
@RouteMapping(value = "/login", method = RouteMethod.POST)
|
|
||||||
public Future<SysUser> login(SysUser user) {
|
|
||||||
log.info("<------- login: {}", user.getUsername());
|
|
||||||
return userService.login(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RouteMapping(value = "/statisticsInfo", method = RouteMethod.GET, order = 99)
|
|
||||||
public Future<StatisticsInfo> statisticsInfo() {
|
|
||||||
return dbService.getStatisticsInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final CacheManager cacheManager = new CacheManager();
|
|
||||||
|
|
||||||
@RouteMapping(value = "/linkInfo", method = RouteMethod.GET)
|
|
||||||
public Future<LinkInfoResp> parse(HttpServerRequest request, String pwd) {
|
|
||||||
Promise<LinkInfoResp> promise = Promise.promise();
|
|
||||||
String url = URLParamUtil.parserParams(request);
|
|
||||||
ParserCreate parserCreate = ParserCreate.fromShareUrl(url).setShareLinkInfoPwd(pwd);
|
|
||||||
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
|
|
||||||
LinkInfoResp build = LinkInfoResp.builder()
|
|
||||||
.downLink(getDownLink(parserCreate, false))
|
|
||||||
.apiLink(getDownLink(parserCreate, true))
|
|
||||||
.shareLinkInfo(shareLinkInfo).build();
|
|
||||||
// 解析次数统计
|
|
||||||
cacheManager.getShareKeyTotal(shareLinkInfo.getCacheKey()).onSuccess(res -> {
|
|
||||||
if (res != null) {
|
|
||||||
build.setCacheHitTotal(res.get("hit_total") == null ? 0: res.get("hit_total"));
|
|
||||||
build.setParserTotal(res.get("parser_total") == null ? 0: res.get("parser_total"));
|
|
||||||
build.setSumTotal(build.getCacheHitTotal() + build.getParserTotal());
|
|
||||||
}
|
|
||||||
promise.complete(build);
|
|
||||||
}).onFailure(t->{
|
|
||||||
t.printStackTrace();
|
|
||||||
promise.complete(build);
|
|
||||||
});
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getDownLink(ParserCreate create, boolean isJson) {
|
|
||||||
|
|
||||||
String linkPrefix = SharedDataUtil.getJsonConfig("server")
|
|
||||||
.getString("domainName");
|
|
||||||
if (StringUtils.isBlank(linkPrefix)) {
|
|
||||||
linkPrefix = "http://127.0.0.1";
|
|
||||||
}
|
|
||||||
return linkPrefix + (isJson ? "/json/" : "/") + create.genPathSuffix();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.controller;
|
|
||||||
|
|
||||||
import cn.qaiu.lz.common.util.URLParamUtil;
|
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
|
||||||
import cn.qaiu.lz.web.service.CacheService;
|
|
||||||
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
|
||||||
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
|
||||||
import cn.qaiu.vx.core.enums.RouteMethod;
|
|
||||||
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
|
||||||
import cn.qaiu.vx.core.util.ResponseUtil;
|
|
||||||
import io.vertx.core.Future;
|
|
||||||
import io.vertx.core.Promise;
|
|
||||||
import io.vertx.core.http.HttpServerRequest;
|
|
||||||
import io.vertx.core.http.HttpServerResponse;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 服务API
|
|
||||||
* <br>Create date 2021/4/28 9:15
|
|
||||||
*
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RouteHandler("/")
|
|
||||||
public class ServerApi {
|
|
||||||
|
|
||||||
private final CacheService cacheService = AsyncServiceUtil.getAsyncServiceInstance(CacheService.class);
|
|
||||||
|
|
||||||
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 1)
|
|
||||||
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String pwd) {
|
|
||||||
Promise<Void> promise = Promise.promise();
|
|
||||||
String url = URLParamUtil.parserParams(request);
|
|
||||||
|
|
||||||
cacheService.getCachedByShareUrlAndPwd(url, pwd)
|
|
||||||
.onSuccess(res -> ResponseUtil.redirect(
|
|
||||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
|
||||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
|
||||||
res.getDirectLink(), promise))
|
|
||||||
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 1)
|
|
||||||
public Future<CacheLinkInfo> parseJson(HttpServerRequest request, String pwd) {
|
|
||||||
String url = URLParamUtil.parserParams(request);
|
|
||||||
return cacheService.getCachedByShareUrlAndPwd(url, pwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET)
|
|
||||||
public Future<CacheLinkInfo> parseKeyJson(String type, String key) {
|
|
||||||
String pwd = "";
|
|
||||||
if (key.contains("@")) {
|
|
||||||
String[] keys = key.split("@");
|
|
||||||
key = keys[0];
|
|
||||||
pwd = keys[1];
|
|
||||||
}
|
|
||||||
return cacheService.getCachedByShareKeyAndPwd(type, key, pwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET)
|
|
||||||
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
|
|
||||||
Promise<Void> promise = Promise.promise();
|
|
||||||
String pwd = "";
|
|
||||||
if (key.contains("@")) {
|
|
||||||
String[] keys = key.split("@");
|
|
||||||
key = keys[0];
|
|
||||||
pwd = keys[1];
|
|
||||||
}
|
|
||||||
cacheService.getCachedByShareKeyAndPwd(type, key, pwd)
|
|
||||||
.onSuccess(res -> ResponseUtil.redirect(
|
|
||||||
response.putHeader("nfd-cache-hit", res.getCacheHit().toString())
|
|
||||||
.putHeader("nfd-cache-expires", res.getExpires()),
|
|
||||||
res.getDirectLink(), promise))
|
|
||||||
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
32
web-service/src/main/java/cn/qaiu/lz/web/http/ParserApi.java
Normal file
32
web-service/src/main/java/cn/qaiu/lz/web/http/ParserApi.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package cn.qaiu.lz.web.http;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.qaiu.lz.web.model.SysUser;
|
||||||
|
import cn.qaiu.lz.web.service.DbService;
|
||||||
|
import cn.qaiu.lz.web.service.UserService;
|
||||||
|
import cn.qaiu.lz.web.model.StatisticsInfo;
|
||||||
|
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
||||||
|
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
||||||
|
import cn.qaiu.vx.core.enums.RouteMethod;
|
||||||
|
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@RouteHandler(value = "/v2", order = 10)
|
||||||
|
@Slf4j
|
||||||
|
public class ParserApi {
|
||||||
|
|
||||||
|
private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
|
||||||
|
private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
|
||||||
|
|
||||||
|
@RouteMapping(value = "/login", method = RouteMethod.POST)
|
||||||
|
public Future<SysUser> login(SysUser user) {
|
||||||
|
log.info("<------- login: {}", user.getUsername());
|
||||||
|
return userService.login(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/statisticsInfo", method = RouteMethod.GET, order = 99)
|
||||||
|
public Future<StatisticsInfo> statisticsInfo() {
|
||||||
|
return dbService.getStatisticsInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
83
web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java
Normal file
83
web-service/src/main/java/cn/qaiu/lz/web/http/ServerApi.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package cn.qaiu.lz.web.http;
|
||||||
|
|
||||||
|
import cn.qaiu.parser.IPanTool;
|
||||||
|
import cn.qaiu.parser.impl.EcTool;
|
||||||
|
import cn.qaiu.parser.impl.QQTool;
|
||||||
|
import cn.qaiu.vx.core.annotaions.RouteHandler;
|
||||||
|
import cn.qaiu.vx.core.annotaions.RouteMapping;
|
||||||
|
import cn.qaiu.vx.core.enums.RouteMethod;
|
||||||
|
import cn.qaiu.vx.core.util.ResponseUtil;
|
||||||
|
import cn.qaiu.vx.core.util.VertxHolder;
|
||||||
|
import io.vertx.core.Future;
|
||||||
|
import io.vertx.core.Promise;
|
||||||
|
import io.vertx.core.http.HttpServerRequest;
|
||||||
|
import io.vertx.core.http.HttpServerResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务API
|
||||||
|
* <br>Create date 2021/4/28 9:15
|
||||||
|
*
|
||||||
|
* @author <a href="https://qaiu.top">QAIU</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RouteHandler("/")
|
||||||
|
public class ServerApi {
|
||||||
|
|
||||||
|
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
|
||||||
|
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String url, String pwd) {
|
||||||
|
|
||||||
|
Promise<Void> promise = Promise.promise();
|
||||||
|
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||||
|
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||||
|
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
|
||||||
|
}
|
||||||
|
if (url.contains(QQTool.SHARE_URL_PREFIX)) {
|
||||||
|
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||||
|
url = url + "&key=" + request.getParam("key") +
|
||||||
|
"&code=" + request.getParam("code") + "&k=" + request.getParam("k") +
|
||||||
|
"&fweb=" + request.getParam("fweb") + "&cl=" + request.getParam("cl");
|
||||||
|
}
|
||||||
|
IPanTool.shareURLPrefixMatching(url, pwd).parse()
|
||||||
|
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
|
||||||
|
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
|
||||||
|
public Future<String> parseJson(HttpServerRequest request, String url, String pwd) {
|
||||||
|
if (url.contains(EcTool.SHARE_URL_PREFIX)) {
|
||||||
|
// 默认读取Url参数会被截断手动获取一下其他参数
|
||||||
|
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
|
||||||
|
}
|
||||||
|
return IPanTool.shareURLPrefixMatching(url, pwd).parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
|
||||||
|
public Future<String> parseKeyJson(String type, String key) {
|
||||||
|
String code = "";
|
||||||
|
if (key.contains("@")) {
|
||||||
|
String[] keys = key.split("@");
|
||||||
|
key = keys[0];
|
||||||
|
code = keys[1];
|
||||||
|
}
|
||||||
|
return IPanTool.typeMatching(type, key, code).parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
|
||||||
|
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
|
||||||
|
Promise<Void> promise = Promise.promise();
|
||||||
|
String code = "";
|
||||||
|
if (key.contains("@")) {
|
||||||
|
String[] keys = key.split("@");
|
||||||
|
key = keys[0];
|
||||||
|
code = keys[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
IPanTool.typeMatching(type, key, code).parse()
|
||||||
|
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
|
||||||
|
.onFailure(t -> promise.fail(t.fillInStackTrace()));
|
||||||
|
return promise.future();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.model;
|
|
||||||
|
|
||||||
import cn.qaiu.db.ddl.Length;
|
|
||||||
import cn.qaiu.db.ddl.Table;
|
|
||||||
import cn.qaiu.lz.common.ToJson;
|
|
||||||
import io.vertx.codegen.annotations.DataObject;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/11 16:06
|
|
||||||
*/
|
|
||||||
@Table(value = "api_statistics_info", keyFields = "share_key")
|
|
||||||
@Data
|
|
||||||
@DataObject
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class ApiStatisticsInfo implements ToJson {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pan type 单独拿出来便于统计.
|
|
||||||
*/
|
|
||||||
@Length(varcharSize = 4)
|
|
||||||
private String panType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分享key type:key
|
|
||||||
*/
|
|
||||||
@Length(varcharSize = 4096)
|
|
||||||
private String shareKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 命中缓存次数
|
|
||||||
*/
|
|
||||||
private Integer cacheHitTotal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* api解析次数
|
|
||||||
*/
|
|
||||||
private Integer apiParserTotal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新时间戳
|
|
||||||
*/
|
|
||||||
private Long updateTs;
|
|
||||||
|
|
||||||
// 使用 JsonObject 构造
|
|
||||||
public ApiStatisticsInfo(JsonObject json) {
|
|
||||||
if (json.containsKey("panType")) {
|
|
||||||
this.setPanType(json.getString("panType"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("shareKey")) {
|
|
||||||
this.setShareKey(json.getString("shareKey"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("cacheHitTotal")) {
|
|
||||||
this.setCacheHitTotal(json.getInteger("cacheHitTotal"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("apiParserTotal")) {
|
|
||||||
this.setApiParserTotal(json.getInteger("apiParserTotal"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("updateTs")) {
|
|
||||||
this.setUpdateTs(json.getLong("updateTs"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.model;
|
|
||||||
|
|
||||||
import cn.qaiu.db.ddl.Length;
|
|
||||||
import cn.qaiu.db.ddl.Table;
|
|
||||||
import cn.qaiu.db.ddl.TableGenIgnore;
|
|
||||||
import cn.qaiu.lz.common.ToJson;
|
|
||||||
import io.vertx.codegen.annotations.DataObject;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/11 16:06
|
|
||||||
*/
|
|
||||||
@Table(value = "cache_link_info", keyFields = "share_key")
|
|
||||||
@Data
|
|
||||||
@DataObject
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class CacheLinkInfo implements ToJson {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存key: type:ShareKey; e.g. lz:xxxx
|
|
||||||
*/
|
|
||||||
@Length(varcharSize = 4096)
|
|
||||||
private String shareKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析后的直链
|
|
||||||
*/
|
|
||||||
@Length(varcharSize = 4096)
|
|
||||||
private String directLink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否命中缓存
|
|
||||||
*/
|
|
||||||
@TableGenIgnore
|
|
||||||
private Boolean cacheHit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 到期时间 yyyy-MM-dd hh:mm:ss
|
|
||||||
*/
|
|
||||||
@TableGenIgnore
|
|
||||||
private String expires;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 有效期
|
|
||||||
*/
|
|
||||||
private Long expiration;
|
|
||||||
|
|
||||||
|
|
||||||
// 使用 JsonObject 构造
|
|
||||||
public CacheLinkInfo(JsonObject json) {
|
|
||||||
if (json.containsKey("shareKey")) {
|
|
||||||
this.setShareKey(json.getString("shareKey"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("directLink")) {
|
|
||||||
this.setDirectLink(json.getString("directLink"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("expires")) {
|
|
||||||
this.setExpires(json.getString("expires"));
|
|
||||||
}
|
|
||||||
if (json.containsKey("expiration")) {
|
|
||||||
this.setExpiration(json.getLong("expiration"));
|
|
||||||
}
|
|
||||||
this.setCacheHit(json.getBoolean("cacheHit", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.model;
|
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
public class LinkInfoResp {
|
|
||||||
// 解析链接
|
|
||||||
private String downLink;
|
|
||||||
private String apiLink;
|
|
||||||
private Integer cacheHitTotal;
|
|
||||||
private Integer parserTotal;
|
|
||||||
private Integer sumTotal;
|
|
||||||
private ShareLinkInfo shareLinkInfo;
|
|
||||||
}
|
|
||||||
@@ -10,14 +10,14 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@DataObject
|
@DataObject
|
||||||
public class StatisticsInfo implements ToJson {
|
public class StatisticsInfo implements ToJson {
|
||||||
Integer parserTotal;
|
Integer fail;
|
||||||
Integer cacheTotal;
|
Integer success;
|
||||||
Integer total;
|
Integer total;
|
||||||
|
|
||||||
|
|
||||||
public StatisticsInfo(JsonObject jsonObject) {
|
public StatisticsInfo(JsonObject jsonObject) {
|
||||||
this.parserTotal = jsonObject.getInteger("parserTotal");
|
this.fail = jsonObject.getInteger("fail");
|
||||||
this.cacheTotal = jsonObject.getInteger("cacheTotal");
|
this.success = jsonObject.getInteger("success");
|
||||||
this.total = jsonObject.getInteger("total");
|
this.total = jsonObject.getInteger("total");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.service;
|
|
||||||
|
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
|
||||||
import cn.qaiu.vx.core.base.BaseAsyncService;
|
|
||||||
import io.vertx.codegen.annotations.ProxyGen;
|
|
||||||
import io.vertx.core.Future;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="https://qaiu.top">QAIU</a>
|
|
||||||
* @date 2024/9/12 8:26
|
|
||||||
*/
|
|
||||||
@ProxyGen
|
|
||||||
public interface CacheService extends BaseAsyncService {
|
|
||||||
|
|
||||||
Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd);
|
|
||||||
|
|
||||||
Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd);
|
|
||||||
}
|
|
||||||
@@ -19,5 +19,4 @@ public interface DbService extends BaseAsyncService {
|
|||||||
Future<JsonObject> sayOk2(String data, UserInfo holder);
|
Future<JsonObject> sayOk2(String data, UserInfo holder);
|
||||||
|
|
||||||
Future<StatisticsInfo> getStatisticsInfo();
|
Future<StatisticsInfo> getStatisticsInfo();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package cn.qaiu.lz.web.service;
|
||||||
|
|
||||||
|
import cn.qaiu.vx.core.util.CastUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JDK代理类工厂
|
||||||
|
*/
|
||||||
|
public class JdkProxyFactory {
|
||||||
|
public static <T> T getProxy(T target) {
|
||||||
|
return CastUtil.cast(Proxy.newProxyInstance(
|
||||||
|
target.getClass().getClassLoader(),
|
||||||
|
target.getClass().getInterfaces(),
|
||||||
|
new ServiceJdkProxy<>(target))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package cn.qaiu.lz.web.service.impl;
|
|
||||||
|
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
|
||||||
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
|
||||||
import cn.qaiu.lz.common.cache.CacheManager;
|
|
||||||
import cn.qaiu.lz.web.model.CacheLinkInfo;
|
|
||||||
import cn.qaiu.lz.web.service.CacheService;
|
|
||||||
import cn.qaiu.parser.ParserCreate;
|
|
||||||
import cn.qaiu.vx.core.annotaions.Service;
|
|
||||||
import io.vertx.core.Future;
|
|
||||||
import io.vertx.core.Promise;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class CacheServiceImpl implements CacheService {
|
|
||||||
|
|
||||||
private final CacheManager cacheManager = new CacheManager();
|
|
||||||
|
|
||||||
private Future<CacheLinkInfo> getAndSaveCachedShareLink(ParserCreate parserCreate) {
|
|
||||||
Promise<CacheLinkInfo> promise = Promise.promise();
|
|
||||||
// 构建组合的缓存key
|
|
||||||
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
|
|
||||||
// 尝试从缓存中获取
|
|
||||||
String cacheKey = shareLinkInfo.getCacheKey();
|
|
||||||
cacheManager.get(cacheKey).onSuccess(result -> {
|
|
||||||
// 判断是否已过期
|
|
||||||
// 未命中或者过期
|
|
||||||
if (!result.getCacheHit() || result.getExpiration() < System.currentTimeMillis()) {
|
|
||||||
// parse
|
|
||||||
result.setCacheHit(false);
|
|
||||||
result.setExpiration(0L);
|
|
||||||
parserCreate.createTool().parse().onSuccess(redirectUrl -> {
|
|
||||||
long expires = System.currentTimeMillis() +
|
|
||||||
CacheConfigLoader.getDuration(shareLinkInfo.getType()) * 60 * 1000L;
|
|
||||||
result.setDirectLink(redirectUrl);
|
|
||||||
// result.setExpires(generateDate(expires));
|
|
||||||
promise.complete(result);
|
|
||||||
// 更新缓存
|
|
||||||
// 将直链存储到缓存
|
|
||||||
CacheLinkInfo cacheLinkInfo = new CacheLinkInfo(JsonObject.of(
|
|
||||||
"directLink", redirectUrl,
|
|
||||||
"expiration", expires,
|
|
||||||
"shareKey", cacheKey
|
|
||||||
));
|
|
||||||
cacheManager.cacheShareLink(cacheLinkInfo).onFailure(Throwable::printStackTrace);
|
|
||||||
cacheManager.updateTotalByParser(cacheKey).onFailure(Throwable::printStackTrace);
|
|
||||||
}).onFailure(promise::fail);
|
|
||||||
} else {
|
|
||||||
result.setExpires(generateDate(result.getExpiration()));
|
|
||||||
promise.complete(result);
|
|
||||||
cacheManager.updateTotalByCached(cacheKey).onFailure(Throwable::printStackTrace);
|
|
||||||
}
|
|
||||||
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
|
|
||||||
return promise.future();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateDate(Long ts) {
|
|
||||||
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<CacheLinkInfo> getCachedByShareKeyAndPwd(String type, String shareKey, String pwd) {
|
|
||||||
ParserCreate parserCreate = ParserCreate.fromType(type).shareKey(shareKey).setShareLinkInfoPwd(pwd);
|
|
||||||
return getAndSaveCachedShareLink(parserCreate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<CacheLinkInfo> getCachedByShareUrlAndPwd(String shareUrl, String pwd) {
|
|
||||||
ParserCreate parserCreate = ParserCreate.fromShareUrl(shareUrl).setShareLinkInfoPwd(pwd);
|
|
||||||
return getAndSaveCachedShareLink(parserCreate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -47,11 +47,10 @@ public class DbServiceImpl implements DbService {
|
|||||||
public Future<StatisticsInfo> getStatisticsInfo() {
|
public Future<StatisticsInfo> getStatisticsInfo() {
|
||||||
JDBCPool client = JDBCPoolInit.instance().getPool();
|
JDBCPool client = JDBCPoolInit.instance().getPool();
|
||||||
Promise<StatisticsInfo> promise = Promise.promise();
|
Promise<StatisticsInfo> promise = Promise.promise();
|
||||||
String sql = """
|
String sql = "select COUNT(CASE \"code\" WHEN 500 THEN \"code\" END ) \"fail\",\n" +
|
||||||
select sum(api_parser_total) parserTotal,sum("cache_hit_total") cacheTotal,
|
" COUNT(CASE \"code\" WHEN 200 THEN \"code\" END ) \"success\",\n" +
|
||||||
sum(api_parser_total) + sum("cache_hit_total") total
|
" count(1) \"total\"\n" +
|
||||||
from "api_statistics_info";
|
" from \"t_parser_log_info\"";
|
||||||
""";
|
|
||||||
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
|
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
|
||||||
StatisticsInfo info;
|
StatisticsInfo info;
|
||||||
if ((info = row.iterator().next()) != null) {
|
if ((info = row.iterator().next()) != null) {
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
server:
|
server:
|
||||||
port: 6400
|
port: 6400
|
||||||
contextPath: /
|
contextPath: /
|
||||||
|
# 使用静态页面
|
||||||
|
enableStaticHtmlService: false
|
||||||
# 使用数据库
|
# 使用数据库
|
||||||
enableDatabase: true
|
enableDatabase: true
|
||||||
# 服务域名或者IP 生成二维码链接时需要
|
staticResourcePath: webroot/
|
||||||
domainName: https://lz.qaiu.top
|
|
||||||
|
|
||||||
# 反向代理服务器配置路径(不用加后缀)
|
# 反向代理服务器配置路径(不用加后缀)
|
||||||
proxyConf: server-proxy
|
proxyConf: server-proxy
|
||||||
|
|
||||||
# vertx线程配置, 为0表示eventLoopPoolSize将会采用默认配置(CPU核心*2) workerPoolSize将会采用默认20
|
# vertx线程配置 事件循环线程配置为0: eventLoopPoolSize将会采用默认配置(CPU核心*2) workerPoolSize将会采用默认20
|
||||||
vertx:
|
vertx:
|
||||||
eventLoopPoolSize: 0
|
eventLoopPoolSize: 0
|
||||||
workerPoolSize: 0
|
workerPoolSize: 0
|
||||||
@@ -39,23 +39,3 @@ dataSource:
|
|||||||
driverClassName: org.h2.Driver
|
driverClassName: org.h2.Driver
|
||||||
username: root
|
username: root
|
||||||
password: '123456'
|
password: '123456'
|
||||||
|
|
||||||
# 缓存配置
|
|
||||||
cache:
|
|
||||||
type: h2db
|
|
||||||
# 默认时长: 单位分钟, 实际有效期分钟-1
|
|
||||||
defaultDuration: 59
|
|
||||||
duration:
|
|
||||||
ce:
|
|
||||||
cow:
|
|
||||||
ec:
|
|
||||||
fc:
|
|
||||||
fj: 30
|
|
||||||
iz: 20
|
|
||||||
le: 2879
|
|
||||||
lz:
|
|
||||||
qq: 999999
|
|
||||||
ws:
|
|
||||||
ye:
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
|
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
|
||||||
active: dev
|
active: dev
|
||||||
# 版本号
|
# 版本号
|
||||||
version_app: 0.1.8
|
version_app: 0.1.7
|
||||||
# 公司名称 -> LOGO版权文字
|
# 公司名称 -> LOGO版权文字
|
||||||
copyright: QAIU
|
copyright: QAIU
|
||||||
|
|||||||
3
web-service/src/main/resources/conf/dictionaries.json
Normal file
3
web-service/src/main/resources/conf/dictionaries.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
###超星
|
|
||||||
|
|
||||||
### 直链 Referer
|
|
||||||
https://d0.ananas.chaoxing.com/download/8e8c9baca640d24680d974331390a158?at_=1717958244333&ak_=783925f9ba6eb2d0c711977b777a13e0&ad_=58ffecd38be494bea68f0cda68b18c0a&fn=testgles.c
|
|
||||||
Referer: https://pan-yz.chaoxing.com/external/m/file/1006748113111711744
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
@@ -59,10 +59,3 @@ Origin:https://www.feijipan.com
|
|||||||
Referer:https://www.feijipan.com/
|
Referer:https://www.feijipan.com/
|
||||||
|
|
||||||
{"loginName":"","loginPwd":""}
|
{"loginName":"","loginPwd":""}
|
||||||
|
|
||||||
|
|
||||||
### 240530 规则修改
|
|
||||||
### https://share.feijipan.com/s/nMtCOXL
|
|
||||||
POST https://api.feijipan.com/ws/recommend/list?devType=6&devModel=Chrome&uuid=WQAl5yBy1naGudJEILBvE&extra=2×tamp=A8ECC3C7C50191ACEB9CB8444FD37624&shareId=nMtCOXL&type=0&offset=1&limit=60
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
# curl 'https://ip.ihuan.me/tqdl.html'
|
|
||||||
# -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'
|
|
||||||
# -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8'
|
|
||||||
# -H 'cache-control: no-cache'
|
|
||||||
# -H 'content-type: application/x-www-form-urlencoded'
|
|
||||||
# -H 'cookie: 91e73cee101cc3ecefd9ca31a227d508=4380c9f80effb56e85210a54dc286c06; statistics=16453d6e2683b8800ded2a27c7f595d9; Hm_lvt_8ccd0ef22095c2eebfe4cd6187dea829=1727664099; HMACCOUNT=AA7CAD3BA9E39EC8; 28114211b4e11617bd725475433e69aa=29d9778b3f2c1b30c137fef0d6728e29; Hm_lpvt_8ccd0ef22095c2eebfe4cd6187dea829=1727667138'
|
|
||||||
# -H 'dnt: 1'
|
|
||||||
# -H 'origin: https://ip.ihuan.me'
|
|
||||||
# -H 'pragma: no-cache'
|
|
||||||
# -H 'priority: u=0, i'
|
|
||||||
# -H 'referer: https://ip.ihuan.me/ti.html'
|
|
||||||
# -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"'
|
|
||||||
# -H 'sec-ch-ua-mobile: ?0'
|
|
||||||
# -H 'sec-ch-ua-platform: "Windows"'
|
|
||||||
# -H 'sec-fetch-dest: document'
|
|
||||||
# -H 'sec-fetch-mode: navigate'
|
|
||||||
# -H 'sec-fetch-site: same-origin'
|
|
||||||
# -H 'sec-fetch-user: ?1'
|
|
||||||
# -H 'upgrade-insecure-requests: 1'
|
|
||||||
# -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
|
|
||||||
# --data-raw 'num=100&port=&kill_port=&address=&kill_address=&anonymity=&type=&post=&sort=&key=15146aca2e04e3756a2e79ec33ca5679'
|
|
||||||
#@no-cookie-jar
|
|
||||||
POST https://ip.ihuan.me/tqdl.html
|
|
||||||
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
|
|
||||||
accept-language: zh-CN,zh;q=0.9,en;q=0.8
|
|
||||||
cache-control: no-cache
|
|
||||||
cookie: 91e73cee101cc3ecefd9ca31a227d508=4380c9f80effb56e85210a54dc286c06; statistics=16453d6e2683b8800ded2a27c7f595d9; Hm_lvt_8ccd0ef22095c2eebfe4cd6187dea829=1727664099; HMACCOUNT=AA7CAD3BA9E39EC8; 28114211b4e11617bd725475433e69aa=29d9778b3f2c1b30c137fef0d6728e29; Hm_lpvt_8ccd0ef22095c2eebfe4cd6187dea829=1727667138
|
|
||||||
dnt: 1
|
|
||||||
origin: https://ip.ihuan.me
|
|
||||||
pragma: no-cache
|
|
||||||
priority: u=0, i
|
|
||||||
referer: https://ip.ihuan.me/ti.html
|
|
||||||
sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
|
|
||||||
sec-ch-ua-mobile: ?0
|
|
||||||
sec-ch-ua-platform: "Windows"
|
|
||||||
sec-fetch-dest: document
|
|
||||||
sec-fetch-mode: navigate
|
|
||||||
sec-fetch-site: same-origin
|
|
||||||
sec-fetch-user: ?1
|
|
||||||
upgrade-insecure-requests: 1
|
|
||||||
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
num=100&port=&kill_port=&address=&kill_address=&anonymity=&type=&post=&sort=&key=15146aca2e04e3756a2e79ec33ca5679
|
|
||||||
|
|
||||||
###
|
|
||||||
https://ip.ihuan.me/
|
|
||||||
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
|
|
||||||
accept-language: zh-CN,zh;q=0.9,en;q=0.8
|
|
||||||
cache-control: no-cache
|
|
||||||
#cookie: 91e73cee101cc3ecefd9ca31a227d508=4380c9f80effb56e85210a54dc286c06; statistics=16453d6e2683b8800ded2a27c7f595d9; Hm_lvt_8ccd0ef22095c2eebfe4cd6187dea829=1727664099; HMACCOUNT=AA7CAD3BA9E39EC8; 28114211b4e11617bd725475433e69aa=29d9778b3f2c1b30c137fef0d6728e29; Hm_lpvt_8ccd0ef22095c2eebfe4cd6187dea829=1727667138
|
|
||||||
dnt: 1
|
|
||||||
origin: https://ip.ihuan.me
|
|
||||||
pragma: no-cache
|
|
||||||
priority: u=0, i
|
|
||||||
referer: https://ip.ihuan.me
|
|
||||||
sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
|
|
||||||
sec-ch-ua-mobile: ?0
|
|
||||||
sec-ch-ua-platform: "Windows"
|
|
||||||
sec-fetch-dest: document
|
|
||||||
sec-fetch-mode: navigate
|
|
||||||
sec-fetch-site: same-origin
|
|
||||||
sec-fetch-user: ?1
|
|
||||||
upgrade-insecure-requests: 1
|
|
||||||
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
### PASS 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://www123-asd111.lanzoux.com/ia2cntg
|
GET http://127.0.0.1:6400/parser?url=https://lanzoux.com/ia2cntg
|
||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
@@ -8,19 +8,19 @@ GET http://127.0.0.1:6400/json/parser?url=https://wwsd.lanzoue.com/iLany1e9bbbi
|
|||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/json/lz/i6SqHmp1yfc
|
GET http://127.0.0.1:6400/lz/i6SqHmp1yfc
|
||||||
### 蓝奏云https://acgtools.lanzoui.com/iUr7Qnu3sxc https://wwn.lanzouy.com/tp/ihLkw1gezutg https://wwn.lanzouy.com/ihLkw1gezutg
|
### 蓝奏云https://acgtools.lanzoui.com/iUr7Qnu3sxc https://wwn.lanzouy.com/tp/ihLkw1gezutg https://wwn.lanzouy.com/ihLkw1gezutg
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/lz/ihLkw1gezutg
|
GET http://127.0.0.1:6400/lz/ihLkw1gezutg
|
||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/json/lz/icBp6qqj82b@QAIU
|
GET http://127.0.0.1:6400/lz/icBp6qqj82b@QAIU
|
||||||
|
|
||||||
### 蓝奏云
|
### 蓝奏云
|
||||||
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
GET http://127.0.0.1:6400/json/lz/ia2cntg
|
||||||
|
|
||||||
### PASS 蓝奏云优享
|
### 蓝奏云优享
|
||||||
GET http://127.0.0.1:6400/json/iz/lGFndCM
|
GET http://127.0.0.1:6400/json/iz/lGFndCM
|
||||||
|
|
||||||
###
|
###
|
||||||
@@ -34,7 +34,7 @@ GET http://127.0.0.1:6400/json/parser?url=https://www.ilanzou.com/s/zHkna1S
|
|||||||
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
|
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
|
||||||
Referer: https://cowtransfer.com/
|
Referer: https://cowtransfer.com/
|
||||||
|
|
||||||
### PASS 奶牛
|
### 奶牛
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
|
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
|
||||||
|
|
||||||
@@ -47,9 +47,9 @@ GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
|
|||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://goldrepo.cowtransfer.com/s/026a638795634b
|
GET http://127.0.0.1:6400/parser?url=https://goldrepo.cowtransfer.com/s/026a638795634b
|
||||||
|
|
||||||
### 移动云空间 PASS https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
### 移动云空间 https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web//yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
|
||||||
|
|
||||||
### 移动云空间 https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
|
### 移动云空间 https://www.ecpan.cn/drive/fileextoverrid.do?chainUrlTemplate=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=aa0cae0164d8885e6d35826b5b2901eckbWJBalM&parentId=-1
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
@@ -62,7 +62,21 @@ GET http://127.0.0.1:6400/json/ec/4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx@T6O
|
|||||||
#@no-redirect
|
#@no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1&pwd=T6O1RC
|
GET http://127.0.0.1:6400/parser?url=https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=4b3d786755688b85c6eb0c04b9124f4dalzdaJpXHx&isShare=1&pwd=T6O1RC
|
||||||
|
|
||||||
### 小飞机盘 PASS https://share.feijipan.com/s/nMtCOXL
|
|
||||||
|
|
||||||
|
### UC网盘
|
||||||
|
# @no-redirect
|
||||||
|
GET http://127.0.0.1:6400/uc/33197dd53ace4
|
||||||
|
|
||||||
|
### UC网盘
|
||||||
|
GET http://127.0.0.1:6400/json/uc/33197dd53ace4
|
||||||
|
|
||||||
|
### UC网盘
|
||||||
|
# @no-redirect
|
||||||
|
GET http://127.0.0.1:6400/parser?url=https://fast.uc.cn/s/33197dd53ace4
|
||||||
|
|
||||||
|
|
||||||
|
### 小飞机盘https://share.feijipan.com/s/nMtCOXL
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/nMtCOXL
|
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/nMtCOXL
|
||||||
|
|
||||||
@@ -84,7 +98,7 @@ GET http://127.0.0.1:6400/parser?url=https://v2.fangcloud.com/sharing/fb54bdf03c
|
|||||||
### 360亿方云
|
### 360亿方云
|
||||||
GET http://127.0.0.1:6400/json/fc/fb54bdf03c66c04334fe3687a3
|
GET http://127.0.0.1:6400/json/fc/fb54bdf03c66c04334fe3687a3
|
||||||
|
|
||||||
### 360亿方云 PASS
|
### 360亿方云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/fc/e5079007dc31226096628870c7@QAIU
|
GET http://127.0.0.1:6400/fc/e5079007dc31226096628870c7@QAIU
|
||||||
|
|
||||||
@@ -99,7 +113,7 @@ GET http://127.0.0.1:6400/json/ye/iaKtVv-6OECd@DcGe
|
|||||||
### 123
|
### 123
|
||||||
GET https://lz.qaiu.top/json/ye/iaKtVv-6OECd@DcGe
|
GET https://lz.qaiu.top/json/ye/iaKtVv-6OECd@DcGe
|
||||||
|
|
||||||
### 123 PASS
|
### 123
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
||||||
|
|
||||||
@@ -107,89 +121,46 @@ GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
|
|||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/ye/Ev1lVv-t3SY3
|
GET http://127.0.0.1:6400/ye/Ev1lVv-t3SY3
|
||||||
|
|
||||||
### 123 PASS https://www.123pan.com/s/iaKtVv-6OECd.html
|
### 123 https://www.123pan.com/s/iaKtVv-6OECd.html
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
GET http://127.0.0.1:6400/json/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
||||||
|
|
||||||
### 123 PASS https://www.123865.com/s/iaKtVv-6OECd.html
|
|
||||||
# @no-redirect
|
|
||||||
GET http://127.0.0.1:6400/json/parser?url=https://www.123865.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
|
||||||
|
|
||||||
### 123
|
### 123
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
|
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
|
||||||
|
|
||||||
|
|
||||||
### 联想乐云 PASS
|
### 联想乐云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
|
||||||
|
|
||||||
### 联想乐云 PASS
|
### 联想乐云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
|
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
|
||||||
|
|
||||||
### 联想乐云 PASS
|
### 联想乐云
|
||||||
# @no-redirect
|
# @no-redirect
|
||||||
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
|
||||||
|
|
||||||
### 联想乐云 PASS
|
### 联想乐云
|
||||||
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
|
||||||
|
|
||||||
### PASS 文叔叔
|
### 文叔叔
|
||||||
GET http://127.0.0.1:6400/json/parser?url=https://f.ws28.cn/f/f5wtc2iwxtk
|
GET http://127.0.0.1:6400/json/parser?url=https://f.ws59.cn/f/e3peohu6192
|
||||||
###
|
|
||||||
https://f.wss.cc/f/f25625rv6p6
|
|
||||||
|
|
||||||
### TODO Cloudreve
|
|
||||||
GET http://127.0.0.1:6400/json/ce/pan.huang1111.cn_s_wDz5TK
|
|
||||||
|
|
||||||
### Cloudreve2
|
|
||||||
GET http://127.0.0.1:6400/json/ce/pan.huang1111.cn_s_g31PcQ@qaiu
|
|
||||||
|
|
||||||
### Cloudreve类 PASS
|
|
||||||
### https://pan.huang1111.cn/s/g31PcQ
|
|
||||||
### 看见存储 https://pan.seeoss.com/s/nLNsQ
|
|
||||||
### 亿安云盘 https://dav.yiandrive.com/s/xxx
|
|
||||||
#GET http://127.0.0.1:6400/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
|
||||||
|
|
||||||
# @no-redirect
|
|
||||||
GET http://127.0.0.1:6400/parser?url=https://pan.seeoss.com/s/nLNsQ
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### PASS QQ
|
|
||||||
# @no-redirect
|
|
||||||
GET http://127.0.0.1:6400/json/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=qweqe&code=8c02cf57&k=asdad
|
|
||||||
|
|
||||||
|
### Cloudreve
|
||||||
|
GET http://127.0.0.1:6400/json/ce/https_pan.huang1111.cn_s_wDz5TK
|
||||||
|
|
||||||
|
### Cloudreve https://pan.huang1111.cn/s/g31PcQ
|
||||||
|
GET http://127.0.0.1:6400/json/parser?url=https://pan.huang1111.cn/s/g31PcQ&pwd=qaiu
|
||||||
|
|
||||||
###
|
###
|
||||||
GET http://127.0.0.1:6400/v2/statisticsInfo
|
GET http://127.0.0.1:6400/v2/statisticsInfo
|
||||||
|
|
||||||
###
|
|
||||||
GET http://127.0.0.1:6400/v2/linkInfo?url=https://www.123865.com/s/iaKtVv-6OECd.html&pwd=DcGe
|
|
||||||
###
|
|
||||||
GET http://127.0.0.1:6400/v2/linkInfo?url=https://pan.seeoss.com/s/nLNsQ&pwd=DcGe
|
|
||||||
|
|
||||||
###
|
###
|
||||||
POST http://127.0.0.1:6400/v2/login?username=asd
|
POST http://127.0.0.1:6400/v2/login?username=asd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 6401/v2
|
|
||||||
GET http://127.0.0.1:6401/v2/linkInfo?url=https://pan.seeoss.com/s/nLNsQ&pwd=DcGe
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### PASS QQ
|
|
||||||
# @no-redirect
|
|
||||||
GET http://127.0.0.1:6401/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=qweqe&code=8c02cf57&k=asdad
|
|
||||||
|
|
||||||
|
|
||||||
### PASS QQ
|
|
||||||
# @no-redirect
|
|
||||||
GET http://127.0.0.1:6401/json/parser?url=https://iwx.mail.qq.com/ftn/download?func=3&key=qweqe&code=8c02cf57&k=asdad
|
|
||||||
|
|
||||||
### n1
|
|
||||||
http://127.0.0.1:6401/n1/statisticsInfo
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,22 @@
|
|||||||
# 反向代理
|
# 反向代理
|
||||||
server-name: Vert.x-proxy-server(v4.1.2)
|
server-name: Vert.x-proxy-server(v4.1.2)
|
||||||
|
|
||||||
proxy:
|
#proxy:
|
||||||
- listen: 6401
|
# - listen: 8085
|
||||||
# 404的路径
|
# # 404的路径
|
||||||
page404: webroot/err/404.html
|
# 404: webroot/real-html/index.html
|
||||||
static:
|
# static:
|
||||||
path: /
|
# path: /
|
||||||
add-headers:
|
## add-headers:
|
||||||
x-token: ABC
|
## x-token: ABC
|
||||||
root: webroot/nfd-front/
|
# root: webroot/real-html/
|
||||||
# index: index.html
|
# index: realIndex
|
||||||
# ~开通表示正则匹配否则为前缀匹配, 当origin带子路径时进行路由重写,
|
# location:
|
||||||
# 1.origin代理地址端口后有目录(包括 / ),转发后地址:代理地址+访问URL目录部分去除location匹配目录
|
# - path: /real/
|
||||||
# 2.origin代理地址端口后无任何,转发后地址:代理地址+访问URL目录部
|
# origin: 127.0.0.1:8088
|
||||||
location:
|
# - path: /api/
|
||||||
- path: ~^/(json/|v2/|parser).*
|
# origin: 127.0.0.1:7070/demo/
|
||||||
origin: 127.0.0.1:6400
|
#
|
||||||
|
|
||||||
# json/parser -> xxx/parser
|
|
||||||
# - path: /json/
|
|
||||||
# origin: 127.0.0.1:6400/
|
|
||||||
- path: /n1/
|
|
||||||
origin: 127.0.0.1:6400/v2/
|
|
||||||
|
|
||||||
# # SSL HTTPS配置
|
|
||||||
ssl:
|
|
||||||
enable: false
|
|
||||||
# 强制https 暂不支持
|
|
||||||
#ssl_force: true
|
|
||||||
# SSL 协议版本
|
|
||||||
ssl_protocols: TLSv1.2
|
|
||||||
# 证书
|
|
||||||
ssl_certificate: ssl/server.pem
|
|
||||||
# 私钥
|
|
||||||
ssl_certificate_key: ssl/privkey.key
|
|
||||||
# 加密套件 ssl_ciphers 暂不支持
|
|
||||||
# ssl_ciphers: AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
|
|
||||||
|
|
||||||
# - listen: 8086
|
# - listen: 8086
|
||||||
# static:
|
# static:
|
||||||
# path: /t2/
|
# path: /t2/
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIICXgIBAAKBgQDdpOpVrYhr6bMddEbbVXQebA24vMLJylHoOngN09Rx8KM/wJZz
|
|
||||||
4zDPNJJumsQz/kyAX1pd70rxdNb4MLq/ToVlzKIct45y1HvzVzVZA78gP3OKfO9w
|
|
||||||
JZ+d+MLLw8kmtL2PQSMi0HNHm5LsMyON5eScyBPCR0D2P0bydEiv3df7mwIDAQAB
|
|
||||||
AoGAHDILgWS28wtitr+A+Avx3qDeSf7xRJxRORqfBRtKbkgvbD/Co/Fl1yYX7qwP
|
|
||||||
g5G5B6eZplN3lCnJ4v7H8ug5PdJg3RuGmR4EYTx2k/nd/hGNRZfUgWHCII6BjGxN
|
|
||||||
14n1BcZtDVPR8yvcUXKjlpCemMUQE1xzVLO2nEq/gJtEWpkCQQDzZ4hYjpV6Tsy+
|
|
||||||
DN2oLqAtuyeudM2ZSN6ubnEr+OsBfNlHd38z8liCRztAQkBNgrfGqlng79j5fL/p
|
|
||||||
s/8N5mcvAkEA6R0d/FFe4lBNSxPFrzW73mY2AK5ebuAuB3punkDuS5nkBW+1Jg9p
|
|
||||||
j+ZZrqYv1ZNR7pAN5DkRBPV2c/y2n3WXVQJBAK0RlbIaGBDyTMRpk4YNORmrNKjo
|
|
||||||
JnRyituObevtPvQ82KXnQswl6mcfzPKA6QX0B0Y8RFIbWLT0zeSXdqhkhkMCQQCR
|
|
||||||
SdJ8hgKLLt1do48b6nOS40VZG1RD6nkFtCrpezKJ7/oL4VmXxRdX1ouirHU3ub/g
|
|
||||||
3euqiF6xJoOTOrhfnhyZAkEAncfWxN1U969lkfNo7ECPJEP2RJzZVcb5PJYab1NL
|
|
||||||
ccI6qxt/hwNk3NYYEWXavICowVVUNhn2MakeY9cXRlbs3w==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIB9jCCAV8CFEC4MswPv8GgOAulmgIMioLiNn6GMA0GCSqGSIb3DQEBCwUAMDox
|
|
||||||
CzAJBgNVBAYTAmNuMQ0wCwYDVQQIDARxYWl1MQ0wCwYDVQQHDARxYWl1MQ0wCwYD
|
|
||||||
VQQKDARxYWl1MB4XDTI0MTAwMzIxMDM1MFoXDTM0MTAwMTIxMDM1MFowOjELMAkG
|
|
||||||
A1UEBhMCY24xDTALBgNVBAgMBHFhaXUxDTALBgNVBAcMBHFhaXUxDTALBgNVBAoM
|
|
||||||
BHFhaXUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAN2k6lWtiGvpsx10RttV
|
|
||||||
dB5sDbi8wsnKUeg6eA3T1HHwoz/AlnPjMM80km6axDP+TIBfWl3vSvF01vgwur9O
|
|
||||||
hWXMohy3jnLUe/NXNVkDvyA/c4p873Aln534wsvDySa0vY9BIyLQc0ebkuwzI43l
|
|
||||||
5JzIE8JHQPY/RvJ0SK/d1/ubAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAOGALPb49
|
|
||||||
rQWM2gctt4jZECtV9t8/eU5XArtNuf3ixKZVmg5LEOmSQ8v9Qs20l5KwIBxiyKIz
|
|
||||||
GW7kip1lF5gp/sxuYHOSzVhhpbRZkQsANf7yUpAZtlHP4W4vgf+pdZ8DcF/az9KT
|
|
||||||
Ne9Z+nkXdXoTp9E4PQqbgAteWFRcg5MjbsE=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
OpenSSL 证书
|
|
||||||
|
|
||||||
第一步:生成私钥
|
|
||||||
|
|
||||||
在使用 openssl 生成 SSL 证书之前,我们需要先生成私钥。可以使用如下命令行来生成私钥:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
openssl genrsa -out key.pem 1024
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
在这个命令中,我们使用了 genrsa 子命令,并且设置了输出文件的名称,以及私钥的长度为 1024。运行此命令会在当前文件夹中生成一个 key.pem 文件,这个文件就是我们所需要的私钥文件。
|
|
||||||
|
|
||||||
第二步:生成证书请求
|
|
||||||
|
|
||||||
有了私钥之后,我们就可以生成证书请求了。在使用 openssl 生成证书请求时,可以按照以下命令行来执行:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
openssl req -new -key key.pem -out csr.pem
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
在这个命令中,我们使用 req 子命令,并且按照要求设置了私钥和输出文件名。在生成证书请求时,需要输入一些信息,比如你的城市,国家等信息,请根据需求填写相应的信息。
|
|
||||||
|
|
||||||
第三步:签名证书请求
|
|
||||||
|
|
||||||
生成证书请求后,我们需要将证书请求发送给证书颁发机构进行签名。为了在服务器上轻松地创建自签名的 SSL 证书,我们可以使用类似如下的命令或使用 CA 来签署证书请求:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
openssl x509 -req -in csr.pem -out cert.pem -signkey key.pem -days 3650
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
在这个命令中,我们使用了 x509 命令,并且指定了证书请求文件名和输出文件名。我们还使用了 -signkey 选项来指定要使用的私钥文件,以便签署证书。再需要指定 -days 选项均是附带了,控制证书的有效期。
|
|
||||||
|
|
||||||
这些选项组合将使用我们之前生成的私钥来签署证书请求,并将其输出到我们指定的文件 cert.pem 中。
|
|
||||||
|
|
||||||
第四步:配置服务器
|
|
||||||
|
|
||||||
在拥有 SSL 证书之后,我们需要将其配置到服务器当中。不同的 Web 服务器和操作系统可能需要不同的配置方式。在这里以 Nginx 服务器作为例子,以简单介绍如何启用 HTTPS。
|
|
||||||
|
|
||||||
首先,请将服务器的私钥和证书文件(key.pem 和 cert.pem)上传到服务器上。
|
|
||||||
|
|
||||||
然后,请在 Nginx 的配置文件中添加以下内容:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
server {
|
|
||||||
|
|
||||||
listen 443;
|
|
||||||
|
|
||||||
server_name your-domain.com;
|
|
||||||
|
|
||||||
ssl on;
|
|
||||||
|
|
||||||
ssl_certificate /path/to/cert.pem;
|
|
||||||
|
|
||||||
ssl_certificate_key /path/to/key.pem;
|
|
||||||
|
|
||||||
# other server configurations…
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
其中,在 ssl_certificate 中指定证书文件的路径(cert.pem),在 ssl_certificate_key 中指定私钥文件的路径(key.pem)。之后重启 Nginx 服务器即可启用 HTTPS。
|
|
||||||
|
|
||||||
在实际项目中,使用 openssl 工具来生成 SSL 证书是常见的一个方法。通过以上的介绍,大家可以了解到 openssl 证书的生成步骤,并能够轻松地将证书配置到自己的 Web 服务器上,确保网站的数据传输安全,提升用户的信任度。
|
|
||||||
@@ -2,7 +2,7 @@ package cn.qaiu.vx.core.util;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class ParamUtilTest {
|
public class ParamUtilTest {
|
||||||
|
|
||||||
@@ -11,10 +11,5 @@ public class ParamUtilTest {
|
|||||||
System.out.println(ParamUtil.paramsToMap(""));
|
System.out.println(ParamUtil.paramsToMap(""));
|
||||||
System.out.println(ParamUtil.paramsToMap("a=asd&d=23"));
|
System.out.println(ParamUtil.paramsToMap("a=asd&d=23"));
|
||||||
System.out.println(ParamUtil.paramsToMap("asdasd&dd"));
|
System.out.println(ParamUtil.paramsToMap("asdasd&dd"));
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Pattern pattern = Pattern.compile("^(/v2/|/json/).*");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ public class TestRegex {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void regexYFC() {
|
public void regexYFC() {
|
||||||
String html = """
|
String html = "<input type=\"hidden\" id=\"typed_id\" value=\"file_559003251828\">\n" +
|
||||||
<input type="hidden" id="typed_id" value="file_559003251828">
|
" <input type=\"hidden\" id=\"share_link_token\" " +
|
||||||
<input type="hidden" id="share_link_token" value="9cbe4b73521ba4d65a8cd38a8c">
|
"value=\"9cbe4b73521ba4d65a8cd38a8c\">";
|
||||||
""";
|
|
||||||
|
|
||||||
Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\"");
|
Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\"");
|
||||||
Matcher matcher = compile.matcher(html);
|
Matcher matcher = compile.matcher(html);
|
||||||
|
|||||||
@@ -154,14 +154,14 @@ public class WebProxyExamples {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
final WebProxyExamples examples = new WebProxyExamples();
|
final WebProxyExamples examples = new WebProxyExamples();
|
||||||
examples.vertx.executeBlocking(rs -> {
|
// examples.vertx.executeBlocking(rs -> {
|
||||||
rs.complete();
|
// rs.complete();
|
||||||
examples.origin();
|
// examples.origin();
|
||||||
});
|
// });
|
||||||
examples.vertx.executeBlocking(rs -> {
|
// examples.vertx.executeBlocking(rs -> {
|
||||||
rs.complete();
|
// rs.complete();
|
||||||
examples.route();
|
// examples.route();
|
||||||
});
|
// });
|
||||||
System.out.println("ok");
|
System.out.println("ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user