Compare commits

...

57 Commits

Author SHA1 Message Date
BLSKWOLF
5ec3c5716e 修改不同时区导致的问题 2023-12-16 11:32:46 +00:00
qaiu
a682d2d876 加入Jansi 让windows cmd日志彩色输出 2023-12-15 10:14:00 +08:00
BLSKWOLF
72f363d0e6 更新 README.md 2023-12-11 23:56:10 +08:00
BLSKWOLF
4b84ecd5fc 添加文叔叔解析
1.修复QQ邮箱解析
2.添加文叔叔解析
2023-12-11 23:51:49 +08:00
BLSKWOLF
632945d39a 更新 README.md 2023-12-11 17:46:42 +08:00
BLSKWOLF
40bbd3f135 [未完成]添加QQ邮箱文件解析
添加QQ邮箱文件解析,暂时未解决cookie的问题
2023-12-11 17:36:39 +08:00
qaiu
d822090c76 Update README.md 2023-12-05 17:32:17 +08:00
qaiu
bd2ab30e7a Update README.md 2023-12-05 12:49:07 +08:00
qaiu
f306170994 Update README.md 2023-12-05 11:43:04 +08:00
qaiu
c8e12de768 Update README.md 2023-12-05 11:39:26 +08:00
qaiu
d602c8a020 Merge pull request #27 from qaiu/dependabot/maven/ch.qos.logback-logback-classic-1.4.12
Bump ch.qos.logback:logback-classic from 1.4.7 to 1.4.12
2023-12-05 09:48:52 +08:00
dependabot[bot]
efe39eef8d Bump ch.qos.logback:logback-classic from 1.4.7 to 1.4.12
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.4.7 to 1.4.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.4.7...v_1.4.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 01:47:03 +00:00
qaiu
d828c7ac8b Update README.md 2023-12-05 09:46:22 +08:00
qaiu
37babc9a89 1. 日志配置微调 2. 乐云验证 2023-11-29 14:44:42 +08:00
qaiu
8be497a290 vertx升级4.5.0 2023-11-28 17:00:11 +08:00
qaiu
9ff1ea2ff2 Merge remote-tracking branch 'origin/main' 2023-11-28 16:53:48 +08:00
qaiu
fc64363b47 1. 添加联想乐云直链解析 2. 优化代码逻辑 2023-11-28 16:51:51 +08:00
qaiu
36ab2985b1 Merge pull request #25 from qaiu/dependabot/npm_and_yarn/web-front/axios-1.6.0
Bump axios from 1.2.2 to 1.6.0 in /web-front
2023-11-10 10:03:31 +08:00
dependabot[bot]
8ba95932ba Bump axios from 1.2.2 to 1.6.0 in /web-front
Bumps [axios](https://github.com/axios/axios) from 1.2.2 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/1.2.2...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-10 02:02:37 +00:00
qaiu
b8aaa59437 Merge pull request #22 from qaiu/dependabot/npm_and_yarn/web-front/babel/traverse-7.23.2
Bump @babel/traverse from 7.20.12 to 7.23.2 in /web-front
2023-11-10 10:02:09 +08:00
qaiu
1d6953d7c7 小飞机网盘分享新URL格式适配 2023-11-02 10:43:10 +08:00
dependabot[bot]
98c336ab84 Bump @babel/traverse from 7.20.12 to 7.23.2 in /web-front
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.12 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 17:31:32 +00:00
qaiu
d5e2b9cb3e 更新 README.md 2023-09-23 11:21:47 +08:00
qaiu
7c4a243429 Update README.md 2023-09-21 17:32:09 +08:00
qaiu
64e312f108 Update README.md 更新jdk下载网盘为移动云空间 2023-09-21 17:30:55 +08:00
qaiu
265d11e74c Update README.md 2023-09-17 09:54:39 +08:00
qaiu
fcc0f71c23 蓝奏云js引入问题修复 2023-09-17 09:48:23 +08:00
QAIU
4fd4bfb048 Merge remote-tracking branch 'origin/main' 2023-09-08 17:42:09 +08:00
QAIU
da07cfaaff update test 2023-09-08 17:41:30 +08:00
qaiu
7e31adcad9 Update README.md 2023-09-07 08:52:43 +08:00
qaiu
378ae84816 Update README.md 错别字 2023-09-06 03:08:17 +08:00
qaiu
a32abe8bfa Update README.md 2023-09-06 03:06:55 +08:00
qaiu
f7ecf3e66c Update README.md 2023-09-06 03:05:47 +08:00
QAIU
e9e503c685 解析用到的js文件改为String方式引入 2023-09-01 18:07:21 +08:00
qaiu
85c697ea92 Update README.md 2023-08-28 22:37:26 +08:00
QAIU
2018b20be5 去除web-service无用依赖 2023-08-28 17:58:00 +08:00
QAIU
e1c61fb51a 预览首页统计修复,123盘大文件长度取值错误问题 2023-08-28 17:54:39 +08:00
QAIU
cdeb63e225 aaa 2023-08-25 18:30:27 +08:00
QAIU
7b6a79f83a Merge remote-tracking branch 'origin/main' 2023-08-25 18:29:00 +08:00
QAIU
dfd7e7a934 版本更新至0.1.7,启用h2db,添加统计功能,框架优化 2023-08-25 18:28:45 +08:00
qaiu
8deb932685 错别字 2023-08-25 17:04:37 +08:00
QAIU
da063461be Merge remote-tracking branch 'origin/main' 2023-08-25 16:56:00 +08:00
QAIU
3234f643d5 版本更新至0.1.7,启用h2db,添加统计功能,框架优化 2023-08-25 16:55:38 +08:00
qaiu
332cc587a2 Update README.md 2023-08-25 10:04:44 +08:00
qaiu
b30d01e154 Update README.md 2023-08-25 10:04:27 +08:00
QAIU
79d8aa0868 蓝奏云规则微调, 框架优化(70%) 2023-08-23 14:55:08 +08:00
qaiu
a724d703ed Update LzTool.java 2023-08-21 12:27:39 +08:00
qaiu
18478b2a0b Update README.md 2023-08-20 10:36:39 +08:00
qaiu
dde54ed9cc Merge pull request #13 from qaiu/dev
蓝奏云规则修改, 奶牛功能增强
2023-08-20 10:35:02 +08:00
qaiu
3c56a7183e 修复蓝奏云pan解析错误的问题#12 2023-08-20 10:29:00 +08:00
QAIU
38a3ae8a56 Merge remote-tracking branch 'origin/dev' into dev 2023-08-19 17:29:04 +08:00
QAIU
00b30331eb 奶牛快传支持下载zip目录分享#11, core框架优化 2023-08-19 17:28:30 +08:00
qaiu
8397c24715 Update README.md 2023-08-13 00:58:12 +08:00
qaiu
3ee113e81f Update README.md 2023-08-13 00:55:20 +08:00
qaiu
cde641c8db Update README.md 2023-08-13 00:54:21 +08:00
QAIU
30cd2f5850 奶牛快传支持下载zip目录分享#11, core框架优化 2023-08-11 15:28:37 +08:00
QAIU
71066ded98 core框架优化 2023-08-10 14:54:45 +08:00
96 changed files with 2291 additions and 634 deletions

1
.gitignore vendored
View File

@@ -38,3 +38,4 @@ gradlew
gradlew.bat
unused.txt
/web-service/src/main/generated/
/db

View File

@@ -1,3 +1,5 @@
{
"java.compile.nullAnalysis.mode": "automatic"
"java.compile.nullAnalysis.mode": "automatic",
"cmake.configureOnOpen": false,
"java.configuration.updateBuildConfiguration": "interactive"
}

118
README.md
View File

@@ -1,15 +1,24 @@
# netdisk-fast-download
云盘解析服务 (nfd云解析)
预览地址 https://lz.qaiu.top
注意: lz.qaiu.top因解析量过大IP已被123和小飞机禁止访问,
请不要过度依赖预览地址服务,建议本地搭建或者云服务器自行搭建
[![Java CI with Maven](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml/badge.svg)](https://github.com/qaiu/netdisk-fast-download/actions/workflows/maven.yml)
[![jdk](https://img.shields.io/badge/jdk-%3E%3D17-blue)](https://www.oracle.com/cn/java/technologies/downloads/)
[![vert.x](https://img.shields.io/badge/vert.x-4.4.1-blue)](https://vertx-china.github.io/)
[![vert.x](https://img.shields.io/badge/vert.x-4.5.0-blue)](https://vertx-china.github.io/)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/qaiu/netdisk-fast-download)](https://github.com/qaiu/netdisk-fast-download/releases/tag/0.1.6-releases)
## 网盘支持情况:
> 20230722 UC网盘解析失效需要登录
## 项目介绍
网盘直链解析工具能把网盘分享下载链接转化为直链,已支持蓝奏云/奶牛快传/移动云云空间/小飞机盘/亿方云/123云盘等支持私密分享。
*重要声明:本项目仅供学习参考;请不要将此项目用于任何商业用途,否则可能带来严重的后果。*
## 网盘支持情况:
> 20230905 奶牛云直链做了防盗链需加入请求头Referer: https://cowtransfer.com/
> 20230824 123云盘解析大文件(>100MB)失效,需要登录
> 20230722 UC网盘解析失效需要登录
`网盘名称(网盘标识):`
- [蓝奏云 (lz)](https://pc.woozooo.com/)
@@ -34,6 +43,11 @@
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- [文叔叔 (ws) 开发中](https://www.wenshushu.cn/)
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析
- [QQ邮箱 (qq) 开发中](https://wx.mail.qq.com/)
- [ ] 登录, 上传, 下载, 分享
- [X] 直链解析(用户无法直接使用直链)
- [夸克网盘 (qk) 开发中](https://pan.quark.cn/)
**TODO:**
@@ -48,24 +62,24 @@ Core模块集成Vert.x实现类似spring的注解式路由API
API接口
```
网盘标识参考上面网盘支持情况, 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
parser接口可以直接解析分享链接: 加密分享需要加上参数pwd=密码;
网盘标识参考上面网盘支持情况, 括号内是可选内容: 表示当带有分享密码时需要加上密码参数
parser接口可以直接解析分享链接: 加密分享需要加上参数pwd=密码;
其他接口在分享Key后面加上@密码;
1. 解析并自动302跳转 :
1. 解析并自动302跳转 :
http(s)://your_host/parser?url=分享链接(&pwd=xxx)
http(s)://your_host/网盘标识/分享key(@分享密码)
2. 获取解析后的直链--JSON格式
http(s)://your_host/json/parser?url=分享链接(&pwd=xxx)
http(s)://your_host/json/网盘标识/分享key(@分享密码)
3. 特别注意的地方:
3. 特别注意的地方:
- 有些网盘的加密分享的密码可以忽略: 如移动云空间,小飞机网盘
- 移动云空间(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
```
json返回数据格式示例:
json返回数据格式示例:
```json
{
"code": 200,
@@ -116,70 +130,90 @@ GET http://127.0.0.1:6400/json/fc/e5079007dc31226096628870c7@QAIU
# 网盘对比
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|------------|------------------------|----------|-----------|---------|------|
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
| UC网盘 | 需要登录 | √ | 10G | 不限大小 | TODO |
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
| 360亿方云 | √(试用账号有时间限制企业版需要599续费) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
| 123云盘 | √ | √ | 2T | 100G | TODO |
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 5GB | TODO |
| 夸克网盘(TODO) | 需要登录 | √ | 10G | 不限大小 | TODO |
| 网盘名称 | 可直接下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 | 登录接口 |
|------------|------------------------|----------|-----------|-----------------|------|
| 蓝奏云 | √ | √ | 不限空间 | 100M | TODO |
| 奶牛快传 | √ | X | 10G | 不限大小 | TODO |
| 移动云空间 | √ | √(密码可忽略) | 5G(个人) | 不限大小 | TODO |
| UC网盘 | 需要登录 | √ | 10G | 不限大小 | TODO |
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 | TODO |
| 360亿方云 | √(试用账号有时间限制企业版需要599续费) | √(密码可忽略) | 100G(须实名) | 不限大小 | TODO |
| 123云盘 | √ | √ | 2T | 100G>100M需要登录 | TODO |
| 文叔叔(TODO) | √(注意有时间限制) | √ | 10G | 5GB | TODO |
| 夸克网盘(TODO) | 需要登录 | √ | 10G | 不限大小 | TODO |
# 打包部署
## JDK下载lz.qaiu.top提供直链云解析服务
- [阿里jdk17(Dragonwell17-windows-x86)](https://lz.qaiu.top/ye/iaKtVv-hbECd)
- [阿里jdk17(Dragonwell17-linux-x86)](https://lz.qaiu.top/ye/iaKtVv-AbECd)
- [阿里jdk17(Dragonwell17-linux-aarch64)](https://lz.qaiu.top/ye/iaKtVv-HbECd)
- [123云盘解析有效性测试-阿里jdk17(Dragonwell17-linux-aarch64)](https://lz.qaiu.top/json/ye/iaKtVv-HbECd)
- [阿里jdk17(Dragonwell17-windows-x86)](https://lz.qaiu.top/ec/e957acef36ce89e1053979672a90d219n)
- [阿里jdk17(Dragonwell17-linux-x86)](https://lz.qaiu.top/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
- [阿里jdk17(Dragonwell17-linux-aarch64)](https://lz.qaiu.top/ec/d14c2d06296f61b52a876b525265e0f8tzxTc5)
- [解析有效性测试-移动云空间-阿里jdk17-linux-x86](https://lz.qaiu.top/json/ec/6ebc9f2e0bbd53b4c4d5b11013f40a80NHvcYU)
## 开发和打包
```shell
# 环境要求: Jdk17 + maven;
mvn clean
# 环境要求: Jdk17 + maven;
mvn clean
mvn package
```
打包好的文件位于 web-service/target/netdisk-fast-download-x.x.x-bin.zip
打包好的文件位于 web-service/target/netdisk-fast-download-bin.zip
## Linux服务部署
> 注意: netdisk-fast-download.service中的ExecStart的路径改为实际路径
```shell
cd ~
wget -O netdisk-fast-download-0.1.6-bin.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.6-releases/netdisk-fast-download-0.1.6-bin.zip
unzip netdisk-fast-download-*-bin.zip
cd netdisk-fast-download-*-bin
wget -O netdisk-fast-download.zip https://github.com/qaiu/netdisk-fast-download/releases/download/0.1.7/netdisk-fast-download.zip
unzip netdisk-fast-download-bin.zip
cd netdisk-fast-download
bash service-install.sh
```
服务相关命令:
1、查看服务状态
服务相关命令:
1、查看服务状态
systemctl status netdisk-fast-download.service
2、控制服务
启动服务
2、控制服务
启动服务
systemctl start netdisk-fast-download.service
重启服务
重启服务
systemctl restart netdisk-fast-download.service
停止服务
停止服务
systemctl stop netdisk-fast-download.service
开机启动服务
开机启动服务
systemctl enable netdisk-fast-download.servic
停止开机启动
停止开机启动
systemctl disable netdisk-fast-download.servic
## Windows服务部署
1. 下载并解压releases版本netdisk-fast-download-0.1.6-bin.zip
2. 进入netdisk-fast-download-0.1.6-bin目录
1. 下载并解压releases版本netdisk-fast-download-bin.zip
2. 进入netdisk-fast-download下的bin目录
3. 使用管理员权限运行nfd-service-install.bat
如果不想使用服务运行可以直接运行run.bat
> 注意: 如果jdk环境变量的java版本不是17请修改nfd-service-template.xml中的java命令的路径改为实际路径
## Docker部署
TODO
## 0.1.8 开发计划
- Docker部署
- 联想乐云解析支持
- CLoudreve解析解析
- 直链缓存
- 日志优化
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=qaiu/netdisk-fast-download&type=Date)](https://star-history.com/#qaiu/netdisk-fast-download&Date)
## 支持该项目
本项目长期维护如果觉得有帮助, 可以请作者喝杯咖啡, 感谢支持
支付宝发大额红包了...就这几天, 不要错过哦
![image](https://github.com/qaiu/netdisk-fast-download/assets/29825328/54276aee-cc3f-4ebd-8973-2e15f6295819)
[手机端支付宝打赏跳转链接](https://qr.alipay.com/fkx01882dnoxxtjenhlxt53)

View File

@@ -8,7 +8,7 @@ Wants=network-online.target
Type=simple
# User=USER
# 需要JDK17及以上版本 注意修改为自己的路径
ExecStart=/root/java/jdk-17.0.2/bin/java -server -Xmx128m -jar /root/java/netdisk-fast-download-0.1.6/netdisk-fast-download-0.1.6.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
Restart=always
StandOutput=syslog

View File

@@ -7,7 +7,7 @@ pushd %~dp0
set MY_DIR=%~dp0
set MY_DIR=%MY_DIR:~0,-1%
for /f "delims=X" %%i in ('dir /b %MY_DIR%\netdisk-fast-download-*.jar') do (
for /f "delims=X" %%i in ('dir /b %MY_DIR%\netdisk-fast-download.jar') do (
set LAUNCH_JAR=%MY_DIR%\%%i
)

View File

@@ -1,7 +1,7 @@
@echo off && @chcp 65001 > nul
pushd %~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 (
set LAUNCH_JAR=%LIB_DIR%%%i
)

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# set -x
LAUNCH_JAR="netdisk-fast-download-0.1.6.jar"
LAUNCH_JAR="netdisk-fast-download.jar"
nohup java -Xmx512M -jar "$LAUNCH_JAR" "$@" >startup.log 2>&1 &
tail -f startup.log

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.6</version>
<version>0.1.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,7 +16,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.0.5</slf4j.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<vertx.version>4.4.1</vertx.version>
<vertx.version>4.5.0</vertx.version>
</properties>
<dependencies>

View File

@@ -1,7 +1,7 @@
package cn.qaiu;
public class StartH2DatabaseServer {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -38,6 +38,7 @@ public class CreateTable {
javaProperty2SqlColumnMap.put(Boolean.class, "BOOLEAN");
javaProperty2SqlColumnMap.put(String.class, "VARCHAR");
javaProperty2SqlColumnMap.put(java.util.Date.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.time.LocalDateTime.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.sql.Timestamp.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.sql.Date.class, "DATE");
javaProperty2SqlColumnMap.put(java.sql.Time.class, "TIME");
@@ -157,8 +158,8 @@ public class CreateTable {
return sql.substring(0, sql.length() - 1) + ");\r\n";
}
public static void createTable(JDBCPool pool, String tableClassPath) {
Set<Class<?>> tableClassList = ReflectionUtil.getReflections(tableClassPath).getTypesAnnotatedWith(Table.class);
public static void createTable(JDBCPool pool) {
Set<Class<?>> tableClassList = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class);
if (tableClassList.isEmpty()) LOGGER.info("Table model class not fount");
tableClassList.forEach(clazz -> {
String createTableSQL = getCreateTableSQL(clazz);

View File

@@ -92,7 +92,7 @@ public class JDBCPoolInit {
private void poolInitExecute(Promise<String> promise) {
// 初始化连接池
pool = JDBCPool.pool(vertx, dbConfig);
CreateTable.createTable(pool, dbConfig.getString("tableClassPath"));
CreateTable.createTable(pool);
promise.complete("init jdbc pool success");
}

View File

@@ -5,21 +5,14 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.6</version>
<version>0.1.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>1.0.8</version>
<artifactId>core</artifactId>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<vertx.version>4.4.1</vertx.version>
<org.reflections.version>0.10.2</org.reflections.version>
<lombok.version>1.18.12</lombok.version>
<slf4j.version>2.0.5</slf4j.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jackson.version>2.14.2</jackson.version>
</properties>
<dependencyManagement>
@@ -39,7 +32,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.6</version>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -90,6 +83,12 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>

View File

@@ -1,6 +1,5 @@
package cn.qaiu.vx.core;
import cn.qaiu.vx.core.util.ConfigConstant;
import cn.qaiu.vx.core.util.ConfigUtil;
import cn.qaiu.vx.core.util.VertxHolder;
import cn.qaiu.vx.core.verticle.ReverseProxyVerticle;
@@ -16,8 +15,11 @@ import org.slf4j.LoggerFactory;
import java.lang.management.ManagementFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.locks.LockSupport;
import static cn.qaiu.vx.core.util.ConfigConstant.*;
/**
* vertx启动类 需要在主启动类完成回调
* <br>Create date 2021-05-07 10:26:54
@@ -43,6 +45,11 @@ public final class Deploy {
return INSTANCE;
}
/**
*
* @param args 启动参数
* @param handle 启动完成后回调处理函数
*/
public void start(String[] args, Handler<JsonObject> handle) {
this.mainThread = Thread.currentThread();
this.handle = handle;
@@ -51,6 +58,9 @@ public final class Deploy {
path.append("-").append(args[0]);
}
// 设置时区
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
// 读取yml配置
ConfigUtil.readYamlConfig(path.toString(), tempVertx)
.onSuccess(this::readConf)
@@ -81,7 +91,7 @@ public final class Deploy {
var calendar = Calendar.getInstance();
calendar.setTime(new Date());
var year = calendar.get(Calendar.YEAR);
var logoTemplete = """
var logoTemplate = """
Web Server powered by:\s
____ ____ _ _ _ \s
@@ -94,7 +104,7 @@ public final class Deploy {
""";
System.out.printf(logoTemplete,
System.out.printf(logoTemplate,
conf.getString("version_app"),
VersionCommand.getVersion(),
conf.getString("copyright"),
@@ -108,10 +118,10 @@ public final class Deploy {
private void deployVerticle() {
tempVertx.close();
LOGGER.info("配置读取成功");
customConfig = globalConfig.getJsonObject(ConfigConstant.CUSTOM);
customConfig = globalConfig.getJsonObject(CUSTOM);
JsonObject vertxConfig = globalConfig.getJsonObject(ConfigConstant.VERTX);
Integer vertxConfigELPS = vertxConfig.getInteger(ConfigConstant.EVENT_LOOP_POOL_SIZE);
JsonObject vertxConfig = globalConfig.getJsonObject(VERTX);
Integer vertxConfigELPS = vertxConfig.getInteger(EVENT_LOOP_POOL_SIZE);
var vertxOptions = vertxConfigELPS == 0 ?
new VertxOptions() : new VertxOptions(vertxConfig);
@@ -122,10 +132,10 @@ public final class Deploy {
VertxHolder.init(vertx);
//配置保存在共享数据中
var sharedData = vertx.sharedData();
LocalMap<String, Object> localMap = sharedData.getLocalMap(ConfigConstant.LOCAL);
localMap.put(ConfigConstant.GLOBAL_CONFIG, globalConfig);
localMap.put(ConfigConstant.CUSTOM_CONFIG, customConfig);
localMap.put(ConfigConstant.SERVER, globalConfig.getJsonObject(ConfigConstant.SERVER));
LocalMap<String, Object> localMap = sharedData.getLocalMap(LOCAL);
localMap.put(GLOBAL_CONFIG, globalConfig);
localMap.put(CUSTOM_CONFIG, customConfig);
localMap.put(SERVER, globalConfig.getJsonObject(SERVER));
var future0 = vertx.createSharedWorkerExecutor("other-handle").executeBlocking(bch -> {
handle.handle(globalConfig);
bch.complete("other handle complete");
@@ -169,7 +179,7 @@ public final class Deploy {
* @return Deployment Options
*/
private DeploymentOptions getWorkDeploymentOptions(String name) {
return getWorkDeploymentOptions(name, customConfig.getInteger(ConfigConstant.ASYNC_SERVICE_INSTANCES));
return getWorkDeploymentOptions(name, customConfig.getInteger(ASYNC_SERVICE_INSTANCES));
}
private DeploymentOptions getWorkDeploymentOptions(String name, int ins) {

View File

@@ -0,0 +1,15 @@
package cn.qaiu.vx.core.annotaions;
import java.lang.annotation.*;
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandleSortFilter {
/**
* 注册顺序,数字越大越先注册<br>
* 值<0时会过滤掉该处理器
*/
int value() default 0;
}

View File

@@ -0,0 +1,23 @@
package cn.qaiu.vx.core.annotaions;
import java.lang.annotation.*;
/**
* 拦截器配置注解
* 正则匹配拦截途径
*
* @author <a href="https://qaiu.top">QAIU</a>
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptorConfig {
String pattern() default "";
/**
* 注册顺序,数字越大越先注册
*/
int order() default 0;
}

View File

@@ -1,9 +1,16 @@
package cn.qaiu.vx.core.base;
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.ReflectionUtil;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.reflections.Reflections;
import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
import java.util.Set;
import static cn.qaiu.vx.core.util.ResponseUtil.*;
/**
* 统一响应处理
@@ -13,22 +20,43 @@ import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
*/
public interface BaseHttpApi {
default void fireJsonResponse(RoutingContext ctx, JsonObject jsonResult) {
ctx.response().putHeader(CONTENT_TYPE, "application/json; charset=utf-8")
.setStatusCode(200)
.end(jsonResult.encode());
// 需要扫描注册的Router路径
Reflections reflections = ReflectionUtil.getReflections();
default void doFireJsonObjectResponse(RoutingContext ctx, JsonObject jsonObject) {
if (!ctx.response().ended()) {
fireJsonObjectResponse(ctx, jsonObject);
}
handleAfterInterceptor(ctx, jsonObject);
}
default <T> void fireJsonResponse(RoutingContext ctx, T jsonResult) {
JsonObject jsonObject = JsonObject.mapFrom(jsonResult);
fireJsonResponse(ctx, jsonObject);
default <T> void doFireJsonResultResponse(RoutingContext ctx, JsonResult<T> jsonResult) {
if (!ctx.response().ended()) {
fireJsonResultResponse(ctx, jsonResult);
}
handleAfterInterceptor(ctx, jsonResult.toJsonObject());
}
default void fireTextResponse(RoutingContext ctx, String text) {
ctx.response().putHeader("content-type", "text/html; charset=utf-8").end(text);
default Set<AfterInterceptor> getAfterInterceptor() {
Set<Class<? extends AfterInterceptor>> afterInterceptorClassSet =
reflections.getSubTypesOf(AfterInterceptor.class);
if (afterInterceptorClassSet == null) {
return null;
}
return CommonUtil.sortClassSet(afterInterceptorClassSet);
}
default void sendError(int statusCode, RoutingContext ctx) {
ctx.response().setStatusCode(statusCode).end();
default void handleAfterInterceptor(RoutingContext ctx, JsonObject jsonObject) {
Set<AfterInterceptor> afterInterceptor = getAfterInterceptor();
if (afterInterceptor != null) {
afterInterceptor.forEach(ai -> ai.handle(ctx, jsonObject));
}
if (!ctx.response().ended()) {
fireTextResponse(ctx, "handleAfterInterceptor: response not end");
}
}
}

View File

@@ -5,9 +5,10 @@ import cn.qaiu.vx.core.annotaions.RouteHandler;
import cn.qaiu.vx.core.annotaions.RouteMapping;
import cn.qaiu.vx.core.annotaions.SockRouteMapper;
import cn.qaiu.vx.core.base.BaseHttpApi;
import cn.qaiu.vx.core.enums.MIMEType;
import cn.qaiu.vx.core.interceptor.BeforeInterceptor;
import cn.qaiu.vx.core.model.JsonResult;
import cn.qaiu.vx.core.util.*;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
@@ -19,15 +20,12 @@ import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.*;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
import javassist.CtClass;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +36,7 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.qaiu.vx.core.util.ConfigConstant.ROUTE_TIME_OUT;
import static io.vertx.core.http.HttpHeaders.*;
/**
@@ -57,15 +56,11 @@ public class RouterHandlerFactory implements BaseHttpApi {
add(HttpMethod.DELETE);
add(HttpMethod.HEAD);
}};
// 需要扫描注册的Router路径
private static volatile Reflections reflections;
private final String gatewayPrefix;
public RouterHandlerFactory(String routerScanAddress, String gatewayPrefix) {
Objects.requireNonNull(routerScanAddress, "The router package address scan is empty.");
public RouterHandlerFactory(String gatewayPrefix) {
Objects.requireNonNull(gatewayPrefix, "The gateway prefix is empty.");
reflections = ReflectionUtil.getReflections(routerScanAddress);
this.gatewayPrefix = gatewayPrefix;
}
@@ -73,21 +68,22 @@ public class RouterHandlerFactory implements BaseHttpApi {
* 开始扫描并注册handler
*/
public Router createRouter() {
Router router = Router.router(VertxHolder.getVertxInstance());
// 主路由
Router mainRouter = Router.router(VertxHolder.getVertxInstance());
// 静态资源
String path = SharedDataUtil.getJsonConfig("server")
.getString("staticResourcePath");
if (!StringUtils.isEmpty(path)) {
// 静态资源
router.route("/*").handler(StaticHandler
mainRouter.route("/*").handler(StaticHandler
.create(path)
.setCachingEnabled(true)
.setDefaultContentEncoding("UTF-8"));
}
router.route().handler(ctx -> {
mainRouter.route().handler(ctx -> {
LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}",
ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method());
ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
@@ -98,11 +94,15 @@ public class RouterHandlerFactory implements BaseHttpApi {
ctx.next();
});
// 添加跨域的方法
router.route().handler(CorsHandler.create().addRelativeOrigin(".*").allowCredentials(true).allowedMethods(httpMethods));
mainRouter.route().handler(CorsHandler.create().addRelativeOrigin(".*").allowCredentials(true).allowedMethods(httpMethods));
// 配置文件上传路径
router.route().handler(BodyHandler.create().setUploadsDirectory("uploads"));
mainRouter.route().handler(BodyHandler.create().setUploadsDirectory("uploads"));
// 拦截器
Set<Handler<RoutingContext>> interceptorSet = getInterceptorSet();
Route route0 = mainRouter.route("/*");
interceptorSet.forEach(route0::handler);
try {
Set<Class<?>> handlers = reflections.getTypesAnnotatedWith(RouteHandler.class);
@@ -116,7 +116,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
for (Class<?> handler : sortedHandlers) {
try {
// 注册请求处理方法
registerNewHandler(router, handler);
registerNewHandler(mainRouter, handler);
} catch (Throwable e) {
LOGGER.error("Error register {}, Error details", handler, e.getCause());
@@ -126,12 +126,12 @@ public class RouterHandlerFactory implements BaseHttpApi {
LOGGER.error("Manually Register Handler Fail, Error details" + e.getMessage());
}
// 错误请求处理
router.errorHandler(405, ctx -> fireJsonResponse(ctx, JsonResult
mainRouter.errorHandler(405, ctx -> doFireJsonResultResponse(ctx, JsonResult
.error("Method Not Allowed", 405)));
router.errorHandler(404, ctx -> ctx.response().setStatusCode(404).setChunked(true)
mainRouter.errorHandler(404, ctx -> ctx.response().setStatusCode(404).setChunked(true)
.end("Internal server error: 404 not found"));
return router;
return mainRouter;
}
/**
@@ -155,15 +155,13 @@ public class RouterHandlerFactory implements BaseHttpApi {
method -> method.isAnnotationPresent(SockRouteMapper.class)
).toList());
// 拦截器
Handler<RoutingContext> interceptor = getInterceptor();
// 依次注册处理方法
for (Method method : methodList) {
if (method.isAnnotationPresent(RouteMapping.class)) {
// 普通路由
RouteMapping mapping = method.getAnnotation(RouteMapping.class);
HttpMethod routeMethod = HttpMethod.valueOf(mapping.method().name());
String routeUrl = getRouteUrl(method.getName(), mapping.value());
String routeUrl = getRouteUrl(mapping.value());
String url = root.concat(routeUrl);
// 匹配方法
Route route = router.route(routeMethod, url);
@@ -173,17 +171,18 @@ public class RouterHandlerFactory implements BaseHttpApi {
route.consumes(mineType);
}
// 先执行拦截方法, 再进入业务请求
route.handler(interceptor);
// 设置默认超时
route.handler(TimeoutHandler.create(SharedDataUtil.getCustomConfig().getInteger(ROUTE_TIME_OUT)));
route.handler(ResponseTimeHandler.create());
route.handler(ctx -> handlerMethod(instance, method, ctx)).failureHandler(ctx -> {
if (ctx.response().ended()) return;
ctx.failure().printStackTrace();
fireJsonResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500));
doFireJsonResultResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500));
});
} else if (method.isAnnotationPresent(SockRouteMapper.class)) {
// websocket 基于sockJs
SockRouteMapper mapping = method.getAnnotation(SockRouteMapper.class);
String routeUrl = getRouteUrl(method.getName(), mapping.value());
String routeUrl = getRouteUrl(mapping.value());
String url = root.concat(routeUrl);
LOGGER.info("Register New Websocket Handler -> {}", url);
SockJSHandlerOptions options = new SockJSHandlerOptions()
@@ -210,10 +209,9 @@ public class RouterHandlerFactory implements BaseHttpApi {
/**
* 获取并处理路由URL分隔符
*
* @param methodName 路由method
* @return String
*/
private String getRouteUrl(String methodName, String mapperValue) {
private String getRouteUrl(String mapperValue) {
String routeUrl;
if ("/".equals(mapperValue)) {
routeUrl = mapperValue;
@@ -229,15 +227,10 @@ public class RouterHandlerFactory implements BaseHttpApi {
* 配置拦截
*
* @return Handler
* @throws Throwable Throwable
*/
private Handler<RoutingContext> getInterceptor() throws Throwable {
private Set<Handler<RoutingContext>> getInterceptorSet() {
// 配置拦截
Class<?> interceptorClass = Class.forName(SharedDataUtil.getValueForCustomConfig("interceptorClassPath"));
Object handleInstance = ReflectionUtil.newWithNoParam(interceptorClass);
Method doHandle = interceptorClass.getMethod("doHandle");
// 反射调用
return CastUtil.cast(ReflectionUtil.invoke(doHandle, handleInstance));
return getBeforeInterceptor().stream().map(BeforeInterceptor::doHandle).collect(Collectors.toSet());
}
/**
@@ -259,7 +252,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
if (handler.isAnnotationPresent(RouteHandler.class)) {
RouteHandler routeHandler = handler.getAnnotation(RouteHandler.class);
String value = routeHandler.value();
root += ("/".equals(value) ? "" : value);
root += (value.startsWith("/") ? value.substring(1) : value);
}
if (!root.endsWith("/")) {
root = root + "/";
@@ -302,37 +295,12 @@ public class RouterHandlerFactory implements BaseHttpApi {
});
}
final MultiMap queryParams = ctx.queryParams();
if ("POST".equals(ctx.request().method().name())) {
queryParams.addAll(ctx.request().params());
}
JsonArray entityPackagesReg = SharedDataUtil.getJsonArrayForCustomConfig("entityPackagesReg");
// 绑定get或post请求头的请求参数
methodParametersTemp.forEach((k, v) -> {
if (ReflectionUtil.isBasicType(v.getRight())) {
String fmt = getFmt(v.getLeft(), v.getRight());
String value = queryParams.get(k);
parameterValueList.put(k, ReflectionUtil.conversion(v.getRight(), value, fmt));
} else if (RoutingContext.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx);
} else if (HttpServerRequest.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx.request());
} else if (HttpServerResponse.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx.response());
} else if (CommonUtil.matchRegList(entityPackagesReg.getList(), v.getRight().getName())) {
// 绑定实体类
try {
Class<?> aClass = Class.forName(v.getRight().getName());
Object entity = ParamUtil.multiMapToEntity(queryParams, aClass);
parameterValueList.put(k, entity);
} catch (Exception e) {
e.printStackTrace();
}
}
});
final MultiMap queryParams = ctx.queryParams();
// 解析body-json参数
if ("application/json".equals(ctx.parsedHeaders().contentType().value()) && ctx.body().asJsonObject() != null) {
if (HttpHeaderValues.APPLICATION_JSON.toString().equals(ctx.parsedHeaders().contentType().value())
&& ctx.body().asJsonObject() != null) {
JsonObject body = ctx.body().asJsonObject();
if (body != null) {
methodParametersTemp.forEach((k, v) -> {
@@ -352,26 +320,66 @@ public class RouterHandlerFactory implements BaseHttpApi {
}
});
}
} else if (ctx.body() != null) {
queryParams.addAll(ParamUtil.paramsToMap(ctx.body().asString()));
}
// 解析其他参数
if ("POST".equals(ctx.request().method().name())) {
queryParams.addAll(ctx.request().params());
}
// 绑定get或post请求头的请求参数
methodParametersTemp.forEach((k, v) -> {
if (ReflectionUtil.isBasicType(v.getRight())) {
String fmt = getFmt(v.getLeft(), v.getRight());
String value = queryParams.get(k);
parameterValueList.put(k, ReflectionUtil.conversion(v.getRight(), value, fmt));
} else if (RoutingContext.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx);
} else if (HttpServerRequest.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx.request());
} else if (HttpServerResponse.class.getName().equals(v.getRight().getName())) {
parameterValueList.put(k, ctx.response());
} else if (parameterValueList.get(k) == null
&& CommonUtil.matchRegList(entityPackagesReg.getList(), v.getRight().getName())) {
// 绑定实体类
try {
Class<?> aClass = Class.forName(v.getRight().getName());
Object entity = ParamUtil.multiMapToEntity(queryParams, aClass);
parameterValueList.put(k, entity);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 调用handle 获取响应对象
Object[] parameterValueArray = parameterValueList.values().toArray(new Object[0]);
try {
// 反射调用
Object data = ReflectionUtil.invokeWithArguments(method, instance, parameterValueArray);
if (data != null) {
if (data instanceof JsonResult) {
fireJsonResponse(ctx, data);
doFireJsonResultResponse(ctx, (JsonResult<?>) data);
}
if (data instanceof JsonObject) {
doFireJsonObjectResponse(ctx, ((JsonObject) data));
} else if (data instanceof Future) { // 处理异步响应
((Future<?>) data).onSuccess(res -> {
if (res instanceof JsonObject) {
fireJsonResponse(ctx, res);
} else if (res != null) {
fireJsonResponse(ctx, JsonResult.data(res));
if (res instanceof JsonResult) {
doFireJsonResultResponse(ctx, (JsonResult<?>) res);
}
}).onFailure(e -> fireJsonResponse(ctx, JsonResult.error(e.getMessage())));
if (res instanceof JsonObject) {
doFireJsonObjectResponse(ctx, ((JsonObject) res));
} else if (res != null) {
doFireJsonResultResponse(ctx, JsonResult.data(res));
} else {
handleAfterInterceptor(ctx, null);
}
}).onFailure(e -> doFireJsonResultResponse(ctx, JsonResult.error(e.getMessage())));
} else {
ctx.response().headers().set(CONTENT_TYPE, MIMEType.TEXT_HTML.getValue());
ctx.end(data.toString());
doFireJsonResultResponse(ctx, JsonResult.data(data));
}
}
} catch (Throwable e) {
@@ -384,7 +392,7 @@ public class RouterHandlerFactory implements BaseHttpApi {
err = e.getCause().getMessage();
}
}
fireJsonResponse(ctx, JsonResult.error(err));
doFireJsonResultResponse(ctx, JsonResult.error(err));
}
}
@@ -402,4 +410,13 @@ public class RouterHandlerFactory implements BaseHttpApi {
}
return fmt;
}
private Set<BeforeInterceptor> getBeforeInterceptor() {
Set<Class<? extends BeforeInterceptor>> interceptorClassSet =
reflections.getSubTypesOf(BeforeInterceptor.class);
if (interceptorClassSet == null) {
return new HashSet<>();
}
return CommonUtil.sortClassSet(interceptorClassSet);
}
}

View File

@@ -0,0 +1,15 @@
package cn.qaiu.vx.core.interceptor;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
/**
* 后置拦截器接口
*
* @author <a href="https://qaiu.top">QAIU</a>
*/
public interface AfterInterceptor {
void handle(RoutingContext ctx, JsonObject responseData);
}

View File

@@ -0,0 +1,34 @@
package cn.qaiu.vx.core.interceptor;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import static cn.qaiu.vx.core.util.ResponseUtil.sendError;
/**
* 前置拦截器接口
*
* @author <a href="https://qaiu.top">QAIU</a>
*/
public interface BeforeInterceptor extends Handler<RoutingContext> {
String IS_NEXT = "RoutingContextIsNext";
default Handler<RoutingContext> doHandle() {
return ctx -> {
ctx.put(IS_NEXT, false);
BeforeInterceptor.this.handle(ctx);
if (!(Boolean) ctx.get(IS_NEXT) && !ctx.response().ended()) {
sendError(ctx, 403);
}
};
}
default void doNext(RoutingContext context) {
context.put(IS_NEXT, true);
context.next();
}
void handle(RoutingContext context);
}

View File

@@ -1,21 +0,0 @@
package cn.qaiu.vx.core.interceptor;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
/**
* 拦截器接口
* <br>Create date 2021-05-06 09:20:37
*
* @author <a href="https://qaiu.top">QAIU</a>
*/
public interface Interceptor {
default Handler<RoutingContext> doHandle() {
return this::beforeHandle;
}
void beforeHandle(RoutingContext context);
void afterHandle(RoutingContext context);
}

View File

@@ -1,6 +1,9 @@
package cn.qaiu.vx.core.model;
import cn.qaiu.vx.core.util.CastUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vertx.core.json.JsonObject;
import org.apache.commons.lang3.StringUtils;
import java.io.Serial;
@@ -159,4 +162,16 @@ public class JsonResult<T> implements Serializable {
public static <T> JsonResult<T> success() {
return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, null);
}
// 转为json对象
public JsonObject toJsonObject() {
return JsonObject.mapFrom(this);
}
private static final ObjectMapper mapper = new ObjectMapper();
// 转为json对象
public static JsonResult<?> toJsonResult(JsonObject json) {
return CastUtil.cast(json.mapTo(JsonResult.class));
}
}

View File

@@ -1,5 +1,6 @@
package cn.qaiu.vx.core.util;
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import org.apache.commons.beanutils.ConvertUtils;
@@ -15,6 +16,8 @@ import java.net.URL;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* CommonUtil
@@ -70,10 +73,11 @@ public class CommonUtil {
} catch (UnknownHostException e) {
return false;
}
try(Socket ignored = new Socket(Address, port)) {
//建立一个Socket连接
try (Socket ignored = new Socket(Address, port)) {
//建立一个Socket连接
flag = true;
} catch (IOException ignoredException) {}
} catch (IOException ignoredException) {
}
return flag;
}
@@ -126,4 +130,32 @@ public class CommonUtil {
LocalConstant.put(configName, map);
LOGGER.info("读取配置{}成功", configName);
}
public static <T> Set<T> sortClassSet(Set<Class<? extends T>> set) {
return set.stream().filter(c1 -> {
HandleSortFilter s1 = c1.getAnnotation(HandleSortFilter.class);
if (s1 != null) {
return s1.value() > 0;
} else {
return true;
}
}).sorted((c1, c2) -> {
HandleSortFilter s1 = c1.getAnnotation(HandleSortFilter.class);
HandleSortFilter s2 = c2.getAnnotation(HandleSortFilter.class);
int n1 = 0, n2 = 0;
if (s1 != null) {
n1 = s1.value();
}
if (s2 != null) {
n2 = s2.value();
}
return n1 - n2;
}).map(c -> {
try {
return ReflectionUtil.newWithNoParam(c);
} catch (Exception e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toSet());
}
}

View File

@@ -3,11 +3,14 @@ package cn.qaiu.vx.core.util;
public interface ConfigConstant {
String CUSTOM = "custom";
String VERTX = "vertx";
String EVENT_LOOP_POOL_SIZE = "eventLoopPoolSize";
String LOCAL = "local";
String SERVER = "server";
String GLOBAL_CONFIG = "globalConfig";
String CUSTOM_CONFIG = "customConfig";
String ASYNC_SERVICE_INSTANCES = "asyncServiceInstances";
String IGNORES_REG="ignoresReg";
String BASE_LOCATIONS="baseLocations";
String ROUTE_TIME_OUT="routeTimeOut";
}

View File

@@ -27,12 +27,12 @@ public final class ParamUtil {
return map;
}
public static <T> T multiMapToEntity(MultiMap multiMap,Class<T> tClass) throws NoSuchMethodException {
Map<String,String> map = multiMapToMap(multiMap);
public static <T> T multiMapToEntity(MultiMap multiMap, Class<T> tClass) throws NoSuchMethodException {
Map<String, String> map = multiMapToMap(multiMap);
T obj = null;
try {
obj = tClass.getDeclaredConstructor().newInstance();
BeanUtils.populate(obj,map);
BeanUtils.populate(obj, map);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
LOGGER.error("实例化异常");
@@ -42,4 +42,21 @@ public final class ParamUtil {
}
return obj;
}
public static MultiMap paramsToMap(String paramString) {
MultiMap entries = MultiMap.caseInsensitiveMultiMap();
if (paramString == null) return entries;
String[] params = paramString.split("&");
if (params.length == 0) return entries;
for (String param : params) {
String[] kv = param.split("=");
if (kv.length == 2) {
entries.set(kv[0], kv[1]);
} else {
entries.set(kv[0], "");
}
}
return entries;
}
}

View File

@@ -24,6 +24,8 @@ import java.net.URL;
import java.text.ParseException;
import java.util.*;
import static cn.qaiu.vx.core.util.ConfigConstant.BASE_LOCATIONS;
/**
* 基于org.reflection和javassist的反射工具包
* 通过包扫描实现路由地址的注解映射
@@ -33,6 +35,16 @@ import java.util.*;
*/
public final class ReflectionUtil {
/**
* 以默认配置的基础包路径获取反射器
*
* @return Reflections object
*/
public static Reflections getReflections() {
return getReflections(SharedDataUtil.getStringForCustomConfig(BASE_LOCATIONS));
}
/**
* 获取反射器
*
@@ -48,6 +60,7 @@ public final class ReflectionUtil {
} else {
packageAddressList = Collections.singletonList(packageAddress);
}
return getReflections(packageAddressList);
}
@@ -70,10 +83,11 @@ public final class ReflectionUtil {
// 发现注解api层 没有继承父类时 这里反射一直有问题(Scanner SubTypesScanner was not configured)
// 因此这里需要手动配置各种Scanner扫描器 -- https://blog.csdn.net/qq_29499107/article/details/106889781
configurationBuilder.setScanners(
Scanners.SubTypes.filterResultsBy(s -> true), //允许getAllTypes获取所有Object的子类, 不设置为false则 getAllTypes 会报错.默认为true.
Scanners.SubTypes.filterResultsBy(s -> true), //允许getAllTypes获取所有Object的子类, 不设置为false则 getAllTypes
// 会报错.默认为true.
new MethodParameterNamesScanner(), //设置方法参数名称 扫描器,否则调用getConstructorParamNames 会报错
Scanners.MethodsAnnotated, //设置方法注解 扫描器, 否则getConstructorsAnnotatedWith,getMethodsAnnotatedWith 会报错
new MemberUsageScanner(), //设置 member 扫描器,否则 getMethodUsage 会报错, 不推荐使用,有可能会报错 Caused by: java.lang.ClassCastException: javassist.bytecode.InterfaceMethodrefInfo cannot be cast to javassist.bytecode.MethodrefInfo
new MemberUsageScanner(), //设置 member 扫描器,否则 getMethodUsage 会报错
Scanners.TypesAnnotated //设置类注解 扫描器 ,否则 getTypesAnnotatedWith 会报错
);
@@ -98,7 +112,8 @@ public final class ReflectionUtil {
MethodInfo methodInfo = cm.getMethodInfo();
CtClass[] parameterTypes = cm.getParameterTypes();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
LocalVariableAttribute attr =
(LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
boolean flag = true;
boolean flag2 = cm.getModifiers() - 1 != AccessFlag.STATIC;
@@ -110,7 +125,8 @@ public final class ReflectionUtil {
continue;
}
flag = false;
paramMap.put(attr.variableName(j + (flag2 ? 1 : 0)), Pair.of(parameterAnnotations[j - k], parameterTypes[j - k]));
paramMap.put(attr.variableName(j + (flag2 ? 1 : 0)), Pair.of(parameterAnnotations[j - k],
parameterTypes[j - k]));
}
} catch (NotFoundException e) {
e.printStackTrace();
@@ -214,7 +230,8 @@ public final class ReflectionUtil {
if (ctClass.isPrimitive() || "java.util.Date".equals(ctClass.getName())) {
return true;
}
return ctClass.getName().matches("^java\\.lang\\.((Boolean)|(Character)|(Byte)|(Short)|(Integer)|(Long)|(Float)|(Double)|(String))$");
return ctClass.getName().matches("^java\\.lang\\.((Boolean)|(Character)|(Byte)|(Short)|(Integer)|(Long)|" +
"(Float)|(Double)|(String))$");
}
/**
@@ -238,7 +255,8 @@ public final class ReflectionUtil {
* @throws InstantiationException InstantiationException
* @throws IllegalAccessException IllegalAccessException
*/
public static <T> T newWithNoParam(Class<T> handler) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
public static <T> T newWithNoParam(Class<T> handler) throws NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
return handler.getConstructor().newInstance();
}

View File

@@ -0,0 +1,40 @@
package cn.qaiu.vx.core.util;
import cn.qaiu.vx.core.model.JsonResult;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
public class ResponseUtil {
public static void redirect(HttpServerResponse response, String url) {
response.putHeader(HttpHeaders.LOCATION, url).setStatusCode(302).end();
}
public static void redirect(HttpServerResponse response, String url, Promise<?> promise) {
redirect(response, url);
promise.complete();
}
public static void fireJsonObjectResponse(RoutingContext ctx, JsonObject jsonObject) {
ctx.response().putHeader(CONTENT_TYPE, "application/json; charset=utf-8")
.setStatusCode(200)
.end(jsonObject.encode());
}
public static <T> void fireJsonResultResponse(RoutingContext ctx, JsonResult<T> jsonResult) {
fireJsonObjectResponse(ctx, jsonResult.toJsonObject());
}
public static void fireTextResponse(RoutingContext ctx, String text) {
ctx.response().putHeader(CONTENT_TYPE, "text/html; charset=utf-8").end(text);
}
public static void sendError(RoutingContext ctx, int statusCode) {
ctx.response().setStatusCode(statusCode).end();
}
}

View File

@@ -32,11 +32,11 @@ public class SharedDataUtil {
return (JsonObject) localMap.get(key);
}
public static JsonObject getJsonObjectForCustomConfig(String key) {
return getJsonConfig("customConfig").getJsonObject(key);
public static JsonObject getCustomConfig() {
return getJsonConfig("customConfig");
}
public static String getJsonStringForCustomConfig(String key) {
public static String getStringForCustomConfig(String key) {
return getJsonConfig("customConfig").getString(key);
}

View File

@@ -23,7 +23,6 @@ public class RouterVerticle extends AbstractVerticle {
private static final int port = SharedDataUtil.getValueForServerConfig("port");
private static final Router router = new RouterHandlerFactory(
SharedDataUtil.getJsonStringForCustomConfig("routerLocations"),
SharedDataUtil.getJsonStringForServerConfig("contextPath")).createRouter();
private static final JsonObject globalConfig = SharedDataUtil.getJsonConfig("globalConfig");

View File

@@ -3,7 +3,6 @@ package cn.qaiu.vx.core.verticle;
import cn.qaiu.vx.core.annotaions.Service;
import cn.qaiu.vx.core.base.BaseAsyncService;
import cn.qaiu.vx.core.util.ReflectionUtil;
import cn.qaiu.vx.core.util.SharedDataUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.serviceproxy.ServiceBinder;
@@ -27,8 +26,7 @@ public class ServiceVerticle extends AbstractVerticle {
private static final Set<Class<?>> handlers;
static {
String handlerLocations = SharedDataUtil.getJsonStringForCustomConfig("handlerLocations");
Reflections reflections = ReflectionUtil.getReflections(handlerLocations);
Reflections reflections = ReflectionUtil.getReflections();
handlers = reflections.getTypesAnnotatedWith(Service.class);
}

68
parser/pom.xml Normal file
View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>parser</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--logback日志实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.nashorn/nashorn-core -->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,24 @@
package cn.qaiu;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebClientVertxInit {
private Vertx vertx = null;
private static final WebClientVertxInit INSTANCE = new WebClientVertxInit();
private static final Logger log = LoggerFactory.getLogger(WebClientVertxInit.class);
public static void init(Vertx vx) {
INSTANCE.vertx = vx;
}
public static Vertx get() {
if (INSTANCE.vertx == null) {
log.info("getVertx: Vertx实例不存在, 创建Vertx实例.");
INSTANCE.vertx = Vertx.vertx();
}
return INSTANCE.vertx;
}
}

View File

@@ -1,6 +1,6 @@
package cn.qaiu.lz.common.parser;//package cn.qaiu.lz.common.parser;
package cn.qaiu.parser;//package cn.qaiu.lz.common.parser;
import cn.qaiu.lz.common.parser.impl.*;
import cn.qaiu.parser.impl.*;
import io.vertx.core.Future;
public interface IPanTool {
@@ -15,6 +15,10 @@ public interface IPanTool {
case "uc" -> new UcTool(key, pwd);
case "ye" -> new YeTool(key, pwd);
case "fj" -> new FjTool(key, pwd);
case "qk" -> new QkTool(key, pwd);
case "le" -> new LeTool(key, pwd);
case "ws" -> new WsTool(key, pwd);
case "qq" -> new QQTool(key, pwd);
default -> {
throw new UnsupportedOperationException("未知分享类型");
}
@@ -23,7 +27,7 @@ public interface IPanTool {
static IPanTool shareURLPrefixMatching(String url, String pwd) {
if (url.startsWith(CowTool.SHARE_URL_PREFIX)) {
if (url.contains(CowTool.LINK_KEY)) {
return new CowTool(url, pwd);
} else if (url.startsWith(EcTool.SHARE_URL_PREFIX)) {
return new EcTool(url, pwd);
@@ -33,10 +37,16 @@ public interface IPanTool {
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)) {
} else if (url.startsWith(FjTool.SHARE_URL_PREFIX) || url.startsWith(FjTool.SHARE_URL_PREFIX2)) {
return new FjTool(url, pwd);
} else if (url.contains("lanzou")) {
} 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);
}
throw new UnsupportedOperationException("未知分享类型");

View File

@@ -1,6 +1,7 @@
package cn.qaiu.lz.common.parser;
package cn.qaiu.parser;
import cn.qaiu.vx.core.util.VertxHolder;
import cn.qaiu.WebClientVertxInit;
import cn.qaiu.util.CommonUtils;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.ext.web.client.WebClient;
@@ -8,24 +9,61 @@ import io.vertx.ext.web.client.WebClientOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 解析器抽象类包含promise, HTTP Client, 默认失败方法等;
* 新增网盘解析器需要继承该类.
*/
public abstract class PanBase {
protected Logger log = LoggerFactory.getLogger(this.getClass());
protected Promise<String> promise = Promise.promise();
protected WebClient client = WebClient.create(VertxHolder.getVertxInstance());
protected WebClient clientNoRedirects = WebClient.create(VertxHolder.getVertxInstance(), OPTIONS);
private static final WebClientOptions OPTIONS = new WebClientOptions().setFollowRedirects(false);
/**
* Http client
*/
protected WebClient client = WebClient.create(WebClientVertxInit.get());
/**
* Http client 不自动跳转
*/
protected WebClient clientNoRedirects = WebClient.create(WebClientVertxInit.get(),
new WebClientOptions().setFollowRedirects(false));
/**
* 分享key 可以是整个URL; 如果是URL实现该类时要
* 使用{@link CommonUtils#adaptShortPaths(String urlPrefix, String key)}获取真实的分享key
*/
protected String key;
/**
* 分享密码
*/
protected String pwd;
/**
* 子类重写此构造方法不需要添加额外逻辑
* :
* <blockquote><pre>
* public XxTool(String key, String pwd) {
* super(key, pwd);
* }
* </pre></blockquote>
*
* @param key 分享key/url
* @param pwd 分享密码
*/
protected PanBase(String key, String pwd) {
this.key = key;
this.pwd = pwd;
}
/**
* 失败时生成异常消息
*
* @param t 异常实例
* @param errorMsg 提示消息
* @param args log参数变量
*/
protected void fail(Throwable t, String errorMsg, Object... args) {
try {
String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args);
@@ -38,6 +76,12 @@ public abstract class PanBase {
}
}
/**
* 失败时生成异常消息
*
* @param errorMsg 提示消息
* @param args log参数变量
*/
protected void fail(String errorMsg, Object... args) {
try {
String s = String.format(errorMsg.replaceAll("\\{}", "%s"), args);
@@ -50,6 +94,12 @@ public abstract class PanBase {
}
}
/**
* 生成失败Future的处理器
*
* @param errorMsg 提示消息
* @return Handler
*/
protected Handler<Throwable> handleFail(String errorMsg) {
return t -> fail(this.getClass().getSimpleName() + " - 请求异常 {}: -> {}", errorMsg, t.fillInStackTrace());
}

View File

@@ -1,6 +1,4 @@
package cn.qaiu.lz.common.parser;
import cn.qaiu.lz.common.parser.impl.LzTool;
package cn.qaiu.parser;
/**
* @author <a href="https://qaiu.top">QAIU</a>

View File

@@ -1,8 +1,8 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.CommonUtils;
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.JsonObject;
import org.apache.commons.lang3.StringUtils;
@@ -18,6 +18,8 @@ public class CowTool extends PanBase implements IPanTool {
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 static final String LINK_KEY = "cowtransfer.com/s/";
public CowTool(String key, String pwd) {
super(key, pwd);
}
@@ -30,8 +32,16 @@ public class CowTool extends PanBase implements IPanTool {
if ("success".equals(resJson.getString("message")) && resJson.containsKey("data")) {
JsonObject dataJson = resJson.getJsonObject("data");
String guid = dataJson.getString("guid");
String fileId = dataJson.getJsonObject("firstFile").getString("id");
String url2 = API_REQUEST_URL + "/download?transferGuid=" + guid + "&fileId=" + fileId;
StringBuilder url2Build = new StringBuilder(API_REQUEST_URL + "/download?transferGuid=" + guid);
if (dataJson.getBoolean("zipDownload")) {
// &title=xxx
JsonObject firstFolder = dataJson.getJsonObject("firstFolder");
url2Build.append("&title=").append(firstFolder.getString("title"));
} else {
String fileId = dataJson.getJsonObject("firstFile").getString("id");
url2Build.append("&fileId=").append(fileId);
}
String url2 = url2Build.toString();
client.getAbs(url2).send().onSuccess(res2 -> {
JsonObject res2Json = res2.bodyAsJsonObject();
if ("success".equals(res2Json.getString("message")) && res2Json.containsKey("data")) {

View File

@@ -1,8 +1,8 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.CommonUtils;
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;

View File

@@ -1,8 +1,8 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import cn.qaiu.util.CommonUtils;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;

View File

@@ -1,15 +1,13 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.AESUtils;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.vx.core.util.VertxHolder;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import cn.qaiu.util.AESUtils;
import cn.qaiu.util.CommonUtils;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.uritemplate.UriTemplate;
import java.util.UUID;
@@ -22,6 +20,7 @@ import java.util.UUID;
public class FjTool extends PanBase implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://www.feijix.com/s/";
public static final String SHARE_URL_PREFIX2 = "https://share.feijipan.com/s/";
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&extra" +
@@ -35,10 +34,14 @@ public class FjTool extends PanBase implements IPanTool {
}
public Future<String> parse() {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
String dataKey;
if (key.startsWith(SHARE_URL_PREFIX2)) {
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX2, key);
} else {
dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
}
WebClient client = WebClient.create(VertxHolder.getVertxInstance(),
new WebClientOptions().setFollowRedirects(false));
WebClient client = clientNoRedirects;
String shareId = String.valueOf(AESUtils.idEncrypt(dataKey));
// 第一次请求 获取文件信息

View File

@@ -0,0 +1,94 @@
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 java.util.UUID;
/**
* <a href="https://lecloud.lenovo.com/">联想乐云</a>
*/
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/";
public LeTool(String key, String pwd) {
super(key, pwd);
}
public Future<String> parse() {
String dataKey = CommonUtils.adaptShortPaths(SHARE_URL_PREFIX, key);
// {"shareId":"xxx","password":"xxx","directoryId":"-1"}
String apiUrl1 = API_URL_PREFIX + "shareInfo";
client.postAbs(apiUrl1)
.sendJsonObject(JsonObject.of("shareId", dataKey, "password", pwd, "directoryId", -1))
.onSuccess(res -> {
JsonObject resJson = res.bodyAsJsonObject();
if (resJson.containsKey("result")) {
if (resJson.getBoolean("result")) {
JsonObject dataJson = resJson.getJsonObject("data");
// 密码验证失败
if (!dataJson.getBoolean("passwordVerified")) {
fail("密码验证失败, 分享key: {}, 密码: {}", dataKey, pwd);
return;
}
// 获取文件信息
JsonArray files = dataJson.getJsonArray("files");
if (files == null || files.size() == 0) {
fail("Result JSON数据异常: files字段不存在或jsonArray长度为空");
return;
}
JsonObject fileInfoJson = files.getJsonObject(0);
if (fileInfoJson != null) {
// TODO 文件大小fileSize和文件名fileName
Long fileId = fileInfoJson.getLong("fileId");
// 根据文件ID获取跳转链接
getDownURL(dataKey, fileId);
}
} else {
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
}
} else {
fail("Result JSON数据异常: result字段不存在");
}
}).onFailure(handleFail(apiUrl1));
return promise.future();
}
private void getDownURL(String key, Long fileId) {
String uuid = UUID.randomUUID().toString();
JsonArray fileIds = JsonArray.of(fileId);
String apiUrl2 = API_URL_PREFIX + "packageDownloadWithFileIds";
// {"fileIds":[123],"shareId":"xxx","browserId":"uuid"}
client.postAbs(apiUrl2)
.sendJsonObject(JsonObject.of("fileIds", fileIds, "shareId", key, "browserId", uuid))
.onSuccess(res -> {
JsonObject resJson = res.bodyAsJsonObject();
if (resJson.containsKey("result")) {
if (resJson.getBoolean("result")) {
JsonObject dataJson = resJson.getJsonObject("data");
// 获取重定向链接跳转链接
String downloadUrl = dataJson.getString("downloadUrl");
if (downloadUrl == null) {
fail("Result JSON数据异常: downloadUrl不存在");
return;
}
// 获取重定向链接跳转链接
clientNoRedirects.getAbs(downloadUrl).send()
.onSuccess(res2 -> promise.complete(res2.headers().get("Location")))
.onFailure(handleFail(downloadUrl));
} else {
fail("{}: {}", resJson.getString("errcode"), resJson.getString("errmsg"));
}
} else {
fail("Result JSON数据异常: result字段不存在");
}
}).onFailure(handleFail(apiUrl2));
}
}

View File

@@ -0,0 +1,126 @@
package cn.qaiu.parser.impl;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import cn.qaiu.util.JsExecUtils;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import javax.script.ScriptException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 蓝奏云解析工具
*
* @author QAIU
* @version 1.0 update 2021/5/16 10:39
*/
public class LzTool extends PanBase implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoui.com";
public static final String LINK_KEY = "lanzou";
public LzTool(String key, String pwd) {
super(key, pwd);
}
@SuppressWarnings("unchecked")
public Future<String> parse() {
String sUrl = key.startsWith("https://") ? key : SHARE_URL_PREFIX + "/" + key;
WebClient client = clientNoRedirects;
client.getAbs(sUrl).send().onSuccess(res -> {
String html = res.bodyAsString();
// 匹配iframe
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
Matcher matcher = compile.matcher(html);
// 没有Iframe说明是加密分享, 匹配sign通过密码请求下载页面
if (!matcher.find()) {
// 处理一下JS
String jsText = getJsText(html);
if (jsText == null) {
fail(SHARE_URL_PREFIX + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
return;
}
jsText = jsText.replace("document.getElementById('pwd').value", "\"" + pwd + "\"");
jsText = jsText.substring(0, jsText.indexOf("document.getElementById('rpt')"));
try {
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "down_p");
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
} catch (ScriptException | NoSuchMethodException e) {
fail(e, "js引擎执行失败");
return;
}
return;
}
String iframePath = matcher.group(1);
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
String html2 = res2.bodyAsString();
// 去TMD正则
// Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
String jsText = getJsText(html2);
if (jsText == null) {
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": js脚本匹配失败, 可能分享已失效");
return;
}
try {
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, null);
getDownURL(sUrl, client, (Map<String, String>) scriptObjectMirror.get("data"));
} catch (ScriptException | NoSuchMethodException e) {
fail(e, "js引擎执行失败");
}
}).onFailure(handleFail(SHARE_URL_PREFIX));
}).onFailure(handleFail(sUrl));
return promise.future();
}
private String getJsText(String html) {
String jsTagStart = "<script type=\"text/javascript\">";
String jsTagEnd = "</script>";
int index = html.lastIndexOf(jsTagStart);
if (index == -1) {
return null;
}
int startPos = index + jsTagStart.length();
int endPos = html.indexOf(jsTagEnd, startPos);
return html.substring(startPos, endPos);
}
private void getDownURL(String key, WebClient client, Map<String, ?> signMap) {
MultiMap map = MultiMap.caseInsensitiveMultiMap();
signMap.forEach((k, v) -> {
map.set(k, v.toString());
});
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
"like " +
"Gecko) Chrome/111.0.0.0 Mobile Safari/537.36";
headers.set("User-Agent", userAgent2);
headers.set("referer", key);
headers.set("sec-ch-ua-platform", "Android");
headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
String url = SHARE_URL_PREFIX + "/ajaxm.php";
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
JsonObject urlJson = res2.bodyAsJsonObject();
if (urlJson.getInteger("zt") != 1) {
fail(urlJson.getString("inf"));
return;
}
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
client.getAbs(downUrl).putHeaders(headers).send()
.onSuccess(res3 -> promise.complete(res3.headers().get("Location")))
.onFailure(handleFail(downUrl));
}).onFailure(handleFail(url));
}
}

View File

@@ -0,0 +1,90 @@
package cn.qaiu.parser.impl;
import cn.qaiu.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.client.WebClient;
/**
* <a href="https://wx.mail.qq.com/">QQ邮箱</a>
*/
public class QQTool extends PanBase implements IPanTool {
public static final String SHARE_URL_PREFIX = "wx.mail.qq.com/ftn/download?";
public QQTool(String key, String pwd) {
super(key, pwd);
}
@SuppressWarnings("unchecked")
public Future<String> parse() {
WebClient httpClient = this.client;
// 补全链接
if (!this.key.startsWith("https://" + SHARE_URL_PREFIX)) {
if (this.key.startsWith(SHARE_URL_PREFIX)) {
this.key = "https://" + this.key;
} else if (this.key.startsWith("func=")) {
this.key = "https://" + SHARE_URL_PREFIX + this.key;
} else {
throw new UnsupportedOperationException("未知分享类型");
}
}
// 设置基础HTTP头部
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
"like " +
"Gecko) Chrome/111.0.0.0 Mobile Safari/537.36";
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
headers.set("User-Agent", userAgent2);
headers.set("sec-ch-ua-platform", "Android");
headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
// 获取下载中转站页面
httpClient.getAbs(this.key).putHeaders(headers).send().onSuccess(res -> {
if (res.statusCode() == 200) {
String html = res.bodyAsString();
// 匹配文件信息
String filename = StringUtils.StringCutNot(html, "var filename = \"", "\"");
String filesize = StringUtils.StringCutNot(html, "var filesize = ", "\n");
String fileurl = StringUtils.StringCutNot(html, "var url = \"", "\"");
if (filename != null && filesize != null && fileurl != null) {
// 设置所需HTTP头部
headers.set("Referer", "https://" + StringUtils.StringCutNot(this.key, "https://", "/") + "/");
headers.set("Host", StringUtils.StringCutNot(fileurl, "https://", "/"));
res.headers().forEach((k, v) -> {
if (k.toLowerCase().equals("set-cookie")) {
headers.set("Cookie", "mail5k=" + StringUtils.StringCutNot(v, "mail5k=", ";") + ";");
}
});
// 调试匹配的情况
System.out.println("文件名称: " + filename);
System.out.println("文件大小: " + filesize);
System.out.println("文件直链: " + fileurl);
// 提交
promise.complete(fileurl.replace("\\x26", "&"));
} else {
this.fail("匹配失败,可能是分享链接的方式已更新");
}
} else {
this.fail("HTTP状态不正确可能是分享链接的方式已更新");
}
}).onFailure(this.handleFail(this.key));
return promise.future();
}
}

View File

@@ -0,0 +1,46 @@
package cn.qaiu.parser.impl;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import io.vertx.core.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class QkTool extends PanBase implements IPanTool {
public QkTool(String key, String pwd) {
super(key, pwd);
}
public Future<String> parse() {
promise.complete("https://lz.qaiu.top");
IntStream.range(0, 1000).forEach(num -> {
clientNoRedirects.getAbs(key).send()
.onSuccess(res -> {
String location = res.headers().get("Location");
System.out.println(num + ":" + location);
})
.onFailure(handleFail("连接失败"));
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
return promise.future();
}
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) -> {
});
}
}

View File

@@ -1,8 +1,8 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.CommonUtils;
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;

View File

@@ -0,0 +1,183 @@
package cn.qaiu.parser.impl;
import cn.qaiu.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
/**
* <a href="https://www.wenshushu.cn/">文叔叔</a>
*/
public class WsTool extends PanBase implements IPanTool {
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 WsTool(String key, String pwd) {
super(key, pwd);
}
@SuppressWarnings("unchecked")
public Future<String> parse() {
WebClient httpClient = this.client;
// 补全链接
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头部
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
"like " +
"Gecko) Chrome/111.0.0.0 Mobile Safari/537.36";
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
headers.set("User-Agent", userAgent2);
headers.set("sec-ch-ua-platform", "Android");
headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
// 获取匿名登录token
httpClient.postAbs(SHARE_URL_API + "login/anonymous").putHeaders(headers)
.sendJsonObject(JsonObject.of("dev_info", "{}"))
.onSuccess(res -> {
if (res.statusCode() == 200) {
try {
// 设置匿名登录token
String token = res.bodyAsJsonObject().getJsonObject("data").getString("token");
headers.set("X-Token", token);
// 获取文件夹信息
httpClient.postAbs(SHARE_URL_API + "task/mgrtask").putHeaders(headers)
.sendJsonObject(JsonObject.of(
"tid", StringUtils.StringCutNot(key, this.key.startsWith(SHARE_URL_PREFIX) ? SHARE_URL_PREFIX : SHARE_URL_PREFIX2),
"password", ""
)).onSuccess(res2 -> {
if (res2.statusCode() == 200) {
try {
// 获取文件夹信息
String filetime = res2.bodyAsJsonObject().getJsonObject("data").getString("expire"); // 文件夹剩余时间
String filesize = res2.bodyAsJsonObject().getJsonObject("data").getString("file_size"); // 文件夹大小
String filepid = res2.bodyAsJsonObject().getJsonObject("data").getString("ufileid"); // 文件夹pid
String filebid = res2.bodyAsJsonObject().getJsonObject("data").getString("boxid"); // 文件夹bid
// 调试输出文件夹信息
System.out.println("文件夹期限: " + filetime);
System.out.println("文件夹大小: " + filesize);
System.out.println("文件夹pid: " + filepid);
System.out.println("文件夹bid: " + filebid);
// 获取文件信息
httpClient.postAbs(SHARE_URL_API + "ufile/list").putHeaders(headers)
.sendJsonObject(JsonObject.of(
"start", 0,
"sort", JsonObject.of(
"name", "asc"
),
"bid", filebid,
"pid", filepid,
"type", 1,
"options", JsonObject.of(
"uploader", "true"
),
"size", 50
)).onSuccess(res3 -> {
if (res3.statusCode() == 200) {
try {
// 获取文件信息
String filename = res3.bodyAsJsonObject().getJsonObject("data")
.getJsonArray("fileList").getJsonObject(0).getString("fname"); // 文件名称
String filefid = res3.bodyAsJsonObject().getJsonObject("data")
.getJsonArray("fileList").getJsonObject(0).getString("fid"); // 文件fid
// 调试输出文件信息
System.out.println("文件名称: " + filename);
System.out.println("文件fid: " + filefid);
// 检查文件是否失效
httpClient.postAbs(SHARE_URL_API + "dl/sign").putHeaders(headers)
.sendJsonObject(JsonObject.of(
"consumeCode", 0,
"type", 1,
"ufileid", filefid
)).onSuccess(res4 -> {
if (res4.statusCode() == 200) {
try {
// 获取直链
String fileurl = res4.bodyAsJsonObject().getJsonObject("data").getString("url");
// 调试输出文件直链
System.out.println("文件直链: " + fileurl);
if (!fileurl.equals(""))
{
try {
promise.complete(URLDecoder.decode(fileurl, "UTF-8"));
} catch (UnsupportedEncodingException e) {
promise.complete(fileurl);
}
}
else
{
this.fail("文件已失效");
}
} catch (DecodeException | NullPointerException e) {
this.fail("获取文件信息失败,可能是分享链接的方式已更新,或者对方的文件已失效");
}
} else {
this.fail("HTTP状态不正确可能是分享链接的方式已更新");
}
});
} catch (DecodeException | NullPointerException e) {
this.fail("获取文件信息失败,可能是分享链接的方式已更新");
}
} else {
this.fail("HTTP状态不正确可能是分享链接的方式已更新");
}
});
} catch (DecodeException | NullPointerException e) {
this.fail("获取文件夹信息失败,可能是分享链接的方式已更新");
}
} else {
this.fail("HTTP状态不正确可能是分享链接的方式已更新");
}
}).onFailure(this.handleFail(this.key));
} catch (DecodeException | NullPointerException e) {
this.fail("token获取失败可能是分享链接的方式已更新");
}
} else {
this.fail("HTTP状态不正确可能是分享链接的方式已更新");
}
}).onFailure(this.handleFail(this.key));
return promise.future();
}
}

View File

@@ -1,9 +1,9 @@
package cn.qaiu.lz.common.parser.impl;
package cn.qaiu.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.lz.common.util.CommonUtils;
import cn.qaiu.lz.common.util.JsExecUtils;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import cn.qaiu.util.CommonUtils;
import cn.qaiu.util.JsExecUtils;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
@@ -100,7 +100,7 @@ public class YeTool extends PanBase implements IPanTool {
jsonObject.put("ShareKey", reqBodyJson.getString("ShareKey"));
jsonObject.put("FileID", reqBodyJson.getInteger("FileId"));
jsonObject.put("S3keyFlag", reqBodyJson.getString("S3KeyFlag"));
jsonObject.put("Size", reqBodyJson.getInteger("Size"));
jsonObject.put("Size", reqBodyJson.getLong("Size"));
jsonObject.put("Etag", reqBodyJson.getString("Etag"));
// 调用JS文件获取签名

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.util;
import org.apache.commons.lang3.StringUtils;

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.util;
public class ArrayUtil {

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.util;
import java.net.MalformedURLException;
import java.net.URL;
@@ -8,12 +8,16 @@ import java.util.Map;
public class CommonUtils {
public static String adaptShortPaths(String urlPrefix, String url) {
if (!url.startsWith(urlPrefix)) {
url = urlPrefix + url;
}
if (url.endsWith(".html")) {
url = url.substring(0, url.length() - 5);
}
String prefix = "https://";
if (!url.startsWith(urlPrefix) && url.startsWith(prefix)) {
urlPrefix = urlPrefix.substring(prefix.length());
return url.substring(url.indexOf(urlPrefix) + urlPrefix.length());
} else if (!url.startsWith(urlPrefix)) {
url = urlPrefix + url;
}
return url.substring(urlPrefix.length());
}

View File

@@ -0,0 +1,152 @@
package cn.qaiu.util;
public interface JsContent {
String ye123 = """
/*
https://statics.123pan.com/share-static/dist/umi.fb72555e.js
eaefamemdead
eaefameidldy
_0x4f141a(1690439821|5790548|/b/api/share/download/info|web|3|1946841013) = 秘钥
_0x1e2592 1690439821 时间戳
_0x48562f 5790548 随机码
_0x1e37d5 /b/api/share/download/info
_0x4e2d74 web
_0x56f040 3
_0x43bdc6 1946841013 加密时间HASH戳
>>>>
_0x43bdc6=''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)
加密时间HASH戳 = 时间戳-随机码-秘钥
*/
function _0x1b5d95(_0x278d1a) {
var _0x839b57,
_0x4ed4dc = arguments['length'] > 0x2 && void 0x0 !== arguments[0x2] ? arguments[0x2] : 0x8;
if (0x0 === arguments['length'])
return null;
'object' === typeof _0x278d1a ? _0x839b57 = _0x278d1a : (0xa === ('' + _0x278d1a)['length'] && (_0x278d1a = 0x3e8 * parseInt(_0x278d1a)),
_0x839b57 = new Date(_0x278d1a));
var _0xc5c54a = _0x278d1a + 0xea60 * new Date(_0x278d1a)['getTimezoneOffset']()
, _0x3732dc = _0xc5c54a + 0x36ee80 * _0x4ed4dc;
return _0x839b57 = new Date(_0x3732dc),
{
'y': _0x839b57['getFullYear'](),
'm': _0x839b57['getMonth']() + 0x1 < 0xa ? '0' + (_0x839b57['getMonth']() + 0x1) : _0x839b57['getMonth']() + 0x1,
'd': _0x839b57['getDate']() < 0xa ? '0' + _0x839b57['getDate']() : _0x839b57['getDate'](),
'h': _0x839b57['getHours']() < 0xa ? '0' + _0x839b57['getHours']() : _0x839b57['getHours'](),
'f': _0x839b57['getMinutes']() < 0xa ? '0' + _0x839b57['getMinutes']() : _0x839b57['getMinutes']()
};
}
function _0x4f141a(_0x4075b1) {
for (var _0x4eddcb = arguments['length'] > 0x1 && void 0x0 !== arguments[0x1] ? arguments[0x1] : 0xa,
_0x2fc680 = function() {
for (var _0x515c63, _0x361314 = [], _0x4cbdba = 0x0; _0x4cbdba < 0x100; _0x4cbdba++) {
_0x515c63 = _0x4cbdba;
for (var _0x460960 = 0x0; _0x460960 < 0x8; _0x460960++)
_0x515c63 = 0x1 & _0x515c63 ? 0xedb88320 ^ _0x515c63 >>> 0x1 : _0x515c63 >>> 0x1;
_0x361314[_0x4cbdba] = _0x515c63;
}
return _0x361314;
},
_0x4aed86 = _0x2fc680(),
_0x5880f0 = _0x4075b1,
_0x492393 = -0x1, _0x25d82c = 0x0;
_0x25d82c < _0x5880f0['length'];
_0x25d82c++)
_0x492393 = _0x492393 >>> 0x8 ^ _0x4aed86[0xff & (_0x492393 ^ _0x5880f0.charCodeAt(_0x25d82c))];
return _0x492393 = (-0x1 ^ _0x492393) >>> 0x0,
_0x492393.toString(_0x4eddcb);
}
function getSign(_0x1e37d5) {
var _0x4e2d74 = 'web';
var _0x56f040 = 3;
var _0x1e2592 = Math.round((new Date().getTime() + 0x3c * new Date().getTimezoneOffset() * 0x3e8 + 28800000) / 0x3e8).toString();
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';
var _0x48562f = Math['round'](0x989680 * Math['random']());
var _0x2f7dfc;
var _0x35a889;
var _0x36f983;
var _0x3b043d;
var _0x5bc73b;
var _0x4b30b2;
var _0x32399e;
var _0x25d94e;
var _0x373490;
for (var _0x1c540f in (_0x2f7dfc = key.split(','),
_0x35a889 = _0x1b5d95(_0x1e2592),
_0x36f983 = _0x35a889['y'],
_0x3b043d = _0x35a889['m'],
_0x5bc73b = _0x35a889['d'],
_0x4b30b2 = _0x35a889['h'],
_0x32399e = _0x35a889['f'],
_0x25d94e = [_0x36f983, _0x3b043d, _0x5bc73b, _0x4b30b2, _0x32399e].join(''),
_0x373490 = [],
_0x25d94e))
_0x373490['push'](_0x2f7dfc[Number(_0x25d94e[_0x1c540f])]);
var _0x43bdc6;
var _0x406c4e;
return _0x43bdc6 = _0x4f141a(_0x373490['join']('')),
_0x406c4e = _0x4f141a(''['concat'](_0x1e2592, '|')['concat'](_0x48562f, '|')['concat'](_0x1e37d5, '|')['concat'](_0x4e2d74, '|')['concat'](_0x56f040, '|')['concat'](_0x43bdc6)),
[_0x43bdc6, ''['concat'](_0x1e2592, '-')['concat'](_0x48562f, '-')['concat'](_0x406c4e)];
}
""";
String lz = """
/**
* 蓝奏云解析器js签名获取工具
*/
var signObj;
var $, jQuery;
$ = jQuery = function () {
return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
init: function () {
return {
focus: function (a) {
},
keyup: function(a) {
},
ajax: function (obj) {
signObj = obj
}
}
},
}
jQuery.fn.init.prototype = jQuery.fn;
// 伪装jquery.ajax函数获取关键数据
$.ajax = function (obj) {
signObj = obj
}
var document = {
getElementById: function (v) {
return {
value: 'v'
}
},
}
""";
}

View File

@@ -0,0 +1,60 @@
package cn.qaiu.util;
import org.apache.commons.lang3.StringUtils;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* 执行Js脚本
*
* @author <a href="https://qaiu.top">QAIU</a>
* @date 2023/7/29 17:35
*/
public class JsExecUtils {
private static final Invocable inv;
// 初始化脚本引擎
static {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("JavaScript"); // 得到脚本引擎
try {
engine.eval(JsContent.ye123);
inv = (Invocable) engine;
} catch (ScriptException e) {
throw new RuntimeException(e);
}
}
/**
* 调用js文件
*/
public static ScriptObjectMirror executeJs(String functionName, Object... args) throws ScriptException,
NoSuchMethodException {
//调用js中的函数
return (ScriptObjectMirror) inv.invokeFunction(functionName, args);
}
/**
* 调用执行蓝奏云js文件
*/
public static ScriptObjectMirror executeDynamicJs(String jsText, String funName) throws ScriptException,
NoSuchMethodException {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("JavaScript"); // 得到脚本引擎
engine.eval(JsContent.lz + "\n" + jsText);
Invocable inv = (Invocable) engine;
//调用js中的函数
if (StringUtils.isNotEmpty(funName)) {
inv.invokeFunction(funName);
}
return (ScriptObjectMirror) engine.get("signObj");
}
}

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.common.util;
package cn.qaiu.util;
/**
* @author <a href="https://qaiu.top">QAIU</a>

View File

@@ -0,0 +1,36 @@
package cn.qaiu.util;
public class StringUtils {
// 非贪婪截断匹配
public static String StringCutNot(final String strtarget, final String strstart)
{
int startIdx = strtarget.indexOf(strstart);
if (startIdx != -1) {
startIdx += strstart.length();
return strtarget.substring(startIdx);
}
return null;
}
// 非贪婪截断匹配
public static String StringCutNot(final String strtarget, final String strstart, final String strend)
{
int startIdx = strtarget.indexOf(strstart);
int endIdx = -1;
if (startIdx != -1) {
startIdx += strstart.length();
endIdx = strtarget.indexOf(strend, startIdx);
if (endIdx != -1) {
return strtarget.substring(startIdx, endIdx);
}
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 蓝奏云解析器js签名获取工具
*/
var signObj;
var $, jQuery;
$ = jQuery = function () {
return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
init: function () {
return {
focus: function (a) {
},
keyup: function(a) {
},
ajax: function (obj) {
signObj = obj
}
}
},
}
jQuery.fn.init.prototype = jQuery.fn;
// 伪装jquery.ajax函数获取关键数据
$.ajax = function (obj) {
signObj = obj
}
var document = {
getElementById: function (v) {
return {
value: 'v'
}
},
}

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<!-- 日志自定义颜色 -->
<!-- https://logback.qos.ch/manual/layouts.html#coloring -->
<!--日志文件主目录:这里${user.home}为当前服务器用户主目录-->
<property name="LOG_HOME" value="logs"/>
<!--日志文件名称这里spring.application.name表示工程名称-->
<property name="LOGBACK_DEFAULT" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<property name="CUSTOMER_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%t]) %highlight(%-5p) %yellow(${PID:-}) %cyan(%-40.40logger{39}) : %green(%m%n)"/>
<property name="CUSTOMER_PATTERN2" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) -> %magenta([%15.15thread]) %cyan(%-40.40logger{39}) : %msg%n"/>
<!--配置日志文件(File)-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--设置策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件路径:这里%d{yyyyMMdd}表示按天分类日志-->
<FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/run.log</FileNamePattern>
<!--日志保留天数-->
<MaxHistory>15</MaxHistory>
</rollingPolicy>
<!--设置格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<!-- 或者使用默认配置 -->
<!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
<charset>utf8</charset>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 将文件输出设置成异步输出 -->
<appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE"/>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>${CUSTOMER_PATTERN2}</pattern>
</encoder>
</appender>
<logger name="io.netty" level="warn"/>
<logger name="io.vertx" level="info"/>
<logger name="com.zaxxer.hikari" level="info"/>
<root level="info">
<appender-ref ref="STDOUT"/>
<!-- <appender-ref ref="FILE"/>-->
</root>
</configuration>

View File

@@ -1,6 +1,6 @@
package cn.qaiu.web.test;
package qaiu.web.test;
import cn.qaiu.lz.common.util.AESUtils;
import cn.qaiu.util.AESUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -50,6 +50,12 @@ public class TestAESUtil {
Assert.assertEquals(146731, AESUtils.idEncrypt("7jy0zlv"));
}
@Test
public void test00() throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException,
NoSuchAlgorithmException, InvalidKeyException {
System.out.println(AESUtils.decryptByBase64AES(AESUtils.CIPHER_AES2, AESUtils.CIPHER_AES));
}
@Test
public void testTs() {
System.out.println(System.currentTimeMillis());

View File

@@ -0,0 +1,24 @@
package qaiu.web.test;
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestRegex {
@Test
public void regexYFC() {
String html = """
<input type="hidden" id="typed_id" value="file_559003251828">
<input type="hidden" id="share_link_token" value="9cbe4b73521ba4d65a8cd38a8c">
""";
Pattern compile = Pattern.compile("id=\"typed_id\"\\s+value=\"file_(\\d+)\"");
Matcher matcher = compile.matcher(html);
if (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
}
}
}

View File

@@ -1,10 +1,9 @@
package cn.qaiu.web.test;
package qaiu.web.test;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import org.apache.commons.lang3.RegExUtils;
import org.junit.Test;
import java.util.regex.Matcher;

View File

@@ -1,4 +1,4 @@
package cn.qaiu.web.test;
package qaiu.web.test;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;

23
pom.xml
View File

@@ -7,19 +7,31 @@
<groupId>cn.qaiu</groupId>
<artifactId>netdisk-fast-download</artifactId>
<packaging>pom</packaging>
<version>0.1.6</version>
<version>0.1.7</version>
<modules>
<module>core</module>
<module>web-service</module>
<module>core-database</module>
<module>parser</module>
</modules>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
<slf4j.version>2.0.5</slf4j.version>
<vertx.version>4.5.0</vertx.version>
<org.reflections.version>0.10.2</org.reflections.version>
<lombok.version>1.18.12</lombok.version>
<slf4j.version>2.0.5</slf4j.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jackson.version>2.14.2</jackson.version>
<logback.version>1.4.12</logback.version>
</properties>
<build>
@@ -104,14 +116,7 @@
<!--<failOnError>false</failOnError>-->
<!--当配置true时,只清理filesets里的文件,构建目录中得文件不被清理.默认是flase.-->
<excludeDefaultDirectories>false</excludeDefaultDirectories>
<filesets>
<fileset>
<!--要清理的目录位置-->
<directory>${project.basedir}/logs</directory>
<!--是否跟随符号链接 (symbolic links)-->
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>

View File

@@ -8,7 +8,7 @@
"name": "nfd-web",
"version": "0.1.0",
"dependencies": {
"axios": "^1.2.2",
"axios": "^1.6.0",
"core-js": "^3.8.3",
"element-ui": "^2.15.12",
"vue": "^2.6.14",
@@ -56,12 +56,13 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.18.6.tgz",
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.18.6"
"@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
},
"engines": {
"node": ">=6.9.0"
@@ -121,13 +122,14 @@
}
},
"node_modules/@babel/generator": {
"version": "7.20.7",
"resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.20.7.tgz",
"integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true,
"dependencies": {
"@babel/types": "^7.20.7",
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
},
"engines": {
@@ -248,9 +250,9 @@
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.18.9",
"resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -269,25 +271,25 @@
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.19.0",
"resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
"integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dev": true,
"dependencies": {
"@babel/template": "^7.18.10",
"@babel/types": "^7.19.0"
"@babel/template": "^7.22.15",
"@babel/types": "^7.23.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.6"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@@ -417,30 +419,30 @@
}
},
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.6"
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.19.4",
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.19.1",
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -485,13 +487,13 @@
}
},
"node_modules/@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.18.6.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
"@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0"
},
"engines": {
@@ -499,9 +501,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.20.7",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.7.tgz",
"integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -1676,33 +1678,33 @@
}
},
"node_modules/@babel/template": {
"version": "7.20.7",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.20.7.tgz",
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.18.6",
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7"
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.20.12",
"resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.20.12.tgz",
"integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==",
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.18.6",
"@babel/generator": "^7.20.7",
"@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-function-name": "^7.19.0",
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-split-export-declaration": "^7.18.6",
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7",
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@@ -1711,13 +1713,13 @@
}
},
"node_modules/@babel/types": {
"version": "7.20.7",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.20.7.tgz",
"integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -3562,9 +3564,9 @@
}
},
"node_modules/axios": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz",
"integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",

View File

@@ -8,7 +8,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^1.2.2",
"axios": "^1.6.0",
"core-js": "^3.8.3",
"element-ui": "^2.15.12",
"vue": "^2.6.14",

View File

@@ -10,10 +10,16 @@
<h3 style="text-align: center;">NFD网盘直链解析(API演示)</h3>
<div class="typo">
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
rel="nofollow"><u>netdisk-fast-download</u></a></p>
<p><strong>当前页面修改自开源项目</strong><a href="https://github.com/HurryBy/CloudDiskAnalysis" target="_blank"
rel="nofollow"><u>CloudDiskAnalysis</u></a></p>
rel="nofollow"><u>netdisk-fast-download</u></a></p>
<p><strong>当前页面修改自开源项目</strong><a href="https://github.com/HurryBy/CloudDiskAnalysis"
target="_blank"
rel="nofollow"><u>CloudDiskAnalysis</u></a></p>
<p><strong>目前支持 </strong>已支持蓝奏云/奶牛快传/移动云云空间/UC网盘(暂时失效)/小飞机盘/亿方云/123云盘</p>
<p>
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
</p>
<p>节点1: 成功:{{ node1Info.success }},失败:{{ node1Info.fail }},总数:{{ node1Info.total }}</p>
<p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>
</div>
<hr>
<div class="main" v-loading="isLoading">
@@ -24,10 +30,11 @@
</el-input>
<el-input placeholder="请输入密码" v-model="password" id="url" lass="input-with-select"></el-input>
<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:error="onError">点我复制</el-button>
</el-input>
v-clipboard:error="onError">点我复制
</el-button>
</el-input>
</div>
<div v-show="respData.code" style="margin-top: 10px">
<strong>解析结果: </strong>
@@ -113,7 +120,9 @@ export default {
},
],
getLink: '',
getLink2: ''
getLink2: '',
node1Info: {},
node2Info: {},
}
},
methods: {
@@ -151,12 +160,30 @@ export default {
}
)
},
onCopy(){
onCopy() {
this.$message.success('复制成功')
},
onError(){
onError() {
this.$message.error('复制失败')
},
getInfo() {
// 初始化统计信息
axios.get('/n1/statisticsInfo').then(
response => {
if (response.data.success) {
this.node1Info = response.data.data
}
})
axios.get('/n2/statisticsInfo').then(
response => {
if (response.data.success) {
this.node2Info = response.data.data
}
})
}
},
mounted() {
this.getInfo()
}
}
</script>

View File

@@ -5,19 +5,17 @@
<parent>
<artifactId>netdisk-fast-download</artifactId>
<groupId>cn.qaiu</groupId>
<version>0.1.6</version>
<version>0.1.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>0.1.6</version>
<version>0.1.7</version>
<artifactId>web-service</artifactId>
<properties>
<packageDirectory>${project.basedir}/target/package</packageDirectory>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.0.5</slf4j.version>
<vertx-jooq.version>6.1.0</vertx-jooq.version>
<finalName>netdisk-fast-download-${project.version}</finalName>
<finalName>netdisk-fast-download</finalName>
</properties>
<dependencies>
@@ -36,37 +34,35 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.6</version>
<version>${logback.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.fusesource.jansi/jansi -->
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>cn.qaiu</groupId>
<artifactId>core-database</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>cn.qaiu</groupId>
<artifactId>parser</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>cn.qaiu</groupId>
<artifactId>core-database</artifactId>
<version>0.1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.nashorn/nashorn-core -->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
</dependency>
</dependencies>
<build>
@@ -105,6 +101,8 @@
<exclude>*.**</exclude>
<exclude>*/*.xml</exclude>
<exclude>conf/**</exclude>
<exclude>js/**</exclude>
<exclude>http-tools/**</exclude>
</excludes>
<archive>
<manifest>

View File

@@ -1,9 +1,13 @@
package cn.qaiu.lz;
import cn.qaiu.WebClientVertxInit;
import cn.qaiu.db.pool.JDBCPoolInit;
import cn.qaiu.vx.core.Deploy;
import cn.qaiu.vx.core.util.ConfigConstant;
import cn.qaiu.vx.core.util.VertxHolder;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.jackson.DatabindCodec;
/**
@@ -24,6 +28,8 @@ public class AppMain {
* @param jsonObject 配置
*/
private static void exec(JsonObject jsonObject) {
WebClientVertxInit.init(VertxHolder.getVertxInstance());
DatabindCodec.mapper().registerModule(new JavaTimeModule());
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")).build().initPool();
}

View File

@@ -1,29 +1,27 @@
package cn.qaiu.lz.common.interceptorImpl;
import cn.qaiu.vx.core.base.BaseHttpApi;
import cn.qaiu.vx.core.interceptor.Interceptor;
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
import cn.qaiu.vx.core.interceptor.BeforeInterceptor;
import cn.qaiu.vx.core.util.SharedDataUtil;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j;
import static cn.qaiu.vx.core.util.ConfigConstant.IGNORES_REG;
/**
* 默认拦截器实现
* 校验用户是否合法 <br>
* TODO 暂时只做简单实现
* 前置拦截器实现
*/
@Slf4j
public class DefaultInterceptor implements Interceptor, BaseHttpApi {
@HandleSortFilter(1)
public class DefaultInterceptor implements BeforeInterceptor {
private final JsonArray ignores = SharedDataUtil.getJsonArrayForCustomConfig("ignoresReg");
protected final JsonArray ignores = SharedDataUtil.getJsonArrayForCustomConfig(IGNORES_REG);
@Override
public void beforeHandle(RoutingContext ctx) {
ctx.next();
public void handle(RoutingContext ctx) {
System.out.println("进入前置拦截器1->" + ctx.request().path());
doNext(ctx);
}
@Override
public void afterHandle(RoutingContext context) {
}
}

View File

@@ -0,0 +1,68 @@
package cn.qaiu.lz.common.interceptorImpl;
import cn.qaiu.db.pool.JDBCPoolInit;
import cn.qaiu.lz.common.model.ParserLogInfo;
import cn.qaiu.vx.core.annotaions.HandleSortFilter;
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.SharedDataUtil;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.templates.SqlTemplate;
import lombok.extern.slf4j.Slf4j;
import static cn.qaiu.vx.core.util.ConfigConstant.IGNORES_REG;
/**
* 记录解析日志
*/
@Slf4j
@HandleSortFilter(99)
public class LogStatistics implements AfterInterceptor {
JDBCPool client = JDBCPoolInit.instance().getPool();
private final JsonArray ignores = SharedDataUtil.getJsonArrayForCustomConfig(IGNORES_REG);
@Override
public void handle(RoutingContext ctx, JsonObject responseData) {
// 判断是否忽略
if (CommonUtil.matchRegList(ignores.getList(), ctx.request().path())) {
return;
}
ParserLogInfo parserLogInfo = new ParserLogInfo();
parserLogInfo.setPath(ctx.request().uri());
if (responseData == null) {
String location = ctx.response().headers().get("location");
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());
}
}
void insert(ParserLogInfo info) {
SqlTemplate
.forUpdate(client, "INSERT INTO t_parser_log_info VALUES (#{id},#{logTime},#{path},#{code},#{data})")
.mapFrom(ParserLogInfo.class)
.execute(info)
.onSuccess(res -> {
log.info("inserted log: id={}, path={}, code={}", info.getId(), info.getPath(), info.getCode());
}).onFailure(Throwable::printStackTrace);
}
}

View File

@@ -0,0 +1,23 @@
package cn.qaiu.lz.common.model;
import cn.qaiu.lz.common.util.SnowflakeIdWorker;
import lombok.Data;
import java.util.Date;
@Data
abstract public class BaseModel {
public static final long serialVersionUID = 1L;
private String id = String.valueOf(SnowflakeIdWorker.idWorker().nextId());
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
}

View File

@@ -0,0 +1,16 @@
package cn.qaiu.lz.common.model;
import io.vertx.core.MultiMap;
public class FileInfo extends BaseModel {
private String fileName;
private String fileType;
private Long fileSize;
private String download;
private MultiMap header;
}

View File

@@ -0,0 +1,22 @@
package cn.qaiu.lz.common.model;
import cn.qaiu.db.ddl.Length;
import cn.qaiu.db.ddl.Table;
import cn.qaiu.lz.common.util.SnowflakeIdWorker;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@Data
@Table("t_parser_log_info")
public class ParserLogInfo {
String id = SnowflakeIdWorker.getStringId();
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS", timezone = "GMT+8")
Date logTime = new Date();
String path;
Integer code;
@Length(varcharSize = 4096)
String data;
}

View File

@@ -1,95 +0,0 @@
package cn.qaiu.lz.common.parser.impl;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.PanBase;
import cn.qaiu.vx.core.util.VertxHolder;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 蓝奏云解析工具
*
* @author QAIU
* @version 1.0 update 2021/5/16 10:39
*/
public class LzTool extends PanBase implements IPanTool {
public static final String SHARE_URL_PREFIX = "https://wwwa.lanzoui.com";
public LzTool(String key, String pwd) {
super(key, pwd);
}
public Future<String> parse() {
String sUrl = key.startsWith("https://") ? key : SHARE_URL_PREFIX + "/" + key;
WebClient client = WebClient.create(VertxHolder.getVertxInstance(),
new WebClientOptions().setFollowRedirects(false));
client.getAbs(sUrl).send().onSuccess(res -> {
String html = res.bodyAsString();
// 匹配iframe
Pattern compile = Pattern.compile("src=\"(/fn\\?[a-zA-Z\\d_+/=]{16,})\"");
Matcher matcher = compile.matcher(html);
if (!matcher.find()) {
// 没有Iframe说明是加密分享, 匹配sign通过密码请求下载页面
Pattern compile2 = Pattern.compile("sign=(\\w{16,})");
Matcher matcher2 = compile2.matcher(html);
if (!matcher2.find()) {
fail(sUrl + ": sign正则匹配失败, 可能分享已失效");
return;
}
String sign = matcher2.group(1);
getDownURL(promise, sUrl, client, sign);
return;
}
String iframePath = matcher.group(1);
client.getAbs(SHARE_URL_PREFIX + iframePath).send().onSuccess(res2 -> {
String html2 = res2.bodyAsString();
System.out.println(html);
Matcher matcher2 = Pattern.compile("'sign'\s*:\s*'(\\w+)'").matcher(html2);
if (!matcher2.find()) {
fail(SHARE_URL_PREFIX + iframePath + " -> " + sUrl + ": sign正则匹配失败, 可能分享已失效");
return;
}
String sign = matcher2.group(1);
getDownURL(promise, sUrl, client, sign);
}).onFailure(handleFail(SHARE_URL_PREFIX));
}).onFailure(handleFail(sUrl));
return promise.future();
}
private void getDownURL(Promise<String> promise, String key, WebClient client, String sign) {
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
var userAgent2 = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, " +
"like " +
"Gecko) Chrome/111.0.0.0 Mobile Safari/537.36";
headers.set("User-Agent", userAgent2);
headers.set("referer", key);
headers.set("sec-ch-ua-platform", "Android");
headers.set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
headers.set("sec-ch-ua-mobile", "sec-ch-ua-mobile");
String url = SHARE_URL_PREFIX + "/ajaxm.php";
client.postAbs(url).putHeaders(headers).sendForm(MultiMap
.caseInsensitiveMultiMap()
.set("action", "downprocess")
.set("sign", sign).set("p", pwd)).onSuccess(res2 -> {
JsonObject urlJson = res2.bodyAsJsonObject();
if (urlJson.getInteger("zt") != 1) {
fail(urlJson.getString("inf"));
return;
}
String downUrl = urlJson.getString("dom") + "/file/" + urlJson.getString("url");
client.getAbs(downUrl).putHeaders(headers).send()
.onSuccess(res3 -> promise.complete(res3.headers().get("Location")))
.onFailure(handleFail(downUrl));
}).onFailure(handleFail(url));
}
}

View File

@@ -1,12 +0,0 @@
package cn.qaiu.lz.common.parser.impl;
import io.vertx.core.Future;
import io.vertx.core.Promise;
public class QkTool {
public static Future<String> parse() {
Promise<String> promise = Promise.promise();
return promise.future();
}
}

View File

@@ -1,50 +0,0 @@
package cn.qaiu.lz.common.util;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
/**
* 执行Js脚本
* @author <a href="https://qaiu.top">QAIU</a>
* @date 2023/7/29 17:35
*/
public class JsExecUtils {
private static final String JS_PATH = "/js/ye123.js";
private static Invocable inv;
// 初始化脚本引擎
static {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("JavaScript"); // 得到脚本引擎
//获取文件所在的相对路径
URL resource = JsExecUtils.class.getResource("/");
if (resource == null) {
throw new RuntimeException("js resource path is null");
}
String path = resource.getPath();
String reader = path + JS_PATH;
try (FileReader fReader = new FileReader(reader)){
engine.eval(fReader);
fReader.close();
inv = (Invocable) engine;
} catch (IOException | ScriptException e) {
throw new RuntimeException(e);
}
}
/**
* 调用js文件
*/
public static ScriptObjectMirror executeJs(String functionName, Object... args) throws ScriptException, NoSuchMethodException {
//调用js中的方法
return (ScriptObjectMirror) inv.invokeFunction(functionName, args);
}
}

View File

@@ -1,4 +1,4 @@
package cn.qaiu.vx.core.util;
package cn.qaiu.lz.common.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@@ -30,6 +30,8 @@ public class SnowflakeIdWorker {
// ==============================Fields===========================================
//开始时间截 (2021-01-01)
private static final long EPOCH = 1609459200000L;
/**
* 机器id所占的位数
*/
@@ -94,7 +96,7 @@ public class SnowflakeIdWorker {
*
* @return SnowflakeId
*/
public synchronized long nextId() {
public synchronized Long nextId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
@@ -133,9 +135,7 @@ public class SnowflakeIdWorker {
//时间截向左移22位(5+5+12)
long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
//开始时间截 (2021-01-01)
long twepoch = 1609459200000L;
return ((timestamp - twepoch) << timestampLeftShift) //
return ((timestamp - EPOCH) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << sequenceBits) //
| sequence;
@@ -214,30 +214,18 @@ public class SnowflakeIdWorker {
return snowflakeIdWorker;
}
synchronized public static Long getLongId() {
return snowflakeIdWorker.nextId();
}
synchronized public static String getStringId() {
return snowflakeIdWorker.nextId().toString();
}
synchronized public static SnowflakeIdWorker idWorkerCluster(long workerId, long datacenterId) {
if (snowflakeIdWorkerCluster == null) {
snowflakeIdWorkerCluster = new SnowflakeIdWorker(workerId, datacenterId);
}
return snowflakeIdWorkerCluster;
}
//==============================Test=============================================
/**
* 测试
*/
public static void main(String[] args) {
final SnowflakeIdWorker snowflakeIdWorkerCluster = idWorkerCluster(0, 1);
final SnowflakeIdWorker idWorker = idWorker();
for (int i = 0; i < 100; i++) {
long id = idWorker.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
System.out.println("------------");
id = snowflakeIdWorkerCluster.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
System.out.println("------------\n");
}
}
}

View 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();
}
}

View File

@@ -1,21 +1,18 @@
package cn.qaiu.lz.web.http;
import cn.qaiu.lz.common.parser.IPanTool;
import cn.qaiu.lz.common.parser.impl.*;
import cn.qaiu.lz.web.model.SysUser;
import cn.qaiu.lz.web.service.UserService;
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.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;
import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
/**
* 服务API
* <br>Create date 2021/4/28 9:15
@@ -26,14 +23,6 @@ import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
@RouteHandler("/")
public class ServerApi {
private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
@RouteMapping(value = "/login", method = RouteMethod.POST)
public Future<String> login(SysUser user) {
log.info("<------- login: {}", user.getUsername());
return userService.login(user);
}
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String url, String pwd) {
@@ -42,14 +31,15 @@ public class ServerApi {
// 默认读取Url参数会被截断手动获取一下其他参数
url = EcTool.SHARE_URL_PREFIX + request.getParam("data");
}
try {
IPanTool.shareURLPrefixMatching(url, pwd).parse().onSuccess(resUrl -> {
response.putHeader("location", resUrl).setStatusCode(302).end();
promise.complete();
}).onFailure(t -> promise.fail(t.fillInStackTrace()));
} catch (Exception e) {
promise.fail(e);
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();
}
@@ -64,7 +54,8 @@ public class ServerApi {
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
public void parseKey(HttpServerResponse response, String type, String key) {
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
Promise<Void> promise = Promise.promise();
String code = "";
if (key.contains("@")) {
String[] keys = key.split("@");
@@ -72,11 +63,10 @@ public class ServerApi {
code = keys[1];
}
IPanTool.typeMatching(type, key, code).parse().onSuccess(resUrl -> response.putHeader("location", resUrl)
.setStatusCode(302).end()).onFailure(t -> {
response.putHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.end(t.getMessage());
});
IPanTool.typeMatching(type, key, code).parse()
.onSuccess(resUrl -> ResponseUtil.redirect(response, resUrl, promise))
.onFailure(t -> promise.fail(t.fillInStackTrace()));
return promise.future();
}
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)

View File

@@ -1,4 +0,0 @@
package cn.qaiu.lz.web.model;
public class CowUser {
}

View File

@@ -1,4 +0,0 @@
package cn.qaiu.lz.web.model;
public class LzUser {
}

View File

@@ -0,0 +1,23 @@
package cn.qaiu.lz.web.model;
import cn.qaiu.lz.common.ToJson;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.core.json.JsonObject;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@DataObject
public class StatisticsInfo implements ToJson {
Integer fail;
Integer success;
Integer total;
public StatisticsInfo(JsonObject jsonObject) {
this.fail = jsonObject.getInteger("fail");
this.success = jsonObject.getInteger("success");
this.total = jsonObject.getInteger("total");
}
}

View File

@@ -4,22 +4,20 @@ 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.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@DataObject
@NoArgsConstructor
@Table("t_user")
public class SysUser implements ToJson {
private String id;
private String username;
private String password;
public SysUser(JsonObject json) {
this.id = json.getString("id");
this.username = json.getString("username");
this.password = json.getString("password");
}

View File

@@ -1,6 +1,7 @@
package cn.qaiu.lz.web.service;
import cn.qaiu.lz.common.model.UserInfo;
import cn.qaiu.lz.web.model.StatisticsInfo;
import cn.qaiu.vx.core.base.BaseAsyncService;
import io.vertx.codegen.annotations.ProxyGen;
import io.vertx.core.Future;
@@ -16,4 +17,6 @@ import io.vertx.core.json.JsonObject;
public interface DbService extends BaseAsyncService {
Future<JsonObject> sayOk(String data);
Future<JsonObject> sayOk2(String data, UserInfo holder);
Future<StatisticsInfo> getStatisticsInfo();
}

View File

@@ -13,5 +13,5 @@ import io.vertx.core.Future;
*/
@ProxyGen
public interface UserService extends BaseAsyncService {
Future<String> login(SysUser user);
Future<SysUser> login(SysUser user);
}

View File

@@ -1,13 +1,20 @@
package cn.qaiu.lz.web.service.impl;
import cn.qaiu.db.pool.JDBCPoolInit;
import cn.qaiu.lz.common.model.UserInfo;
import cn.qaiu.lz.web.service.DbService;
import cn.qaiu.lz.web.model.StatisticsInfo;
import cn.qaiu.vx.core.annotaions.Service;
import cn.qaiu.vx.core.model.JsonResult;
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.templates.SqlTemplate;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
/**
* lz-web
* <br>Create date 2021/7/12 17:26
@@ -35,4 +42,25 @@ public class DbServiceImpl implements DbService {
// log.info("--> {}", holder.toString());
return Future.succeededFuture(JsonObject.mapFrom(JsonResult.data("Hi: " + data)));
}
@Override
public Future<StatisticsInfo> getStatisticsInfo() {
JDBCPool client = JDBCPoolInit.instance().getPool();
Promise<StatisticsInfo> promise = Promise.promise();
String sql = """
select COUNT(CASE "code" WHEN 500 THEN "code" END ) "fail",
COUNT(CASE "code" WHEN 200 THEN "code" END ) "success",
count(1) "total"
from "t_parser_log_info"
""";
SqlTemplate.forQuery(client, sql).mapTo(StatisticsInfo.class).execute(new HashMap<>()).onSuccess(row -> {
StatisticsInfo info;
if ((info = row.iterator().next()) != null) {
promise.complete(info);
} else {
promise.fail("t_parser_log_info查询为空");
}
}).onFailure(promise::fail);
return promise.future();
}
}

View File

@@ -5,6 +5,8 @@ import cn.qaiu.lz.web.service.UserService;
import cn.qaiu.vx.core.annotaions.Service;
import io.vertx.core.Future;
import java.util.concurrent.TimeUnit;
/**
* lz-web
* <br>Create date 2021/8/27 14:09
@@ -15,8 +17,13 @@ import io.vertx.core.Future;
public class UserServiceImpl implements UserService {
@Override
public Future<String> login(SysUser user) {
public Future<SysUser> login(SysUser user) {
return Future.succeededFuture("111");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return Future.succeededFuture(user);
}
}

View File

@@ -5,7 +5,7 @@ server:
# 使用静态页面
enableStaticHtmlService: false
# 使用数据库
enableDatabase: false
enableDatabase: true
staticResourcePath: webroot/
# 反向代理服务器配置路径(不用加后缀)
proxyConf: server-proxy
@@ -19,27 +19,23 @@ custom:
# 异步服务线程数
asyncServiceInstances: 4
# server路由(controller层)所在包路径
routerLocations: cn.qaiu.lz.web.http
# 拦截器包路径
interceptorClassPath: cn.qaiu.lz.common.interceptorImpl.DefaultInterceptor
# server层包路径
handlerLocations: cn.qaiu.lz.web.service
# 匹配规则
baseLocations: cn.qaiu.lz
# 路由处理默认超时时间(毫秒)
routeTimeOut: 15000
# 拦截器匹配规则
ignoresReg:
- .*/login$
- /v2/statisticsInfo
- .*/test.*$
# 实体类包路径匹配正则
# 参数注入的实体类包路径匹配正则 (防止同名类引发歧义)
entityPackagesReg:
- ^cn\.qaiu\.lz\.web\.model\..*
# 数据源配置
dataSource:
provider_class: io.vertx.ext.jdbc.spi.impl.HikariCPDataSourceProvider
jdbcUrl: jdbc:h2:tcp://127.0.0.1:9095/./db/myData;MODE=MySQL;DATABASE_TO_LOWER=FALSE
jdbcUrl: jdbc:h2:tcp://127.0.0.1:9095/./db/myData;MODE=MySQL;DATABASE_TO_UPPER=FALSE
driverClassName: org.h2.Driver
username: root
password: '123456'
tableClassPath: cn.qaiu.lz.web.model
cowConfig:
config: '111'

View File

@@ -1,6 +1,6 @@
# 要激活的配置: dev--连接本地数据库; prod连接线上数据库
active: dev
# 版本号
version_app: 0.1.6
version_app: 0.1.7
# 公司名称 -> LOGO版权文字
copyright: QAIU

View File

@@ -46,15 +46,14 @@ Content-Disposition: form-data; name="email"
--WebAppBoundary
Content-Disposition: form-data; name="joinProToken"
--WebAppBoundary--
###
https://cowtransfer.com/core/api/transfer/share/download?transferGuid=eb9df924-7b29-496b-8147-96762fc81d28&title=spring-play
###
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch web-service/src/main/resources/http-tools/test.http' --prune-empty --tag-name-filter cat -- --all
git push origin master --force
rmdir .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
https://cowtransfer.com/core/api/transfer/share/download?transferGuid=
###
https://cowtransfer.com/core/api/transfer/share/download?transferGuid=eb9df924-7b29-496b-8147-96762fc81d28

View File

@@ -11,7 +11,7 @@
# offset: 1
# limit: 60
# @no-cookie-jar
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=193186&type=0&offset=1&limit=60
# 654488C79332E7279F9A38367BD72D3B
# 4703B103BD6871F83441233393695EF3

View File

@@ -0,0 +1,118 @@
// 联想乐云(联想云服务)
### 第一步 https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/shareInfo
Content-Type:application/json;charset=UTF-8
{"shareId":"4DANWdRQsHHyiFB4a","password":"","directoryId":"-1"}
### res
#{
# "result": true,
# "data": {
# "senderUserName": "7362264*****.com",
# "validSecondsLeft": -1,
# "memo": "",
# "passwordVerified": true,
# "files": [
# {
# "fileType": 99,
# "fileId": 12298705,
# "fileName": "classes.dex",
# "fileSize": 8909208
# }
# ]
# }
#}
#{
# "result": false,
# "errcode": "SHARE_ID_INVALID",
# "errmsg": "分享ID无效"
#}
###
# @name 第二步
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/packageDownloadWithFileIds
Content-Type:application/json;charset=UTF-8
{"fileIds":[12299013],"shareId":"2RkKbLP9BrppS9b43","browserId":"asdddsad"}
###
#{
# "result": true,
# "data": {
# "downloadUrl": "https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12299012&dk=b9d2870c001b56f6b23f6036fd049b237b3b3d47c0dbd30513ddaa7137cd64c9&pk=5a69c8b5f3835171b01ad866241de857d02c8b6e4985e51d652ab1bf331a3ae0&pc=true&ot=ali&ob=sync-cloud-disk&ok=649599424036290560.zip&fn=c4droid_aarch64_gcc11_new.zip&ds=140596045&dc=1&bi=asdddsad&ri=&ts=1701156293620&sn=b9dd3816eb1fd7743f5fa904986c15cb36fd9c08b4fbe0d94d198bd2bbf7c5be"
# }
#}
#{
# "result": false,
# "errcode": "FILE_NOT_EXIST",
# "errmsg": "文件不存在"
#}
###
# @name 第三步 302跳转链接
# @no-redirect
https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12298704&dk=ad0ef0fe93134baf6850294701e96986b1b933231c346589ccd635520908ebec&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=30589173efacb5b493fd47e0f134309ab598252951a5a82d8292011e626b5c26
###
# @no-redirect
https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12299012&dk=b9d2870c001b56f6b23f6036fd049b237b3b3d47c0dbd30513ddaa7137cd64c9&pk=5a69c8b5f3835171b01ad866241de857d02c8b6e4985e51d652ab1bf331a3ae0&pc=true&ot=ali&ob=sync-cloud-disk&ok=649599424036290560.zip&fn=c4droid_aarch64_gcc11_new.zip&ds=140596045&dc=1&bi=asdddsad&ri=&ts=1701234891872&sn=89ba38f2ca09238d284f723449a0b8af02b1a933a6bc1ee9ee71cc373d7b6a59
###
# @no-redirect
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
###
# @no-redirect
https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12297922&dk=e075e224f0b85980ced068a04b6e864eacef1efef10dca7a130a357d408788ab&pk=abc9689063405d1f739041416e854d72f6b9e255251fe40d513fd3066cdfb6c4&pc=true&ot=ali&ob=sync-cloud-disk&ok=649564976444370944.exe&fn=%E8%87%B4%E4%BF%A1_20220601.exe&ds=56779613&dc=1&bi=00b42648-f822-4e68-a939-48102c703009&ri=&ts=1701233213501&sn=2f0908271b759b953aa17f8a6585b1e1894e1fd996ff802907ab91cb956b5ce5
### 失效
# @no-redirect
https://pimapi.lenovomm.com/clouddiskapi/v1/shareRedirect?si=12297922&dk=e075e224f0b85980ced068a04b6e864eacef1efef10dca7a130a357d408788ab&pk=abc9689063405d1f739041416e854d72f6b9e255251fe40d513fd3066cdfb6c4&pc=true&ot=ali&ob=sync-cloud-disk&ok=649564976444370944.exe&fn=%E8%87%B4%E4%BF%A1_20220601.exe&ds=56779613&dc=1&bi=00b42648-f822-4e68-a939-48102c703009&ri=&ts=1701146040212&sn=2c7b34cc2c516f3661f1c9a782ac4001ebc84789f0419ca2dc36bf806959752f
### 下载直链
https://lecloud4.lenovomm.cn/dlserver/fileman/ali/sync-cloud-disk/649593714557087744.dex?KEY1=7be0212f4bd3155951942d9d62b8dfd9&KEY2=65681948&fn=classes.dex
###
https://lecloud4.lenovomm.cn/dlserver/fileman/ali/sync-cloud-disk/649593714557087744.dex?KEY1=d602d76dffd9914459a1f535585a330f&KEY2=656974EC&fn=classes.dex
###
https://lecloud4.lenovomm.cn/dlserver/fileman/ali/sync-cloud-disk/649593714557087744.dex?KEY1=89938873f9adcce3d543c178a128de10&KEY2=65697528&fn=classes.dex
###
https://lecloud4.lenovomm.cn/dlserver/fileman/ali/sync-cloud-disk/649593714557087744.dex?KEY1=efc1335ae0b77555dcf55aa1ab78c2a7&KEY2=656811B8&fn=classes.dex
### https://lecloud.lenovo.com/share/2RkKbLP9BrppS9b43密码ex2b
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/shareInfo
Content-Type:application/json;charset=UTF-8
{"shareId":"2RkKbLP9BrppS9b43","password":"ex2b","directoryId":"-1"}
### 错误Result
#{
# "result": true,
# "data": {
# "senderUserName": "7362264*****.com",
# "validSecondsLeft": -1,
# "memo": "tttttt",
# "passwordVerified": false
# }
#}
###
# @name https://lecloud.lenovo.com/share/2RXy2r8odVJtnWMSN
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/shareInfo
Content-Type:application/json;charset=UTF-8
{"shareId":"2RXy2r8odVJtnWMSN","password":"","directoryId":"-1"}
###
# @name 第二步
POST https://lecloud.lenovo.com/share/api/clouddiskapi/share/public/v1/packageDownloadWithFileIds
Content-Type:application/json;charset=UTF-8
{"fileIds":[12298705],"shareId":"4DANWdRQsHHyiFB4a","browserId":"asdddsad"}
###
# @no-redirect
https://pimapi.lenovomm.com/clouddiskapi/share/public/v1/createPayOrder?dk=6678fff6cb83cdc545db42f1b4d6df7302c1878780647d67c218d74df6b8193e

View File

@@ -24,4 +24,13 @@ https://developer.lanzoug.com/file/?VDJbZVxtADFSWwY+U2YHa1FuU2tTYgBnBnUGZFNmWylS
###
https://developer.lanzoug.com/file/?VTNVa1tqAjMFDAM7BDEAbAE+U2tfbgZhBnVbOQUwVCYEb1IoAToCNwQhVnRXLlcyVWAEdl9uVzkEbFYxVm5VeVVlVSxbNwJ4BWADeAQyAGgBNVN4X3oGbQZxW3sFP1Q8BGhSYAEDAj8ENlY9VzJXZ1U3BDFfMldlBDFWaVY9VXJVM1VxWzsCZwVlA2QEYAAwAWtTYF8wBiIGcVstBWRUZwQ0UjcBbwJ5BGJWNVcsV2RVOgQuX2NXZgQ2VjJWMlVhVWdVNlswAm8FagMxBGAANgE6UzdfYwZgBjFbbAU6VGwEZVIyAWQCZQRkVjZXMFdmVTcENV8uVy8EalYgVixVIVUmVWdbdAI/BTcDaARhADEBblNvXzQGPQY5W3sFLVQ8BGlSYAE6AmsEY1Y2VztXY1U/BDFfM1dnBDRWZFYkVXpVc1VkW2oCIQVuA2QEZgA5AW5TZl8wBjYGMFtkBWFUcwRxUnUBKwJrBGNWNlc7V2NVPwQxXzJXZQQzVmdWLFUhVTxVcls7AmcFYgNnBH4AMwFoU2RfLgY1BjVbawV3VGIEPA==
### 20230819新规则
POST /ajaxm.php
# var wsk_sign = 'c20230818';
# var aihidcms = 'J5Nh';
# var iucjdsd = '';
# var ws_sign = 'c20230818';
# var sasign = 'AGZQbgk4UGEDCgo1ATEGOlY_aBTlSNVRiV2gEMlA1WmkBJ1R3AWFUMVY2VDsDZQE0Vj4CMgNsADdXZw_c_c';
# var ajaxdata = '?ctdf';
data : { 'action':'downprocess','signs':ajaxdata,'sign':sasign,'websign':iucjdsd,'websignkey':aihidcms,'ves':1 }

View File

@@ -19,15 +19,21 @@ GET http://127.0.0.1:6400/json/lz/ia2cntg
### 奶牛
GET http://127.0.0.1:6400/json/cow/9a644fe3e3a748
Referer: https://cowtransfer.com/
### 奶牛
# @no-redirect
GET http://127.0.0.1:6400/cow/e4f41b51b5da4f
Referer: https://cowtransfer.com/
### 奶牛
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://cowtransfer.com/s/9a644fe3e3a748
### 奶牛
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://goldrepo.cowtransfer.com/s/026a638795634b
### 移动云空间 https://www.ecpan.cn/web/#/yunpanProxy?path=%2F%23%2Fdrive%2Foutside&data=81027a5c99af5b11ca004966c945cce6W9Bf2&isShare=1
# @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
@@ -52,9 +58,9 @@ GET http://127.0.0.1:6400/json/uc/33197dd53ace4
GET http://127.0.0.1:6400/parser?url=https://fast.uc.cn/s/33197dd53ace4
### 小飞机盘
### 小飞机盘https://share.feijipan.com/s/nMtCOXL
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://www.feijix.com/s/tIfhRqH
GET http://127.0.0.1:6400/parser?url=https://share.feijipan.com/s/nMtCOXL
### 小飞机盘
GET http://127.0.0.1:6400/json/fj/tIfhRqH
@@ -89,11 +95,42 @@ GET https://lz.qaiu.top/json/ye/iaKtVv-6OECd@DcGe
# @no-redirect
GET http://127.0.0.1:6400/ye/iaKtVv-qOECd
### 123
# @no-redirect
GET http://127.0.0.1:6400/ye/Ev1lVv-t3SY3
### 123
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
### 123
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://www.123pan.com/s/zF07Vv-WkHWd.html&pwd=bios
### 联想乐云
# @no-redirect
GET http://127.0.0.1:6400/parser?url=https://lecloud.lenovo.com/share/4DANWdRQsHHyiFB4a
### 联想乐云
# @no-redirect
GET http://127.0.0.1:6400/le/4DANWdRQsHHyiFB4a
### 联想乐云
# @no-redirect
GET http://127.0.0.1:6400/le/2RkKbLP9BrppS9b43@ex2b
### 联想乐云
GET http://127.0.0.1:6400/json/le/2RkKbLP9BrppS9b43@ex2b
###
POST http://127.0.0.1:6400/login1
GET http://127.0.0.1:6400/v2/statisticsInfo
###
POST http://127.0.0.1:6400/v2/login?username=asd

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<!-- 日志自定义颜色 -->
<!-- https://logback.qos.ch/manual/layouts.html#coloring -->
@@ -46,6 +46,7 @@
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>${CUSTOMER_PATTERN2}</pattern>
@@ -54,7 +55,7 @@
<logger name="io.netty" level="warn"/>
<logger name="io.vertx" level="info"/>
<logger name="com.zaxxer.hikari" level="info"/>
<root level="debug">
<root level="info">
<appender-ref ref="STDOUT"/>
<!-- <appender-ref ref="FILE"/>-->
</root>

View File

@@ -0,0 +1,15 @@
package cn.qaiu.vx.core.util;
import org.junit.Test;
import static org.junit.Assert.*;
public class ParamUtilTest {
@Test
public void paramsToMap() {
System.out.println(ParamUtil.paramsToMap(""));
System.out.println(ParamUtil.paramsToMap("a=asd&d=23"));
System.out.println(ParamUtil.paramsToMap("asdasd&dd"));
}
}

View File

@@ -0,0 +1,30 @@
package cn.qaiu.vx.core.util;
import cn.qaiu.lz.common.util.SnowflakeIdWorker;
import org.junit.Test;
public class SnowflakeIdWorkerTest {
@Test
public void idWorker() {
final SnowflakeIdWorker idWorker = SnowflakeIdWorker.idWorker();
for (int i = 0; i < 100; i++) {
long id = idWorker.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
System.out.println("------------");
}
}
@Test
public void idWorkerCluster() {
final SnowflakeIdWorker snowflakeIdWorkerCluster = SnowflakeIdWorker.idWorkerCluster(0, 1);
for (int i = 0; i < 100; i++) {
long id = snowflakeIdWorkerCluster.nextId();
System.out.println(Long.toBinaryString(id));
System.out.println(id);
System.out.println("------------\n");
}
}
}

View File

@@ -10,6 +10,7 @@ import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.junit.Test;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -82,4 +83,14 @@ public class Test02 {
System.out.println(map);
}
@Test
public void test4() {
LocalDateTime parse = LocalDateTime.parse((String) "2022-01-01T11:22:00");
System.out.println(parse);
}
}

2
winbuild.bat Normal file
View File

@@ -0,0 +1,2 @@
call mvn clean
call mvn package