Compare commits

...

133 Commits
2.8 ... 4.0.0

Author SHA1 Message Date
zhaojun
3b6ed1c78d fix: 修改数据库和日志等文件默认路径为新版路径 2022-07-10 21:56:43 +08:00
zhaojun
5d888bb68f 🔖 发布 4.0.0 版本 2022-07-10 21:44:14 +08:00
zhaojun
082bb07213 fix: 修复 webdav 账号密码错误问题 2022-06-16 20:11:54 +08:00
zhaojun
803b8cdf71 🔖 发布 3.2.5 版本 2022-05-16 21:41:59 +08:00
zhaojun
eca5f7e48b feature: 新增挂载 webdav client pom 依赖 2022-05-16 21:41:36 +08:00
zhaojun
b70c37f3f0 feature: 新增挂载 webdav 功能 2022-05-16 21:40:58 +08:00
zhaojun
5cb2844141 🐛 修复错误的 content_disposition 和 contentType 导致下载文件格式被浏览器识别错误 2022-05-16 20:50:49 +08:00
赵俊
4dd6cdb4b3 合并拉取请求 #348
fix: modify extract driveId regex in WebDavController
2022-05-03 21:16:40 +08:00
quericy
c6127c029f fix: modify extract driveId regex in WebDavController 2022-04-14 10:06:46 +08:00
zhaojun
e149039ecb 🔖 发布 3.2.3 版本 2022-04-13 00:03:49 +08:00
zhaojun
37688d83cf feature: 降低 webdav 日志级别 2022-04-13 00:03:28 +08:00
zhaojun
6692016642 feature: 新增 webdav 开关功能 2022-04-13 00:01:59 +08:00
zhaojun
3f41aeda9a fix(bug): 修复因字段名称修改升级后导致短链异常 bug 2022-04-12 23:38:49 +08:00
赵俊
de86d5c47d 合并拉取请求 #347
feat: support webdav
2022-04-11 09:56:11 +08:00
quericy
c89e072005 feat: support webdav
1, feat:config webDav and milton in configuration;
2, feat:add event handler in WebDavController;
3, feat:check auth by admin in systemConfig;
4, feat:download webDav file by redirect to site DirectLink;
2022-04-10 04:11:34 +08:00
zhaojun
b533b5e959 🔖 发布 3.2.2 版本 2022-04-02 18:50:50 +08:00
zhaojun
d35cf27d47 fix(bug): ftp 默认为被动模式,防止因端口不同,无法读取。 2022-04-02 18:50:26 +08:00
zhaojun
796c4c1fb0 页面更新 2022-04-02 18:49:26 +08:00
zhaojun
1033d6c1a9 fix(bug): 修复某些情况下直链无法正常使用的 BUG 2022-04-02 18:36:44 +08:00
zhaojun
e09c6b4e58 fix(bug): 修复又拍云文件中包含特殊字符时,调用 upyun 接口未进行 url encode 返回 400,影响又拍云单文件直链无法使用。 2022-04-02 18:26:32 +08:00
zhaojun
168b0b08f3 fix(bug): 修复本地存储可以通过特殊路径符获取任意目录的 BUG. 2022-04-02 18:24:49 +08:00
zhaojun
60a6a5348c fix(bug): 修复获取音频信息时,因文件链接 302, 导致无法正常获取音频文件信息的 BUG. 2022-04-02 18:24:00 +08:00
zhaojun
2ec8a5df1f fix(bug): 修复 sharepoint 未自动刷新 accessToken 和 refreshToken 的 bug 2022-02-02 20:50:13 +08:00
zhaojun
47b5f6ac12 🔖 发布 3.2.1 版本 2022-02-02 20:46:45 +08:00
zhaojun
c64c8465f2 fix(bug): 升级相关有漏洞的依赖版本为最新版 2022-02-02 20:46:08 +08:00
zhaojun
f636681dd8 fix(bug): 修复本地存储可通过特殊命令访问到非指定目录的文件 2022-02-02 20:45:05 +08:00
赵俊
eadd2434e0 新增 ZFILE 社区地址 2021-10-30 21:05:11 +08:00
赵俊
b84c0bff42 Merge remote-tracking branch 'origin/master' 2021-10-07 17:53:19 +08:00
赵俊
4cb5b84bfe 🐛 修复 S3 协议无法正常保存的 BUG 2021-10-07 17:52:45 +08:00
赵俊
0351b4401c Merge pull request #286 from pippen57/master
转义SQL关键字, 防止创建表时出现错误
2021-09-25 18:29:42 +08:00
pippen
48cb14be8a 转义SQL关键字, 防止创建表时出现错误 2021-09-25 12:48:31 +08:00
赵俊
eea2ff11f9 ✏️ 修改文档拼写错误 2021-09-21 11:22:00 +08:00
赵俊
325ec1a348 对于因前端 vue history 路由模式下, 直接访问路径时, 出现的 404 响应码, 转化为 200 响应码. (404 下会将请求转发到首页) 2021-09-20 17:06:27 +08:00
赵俊
93205266d3 更新文档 2021-09-20 16:35:30 +08:00
赵俊
6849a4347f 去除自定义 404 错误页面 2021-09-20 16:33:54 +08:00
赵俊
cb5c6a5945 🎨 提交前端页面 2021-09-20 16:33:31 +08:00
赵俊
5ed45c3bb3 🔒 修复因升级 springboot 版本导致的安全和兼容问题 2021-09-20 16:20:25 +08:00
赵俊
4442ec3165 修复因前端 vue history 路由模式下, 直接访问路径时, 请求 /admin/ 下的路由会被权限框架拦截并校验登陆状态, 导致静态页面报错问题. 2021-09-20 16:19:21 +08:00
赵俊
b268a24333 修复因前端 vue history 路由模式下, 直接访问路径时, 接口和路径名冲突问题 2021-09-20 16:17:59 +08:00
赵俊
84f9354d4e 对于因前端 vue history 路由模式下, 直接访问路径时, 出现的 404 响应码, 转化为 200 响应码. (404 下会将请求转发到首页) 2021-09-20 16:17:16 +08:00
赵俊
8dfc4f8004 跨域请求过滤器修改为自注解注册方式 2021-09-20 16:14:45 +08:00
赵俊
1158f5c2b9 🐛 修复 OneDrive 加速域名在直链下未生效的 BUG 2021-09-20 11:18:13 +08:00
赵俊
c5f0e15207 🐛 修复因升级 spring boot 后部分参数过时, 导致的第一次启动无法初始化参数的 BUG 2021-09-19 21:33:07 +08:00
赵俊
68a842ce75 🐛 修复页面路径和接口地址冲突的 BUG 2021-09-19 16:08:19 +08:00
赵俊
3bd4f74dae 🔖 发布 3.2 版本 2021-09-19 15:37:40 +08:00
赵俊
4a0bdc3baf 💬 修改文档和启动日志描述,取消 /#/ 提示 2021-09-19 15:37:10 +08:00
赵俊
603c1b4654 🎨 提交前端页面 2021-09-19 15:36:19 +08:00
赵俊
1136d582df 增加异常处理器,细化异常提示 2021-09-19 14:10:09 +08:00
赵俊
187544fc06 当密码文件无法正常打开时,给予友好提示 2021-09-19 14:09:52 +08:00
赵俊
aa3cde8f59 minio 和 s3 存储引擎支持自定义 region 功能 2021-09-19 11:30:05 +08:00
赵俊
e29a702b6e ✏️ 拼写错误修改 2021-09-19 11:28:40 +08:00
赵俊
e4f663c9f0 🔒 新增自动检测驱动器已存储参数和驱动器支持所有参数比对功能, 防止驱动器新增参数后, 系统已存在的驱动器因 NPE 问题无法正常加载的问题. 2021-09-19 11:27:57 +08:00
赵俊
fb08ef6e78 🐛 如果请求的密码文件链接是 http 的, 且会自动跳转到 https 时, 密码文件无法正常加载的 BUG (原因是 restTemplate http -> https 不会自动重定向) 2021-09-19 10:01:24 +08:00
赵俊
10c465d159 ♻️ 修改 Spring Security 为 Sa-Token 框架. 2021-09-19 09:57:09 +08:00
赵俊
d15d1203b7 跨域配置 2021-09-19 09:56:12 +08:00
赵俊
de48ed3b61 兼容前端 history 模式, 对于 404 请求, 指向到 /index.html 页面 2021-09-19 09:54:23 +08:00
赵俊
d22e2e872a ⬆️ 因旧版本 Spring 的安全问题, 升级 Spring 为 2.5.4 版本, 兼容新版本 2021-09-19 09:53:19 +08:00
赵俊
7da1b454dc 🐛 修复非个人版的 OneDrive CF 加速域名无法正常使用的 BUG 2021-09-18 22:22:50 +08:00
赵俊
463f311dd3 增加本地文件下载/预览行为控制 2021-07-13 21:43:25 +08:00
赵俊
73b42cf654 🔖 发布 3.1 版本 2021-05-30 17:43:37 +08:00
赵俊
1fac59c4cd 🎨 更新静态页面 2021-05-30 17:41:46 +08:00
赵俊
6ed6b4a019 驱动器添加时,默认 ID 修改为 1 2021-05-30 17:40:48 +08:00
赵俊
7bd02437f0 数据库初始化参数增加默认值 2021-05-30 17:40:14 +08:00
赵俊
57aeb5771c 驱动器列表中返回是否已初始化信息 2021-05-30 17:39:40 +08:00
赵俊
695c03a530 Merge remote-tracking branch 'origin/master' 2021-05-30 16:24:58 +08:00
赵俊
6ebc403572 🔧 添加 ZFile Banner 文件 2021-05-30 16:24:28 +08:00
赵俊
7ff6fe43b5 新增自定义直链名称功能功能 2021-05-30 16:23:41 +08:00
赵俊
b34f141181 🔥 移除无用代码 2021-05-30 16:21:11 +08:00
赵俊
87dd7b58d1 🔥 移除无用代码 2021-05-30 16:19:11 +08:00
赵俊
2a949db5d2 m3u8 响应头兼容部分浏览器直接解析 m3u8 文件 2021-05-30 15:53:55 +08:00
赵俊
b889e91fb5 📝 更新文档,更新赞助信息位置 2021-05-14 21:15:54 +08:00
赵俊
b5c757f9f0 📝 更新文档,更新赞助信息位置 2021-04-21 21:04:02 +08:00
赵俊
a465f48b94 📝 更新文档,增加项目 LOGO,修改项目描述信息。 2021-04-21 20:23:54 +08:00
赵俊
797a0a0e4c 新增直链和短链是否显示功能 2021-04-21 20:21:47 +08:00
赵俊
e7ff159b6d 🐛 修复某些下载地址带密钥的存储策略,m3u8 视频无法正常播放的功能 2021-04-11 19:15:50 +08:00
赵俊
a9fbf54bb2 🔊 完善日志输出 2021-04-11 16:02:49 +08:00
赵俊
81f9e262f5 增加获取站点域名方法 2021-04-11 16:00:01 +08:00
赵俊
23bb3960ab 已关闭的驱动器不允许下载 2021-04-11 15:56:08 +08:00
赵俊
c4a17985a4 优化下载地址获取逻辑,直接列表时不直接调用 API 获取下载地址,访问单文件时再调用。 2021-04-11 15:49:33 +08:00
赵俊
75ddcd47f4 :zip: 优化存储器保存逻辑,防止新增加的字段无法正常保存的 BUG。 2021-04-10 20:21:19 +08:00
赵俊
2dd03ae490 🐛 修复 FTP 模式在 Linux 环境下无法正常读取的 BUG 2021-04-10 20:20:03 +08:00
赵俊
5b383c8741 🐛 日志文件无法正常下载功能修复 2021-04-10 20:19:26 +08:00
赵俊
73198d7852 优化本地文件下载功能, 支持断点续传和多线程下载 2021-04-10 18:06:10 +08:00
赵俊
fb0d9721aa OneDrive 和 SharePoint 反代功能 2021-04-10 15:38:16 +08:00
赵俊
b24c663fd6 更新 star 趋势链接 2021-03-27 12:01:37 +08:00
zhaojun1998
6e62cfc84d 📝 更新文档,修改赞助二维码,增加服务器赞助商链接 2021-03-27 11:52:59 +08:00
zhaojun1998
eee22e9dc9 📝 更新文档 2021-03-26 18:24:02 +08:00
zhaojun1998
5109c51ffc 🔖 发布 3.0 版 2021-03-10 21:43:14 +08:00
zhaojun1998
66d6d311ea 🎨 更新静态页面 2021-03-10 21:42:57 +08:00
zhaojun1998
ef7cbdcbd7 🐛 修复旧版本时,直链多次创建后,根据条件查询时数据不唯一的 BUG 2021-03-10 21:42:36 +08:00
zhaojun1998
63bcbebb4b OneDrive 回调地址增加旧版本兼容 2021-03-10 21:41:42 +08:00
zhaojun1998
50ce1bb6db 修改直链 Key 时检测是否重复功能 2021-03-10 21:41:14 +08:00
zhaojun1998
3c88659679 🎨 更新静态页面 2021-03-09 21:14:42 +08:00
zhaojun1998
d79a993eea 新增设置默认打开图片模式功能 2021-03-09 21:14:21 +08:00
zhaojun1998
afafb311b8 合并文件列表和参数信息接口 2021-03-09 21:13:48 +08:00
zhaojun1998
2e280e4931 新增批量删除直链功能,修改直链 Key 功能 2021-03-08 21:12:42 +08:00
zhaojun1998
ed6efac8b7 🐛 修复切换存储策略时,存储参数值匹配错误的 BUG 2021-03-05 23:00:15 +08:00
zhaojun1998
7409df85d7 🐛 修复切换存储器 ID 时,为级联修改或清理缓存、过滤器、短链的问题 2021-03-05 22:59:08 +08:00
zhaojun1998
4d42529c4d 短链生成逻辑优化, 同链接不重复生成 2021-03-05 22:24:28 +08:00
zhaojun1998
65224685c8 短链生成逻辑优化, 同链接不重复生成 2021-03-05 22:23:41 +08:00
zhaojun1998
080a84986e 💩 优化代码, 添加注释, 优化方法命名, 2021-02-24 18:42:17 +08:00
zhaojun1998
3f8beb2f0b debug 模式优化, 增加到系统参数接口中 2021-02-24 18:40:10 +08:00
zhaojun1998
410a87c426 debug 模式优化, 增加到系统参数接口中 2021-02-24 18:39:47 +08:00
zhaojun1998
c14b8343d2 文件解析异常提示信息优化 2021-02-08 17:58:37 +08:00
zhaojun1998
bea440f6c3 文件解析异常提示信息优化 2021-02-08 17:50:25 +08:00
zhaojun1998
6a5fe15121 密码错误提示信息优化 2021-02-08 17:46:29 +08:00
zhaojun1998
e920ab0ec0 🐛 修复因缓存功能导致密码文件失效的 BUG 2021-02-08 17:46:04 +08:00
zhaojun1998
537e3e0563 🐛 修复 S3 协议新增时的 BUG 2021-02-03 22:01:56 +08:00
赵俊
e208dc7c4c Merge pull request #169 from tianyanli/master
修复S3协议没有指定region问题
2021-02-03 21:56:40 +08:00
zhaojun1998
ed64910a53 🔇 无效的驱动器不输出异常信息,仅返回给页面错误消息 2021-02-03 21:06:38 +08:00
zhaojun1998
5b075c3505 完善 SharePoint 支持 2021-02-03 21:05:46 +08:00
zhaojun1998
a8e6d9af6a 🐛 修复某些存储引擎 API 中文件夹不返回时间和大小时,排序的 NPE 问题。 2021-02-03 21:04:28 +08:00
zhaojun1998
2b21d8a73c 完善 SharePoint 自助获取 SiteId 的逻辑 2021-02-03 21:03:28 +08:00
zhaojun1998
e30289d21b 🔖 发布 2.9.0 版 2021-02-01 23:15:29 +08:00
zhaojun1998
3b6e2be7fe 新增 debug 模式, 可访问数据库控制台和重置密码 2021-02-01 23:14:29 +08:00
zhaojun1998
43c12aa8a7 🎨 更新静态页面 2021-02-01 23:13:29 +08:00
zhaojun1998
c03a7710c0 🎨 更新静态页面 2021-02-01 23:13:18 +08:00
zhaojun1998
1833b23d84 新增 sharepoint 及自助获取 siteId 功能 2021-02-01 23:11:49 +08:00
zhaojun1998
f3e393972d Merge remote-tracking branch 'origin/master' 2021-01-23 23:33:21 +08:00
zhaojun1998
4f46c13369 完善异常处理机制,新增异常类 2021-01-23 13:48:04 +08:00
zhaojun1998
f181959218 支持 SharePoint 2021-01-23 13:46:31 +08:00
zhaojun1998
11effc0ae7 支持自定义 client_id 和 client_secret 2021-01-23 13:45:26 +08:00
zhaojun1998
c8397e17bf 💩 优化代码, 抽取公共方法 2021-01-23 13:44:33 +08:00
zhaojun1998
ed32b9f1d4 限制直链不允许下载密码文件 2021-01-23 13:42:53 +08:00
zhaojun1998
4e184936db 💩 优化代码, 更新注释格式, 去除无用 import 2021-01-23 13:42:14 +08:00
zhaojun1998
fe6aebfdee 自带直链功能 2021-01-23 13:40:16 +08:00
zhaojun1998
d65e1a442d 🔥 移除系统检测功能 2021-01-23 13:36:56 +08:00
zhaojun1998
34647793c8 支持修改驱动器 ID 2021-01-23 13:35:52 +08:00
zhaojun1998
e8c249b9ea 🔥 移除系统检测功能 2021-01-23 13:22:41 +08:00
yanli
d1e613dc10 Update S3ServiceImpl.java 2020-12-17 17:26:55 +08:00
赵俊
1adcfee96f Update README.md 2020-10-07 17:19:26 +08:00
zhaojun1998
75f5de6b9a 🔖 发布 2.8.1 版 2020-08-18 20:43:07 +08:00
zhaojun1998
499942ef70 🎨 更新静态页面 2020-08-18 20:43:02 +08:00
683 changed files with 18143 additions and 6863 deletions

View File

@@ -16,7 +16,7 @@ assignees: ''
请确认你已经做了下面这些事情,若 bug 还是未解决,那么请尽可详细地描述你的问题。
- 我已经安装了最新版的 ZFile
- 我已经阅读了 ZFile 的文档http://docs.zhaojun.im/zfile
- 我已经阅读了 ZFile 的文档https://docs.zfile.vip
- 我已经搜索了已有的 Issues 列表中有关的信息
- 我已经清理过浏览器缓存并重试
-->

15
.gitignore vendored
View File

@@ -1,11 +1,8 @@
HELP.md
target/
.mvn/wrapper/**
!**/src/main/**
**/src/test/**
mvnw
mvnw.cmd
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
@@ -29,6 +26,12 @@ mvnw.cmd
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/.mvn/wrapper/
/mvnw
/mvnw.cmd
/.script/

181
API.md
View File

@@ -1,181 +0,0 @@
## API 标准
所有 API 均返回 `msg`, `code`, `data` 三个属性.
| code | 描述 |
| :---: | :------------: |
| 0 | 请求成功 |
| -1 | 请求失败 |
| -2 | 文件夹需要密码 |
`code == 0` 时, `data` 中为请求所需数据.
`code != 0` 时, 应当将 `msg` 中的内容作为参考值.
## 驱动器列表
### 请求 URL
`/api/drive/list` `GET`
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": [
{
"id": 3, --- ID 是驱动器 ID, 用来唯一区分驱动器
"name": "演示 A 盘", --- 驱动器名称
"enableCache": true, --- 是否开启了缓存
"autoRefreshCache": false, --- 是否开启了缓存自动刷新
"type": { --- 存储源类型
"key": "upyun",
"description": "又拍云 USS"
},
"searchEnable": false, --- 是否开启搜索
"searchIgnoreCase": false, --- 搜索是否忽略大小写
"searchContainEncryptedFile": false --- 搜索是否包含加密文件夹
}
]
}
```
## 获取文件列表
### 请求 URL
`/api/list/{driveId}` `GET`
### URL 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :-----: | :-------------------: | :------: | :------------------------------------: |
| driveId | 驱动器 ID | 是 | 参考 `获取驱动器列表` 接口返回的 id 值 |
### 请求参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :------: | :--------: | :------: | :--------------------------: |
| path | 路径 | 是 | `/`, `/文件夹名称` |
| password | 文件夹密码 | 否 | 当文件夹需要密码时, |
| page | 页数 | 否 | 默认取第一页, 每页固定 30 条 |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": [
{
"name": "密码文件夹",
"time": "2020-01-28 13:17",
"size": 4096,
"type": "FOLDER",
"path": "/",
"url": null
},
{
"name": "新建 文本文档.txt",
"time": "2020-01-28 13:16",
"size": 3,
"type": "FILE",
"path": "/",
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
}
]
}
```
## 获取单个文件信息
### 请求 URL
`/api/directlink/{driveId}` `GET`
### URL 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :-----: | :-------------------: | :------: | :------------------------------------: |
| driveId | 驱动器 ID | 是 | 参考 `获取驱动器列表` 接口返回的 id 值 |
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :----: | :--------: | :------: | :------------------: |
| path | 文件全路径 | 是 | `/新建 文本文档.txt` |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": {
"name": "新建 文本文档.txt",
"time": "2020-01-28 13:16",
"size": 3,
"type": "FILE",
"path": "d:/test",
"url": "http://127.0.0.1:8080/file/新建 文本文档.txt"
}
}
```
## 获取系统配置
### 请求 URL
`/api/config/{driveId}` `GET`
### URL 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :-----: | :-------------------: | :------: | :------------------------------------: |
| driveId | 驱动器 ID | 是 | 参考 `获取驱动器列表` 接口返回的 id 值 |
### 参数
| 参数名 | 描述 | 是否必填 | 参考值 |
| :----: | :--------: | :------: | :-----------: |
| path | 文件夹名称 | 是 | `/文件夹名称` |
### 响应
```json
{
"msg": "操作成功",
"code": 0,
"data": {
"siteName": "ZFile 演示站",
"searchEnable": false,
"username": "zhao",
"domain": "https://zfile.jun6.net",
"customJs": "",
"customCss": "",
"tableSize": "small",
"showOperator": true,
"showDocument": true,
"announcement": "本站是 ZFile 演示站,交流反馈群 180605017",
"showAnnouncement": true,
"layout": "full",
"readme": null
}
}
```

125
README.md
View File

@@ -1,32 +1,49 @@
# Z-File
<p align = "center">
<img alt="ZFile" src="https://cdn.jun6.net/2021/04/21/69a89344e2a84.png" height="150px">
<br><br>
基于 Java 的在线网盘程序,支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP 等存储源,支持在线浏览图片、播放音视频,文本文件等文件类型。
<br><br>
<img src="https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square">
<img src="https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a">
<img src="https://img.shields.io/github/last-commit/zhaojun1998/zfile.svg?style=flat-square">
<img src="https://img.shields.io/github/downloads/zhaojun1998/zfile/total?style=flat-square">
<img src="https://img.shields.io/github/v/release/zhaojun1998/zfile?style=flat-square">
<img src="https://img.shields.io/github/commit-activity/y/zhaojun1998/zfile?style=flat-square">
<br>
<img src="https://img.shields.io/github/issues/zhaojun1998/zfile?style=flat-square">
<img src="https://img.shields.io/github/issues-closed-raw/zhaojun1998/zfile?style=flat-square">
<img src="https://img.shields.io/github/forks/zhaojun1998/zfile?style=flat-square">
<img src="https://img.shields.io/github/stars/zhaojun1998/zfile?style=flat-square">
<img src="https://img.shields.io/github/watchers/zhaojun1998/zfile?style=flat-square">
</p>
![https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square](https://img.shields.io/badge/license-MIT-blue.svg?longCache=true&style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/70b793267f7941d58cbd93f50c9a8e0a)](https://www.codacy.com/manual/zhaojun1998/zfile?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=zhaojun1998/zfile&amp;utm_campaign=Badge_Grade)
![https://img.shields.io/badge/springboot-2.0.6-orange.svg?style=flat-square](https://img.shields.io/badge/springboot-2.0.6-yellow.svg?longCache=true&style=popout-square)
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/zhaojun1998/zfile.svg?style=flat-square)
## 相关地址
此项目是一个在线文件目录的程序, 支持各种对象存储和本地存储, 使用定位是个人放常用工具下载, 或做公共的文件库. 不会向多账户方向开发.
预览地址: [https://zfile.vip](https://zfile.vip)
前端基于 [h5ai](https://larsjung.de/h5ai/) 的原有功能使用 Vue 重新开发了一遍. 后端采用 SpringBoot, 数据库采用内嵌数据库.
文档地址: [https://docs.zfile.vip](https://docs.zfile.vip)
预览地址: [https://zfile.jun6.net](https://zfile.jun6.net)
社区地址: [https://bbs.zfile.vip](https://bbs.zfile.vip)
文档地址: [http://docs.zhaojun.im/zfile](http://docs.zhaojun.im/zfile)
项目源码: [https://github.com/zhaojun1998/zfile](https://github.com/zhaojun1998/zfile)
前端源码: [https://github.com/zhaojun1998/zfile-vue](https://github.com/zhaojun1998/zfile-vue)
## 系统特色
* 内存缓存 (免安装)
* 内存数据库 (免安装)
* 个性化配置
* 自定义目录的 readme 说明文件
* 自定义 JS, CSS
* 文件夹密码
* 目录 README 说明
* 文件直链(短链,永久直链,二维码)
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
* 文件/目录二维码
* 图片模式
* Docker 支持
* 隐藏指定文件夹(通配符支持)
* 自定义 JS, CSS
* 自定义目录 README 说明文件和密码文件名称
* 同时挂载多个存储策略
* 缓存动态开启, ~~缓存自动刷新 (v2.2 及以前版本支持)~~
* ~~全局搜索 (v2.2 及以前版本支持)~~
* 同时挂载多个存储策略
* 支持 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版, OneDrive 世纪互联版, 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
* 支持 S3 协议, 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
## 快速开始
@@ -54,14 +71,14 @@ apt update && apt install -y adoptopenjdk-8-hotspot-jre
> 如为更新程序, 则请先执行 `~/zfile/bin/stop.sh && rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
下载项目:
```bash
cd ~
export ZFILE_INSTALL_PATH=~/zfile
mkdir -p $ZFILE_INSTALL_PATH && cd $ZFILE_INSTALL_PATH
wget https://c.jun6.net/ZFILE/zfile-release.war
mkdir zfile && unzip zfile-release.war -d zfile && rm -rf zfile-release.war
chmod +x zfile/bin/*.sh
unzip zfile-release.war && rm -rf zfile-release.war
chmod +x $ZFILE_INSTALL_PATH/bin/*.sh
```
> 下载指定版本可以将 `zfile-release.war` 改为 `zfile-x.x.war`,如 `zfile-2.2.war`。
@@ -83,62 +100,38 @@ chmod +x zfile/bin/*.sh
~/zfile/bin/start.sh
```
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](http://zhaojun.im/zfile-install)
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](https://docs.zfile.vip)
访问地址:
用户前台: http://127.0.0.1:8080/#/main
用户前台: http://127.0.0.1:8080/main
初始安装: http://127.0.0.1:8080/#/install
初始安装: http://127.0.0.1:8080/install
管理后台: http://127.0.0.1:8080/#/admin
管理后台: http://127.0.0.1:8080/admin
## 预览
![前台首页](https://cdn.jun6.net/2020/04/19/d590d2bde13bb.png)
![后台设置-驱动器设置](https://cdn.jun6.net/2020/04/19/d58fc2debcce8.png)
![后台设置-驱动器设置](https://cdn.jun6.net/2020/04/19/0f321e47fc18c.png)
![后台设置-显示设置](https://cdn.jun6.net/2020/04/19/6d7c300b89671.png)
## 常见问题
### 默认路径
默认 H2 数据库文件地址: `~/.zfile/db/`, `~` 表示用户目录
windows 为 `C:/Users/用户名/`
linux 为 `/home/用户名/`, root 用户为 `/root/`
> 2.3 及以后版本路径为 `~/.zfile-new/db/`
### 文档文件和加密文件
- 目录文档显示文件名为 `readme.md`
- 目录需要密码访问, 添加文件 `password.txt` (无法拦截此文件被下载, 但可以改名文件)
## 开发计划
- [x] API 支持 [点击查看文档](https://github.com/zhaojun1998/zfile/blob/master/API.md)
- [x] 更方便的部署方式
- [x] 布局优化 - 自定义操作按钮 (现为右键实现)
- [x] 后台优化 - 设置按照其功能进行分离
- [x] 体验优化 - 支持前后端分离部署
- [x] 体验优化 - 文本预览更换 vscode 同款编辑器 monaco editor
- [x] 新功能 - Docker 支持
- [x] 架构调整 - 支持多存储策略
- [ ] 新功能 - 后台支持上传、编辑、删除等操作
- [ ] 新功能 - WebDav 支持
- [ ] 新功能 - 离线下载 (aria2)
- [ ] 体验优化 - 忽略文件列表 (正则表达式)
- [ ] 体验优化 - 自定义支持预览的文件后缀 (正则表达式)
- [ ] 体验优化 - 一键安装脚本
![前台首页](https://cdn.jun6.net/2021/03/23/c1f4631ee2de4.png)
![图片预览](https://cdn.jun6.net/2021/03/23/713741d43b939.png)
![视频预览](https://cdn.jun6.net/2021/03/23/9c724383bb506.png)
![文本预览](https://cdn.jun6.net/2021/03/23/b00efdfb4892e.png)
![音频预览](https://cdn.jun6.net/2021/03/23/d15b14378d3f0.png)
![后台设置-驱动器列表](https://cdn.jun6.net/2021/03/23/b4f76f20ea73a.png)
![后台设置-新增驱动器](https://cdn.jun6.net/2021/03/23/e70e04f8cc5b6.png)
![后台设置-站点设置](https://cdn.jun6.net/2021/03/23/fd946991bb6b9.png)
## 支持作者
如果本项目对你有帮助,请作者喝杯咖啡吧。
<img src="http://cdn.jun6.net/alipay.png" width="200" height="312">
<img src="http://cdn.jun6.net/wechat.png" width="222" height="300">
<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">
## Stargazers over time
[![starcharts stargazers over time](https://starchart.cc/zhaojun1998/zfile.svg)](https://starchart.cc/zhaojun1998/zfile.svg)
## 开发工具赞助
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>

196
pom.xml
View File

@@ -1,28 +1,30 @@
<?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">
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>2.8</version>
<version>4.0.0</version>
<name>zfile</name>
<packaging>war</packaging>
<description>一个在线的文件浏览系统</description>
<properties>
<java.version>1.8</java.version>
<log4j2.version>2.17.0</log4j2.version>
<org.mapstruct.version>1.5.1.Final</org.mapstruct.version>
<proguard.version>7.2.1</proguard.version>
<proguard-maven-plugin.version>2.5.3</proguard-maven-plugin.version>
</properties>
<dependencies>
<!-- spring boot 官方相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -32,14 +34,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
@@ -50,20 +44,21 @@
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 数据库驱动-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
<scope>runtime</scope>
</dependency>
@@ -71,7 +66,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.3</version>
<version>5.8.3</version>
</dependency>
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
@@ -83,26 +78,36 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.699</version>
<version>1.12.146</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
<version>3.8.0</version>
</dependency>
<!-- 其他工具类 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.mpatric</groupId>
<artifactId>mp3agic</artifactId>
@@ -112,7 +117,107 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
<version>1.2.83</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.15.0</version>
</dependency>
<!-- 第三方依赖-权限相关 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.75</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>com.github.lookfirst</groupId>
<artifactId>sardine</artifactId>
<version>5.10</version>
</dependency>
<dependency>
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.82</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20200518</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
@@ -122,6 +227,44 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.uyoqu.framework</groupId>
@@ -144,5 +287,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2011-2022, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.mybatisplus.core.handlers;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* 自定义枚举属性转换器
*
* @author hubin
* @since 2017-10-11
*/
public class MybatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private static final Map<String, String> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
private final Class<E> enumClassType;
private final Class<?> propertyType;
private final Invoker getInvoker;
public MybatisEnumTypeHandler(Class<E> enumClassType) {
if (enumClassType == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.enumClassType = enumClassType;
MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY);
String name = "value";
if (!IEnum.class.isAssignableFrom(enumClassType)) {
name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String.format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName())));
}
this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name));
this.getInvoker = metaClass.getGetInvoker(name);
}
/**
* 查找标记标记EnumValue字段
*
* @param clazz class
* @return EnumValue字段
* @since 3.3.1
*/
public static Optional<String> findEnumValueFieldName(Class<?> clazz) {
if (clazz != null && clazz.isEnum()) {
String className = clazz.getName();
return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> {
Optional<Field> fieldOptional = findEnumValueAnnotationField(clazz);
return fieldOptional.map(Field::getName).orElse(null);
}));
}
return Optional.empty();
}
private static Optional<Field> findEnumValueAnnotationField(Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst();
}
/**
* 判断是否为MP枚举处理
*
* @param clazz class
* @return 是否为MP枚举处理
* @since 3.3.1
*/
public static boolean isMpEnums(Class<?> clazz) {
return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent());
}
@SuppressWarnings("Duplicates")
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)
throws SQLException {
if (jdbcType == null) {
ps.setObject(i, this.getValue(parameter));
} else {
// see r3589
ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE);
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object value = rs.getObject(columnName);
if (null == value && rs.wasNull()) {
return null;
}
return this.valueOf(value);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Object value = rs.getObject(columnIndex, this.propertyType);
if (null == value && rs.wasNull()) {
return null;
}
return this.valueOf(value);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Object value = cs.getObject(columnIndex, this.propertyType);
if (null == value && cs.wasNull()) {
return null;
}
return this.valueOf(value);
}
private E valueOf(Object value) {
E[] es = this.enumClassType.getEnumConstants();
return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null);
}
/**
* 值比较
*
* @param sourceValue 数据库字段值
* @param targetValue 当前枚举属性值
* @return 是否匹配
* @since 3.3.0
*/
protected boolean equalsValue(Object sourceValue, Object targetValue) {
String sValue = StringUtils.toStringTrim(sourceValue);
String tValue = StringUtils.toStringTrim(targetValue);
if (sourceValue instanceof Number && targetValue instanceof Number
&& new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) {
return true;
}
return Objects.equals(sValue, tValue);
}
private Object getValue(Object object) {
try {
return this.getInvoker.invoke(object, new Object[0]);
} catch (ReflectiveOperationException e) {
throw ExceptionUtils.mpe(e);
}
}
}

View File

@@ -1,20 +1,43 @@
package im.zhaojun.zfile;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @author zhaojun
*/
@EnableAsync
import javax.annotation.PostConstruct;
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
@ServletComponentScan(basePackages = "im.zhaojun.zfile.*.filter")
@ComponentScan(basePackages = "im.zhaojun.zfile.*")
public class ZfileApplication {
public static void main(String[] args) {
SpringApplication.run(ZfileApplication.class, args);
}
}
@Value("${spring.datasource.driver-class-name}")
private String datasourceDriveClassName;
@Value("${spring.datasource.url}")
private String datasourceUrl;
@PostConstruct
public void init() {
if (StrUtil.equals(datasourceDriveClassName, "org.sqlite.JDBC")) {
String path = datasourceUrl.replace("jdbc:sqlite:", "");
String folderPath = FileUtil.getParent(path, 1);
if (!FileUtil.exist(folderPath)) {
FileUtil.mkdir(folderPath);
}
}
}
}

View File

@@ -0,0 +1,17 @@
package im.zhaojun.zfile.admin.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Referer 校验注解,标注了此注解的请求,会被校验 Referer 是否符合要求.
*
* @author zhaojun
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RefererCheck {
}

View File

@@ -0,0 +1,75 @@
package im.zhaojun.zfile.admin.annoation;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记存储类型参数名称
*
* @author zhaojun
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StorageParamItem {
/**
* 字段显示排序值, 值越小, 越靠前. 默认为 99
*/
int order() default 99;
/**
* 参数键, 如果为空, 则使用字段名称.
*/
String key() default "";
/**
* 参数名称, 用于网页上显示名称.
*/
String name();
/**
* 字段类型, 默认为 input, 可选值为: input, select, switch.
*/
StorageParamTypeEnum type() default StorageParamTypeEnum.INPUT;
/**
* 当 {@link #type} 为 select 时, 选项的值.
*/
StorageParamSelectOption[] options() default {};
/**
* 当 {@link #type} 为 select 时, 选项的值. 通过 {@link StorageParamSelect#getOptions)} 方法获取选项值.
*/
Class<? extends StorageParamSelect> optionsClass() default StorageParamSelect.class;
/**
* 参数值是否可以为空. 如不为空,则抛出异常.
*/
boolean required() default true;
/**
* 如果填写值为空,则给予默认值.
* 支持 ${xxx} 变量, 会读取配置文件中的值, 如获取失败, 会默认为空.
*/
String defaultValue() default "";
/**
* 参数描述信息, 用户在用户填写时, 进行提示.
*/
String description() default "";
/**
* 参数下方的提示链接, 如果为空, 则不显示.
*/
String link() default "";
/**
* 参数下方的提示链接文件信息, 如果为空, 则默认为链接地址.
*/
String linkName() default "";
}

View File

@@ -0,0 +1,28 @@
package im.zhaojun.zfile.admin.annoation;
import im.zhaojun.zfile.admin.model.param.IStorageParam;
import im.zhaojun.zfile.admin.annoation.model.StorageSourceParamDef;
import java.util.List;
/**
* 存储源参数下拉值接口.
*
* @author zhaojun
*/
public interface StorageParamSelect {
/**
* 获取存储源参数下拉选项列表.
*
* @param storageParamItem
* 存储源下拉参数定义
*
* @param targetParam
* 存储源参数
*
* @return 存储源参数下拉选项列表
*/
List<StorageSourceParamDef.Options> getOptions(StorageParamItem storageParamItem, IStorageParam targetParam);
}

View File

@@ -0,0 +1,27 @@
package im.zhaojun.zfile.admin.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记存储类型参数类型为 select 时, 数据的下拉值.
*
* @author zhaojun
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StorageParamSelectOption {
/**
* 选项显示值
*/
String label();
/**
* 选项存储值
*/
String value();
}

View File

@@ -0,0 +1,95 @@
package im.zhaojun.zfile.admin.annoation.model;
import im.zhaojun.zfile.admin.annoation.StorageParamSelectOption;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import java.util.List;
/**
* 存储源参数定义, 包含参数名称、描述、必填、默认值等信息.
*
* @author zhaojun
*/
@Data
@AllArgsConstructor
@Builder
public class StorageSourceParamDef {
/**
* 字段显示排序值, 值越小, 越靠前.
*/
private int order;
/**
* 参数 key
*/
private String key;
/**
* 参数名称
*/
private String name;
/**
* 参数描述
*/
private String description;
/**
* 是否必填
*/
private boolean required;
/**
* 默认值
*/
private String defaultValue;
/**
* 链接地址
*/
private String link;
/**
* 链接名称
*/
private String linkName;
/**
* 字段类型, 默认为 input, 可选值为: input, select, switch.
*/
private StorageParamTypeEnum type;
/**
* 当 {@link #type} 为 select 时, 选项的值.
*/
private List<Options> options;
@Getter
public static class Options {
private final String label;
private final String value;
public Options(String value) {
this.label = value;
this.value = value;
}
public Options(String label, String value) {
this.label = label;
this.value = value;
}
public Options(StorageParamSelectOption storageParamSelectOption) {
this.label = storageParamSelectOption.label();
this.value = storageParamSelectOption.value();
}
}
}

View File

@@ -0,0 +1,30 @@
package im.zhaojun.zfile.admin.annoation.select.impl;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import im.zhaojun.zfile.admin.annoation.StorageParamSelect;
import im.zhaojun.zfile.admin.model.param.IStorageParam;
import im.zhaojun.zfile.admin.annoation.model.StorageSourceParamDef;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* 编码格式动态参数.
*
* @author zhaojun
*/
public class EncodingStorageParamSelect implements StorageParamSelect {
@Override
public List<StorageSourceParamDef.Options> getOptions(StorageParamItem storageParamItem, IStorageParam targetParam) {
List<StorageSourceParamDef.Options> options = new ArrayList<>();
for (String name : Charset.availableCharsets().keySet()) {
StorageSourceParamDef.Options option = new StorageSourceParamDef.Options(name);
options.add(option);
}
return options;
}
}

View File

@@ -0,0 +1,14 @@
package im.zhaojun.zfile.admin.constant;
/**
* 存储源设置字段常量.
*
* @author zhaojun
*/
public class StorageConfigConstant {
public static final String ACCESS_TOKEN_KEY = "accessToken";
public static final String REFRESH_TOKEN_KEY = "refreshToken";
}

View File

@@ -0,0 +1,18 @@
package im.zhaojun.zfile.admin.constant;
/**
* 系统设置字段常量.
*
* @author zhaojun
*/
public class SystemConfigConstant {
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
public static final String LOGIN_VERIFY_MODE = "loginVerifyMode";
public static final String RSA_HEX_KEY = "rsaHexKey";
}

View File

@@ -0,0 +1,108 @@
package im.zhaojun.zfile.admin.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.home.model.dto.CacheInfoDTO;
import im.zhaojun.zfile.admin.service.StorageSourceService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 存储源缓存维护接口
*
* @author zhaojun
*/
@RestController
@Api(tags = "存储源模块-缓存")
@ApiSort(5)
@RequestMapping("/admin/cache")
public class CacheController {
@Resource
private StorageSourceService storageSourceService;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "启用存储源缓存", notes = "开启缓存后N 秒内重复请求相同文件夹,不会重复调用 API。" +
"参数 N 在配置文件中设置 {zfile.cache.timeout},默认为 1800 秒。")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/{storageId}/enable")
public AjaxJson<Void> enableCache(@PathVariable("storageId") Integer storageId) {
storageSourceService.updateCacheStatus(storageId, true);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "禁用存储源缓存")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/{storageId}/disable")
public AjaxJson<Void> disableCache(@PathVariable("storageId") Integer storageId) {
storageSourceService.updateCacheStatus(storageId, false);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 3)
@ApiOperation(value = "查看存储源缓存", notes = "可查看存储源缓存的所有目录路径")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@GetMapping("/{storageId}/info")
public AjaxJson<CacheInfoDTO> cacheInfo(@PathVariable("storageId") Integer storageId) {
CacheInfoDTO cacheInfo = storageSourceService.findCacheInfo(storageId);
return AjaxJson.getSuccessData(cacheInfo);
}
@ApiOperationSupport(order = 4)
@ApiOperation(value = "刷新存储源缓存", notes = "刷新存储源缓存路径,系统会重新预热此路径的内容")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true),
@ApiImplicitParam(paramType = "body", name = "key", value = "缓存 key", required = true)
})
@PostMapping("/{storageId}/refresh")
public AjaxJson<Void> refreshCache(@PathVariable("storageId") Integer storageId, String key) throws Exception {
storageSourceService.refreshCache(storageId, key);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 5)
@ApiOperation(value = "开启缓存自动刷新", notes = "开启后每隔 N 秒检测到期的缓存, 对于过期缓存尝试调用 API, 重新写入缓存." +
"参数 N 在配置文件中设置 {zfile.cache.auto-refresh-interval},默认为 5 秒。")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/{storageId}/auto-refresh/start")
public AjaxJson<Void> enableAutoRefresh(@PathVariable("storageId") Integer storageId) {
storageSourceService.startAutoCacheRefresh(storageId);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 5)
@ApiOperation(value = "关闭缓存自动刷新")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/{storageId}/auto-refresh/stop")
public AjaxJson<Void> disableAutoRefresh(@PathVariable("storageId") Integer storageId) {
storageSourceService.stopAutoCacheRefresh(storageId);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 6)
@ApiOperation(value = "清空缓存")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/{storageId}/clear")
public AjaxJson<Void> clearCache(@PathVariable("storageId") Integer storageId) {
storageSourceService.clearCache(storageId);
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,47 @@
package im.zhaojun.zfile.admin.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.common.util.FileResponseUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.util.Date;
/**
* 获取系统日志接口
*
* @author zhaojun
*/
@Api(tags = "日志")
@ApiSort(8)
@Slf4j
@RestController
@RequestMapping("/admin")
public class LogController {
@Value("${zfile.log.path}")
private String zfileLogPath;
@GetMapping("/log/download")
@ApiOperation(value = "下载系统日志")
public ResponseEntity<Resource> downloadLog() {
if (log.isDebugEnabled()) {
log.debug("下载诊断日志");
}
File fileZip = ZipUtil.zip(zfileLogPath);
String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
return FileResponseUtil.exportSingleThread(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
}
}

View File

@@ -0,0 +1,147 @@
package im.zhaojun.zfile.admin.controller.link;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.convert.DownloadLogConvert;
import im.zhaojun.zfile.admin.model.entity.DownloadLog;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.admin.model.request.link.QueryDownloadLogRequest;
import im.zhaojun.zfile.admin.model.result.link.DownloadLogResult;
import im.zhaojun.zfile.admin.service.DownloadLogService;
import im.zhaojun.zfile.admin.service.StorageSourceService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* 直链下载日志接口
*
* @author zhaojun
*/
@Api(tags = "直链日志管理")
@ApiSort(7)
@Controller
@RequestMapping("/admin/download/log")
public class DownloadLogManagerController {
@Resource
private StorageSourceService storageSourceService;
@Resource
private DownloadLogConvert downloadLogConvert;
@Resource
private DownloadLogService downloadLogService;
@ApiOperationSupport(order = 1)
@GetMapping("/list")
@ApiOperation(value = "直链下载日志")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "key", value = "直链 key"),
@ApiImplicitParam(paramType = "query", name = "page", value = "分页页数"),
@ApiImplicitParam(paramType = "query", name = "limit", value = "每页条数"),
@ApiImplicitParam(paramType = "query", name = "orderBy", defaultValue = "createDate", value = "排序字段"),
@ApiImplicitParam(paramType = "query", name = "orderDirection", defaultValue = "desc", value = "排序顺序")
})
public AjaxJson<?> list(QueryDownloadLogRequest queryDownloadLogRequest,
@RequestParam(required = false, defaultValue = "create_time") String orderBy,
@RequestParam(required = false, defaultValue = "desc") String orderDirection) {
Page<DownloadLog> pages = Page.of(queryDownloadLogRequest.getPage(), queryDownloadLogRequest.getLimit());
boolean asc = Objects.equals(orderDirection, "asc");
pages.addOrder(new OrderItem(orderBy, asc));
DownloadLog downloadLog = new DownloadLog();
QueryWrapper<DownloadLog> queryWrapper = new QueryWrapper<>(downloadLog);
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getStorageKey())) {
queryWrapper.eq("storage_key", queryDownloadLogRequest.getStorageKey());
}
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getPath())) {
queryWrapper.like("path", queryDownloadLogRequest.getPath());
}
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getShortKey())) {
queryWrapper.like("short_key", queryDownloadLogRequest.getShortKey());
}
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getIp())) {
queryWrapper.like("ip", queryDownloadLogRequest.getIp());
}
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getReferer())) {
queryWrapper.like("referer", queryDownloadLogRequest.getReferer());
}
if (StrUtil.isNotEmpty(queryDownloadLogRequest.getUserAgent())) {
queryWrapper.like("user_agent", queryDownloadLogRequest.getUserAgent());
}
if (ObjectUtil.isNotEmpty(queryDownloadLogRequest.getDateFrom())) {
queryWrapper.ge("create_time", queryDownloadLogRequest.getDateFrom());
}
if (ObjectUtil.isNotEmpty(queryDownloadLogRequest.getDateTo())) {
queryWrapper.le("create_time", queryDownloadLogRequest.getDateFrom());
}
Page<DownloadLog> selectResult = downloadLogService.page(pages, queryWrapper);
long total = selectResult.getTotal();
List<DownloadLog> records = selectResult.getRecords();
Map<String, StorageSource> cache = new HashMap<>();
Stream<DownloadLogResult> shortLinkResultList = records.stream().map(model -> {
String storageKey = model.getStorageKey();
StorageSource storageSource;
if (cache.containsKey(storageKey)) {
storageSource = cache.get(storageKey);
} else {
storageSource = storageSourceService.findByStorageKey(storageKey);
cache.put(storageKey, storageSource);
}
return downloadLogConvert.entityToResultList(model, storageSource);
});
return AjaxJson.getPageData(total, shortLinkResultList);
}
@ApiOperationSupport(order = 2)
@DeleteMapping("/delete/{id}")
@ApiOperation(value = "删除直链")
@ApiImplicitParam(paramType = "path", name = "id", value = "直链 id", required = true)
@ResponseBody
public AjaxJson<Void> deleteById(@PathVariable Integer id) {
downloadLogService.removeById(id);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 3)
@DeleteMapping("/delete/batch")
@ResponseBody
@ApiImplicitParam(paramType = "query", name = "ids", value = "直链 id", required = true)
@ApiOperation(value = "批量删除直链")
public AjaxJson<Void> batchDelete(@RequestParam("id[]") Integer[] ids) {
downloadLogService.removeBatchByIds(Arrays.asList(ids));
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,141 @@
package im.zhaojun.zfile.admin.controller.link;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.convert.ShortLinkConvert;
import im.zhaojun.zfile.admin.model.entity.ShortLink;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.admin.model.result.link.ShortLinkResult;
import im.zhaojun.zfile.admin.service.ShortLinkService;
import im.zhaojun.zfile.admin.service.StorageSourceService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* 直链管理接口
*
* @author zhaojun
*/
@Api(tags = "直链管理")
@ApiSort(7)
@Controller
@RequestMapping("/admin")
public class ShortLinkManagerController {
@Resource
private ShortLinkService shortLinkService;
@Resource
private StorageSourceService storageSourceService;
@Resource
private ShortLinkConvert shortLinkConvert;
@ApiOperationSupport(order = 1)
@GetMapping("/link/list")
@ApiOperation(value = "搜索短链")
@ResponseBody
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "key", value = "短链 key"),
@ApiImplicitParam(paramType = "query", name = "storageId", value = "存储源 ID"),
@ApiImplicitParam(paramType = "query", name = "url", value = "短链 url"),
@ApiImplicitParam(paramType = "query", name = "dateFrom", value = "短链创建时间从"),
@ApiImplicitParam(paramType = "query", name = "dateTo", value = "短链创建时间至"),
@ApiImplicitParam(paramType = "query", name = "page", value = "分页页数"),
@ApiImplicitParam(paramType = "query", name = "limit", value = "每页条数"),
@ApiImplicitParam(paramType = "query", name = "orderBy", defaultValue = "createDate", value = "排序字段"),
@ApiImplicitParam(paramType = "query", name = "orderDirection", defaultValue = "desc", value = "排序顺序")
})
public AjaxJson<?> list(String key, String storageId,
String url,
String dateFrom,
String dateTo,
Integer page,
Integer limit,
@RequestParam(required = false, defaultValue = "create_date") String orderBy,
@RequestParam(required = false, defaultValue = "desc") String orderDirection) {
Page<ShortLink> pages = Page.of(page, limit);
boolean asc = Objects.equals(orderDirection, "asc");
pages.addOrder(new OrderItem(orderBy, asc));
QueryWrapper<ShortLink> queryWrapper = new QueryWrapper<>(new ShortLink());
if (StrUtil.isNotEmpty(storageId)) {
queryWrapper.eq("storage_id", storageId);
}
if (StrUtil.isNotEmpty(key)) {
queryWrapper.like("short_key", key);
}
if (StrUtil.isNotEmpty(url)) {
queryWrapper.like("url", url);
}
if (StrUtil.isNotEmpty(dateFrom)) {
queryWrapper.ge("create_date", dateFrom);
}
if (StrUtil.isNotEmpty(dateTo)) {
queryWrapper.le("create_date", dateTo);
}
Page<ShortLink> selectResult = shortLinkService.page(pages, queryWrapper);
long total = selectResult.getTotal();
List<ShortLink> records = selectResult.getRecords();
Map<Integer, StorageSource> cache = new HashMap<>();
Stream<ShortLinkResult> shortLinkResultList = records.stream().map(shortLink -> {
Integer shortLinkStorageId = shortLink.getStorageId();
StorageSource storageSource;
if (cache.containsKey(shortLinkStorageId)) {
storageSource = cache.get(shortLinkStorageId);
} else {
storageSource = storageSourceService.findById(shortLinkStorageId);
cache.put(shortLinkStorageId, storageSource);
}
return shortLinkConvert.entityToResultList(shortLink, storageSource);
});
return AjaxJson.getPageData(total, shortLinkResultList);
}
@ApiOperationSupport(order = 2)
@DeleteMapping("/link/delete/{id}")
@ApiOperation(value = "删除短链")
@ApiImplicitParam(paramType = "path", name = "id", value = "短链 id", required = true)
@ResponseBody
public AjaxJson<Void> deleteById(@PathVariable Integer id) {
shortLinkService.removeById(id);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 3)
@DeleteMapping("/link/delete/batch")
@ResponseBody
@ApiImplicitParam(paramType = "query", name = "ids", value = "短链 id", required = true)
@ApiOperation(value = "批量删除短链")
public AjaxJson<Void> batchDelete(@RequestParam("id[]") Integer[] ids) {
shortLinkService.removeBatchByIds(Arrays.asList(ids));
return AjaxJson.getSuccess();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,103 @@
package im.zhaojun.zfile.admin.controller.setting;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.model.request.setting.UpdateLinkSettingRequest;
import im.zhaojun.zfile.admin.model.request.setting.UpdateSecuritySettingRequest;
import im.zhaojun.zfile.admin.model.request.setting.UpdateSiteSettingRequest;
import im.zhaojun.zfile.admin.model.request.setting.UpdateUserNameAndPasswordRequest;
import im.zhaojun.zfile.admin.model.request.setting.UpdateViewSettingRequest;
import im.zhaojun.zfile.admin.service.SystemConfigService;
import im.zhaojun.zfile.common.util.AjaxJson;
import im.zhaojun.zfile.home.model.dto.SystemConfigDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 站点设定值接口
*
* @author zhaojun
*/
@Api(tags = "站点设置模块")
@ApiSort(2)
@RestController
@RequestMapping("/admin")
public class SettingController {
@Resource
private SystemConfigService systemConfigService;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取站点信息",
notes = "获取站点相关信息,如站点名称,风格样式,是否显示公告,是否显示文档区,自定义 CSSJS 等参数")
@GetMapping("/config")
public AjaxJson<SystemConfigDTO> getConfig() {
SystemConfigDTO systemConfigDTO = systemConfigService.getSystemConfig();
return AjaxJson.getSuccessData(systemConfigDTO);
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "修改管理员账号密码")
@PutMapping("/config/password")
public AjaxJson<?> updatePwd(@Valid @RequestBody UpdateUserNameAndPasswordRequest settingRequest) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
BeanUtils.copyProperties(settingRequest, systemConfigDTO);
systemConfigService.updateSystemConfig(systemConfigDTO);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 3)
@ApiOperation(value = "修改站点设置")
@PutMapping("/config/site")
public AjaxJson<?> updateSiteSetting(@Valid @RequestBody UpdateSiteSettingRequest settingRequest) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
BeanUtils.copyProperties(settingRequest, systemConfigDTO);
systemConfigService.updateSystemConfig(systemConfigDTO);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 4)
@ApiOperation(value = "修改显示设置")
@PutMapping("/config/view")
public AjaxJson<?> updateViewSetting(@Valid @RequestBody UpdateViewSettingRequest settingRequest) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
BeanUtils.copyProperties(settingRequest, systemConfigDTO);
systemConfigService.updateSystemConfig(systemConfigDTO);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 5)
@ApiOperation(value = "修改登陆安全设置")
@PutMapping("/config/security")
public AjaxJson<?> updateSecuritySetting(@Valid @RequestBody UpdateSecuritySettingRequest settingRequest) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
BeanUtils.copyProperties(settingRequest, systemConfigDTO);
systemConfigService.updateSystemConfig(systemConfigDTO);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 6)
@ApiOperation(value = "修改直链设置")
@PutMapping("/config/link")
public AjaxJson<?> updateLinkSetting(@Valid @RequestBody UpdateLinkSettingRequest settingRequest) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
BeanUtils.copyProperties(settingRequest, systemConfigDTO);
systemConfigService.updateSystemConfig(systemConfigDTO);
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,44 @@
package im.zhaojun.zfile.admin.controller.stroage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.common.context.StorageSourceContext;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import im.zhaojun.zfile.admin.annoation.model.StorageSourceParamDef;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 系统元数据接口
*
* @author zhaojun
*/
@Api(tags = "存储源模块-元数据")
@ApiSort(4)
@RestController
@RequestMapping("/admin")
public class StorageMetaDataController {
@GetMapping("/support-storage")
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取支持的存储源类型", notes = "获取系统支持的存储源类型")
public AjaxJson<StorageTypeEnum[]> supportStorage() {
return AjaxJson.getSuccessData(StorageTypeEnum.values());
}
@GetMapping("/storage-params")
@ApiOperationSupport(order = 2)
@ApiOperation(value = "获取指定存储源类型的所有参数信息", notes = "获取指定存储源类型的参数,如本地存储只需要填路径地址,而对象存储需要填 AccessKey, SecretKey 等信息.")
public AjaxJson<List<StorageSourceParamDef>> getFormByStorageType(StorageTypeEnum storageType) {
List<StorageSourceParamDef> storageSourceConfigList = StorageSourceContext.getStorageSourceParamListByType(storageType);
return AjaxJson.getSuccessData(storageSourceConfigList);
}
}

View File

@@ -0,0 +1,134 @@
package im.zhaojun.zfile.admin.controller.stroage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.admin.model.request.SaveStorageSourceRequest;
import im.zhaojun.zfile.admin.model.result.storage.StorageSourceAdminResult;
import im.zhaojun.zfile.admin.service.StorageSourceService;
import im.zhaojun.zfile.common.cache.RefreshTokenCache;
import im.zhaojun.zfile.common.util.AjaxJson;
import im.zhaojun.zfile.home.convert.StorageSourceConvert;
import im.zhaojun.zfile.home.model.dto.StorageSourceDTO;
import im.zhaojun.zfile.home.model.request.UpdateStorageSortRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 存储源基础设置模块接口
*
* @author zhaojun
*/
@Api(tags = "存储源模块-基础")
@ApiSort(3)
@RestController
@RequestMapping("/admin")
public class StorageSourceController {
@Resource
private StorageSourceService storageSourceService;
@Resource
private StorageSourceConvert storageSourceConvert;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取所有存储源列表", notes = "获取所有添加的存储源列表,按照排序值由小到大排序")
@GetMapping("/storages")
public AjaxJson<List<StorageSourceAdminResult>> storageList() {
List<StorageSource> list = storageSourceService.findAllOrderByOrderNum();
List<StorageSourceAdminResult> storageSourceAdminResults = storageSourceConvert.entityToAdminResultList(list);
storageSourceAdminResults.forEach(storageSourceAdminResult -> {
RefreshTokenCache.RefreshTokenInfo refreshTokenInfo = RefreshTokenCache.getRefreshTokenInfo(storageSourceAdminResult.getId());
storageSourceAdminResult.setRefreshTokenInfo(refreshTokenInfo);
});
return AjaxJson.getSuccessData(storageSourceAdminResults);
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "获取指定存储源参数", notes = "获取指定存储源基本信息及其参数")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@GetMapping("/storage/{storageId}")
public AjaxJson<StorageSourceDTO> storageItem(@PathVariable Integer storageId) {
StorageSourceDTO storageSourceDTO = storageSourceService.findStorageSourceDTOById(storageId);
return AjaxJson.getSuccessData(storageSourceDTO);
}
@ApiOperationSupport(order = 3)
@ApiOperation(value = "保存存储源参数", notes = "保存存储源的所有参数")
@PostMapping("/storage")
public AjaxJson<Void> saveStorageItem(@RequestBody SaveStorageSourceRequest saveStorageSourceRequest) {
storageSourceService.saveStorageSource(saveStorageSourceRequest);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 4)
@ApiOperation(value = "删除存储源", notes = "删除存储源基本设置和拓展设置")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@DeleteMapping("/storage/{storageId}")
public AjaxJson<Void> deleteStorageItem(@PathVariable Integer storageId) {
storageSourceService.deleteById(storageId);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 5)
@ApiOperation(value = "启用存储源", notes = "开启存储源后可在前台显示")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/storage/{storageId}/enable")
public AjaxJson<Void> enable(@PathVariable Integer storageId) {
StorageSource storageSource = storageSourceService.findById(storageId);
storageSource.setEnable(true);
storageSourceService.updateById(storageSource);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 6)
@ApiOperation(value = "停止存储源", notes = "停用存储源后不在前台显示")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/storage/{storageId}/disable")
public AjaxJson<Void> disable(@PathVariable Integer storageId) {
StorageSource storageSource = storageSourceService.findById(storageId);
storageSource.setEnable(false);
storageSourceService.updateById(storageSource);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 7)
@ApiOperation(value = "更新存储源顺序")
@PostMapping("/storage/sort")
public AjaxJson<Void> updateStorageSort(@RequestBody List<UpdateStorageSortRequest> updateStorageSortRequestList) {
storageSourceService.updateStorageSort(updateStorageSortRequestList);
return AjaxJson.getSuccess();
}
@ApiOperationSupport(order = 8)
@ApiOperation(value = "校验存储源 key 是否重复")
@ApiImplicitParam(paramType = "query", name = "storageKey", value = "存储源 key", required = true)
@GetMapping("/storage/exist/key")
public AjaxJson<Boolean> existKey(String storageKey) {
boolean exist = storageSourceService.existByStorageKey(storageKey);
return AjaxJson.getSuccessData(exist);
}
}

View File

@@ -0,0 +1,53 @@
package im.zhaojun.zfile.admin.controller.stroage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.model.entity.FilterConfig;
import im.zhaojun.zfile.admin.service.FilterConfigService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 存储源过滤器维护接口
*
* @author zhaojun
*/
@Api(tags = "存储源模块-过滤文件")
@ApiSort(6)
@RestController
@RequestMapping("/admin")
public class StorageSourceFilterController {
@Resource
private FilterConfigService filterConfigService;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取存储源过滤文件列表", notes = "根据存储源 ID 获取存储源设置的过滤文件列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@GetMapping("/storage/{storageId}/filters")
public AjaxJson<List<FilterConfig>> getFilters(@PathVariable Integer storageId) {
return AjaxJson.getSuccessData(filterConfigService.findByStorageId(storageId));
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "保存存储源过滤文件列表", notes = "保存指定存储源 ID 设置的过滤文件列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/storage/{storageId}/filters")
public AjaxJson<Void> saveFilters(@PathVariable Integer storageId, @RequestBody List<FilterConfig> filter) {
filterConfigService.batchSave(storageId, filter);
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,53 @@
package im.zhaojun.zfile.admin.controller.stroage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.model.entity.PasswordConfig;
import im.zhaojun.zfile.admin.service.PasswordConfigService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 存储源密码维护接口
*
* @author zhaojun
*/
@Api(tags = "存储源模块-密码文件夹")
@ApiSort(6)
@RestController
@RequestMapping("/admin")
public class StorageSourcePasswordController {
@Resource
private PasswordConfigService passwordConfigService;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取存储源密码文件夹列表", notes = "根据存储源 ID 获取存储源设置的密码文件夹列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@GetMapping("/storage/{storageId}/password")
public AjaxJson<List<PasswordConfig>> getPasswordList(@PathVariable Integer storageId) {
return AjaxJson.getSuccessData(passwordConfigService.findByStorageId(storageId));
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "保存存储源密码文件夹列表", notes = "保存指定存储源 ID 设置的密码文件夹列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/storage/{storageId}/password")
public AjaxJson<Void> savePasswordList(@PathVariable Integer storageId, @RequestBody List<PasswordConfig> password) {
passwordConfigService.batchSave(storageId, password);
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,53 @@
package im.zhaojun.zfile.admin.controller.stroage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.admin.model.entity.ReadmeConfig;
import im.zhaojun.zfile.admin.service.ReadmeConfigService;
import im.zhaojun.zfile.common.util.AjaxJson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 存储源文档模块维护接口
*
* @author zhaojun
*/
@Api(tags = "存储源模块-README")
@ApiSort(7)
@RestController
@RequestMapping("/admin")
public class StorageSourceReadmeController {
@Resource
private ReadmeConfigService readmeConfigService;
@ApiOperationSupport(order = 1)
@ApiOperation(value = "获取存储源文档文件夹列表", notes = "根据存储源 ID 获取存储源设置的文档文件夹列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@GetMapping("/storage/{storageId}/readme")
public AjaxJson<List<ReadmeConfig>> getReadmeList(@PathVariable Integer storageId) {
return AjaxJson.getSuccessData(readmeConfigService.findByStorageId(storageId));
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "保存存储源文档文件夹列表", notes = "保存指定存储源 ID 设置的文档文件夹列表")
@ApiImplicitParam(paramType = "path", name = "storageId", value = "存储源 id", required = true)
@PostMapping("/storage/{storageId}/readme")
public AjaxJson<Void> saveReadmeList(@PathVariable Integer storageId, @RequestBody List<ReadmeConfig> readme) {
readmeConfigService.batchSave(storageId, readme);
return AjaxJson.getSuccess();
}
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.admin.convert;
import im.zhaojun.zfile.admin.model.entity.DownloadLog;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.admin.model.result.link.DownloadLogResult;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.stereotype.Component;
/**
* @author zhaojun
*/
@Component
@Mapper(componentModel = "spring")
public interface DownloadLogConvert {
@Mapping(source = "downloadLog.id", target = "id")
@Mapping(source = "storageSource.name", target = "storageName")
@Mapping(source = "storageSource.type", target = "storageType")
DownloadLogResult entityToResultList(DownloadLog downloadLog, StorageSource storageSource);
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.admin.convert;
import im.zhaojun.zfile.admin.model.entity.ShortLink;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.admin.model.result.link.ShortLinkResult;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.stereotype.Component;
/**
* @author zhaojun
*/
@Component
@Mapper(componentModel = "spring")
public interface ShortLinkConvert {
@Mapping(source = "shortLink.id", target = "id")
@Mapping(source = "storageSource.name", target = "storageName")
@Mapping(source = "storageSource.type", target = "storageType")
ShortLinkResult entityToResultList(ShortLink shortLink, StorageSource storageSource);
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.admin.exception;
import lombok.Getter;
/**
* 禁止的文件操作异常
*
* @author zhaojun
*/
@Getter
public class ForbidFileOperationException extends RuntimeException {
private final Integer storageId;
private final String action;
public ForbidFileOperationException(Integer storageId, String action) {
this.storageId = storageId;
this.action = action;
}
}

View File

@@ -0,0 +1,21 @@
package im.zhaojun.zfile.admin.exception;
import im.zhaojun.zfile.admin.model.param.IStorageParam;
import lombok.Getter;
/**
* 存储源自动设置 cors 异常
*
* @author zhaojun
*/
@Getter
public class StorageSourceAutoConfigCorsException extends RuntimeException {
private final IStorageParam iStorageParam;
public StorageSourceAutoConfigCorsException(String message, Throwable cause, IStorageParam iStorageParam) {
super(message, cause);
this.iStorageParam = iStorageParam;
}
}

View File

@@ -0,0 +1,15 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.DownloadLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 下载日志 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface DownloadLogMapper extends BaseMapper<DownloadLog> {
}

View File

@@ -0,0 +1,61 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.FilterConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 过滤器配置表 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface FilterConfigMapper extends BaseMapper<FilterConfig> {
/**
* 根据存储源 ID 获取存储源配置列表
*
* @param storageId
* 存储源 ID
*
* @return 存储源过滤器配置列表
*/
List<FilterConfig> findByStorageId(@Param("storageId") Integer storageId);
/**
* 根据存储源 ID 删除过滤器配置
*
* @param storageId
* 存储源 ID
*
* @return 删除条数
*/
int deleteByStorageId(@Param("storageId") Integer storageId);
/**
* 获取所有类型为禁止访问的过滤规则
*
* @param storageId
* 存储 ID
*
* @return 禁止访问的过滤规则列表
*/
List<FilterConfig> findByStorageIdAndInaccessible(@Param("storageId")Integer storageId);
/**
* 获取所有类型为禁止下载的过滤规则
*
* @param storageId
* 存储 ID
*
* @return 禁止下载的过滤规则列表
*/
List<FilterConfig> findByStorageIdAndDisableDownload(@Param("storageId")Integer storageId);
}

View File

@@ -0,0 +1,39 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.PasswordConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 存储源密码配置表 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface PasswordConfigMapper extends BaseMapper<PasswordConfig> {
/**
* 根据存储源 ID 获取密码规则配置
*
* @param storageId
* 存储源 ID
*
* @return 存储源密码规则配置列表
*/
List<PasswordConfig> findByStorageId(@Param("storageId") Integer storageId);
/**
* 根据存储源 ID 删除要密码规则配置
*
* @param storageId
* 存储源 ID
*
* @return 删除记录数
*/
int deleteByStorageId(@Param("storageId") Integer storageId);
}

View File

@@ -0,0 +1,40 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.ReadmeConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 存储源文档配置表 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface ReadmeConfigMapper extends BaseMapper<ReadmeConfig> {
/**
* 根据存储源 ID 查询文档配置
*
* @param storageId
* 存储源ID
*
* @return 存储源文档配置列表
*/
List<ReadmeConfig> findByStorageId(@Param("storageId") Integer storageId);
/**
* 根据存储源 ID 删除文档配置
*
* @param storageId
* 存储源ID
*
* @return 删除记录数
*/
int deleteByStorageId(@Param("storageId") Integer storageId);
}

View File

@@ -0,0 +1,41 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.ShortLink;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 短链接配置表 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface ShortLinkMapper extends BaseMapper<ShortLink> {
/**
* 根据短链接 key 查询短链接
*
* @param key
* 短链接 key
*
* @return 短链接信息
*/
ShortLink findByKey(@Param("key")String key);
/**
* 根据存储源 ID 和文件路径查询短链接
*
* @param storageId
* 存储源 ID
*
* @param url
* 短链接 url
*
* @return 短链接信息
*/
ShortLink findByStorageIdAndUrl(@Param("storageId") Integer storageId, @Param("url") String url);
}

View File

@@ -0,0 +1,65 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.StorageSourceConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 存储源拓展设置 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface StorageSourceConfigMapper extends BaseMapper<StorageSourceConfig> {
/**
* 根据存储源 ID 查询存储源拓展配置, 并按照存储源 id 排序
*
* @param storageId
* 存储源 ID
*
* @return 存储源拓展配置列表
*/
List<StorageSourceConfig> findByStorageIdOrderById(@Param("storageId") Integer storageId);
/**
* 获取指定存储源的指定参数名称
*
* @param storageId
* 存储源 id
*
* @param name
* 参数名
*
* @return 参数信息
*/
StorageSourceConfig findByStorageIdAndName(@Param("storageId") Integer storageId, @Param("name") String name);
/**
* 根据存储源 ID 删除存储源拓展配置
*
* @param storageId
* 存储源 ID
*
* @return 删除记录数
*/
int deleteByStorageId(@Param("storageId") Integer storageId);
/**
* 批量插入存储源拓展配置
*
* @param list
* 存储源拓展配置列表
*
* @return 插入记录数
*/
int insertList(@Param("list") List<StorageSourceConfig> list);
}

View File

@@ -0,0 +1,98 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.StorageSource;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 存储源基本配置 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface StorageSourceMapper extends BaseMapper<StorageSource> {
/**
* 获取所有已启用的存储源, 并按照存储源排序值排序
*
* @return 存储源列表
*/
List<StorageSource> findListByEnableOrderByOrderNum();
/**
* 获取所有存储源, 并按照存储源排序值排序
*
* @return 存储源列表
*/
List<StorageSource> findAllOrderByOrderNum();
/**
* 获取存储源 ID 最大值
*
* @return 存储源 ID 最大值
*/
Integer selectMaxId();
/**
* 根据存储源类型获取存储源列表
*
* @param type
* 存储源类型
*
* @return 存储源列表
*/
List<StorageSource> findByType(@Param("type") StorageTypeEnum type);
/**
* 根据存储源 ID 设置排序值
*
* @param orderNum
* 排序值
*
* @param id
* 存储源 ID
*/
void updateSetOrderNumById(@Param("orderNum") int orderNum, @Param("id") Integer id);
/**
* 根据存储源 key 获取存储源
*
* @param storageKey
* 存储源 key
*
* @return 存储源信息
*/
StorageSource findByStorageKey(@Param("storageKey") String storageKey);
/**
* 根据存储源 key 获取存储源 id
*
* @param storageKey
* 存储源 key
*
* @return 存储源 id
*/
Integer findIdByStorageKey(@Param("storageKey") String storageKey);
/**
* 根据存储源 id 获取存储源 key
*
* @param id
* 存储源 id
*
* @return 存储源 key
*/
String findKeyById(@Param("id")Integer id);
}

View File

@@ -0,0 +1,48 @@
package im.zhaojun.zfile.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import im.zhaojun.zfile.admin.model.entity.SystemConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 系统配置 Mapper 接口
*
* @author zhaojun
*/
@Mapper
public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
/**
* 获取所有系统设置
*
* @return 系统设置列表
*/
List<SystemConfig> findAll();
/**
* 根据系统设置名称获取设置信息
*
* @param name
* 系统设置名称
*
* @return 系统设置信息
*/
SystemConfig findByName(@Param("name")String name);
/**
* 批量保存系统设置
*
* @param list
* 系统设置列表
*
* @return 保存记录数
*/
int saveAll(@Param("list")List<SystemConfig> list);
}

View File

@@ -1,9 +1,11 @@
package im.zhaojun.zfile.model.support;
package im.zhaojun.zfile.admin.model.dto;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
/**
* OneDrive Token DTO
*
* @author zhaojun
*/
@Data
@@ -14,4 +16,5 @@ public class OneDriveToken {
@JSONField(name = "refresh_token")
private String refreshToken;
}
}

View File

@@ -1,18 +1,20 @@
package im.zhaojun.zfile.cache;
package im.zhaojun.zfile.admin.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 缓存对象用户表示那个存储源的那个文件夹.
*
* @author zhaojun
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DriveCacheKey {
public class StorageSourceCacheKey {
private Integer driveId;
private Integer storageId;
private String key;

View File

@@ -0,0 +1,65 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 文件下载日志 entity
*
* @author zhaojun
*/
@Data
@ApiModel(value="文件下载日志")
@TableName(value = "`download_log`")
public class DownloadLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@TableField(value = "`path`")
@ApiModelProperty(value="文件路径")
private String path;
@TableField(value = "`storage_key`")
@ApiModelProperty(value="存储源 key")
private String storageKey;
@TableField(value = "`create_time`")
@ApiModelProperty(value="访问时间")
private Date createTime;
@TableField(value = "`ip`")
@ApiModelProperty(value="访问 ip")
private String ip;
@TableField(value = "short_key")
@ApiModelProperty(value = "短链 key", example = "voldd3")
private String shortKey;
@TableField(value = "`user_agent`")
@ApiModelProperty(value="访问 user_agent")
private String userAgent;
@TableField(value = "`referer`")
@ApiModelProperty(value="访问 referer")
private String referer;
}

View File

@@ -0,0 +1,52 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import im.zhaojun.zfile.admin.model.enums.FilterConfigHiddenModeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 存储源过滤配置 entity
*
* @author zhaojun
*/
@Data
@ApiModel(value="存储源过滤配置")
@TableName(value = "filter_config")
public class FilterConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
@JsonIgnore
private Integer id;
@TableField(value = "storage_id")
@ApiModelProperty(value = "存储源 ID", required = true, example = "1")
private Integer storageId;
@TableField(value = "expression")
@ApiModelProperty(value = "过滤表达式", required = true, example = "/*.png")
private String expression;
@TableField(value = "description")
@ApiModelProperty(value = "表达式描述", required = true, example = "用来辅助记忆表达式")
private String description;
@TableField(value = "mode")
@ApiModelProperty(value = "模式", required = true, example = "隐藏模式,仅隐藏: hidden, 隐藏后不可访问: inaccessible, 隐藏后不可下载: disable_download")
private FilterConfigHiddenModeEnum mode;
}

View File

@@ -0,0 +1,51 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 密码设置 entity
*
* @author zhaojun
*/
@Data
@ApiModel(value="密码设置")
@TableName(value = "password_config")
public class PasswordConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
@JsonIgnore
private Integer id;
@TableField(value = "storage_id")
@ApiModelProperty(value = "存储源 ID", required = true, example = "1")
private Integer storageId;
@TableField(value = "expression")
@ApiModelProperty(value = "密码文件夹表达式", required = true, example = "/*.png")
private String expression;
@TableField(value = "password")
@ApiModelProperty(value = "密码值", required = true, example = "123456")
private String password;
@TableField(value = "description")
@ApiModelProperty(value = "表达式描述", required = true, example = "用来辅助记忆表达式")
private String description;
}

View File

@@ -0,0 +1,57 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import im.zhaojun.zfile.admin.model.enums.ReadmeDisplayModeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* readme 文档配置 entity
*
* @author zhaojun
*/
@Data
@ApiModel(value="readme 文档配置")
@TableName(value = "`readme_config`")
public class ReadmeConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
@JsonIgnore
private Integer id;
@TableField(value = "`storage_id`")
@ApiModelProperty(value="存储源 ID")
private Integer storageId;
@TableField(value = "`description`")
@ApiModelProperty(value = "表达式描述", required = true, example = "用来辅助记忆表达式")
private String description;
@TableField(value = "`expression`")
@ApiModelProperty(value="路径表达式")
private String expression;
@TableField(value = "`readme_text`")
@ApiModelProperty(value="readme 文本内容, 支持 md 语法.")
private String readmeText;
@TableField(value = "`display_mode`")
@ApiModelProperty(value = "显示模式", required = true, example = "readme 显示模式,支持顶部显示: top, 底部显示:bottom, 弹窗显示: dialog")
private ReadmeDisplayModeEnum displayMode;
}

View File

@@ -0,0 +1,48 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 短链信息 entity
*/
@Data
@ApiModel(description = "短链信息")
@TableName(value = "short_link")
public class ShortLink implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@TableField(value = "storage_id")
@ApiModelProperty(value = "存储源 ID", example = "1")
private Integer storageId;
@TableField(value = "short_key")
@ApiModelProperty(value = "短链 key", example = "voldd3")
private String shortKey;
@TableField(value = "url")
@ApiModelProperty(value = "短链 url", example = "/directlink/1/test02.png")
private String url;
@TableField(value = "create_date")
@ApiModelProperty(value = "创建时间", example = "2021-11-22 10:05")
private Date createDate;
}

View File

@@ -0,0 +1,106 @@
package im.zhaojun.zfile.admin.model.entity;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import im.zhaojun.zfile.admin.model.enums.SearchModeEnum;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 存储源基本属性 entity
*/
@Data
@ApiModel(description = "存储源基本属性")
@TableName(value = "storage_source")
public class StorageSource implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@TableField(value = "`enable`")
@ApiModelProperty(value = "是否启用", example = "true")
private Boolean enable;
@TableField(value = "`enable_file_operator`")
@ApiModelProperty(value = "是否启用文件操作功能", example = "true", notes = "是否启用文件上传,编辑,删除等操作.")
private Boolean enableFileOperator;
@TableField(value = "`enable_file_anno_operator`")
@ApiModelProperty(value = "是否允许匿名进行文件操作", example = "true", notes = "是否允许匿名进行文件上传,编辑,删除等操作.")
private Boolean enableFileAnnoOperator;
@TableField(value = "`enable_cache`")
@ApiModelProperty(value = "是否开启缓存", example = "true")
private Boolean enableCache;
@TableField(value = "`name`")
@ApiModelProperty(value = "存储源名称", example = "阿里云 OSS 存储")
private String name;
@TableField(value = "`key`")
@ApiModelProperty(value = "存储源别名", example = "存储源别名,用于 URL 中展示, 如 http://ip:port/{存储源别名}")
private String key;
@TableField(value = "`remark`")
@ApiModelProperty(value = "存储源备注", example = "这是一个备注信息, 用于管理员区分不同的存储源, 此字段仅管理员可见")
private String remark;
@TableField(value = "auto_refresh_cache")
@ApiModelProperty(value = "是否开启缓存自动刷新", example = "true")
private Boolean autoRefreshCache;
@TableField(value = "`type`")
@ApiModelProperty(value = "存储源类型")
private StorageTypeEnum type;
@TableField(value = "search_enable")
@ApiModelProperty(value = "是否开启搜索", example = "true")
private Boolean searchEnable;
@TableField(value = "search_ignore_case")
@ApiModelProperty(value = "搜索是否忽略大小写", example = "true")
private Boolean searchIgnoreCase;
@TableField(value = "`search_mode`")
@ApiModelProperty(value = "搜索模式", example = "SEARCH_CACHE", notes = "仅从缓存中搜索或直接全量搜索")
private SearchModeEnum searchMode;
@TableField(value = "order_num")
@ApiModelProperty(value = "排序值", example = "1")
private Integer orderNum;
@TableField(value = "default_switch_to_img_mode")
@ApiModelProperty(value = "是否默认开启图片模式", example = "true")
private Boolean defaultSwitchToImgMode;
public boolean allowOperator() {
// 允许文件操作,且允许匿名操作或者当前登录用户是管理员
return BooleanUtil.isTrue(enableFileOperator) && (BooleanUtil.isTrue(enableFileAnnoOperator) || StpUtil.isLogin());
}
}

View File

@@ -0,0 +1,53 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 存储源拓展属性 entity
*/
@Data
@ApiModel(description = "存储源拓展属性")
@TableName(value = "storage_source_config")
public class StorageSourceConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@TableField(value = "`name`")
@ApiModelProperty(value = "存储源属性名称 name", example = "bucketName")
private String name;
@TableField(value = "`type`")
@ApiModelProperty(value = "存储源类型")
private StorageTypeEnum type;
@TableField(value = "title")
@ApiModelProperty(value = "存储源属性名称", example = "Bucket 名称")
private String title;
@TableField(value = "storage_id")
@ApiModelProperty(value = "存储源 id", example = "1")
private Integer storageId;
@TableField(value = "`value`")
@ApiModelProperty(value = "存储源对应的值", example = "my-bucket")
private String value;
}

View File

@@ -0,0 +1,42 @@
package im.zhaojun.zfile.admin.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 系统设置 entity
*/
@Data
@ApiModel(description = "系统设置")
@TableName(value = "system_config")
public class SystemConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@TableField(value = "name")
@ApiModelProperty(value = "系统设置名称", example = "siteName")
private String name;
@TableField(value = "`value`")
@ApiModelProperty(value = "系统设置值", example = "ZFile 演示站")
private String value;
@TableField(value = "title")
@ApiModelProperty(value = "系统设置描述", example = "站点名称")
private String title;
}

View File

@@ -0,0 +1,58 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文件操作类型枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum FileOperatorTypeEnum {
/**
* 获取文件上传链接操作
*/
UPLOAD("上传", "upload"),
/**
* 新建文件夹操作
*/
NEW_FOLDER("新建文件夹", "new_folder"),
/**
* 删除文件&文件夹操作
*/
DELETE("删除", "delete"),
/**
* 重命名文件&文件夹操作
*/
RENAME("重命名", "rename"),
/**
* 复制文件&文件夹操作
*/
COPY("复制", "copy"),
/**
* 移动文件&文件夹操作
*/
MOVE("移动", "move"),
/**
* 搜索操作
*/
SEARCH("搜索", "search");
private final String name;
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,36 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文件夹隐藏模式枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum FilterConfigHiddenModeEnum {
/**
* 仅隐藏
*/
HIDDEN("hidden"),
/**
* 隐藏并不可访问 (针对目录)
*/
INACCESSIBLE("inaccessible"),
/**
* 隐藏并不可访问 (针对文件)
*/
DISABLE_DOWNLOAD("disable_download");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,36 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登陆验证方式枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum LoginVerifyModeEnum {
/**
* 不启用登陆模式
*/
OFF_MODE("off"),
/**
* 图形验证码模式
*/
IMG_VERIFY_MODE("image"),
/**
* 图形验证码模式
*/
TWO_FACTOR_AUTHENTICATION_MODE("2fa");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,36 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Readme 展示模式枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum ReadmeDisplayModeEnum {
/**
* 顶部显示
*/
TOP("top"),
/**
* 底部显示
*/
BOTTOM("bottom"),
/**
* 弹窗显示
*/
DIALOG("dialog");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,36 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Referer 防盗链类型枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum RefererTypeEnum {
/**
* 不启用 Referer 防盗链
*/
OFF("off"),
/**
* 启用白名单模式
*/
WHITE_LIST("white_list"),
/**
* 启用黑名单模式
*/
BLACK_LIST("black_list");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,31 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文件搜索模式枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum SearchModeEnum {
/**
* 仅搜索缓存
*/
SEARCH_CACHE_MODE("SEARCH_CACHE"),
/**
* 搜索全部
*/
SEARCH_ALL_MODE("SEARCH_ALL");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,36 @@
package im.zhaojun.zfile.admin.model.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 存储源参数类型枚举
*
* @author zhaojun
*/
@Getter
@AllArgsConstructor
public enum StorageParamTypeEnum {
/**
* 输入框
*/
INPUT("input"),
/**
* 下拉框
*/
SELECT("select"),
/**
* 开关
*/
SWITCH("switch");
@EnumValue
@JsonValue
private final String value;
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* 阿里云初始化参数
*
* @author zhaojun
*/
@Getter
public class AliyunParam extends S3BaseParam {
}

View File

@@ -0,0 +1,41 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import im.zhaojun.zfile.admin.annoation.select.impl.EncodingStorageParamSelect;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import lombok.Getter;
/**
* 本地存储初始化参数
*
* @author zhaojun
*/
@Getter
public class FtpParam extends ProxyDownloadParam {
@StorageParamItem(name = "域名或 IP")
private String host;
@StorageParamItem(name = "端口")
private int port;
@StorageParamItem(name = "编码格式",
defaultValue = "UTF-8",
type = StorageParamTypeEnum.SELECT,
optionsClass = EncodingStorageParamSelect.class,
description = "表示文件夹及文件名称的编码格式,不表示文本内容的编码格式.")
private String encoding;
@StorageParamItem(name = "用户名", required = false)
private String username;
@StorageParamItem(name = "密码", required = false)
private String password;
@StorageParamItem(name = "加速域名", required = false, description = "如不配置加速域名,则使用服务器中转下载, 反之则使用加速域名下载.")
private String domain;
@StorageParamItem(name = "基路径", defaultValue = "/", description = "基路径表示读取的根文件夹,不填写表示允许读取所有。如: '/''/文件夹1'")
private String basePath;
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* 华为云初始化参数
*
* @author zhaojun
*/
@Getter
public class HuaweiParam extends S3BaseParam {
}

View File

@@ -0,0 +1,5 @@
package im.zhaojun.zfile.admin.model.param;
public interface IStorageParam {
}

View File

@@ -0,0 +1,18 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* 本地存储初始化参数
*
* @author zhaojun
*/
@Getter
public class LocalParam extends ProxyDownloadParam {
@StorageParamItem(name = "文件路径", description = "只支持绝对路径<br>Docker 部署需提前映射宿主机路径! " +
"(<a class='link' target='_blank' href='https://docs.docker.com/engine/reference/run/#volume-shared-filesystems'>配置文档</a>)")
private String filePath;
}

View File

@@ -0,0 +1,34 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* 微软云初始化参数
*
* @author zhaojun
*/
@Getter
public class MicrosoftDriveParam implements IStorageParam {
@StorageParamItem(name = "clientId", defaultValue = "${zfile.onedrive.clientId}", order = 1,
description = "可自行更改,但修改后,下方获取访问令牌的地址不可用,需自行获取访问令牌和刷新令牌.")
private String clientId;
@StorageParamItem(name = "SecretKey", defaultValue = "${zfile.onedrive.clientSecret}", order = 2)
private String clientSecret;
@StorageParamItem(name = "访问令牌", link = "/onedrive/authorize", linkName = "前往获取令牌", order = 3)
private String accessToken;
@StorageParamItem(name = "刷新令牌", order = 4)
private String refreshToken;
@StorageParamItem(name = "反代域名", required = false, order = 7,
link = "https://docs.zfile.vip/#/advanced?id=onedrive-cf", linkName = "配置文档")
private String proxyDomain;
@StorageParamItem(name = "基路径", defaultValue = "/", order = 8, description = "基路径表示读取的根文件夹,不填写表示允许读取所有。如: '/''/文件夹1'")
private String basePath;
}

View File

@@ -0,0 +1,20 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* MinIO 初始化参数
*
* @author zhaojun
*/
@Getter
public class MinIOParam extends S3BaseParam {
@StorageParamItem(name = "地域")
private String region;
@StorageParamItem(name = "服务地址", description = "为 minio 的服务地址,非 web 访问地址,一般为 http://ip:9000")
private String endPoint;
}

View File

@@ -0,0 +1,24 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* OneDrive 初始化参数
*
* @author zhaojun
*/
@Getter
public class OneDriveChinaParam extends OneDriveParam {
@StorageParamItem(name = "clientId", defaultValue = "${zfile.onedrive-china.clientId}",
description = "可自行更改,但修改后,则下方获取访问令牌的地址不可用,需自行获取访问令牌和刷新令牌.", order = 1)
private String clientId;
@StorageParamItem(name = "SecretKey", defaultValue = "${zfile.onedrive-china.clientSecret}", order = 2)
private String clientSecret;
@StorageParamItem(name = "访问令牌", link = "/onedrive/china-authorize", linkName = "前往获取令牌", order = 3)
private String accessToken;
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* OneDrive 初始化参数
*
* @author zhaojun
*/
@Getter
public class OneDriveParam extends MicrosoftDriveParam {
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* 代理下载参数
*
* @author zhaojun
*/
@Getter
public class ProxyDownloadParam extends ProxyTransferParam {
}

View File

@@ -0,0 +1,24 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import lombok.Getter;
/**
* 代理上传下载参数
*
* @author zhaojun
*/
@Getter
public class ProxyTransferParam implements IStorageParam {
@StorageParamItem(name = "加速域名", required = false, description = "如不配置加速域名,则使用服务器中转下载, 反之则使用加速域名下载.")
private String domain;
@StorageParamItem(name = "生成签名链接", type = StorageParamTypeEnum.SWITCH, defaultValue = "true", description = "下载会生成带签名的下载链接, 如不想对外开放直链, 可以防止被当做直链使用.")
private boolean isPrivate;
@StorageParamItem(name = "下载签名有效期", required = false, defaultValue = "1800", description = "用于下载签名的有效期, 单位为秒, 如不配置则默认为 1800 秒.")
private Integer tokenTime;
}

View File

@@ -0,0 +1,10 @@
package im.zhaojun.zfile.admin.model.param;
/**
* 代理上传参数
*
* @author zhaojun
*/
public class ProxyUploadParam extends ProxyTransferParam {
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* 七牛云初始化参数
*
* @author zhaojun
*/
@Getter
public class QiniuParam extends S3BaseParam {
}

View File

@@ -0,0 +1,43 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import lombok.Getter;
/**
* S3 通用参数
*
* @author zhaojun
*/
@Getter
public class S3BaseParam implements IStorageParam {
@StorageParamItem(name = "AccessKey", order = 1)
private String accessKey;
@StorageParamItem(name = "SecretKey", order = 2)
private String secretKey;
@StorageParamItem(name = "区域", order = 3, description = "如下拉列表中没有的区域,或想使用内网地址,可直接输入后回车,如: xxx-cn-beijing.example.com")
private String endPoint;
@StorageParamItem(name = "存储空间名称", order = 4)
private String bucketName;
@StorageParamItem(name = "Bucket 域名 / CDN 加速域名", required = false, order = 5)
private String domain;
@StorageParamItem(name = "基路径", order = 6, required = false, defaultValue = "/", description = "基路径表示读取的根文件夹,不填写表示允许读取所有。如: '/''/文件夹1'")
private String basePath;
@StorageParamItem(name = "是否是私有空间", order = 7, type = StorageParamTypeEnum.SWITCH, defaultValue = "true", description = "私有空间会生成带签名的下载链接")
private boolean isPrivate;
@StorageParamItem(name = "下载签名有效期", required = false, defaultValue = "1800", description = "当为私有空间时, 用于下载签名的有效期, 单位为秒, 如不配置则默认为 1800 秒.")
private Integer tokenTime;
@StorageParamItem(name = "是否自动配置 CORS 跨域设置", order = 100, type = StorageParamTypeEnum.SWITCH, defaultValue = "true", description = "如不配置跨域设置,可能会无法导致无法上传,或上传后看不到文件 <br> 此配置会覆盖之前的跨域配置,如您已经配置过,可忽略此选项")
private boolean autoConfigCors;
}

View File

@@ -0,0 +1,28 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import im.zhaojun.zfile.admin.annoation.StorageParamSelectOption;
import im.zhaojun.zfile.admin.model.enums.StorageParamTypeEnum;
import lombok.Getter;
/**
* S3 初始化参数
*
* @author zhaojun
*/
@Getter
public class S3Param extends S3BaseParam {
@StorageParamItem(name = "EndPoint", order = 3)
private String endPoint;
@StorageParamItem(name = "地域", order = 3)
private String region;
@StorageParamItem(name = "域名风格", type = StorageParamTypeEnum.SWITCH,
options = { @StorageParamSelectOption(value = "path-style", label = "path-style"),
@StorageParamSelectOption(value = "bucket-virtual-hosting", label = "bucket-virtual-hosting")},
linkName = "查看 S3 API 说明文档", link = "https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/VirtualHosting.html#path-style-access")
private String pathStyle;
}

View File

@@ -0,0 +1,13 @@
package im.zhaojun.zfile.admin.model.param;
import lombok.Getter;
/**
* SFTP 初始化参数
*
* @author zhaojun
*/
@Getter
public class SftpParam extends FtpParam {
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
/**
* SharePoint 世纪互联初始化参数
*
* @author zhaojun
*/
public class SharePointChinaParam extends SharePointParam {
@StorageParamItem(name = "clientId", defaultValue = "${zfile.onedrive-china.clientId}", order = 1,
description = "可自行更改,但修改后,则下方获取访问令牌的地址不可用,需自行获取访问令牌和刷新令牌.")
private String clientId;
@StorageParamItem(name = "SecretKey", defaultValue = "${zfile.onedrive-china.clientSecret}", order = 2)
private String clientSecret;
@StorageParamItem(name = "访问令牌", link = "/onedrive/china-authorize", linkName = "前往获取令牌", order = 3)
private String accessToken;
}

View File

@@ -0,0 +1,27 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* SharePoint 初始化参数
*
* @author zhaojun
*/
@Getter
public class SharePointParam extends MicrosoftDriveParam {
@StorageParamItem(name = "clientId", defaultValue = "${zfile.onedrive.clientId}", order = 1,
description = "可自行更改,但修改后,下方获取访问令牌的地址不可用,需自行获取访问令牌和刷新令牌.")
private String clientId;
@StorageParamItem(name = "SecretKey", defaultValue = "${zfile.onedrive.clientSecret}", order = 2)
private String clientSecret;
@StorageParamItem(name = "网站", order = 5)
private String siteId;
@StorageParamItem(name = "子目录", order = 6, description = "表示 SharePoint 子列表/子网站,在世纪互联网站 Tab 卡中 \"网站内容\" 新增.")
private String listId;
}

View File

@@ -0,0 +1,17 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* 腾讯云初始化参数
*
* @author zhaojun
*/
@Getter
public class TencentParam extends S3BaseParam {
@StorageParamItem(key = "secretId", name = "SecretId", order = 1)
private String accessKey;
}

View File

@@ -0,0 +1,35 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* 又拍云初始化参数
*
* @author zhaojun
*/
@Getter
public class UpYunParam implements IStorageParam {
@StorageParamItem(name = "存储空间名称")
private String bucketName;
@StorageParamItem(name = "用户名")
private String username;
@StorageParamItem(name = "密码")
private String password;
@StorageParamItem(name = "下载域名", description = "填写您在又拍云绑定的域名.")
private String domain;
@StorageParamItem(name = "基路径", defaultValue = "/", description = "基路径表示读取的根文件夹,不填写表示允许读取所有。如: '/''/文件夹1'")
private String basePath;
@StorageParamItem(name = "Token", required = false, link = "https://help.upyun.com/knowledge-base/cdn-token-limite/", linkName = "官方配置文档",description = "可在又拍云后台开启 \"访问控制\" -> \"Token 防盗链\",控制资源内容的访问时限,即时间戳防盗链。")
private String token;
@StorageParamItem(name = "Token 有效期", required = false, defaultValue = "1800", description = "Token (防盗链)有效期,单位为秒。")
private int tokenTime;
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.zfile.admin.model.param;
import im.zhaojun.zfile.admin.annoation.StorageParamItem;
import lombok.Getter;
/**
* WebDav 初始化参数
*
* @author zhaojun
*/
@Getter
public class WebdavParam extends ProxyDownloadParam {
@StorageParamItem(key = "url", name = "WebDAV 链接")
private String url;
@StorageParamItem(key = "username", name = "用户名", required = false)
private String username;
@StorageParamItem(key = "password", name = "密码", required = false)
private String password;
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.zfile.admin.model.request;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 下载排行请求类
*
* @author zhaojun
*/
@Data
public class DownloadTopInfoRequest {
@ApiModelProperty(value = "排行数量", required = true)
private Integer top;
@ApiModelProperty(value = "开始时间")
private String startTime;
@ApiModelProperty(value = "结束时间")
private String endTime;
}

View File

@@ -0,0 +1,69 @@
package im.zhaojun.zfile.admin.model.request;
import com.baomidou.mybatisplus.annotation.TableField;
import im.zhaojun.zfile.admin.model.enums.SearchModeEnum;
import im.zhaojun.zfile.home.model.dto.StorageSourceAllParam;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 保存存储源信息请求类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "存储源基本参数")
public class SaveStorageSourceRequest {
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@ApiModelProperty(value = "存储源名称", example = "阿里云 OSS 存储")
private String name;
@ApiModelProperty(value = "存储源别名", example = "存储源别名,用于 URL 中展示, 如 http://ip:port/{存储源别名}")
private String key;
@ApiModelProperty(value = "存储源备注", example = "这是一个备注信息, 用于管理员区分不同的存储源, 此字段仅管理员可见")
private String remark;
@ApiModelProperty(value = "存储源类型", example = "ftp")
private StorageTypeEnum type;
@ApiModelProperty(value = "是否启用", example = "true")
private Boolean enable;
@ApiModelProperty(value = "是否启用文件操作功能", example = "true", notes = "是否启用文件上传,编辑,删除等操作.")
private Boolean enableFileOperator;
@ApiModelProperty(value = "是否允许匿名进行文件操作", example = "true", notes = "是否允许匿名进行文件上传,编辑,删除等操作.")
private Boolean enableFileAnnoOperator;
@ApiModelProperty(value = "是否开启缓存", example = "true")
private boolean enableCache;
@ApiModelProperty(value = "是否开启缓存自动刷新", example = "true")
private boolean autoRefreshCache;
@ApiModelProperty(value = "是否开启搜索", example = "true")
private boolean searchEnable;
@ApiModelProperty(value = "搜索是否忽略大小写", example = "true")
private boolean searchIgnoreCase;
@TableField(value = "`search_mode`")
@ApiModelProperty(value = "搜索模式", example = "SEARCH_CACHE", notes = "仅从缓存中搜索或直接全量搜索")
private SearchModeEnum searchMode;
@ApiModelProperty(value = "排序值", example = "1")
private Integer orderNum;
@ApiModelProperty(value = "存储源拓展属性")
private StorageSourceAllParam storageSourceAllParam;
@ApiModelProperty(value = "是否默认开启图片模式", example = "true")
private boolean defaultSwitchToImgMode;
}

View File

@@ -0,0 +1,46 @@
package im.zhaojun.zfile.admin.model.request.link;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* 查询下载日志请求参数
*
* @author zhaojun
*/
@Data
public class QueryDownloadLogRequest {
@ApiModelProperty(value="文件路径")
private String path;
@ApiModelProperty(value="存储源 key")
private String storageKey;
@ApiModelProperty(value="短链 key")
private String shortKey;
@ApiModelProperty(value="访问时间从")
private String dateFrom;
@ApiModelProperty(value="访问时间至")
private String dateTo;
@ApiModelProperty(value="访问 ip")
private String ip;
@ApiModelProperty(value="访问 user_agent")
private String userAgent;
@ApiModelProperty(value="访问 referer")
private String referer;
@NotEmpty(message = "分页页数不能为空")
private Integer page;
@NotEmpty(message = "每页条数不能为空")
private Integer limit;
}

View File

@@ -0,0 +1,28 @@
package im.zhaojun.zfile.admin.model.request.login;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 验证 2FA 认证返回结果
*
* @author zhaojun
*/
@Data
@AllArgsConstructor
@ApiModel(description = "验证二步验证结果")
public class VerifyLogin2FARequest {
@ApiModelProperty(value = "二步验证二维码", required = true, example = "EwBoxxxxxxxxxxxxxxxbAI=")
@NotBlank(message = "二步验证密钥不能为空")
private String secret;
@ApiModelProperty(value = "APP 生成的二步验证验证码", required = true, example = "125612")
@NotBlank(message = "二步验证验证码不能为空")
private String code;
}

View File

@@ -0,0 +1,34 @@
package im.zhaojun.zfile.admin.model.request.s3;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 获取 S3 bucket 列表请求类
*
* @author zhaojun
*/
@Data
@ApiModel(value="S3 bucket 列表请求类")
public class GetS3BucketListRequest {
@NotBlank(message = "accessKey 不能为空")
@ApiModelProperty(value = "accessKey", required = true, example = "XQEWQJI129JAS12")
private String accessKey;
@NotBlank(message = "secretKey 不能为空")
@ApiModelProperty(value = "secretKey", required = true, example = "EWQJI129JAS11AE2")
private String secretKey;
@NotBlank(message = "EndPoint 不能为空")
@ApiModelProperty(value = "Endpoint 接入点", required = true, example = "oss-cn-beijing.aliyuncs.com")
private String endPoint;
@ApiModelProperty(value = "Endpoint 接入点", required = true, example = "cn-beijing")
// @NotBlank(message = "地域不能为空")
private String region;
}

View File

@@ -0,0 +1,41 @@
package im.zhaojun.zfile.admin.model.request.setting;
import im.zhaojun.zfile.admin.model.enums.RefererTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 直链设置请求参数类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "直链设置请求参数类")
public class UpdateLinkSettingRequest {
@ApiModelProperty(value = "是否记录下载日志", example = "true")
private Boolean recordDownloadLog;
@ApiModelProperty(value = "直链 Referer 防盗链类型")
private RefererTypeEnum refererType;
@ApiModelProperty(value = "直链 Referer 是否允许为空")
private Boolean refererAllowEmpty;
@ApiModelProperty(value = "直链 Referer 值")
private String refererValue;
@ApiModelProperty(value = "直链地址前缀")
private String directLinkPrefix;
@ApiModelProperty(value = "是否显示生成直链功能(含直链和路径短链)", example = "true", required = true)
private Boolean showLinkBtn;
@ApiModelProperty(value = "是否显示生成短链功能", example = "true", required = true)
private Boolean showShortLink;
@ApiModelProperty(value = "是否显示生成路径链接功能", example = "true", required = true)
private Boolean showPathLink;
}

View File

@@ -0,0 +1,26 @@
package im.zhaojun.zfile.admin.model.request.setting;
import im.zhaojun.zfile.admin.model.enums.LoginVerifyModeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 登陆安全设置请求参数类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "登陆安全设置请求参数类")
public class UpdateSecuritySettingRequest {
@ApiModelProperty(value = "是否在前台显示登陆按钮", example = "true")
private Boolean showLogin;
@ApiModelProperty(value = "登陆验证方式,支持验证码和 2FA 认证")
private LoginVerifyModeEnum loginVerifyMode;
@ApiModelProperty(value = "登陆验证 Secret")
private String loginVerifySecret;
}

View File

@@ -0,0 +1,35 @@
package im.zhaojun.zfile.admin.model.request.setting;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 站点设置请求参数类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "站点设置请求参数类")
public class UpdateSiteSettingRequest {
@ApiModelProperty(value = "站点名称", required = true, example = "ZFile Site Name")
@NotBlank(message = "站点名称不能为空")
private String siteName;
@ApiModelProperty(value = "站点域名", required = true, example = "https://zfile.vip")
@NotBlank(message = "站点域名不能为空")
private String domain;
@ApiModelProperty(value = "前端域名", notes = "前端域名,前后端分离情况下需要配置.", example = "http://xxx.example.com")
private String frontDomain;
@ApiModelProperty(value = "头像地址", example = "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png")
private String avatar;
@ApiModelProperty(value = "备案号", example = "冀ICP备12345678号-1")
private String icp;
}

View File

@@ -0,0 +1 @@
package im.zhaojun.zfile.admin.model.request.setting;

View File

@@ -0,0 +1,52 @@
package im.zhaojun.zfile.admin.model.request.setting;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 显示设置请求参数类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "显示设置请求参数类")
public class UpdateViewSettingRequest {
@ApiModelProperty(value = "根目录是否显示所有存储源", notes = "根目录是否显示所有存储源, 如果为 true, 则根目录显示所有存储源列表, 如果为 false, 则会自动跳转到第一个存储源.", example = "true", required = true)
private Boolean rootShowStorage;
@ApiModelProperty(value = "页面布局", notes = "full:全屏,center:居中", example = "full", required = true)
private String layout;
@ApiModelProperty(value = "列表尺寸", notes = "large:大,default:中,small:小", example = "default", required = true)
private String tableSize;
@ApiModelProperty(value = "自定义视频文件后缀格式")
private String customVideoSuffix;
@ApiModelProperty(value = "自定义图像文件后缀格式")
private String customImageSuffix;
@ApiModelProperty(value = "自定义音频文件后缀格式")
private String customAudioSuffix;
@ApiModelProperty(value = "自定义文本文件后缀格式")
private String customTextSuffix;
@ApiModelProperty(value = "是否显示文档区", example = "true", required = true)
private Boolean showDocument;
@ApiModelProperty(value = "是否显示网站公告", example = "true", required = true)
private Boolean showAnnouncement;
@ApiModelProperty(value = "网站公告", example = "ZFile 网站公告")
private String announcement;
@ApiModelProperty(value = "自定义 CSS")
private String customCss;
@ApiModelProperty(value = "自定义 JS")
private String customJs;
}

View File

@@ -0,0 +1,47 @@
package im.zhaojun.zfile.admin.model.result.link;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 下载日志结果类
*
* @author zhaojun
*/
@Data
public class DownloadLogResult {
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value="")
private Integer id;
@ApiModelProperty(value="文件路径")
private String path;
@ApiModelProperty(value = "存储源名称", example = "我的本地存储")
private String storageName;
@ApiModelProperty(value = "存储源类型")
private StorageTypeEnum storageType;
@ApiModelProperty(value = "短链 Key")
private String shortKey;
@ApiModelProperty(value="访问时间")
private Date createTime;
@ApiModelProperty(value="访问 ip")
private String ip;
@ApiModelProperty(value="访问 user_agent")
private String userAgent;
@ApiModelProperty(value="访问 referer")
private String referer;
}

View File

@@ -0,0 +1,35 @@
package im.zhaojun.zfile.admin.model.result.link;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 短链结果类
*
* @author zhaojun
*/
@Data
public class ShortLinkResult {
@ApiModelProperty(value = "短链 id", example = "1")
private Integer id;
@ApiModelProperty(value = "存储源名称", example = "我的本地存储")
private String storageName;
@ApiModelProperty(value = "存储源类型")
private StorageTypeEnum storageType;
@ApiModelProperty(value = "短链 key", example = "voldd3")
private String shortKey;
@ApiModelProperty(value = "文件 url", example = "/directlink/1/test02.png")
private String url;
@ApiModelProperty(value = "创建时间", example = "2021-11-22 10:05")
private Date createDate;
}

View File

@@ -0,0 +1,24 @@
package im.zhaojun.zfile.admin.model.result.login;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 登陆 2FA 认证生成返回结果
*
* @author zhaojun
*/
@Data
@AllArgsConstructor
@ApiModel(description = "生成二步验证结果")
public class Login2FAResult {
@ApiModelProperty(value = "二步验证二维码")
private String qrcode;
@ApiModelProperty(value = "二步验证密钥")
private String secret;
}

View File

@@ -0,0 +1,22 @@
package im.zhaojun.zfile.admin.model.result.login;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 生成图片验证码结果类
*
* @author zhaojun
*/
@Data
@ApiModel(description = "生成图片验证码结果类")
public class LoginVerifyImgResult {
@ApiModelProperty(value = "验证码图片", example = "data:image/png;base64,iajsiAAA...")
private String imgBase64;
@ApiModelProperty(value = "验证码 UUID", example = "c140a792-4ca2-4dac-8d4c-35750b78524f")
private String uuid;
}

View File

@@ -0,0 +1,26 @@
package im.zhaojun.zfile.admin.model.result.s3;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
/**
* S3 bucket 名称结果类
*
* @author zhaojun
*/
@Data
@AllArgsConstructor
@ApiModel(value="S3 bucket 名称结果类")
public class S3BucketNameResult {
@ApiModelProperty(value = "bucket 名称", example = "zfile")
private String name;
@ApiModelProperty(value = "bucket 创建时间", example = "2022-01-01 15:22")
private Date date;
}

View File

@@ -0,0 +1,25 @@
package im.zhaojun.zfile.admin.model.result.sharepoint;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* Sharepoint 站点信息
*
* @author zhaojun
*/
@Data
@ApiModel(description = "SharePoint 站点结果类")
public class SharepointSite {
@ApiModelProperty(value="站点 id")
private String id;
@ApiModelProperty(value="站点名称")
private String displayName;
@ApiModelProperty(value="站点地址")
private String webUrl;
}

View File

@@ -0,0 +1,30 @@
package im.zhaojun.zfile.admin.model.result.sharepoint;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* Sharepoint 网站 list 列表
*
* @author zhaojun
*/
@Data
@ApiModel(description = "Sharepoint 网站 list 列表")
public class SharepointSiteList {
@ApiModelProperty(value="站点目录 id")
private String id;
@ApiModelProperty(value="站点目录名称")
private String displayName;
@ApiModelProperty(value="站点目录创建时间")
private Date createdDateTime;
@ApiModelProperty(value="站点目录地址")
private String webUrl;
}

View File

@@ -0,0 +1,82 @@
package im.zhaojun.zfile.admin.model.result.storage;
import im.zhaojun.zfile.admin.model.enums.SearchModeEnum;
import im.zhaojun.zfile.common.cache.RefreshTokenCache;
import im.zhaojun.zfile.home.model.enums.StorageTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 存储源设置后台管理 Result
*
* @author zhaojun
*/
@Data
@ApiModel(description = "存储源设置后台管理 Result")
public class StorageSourceAdminResult {
@ApiModelProperty(value = "ID, 新增无需填写", example = "1")
private Integer id;
@ApiModelProperty(value = "是否启用", example = "true")
private Boolean enable;
@ApiModelProperty(value = "是否启用文件操作功能", example = "true", notes = "是否启用文件上传,编辑,删除等操作.")
private Boolean enableFileOperator;
@ApiModelProperty(value = "是否允许匿名进行文件操作", example = "true", notes = "是否允许匿名进行文件上传,编辑,删除等操作.")
private Boolean enableFileAnnoOperator;
@ApiModelProperty(value = "是否开启缓存", example = "true")
private Boolean enableCache;
@ApiModelProperty(value = "存储源名称", example = "阿里云 OSS 存储")
private String name;
@ApiModelProperty(value = "存储源别名", example = "存储源别名,用于 URL 中展示, 如 http://ip:port/{存储源别名}")
private String key;
@ApiModelProperty(value = "存储源备注", example = "这是一个备注信息, 用于管理员区分不同的存储源, 此字段仅管理员可见")
private String remark;
@ApiModelProperty(value = "是否开启缓存自动刷新", example = "true")
private Boolean autoRefreshCache;
@ApiModelProperty(value = "存储源类型")
private StorageTypeEnum type;
@ApiModelProperty(value = "是否开启搜索", example = "true")
private Boolean searchEnable;
@ApiModelProperty(value = "搜索是否忽略大小写", example = "true")
private Boolean searchIgnoreCase;
@ApiModelProperty(value = "搜索模式", example = "SEARCH_CACHE", notes = "仅从缓存中搜索或直接全量搜索")
private SearchModeEnum searchMode;
@ApiModelProperty(value = "排序值", example = "1")
private Integer orderNum;
@ApiModelProperty(value = "是否默认开启图片模式", example = "true")
private Boolean defaultSwitchToImgMode;
@ApiModelProperty(value = "存储源刷新信息")
private RefreshTokenCache.RefreshTokenInfo refreshTokenInfo;
}

View File

@@ -0,0 +1,56 @@
package im.zhaojun.zfile.admin.model.verify;
import lombok.Data;
/**
* 用于表示校验结果的类
*
* @author zhaojun
*/
@Data
public class VerifyResult {
/**
* 是否成功
*/
private boolean passed;
/**
* 消息
*/
private String msg;
/**
* 代码
*/
private Integer code;
/**
* 表达式
*/
private String pattern;
public static VerifyResult success() {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(true);
return verifyResult;
}
public static VerifyResult success(String pattern) {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(true);
verifyResult.setPattern(pattern);
return verifyResult;
}
public static VerifyResult fail(String msg, Integer code) {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(false);
verifyResult.setMsg(msg);
verifyResult.setCode(code);
return verifyResult;
}
}

View File

@@ -0,0 +1,17 @@
package im.zhaojun.zfile.admin.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import im.zhaojun.zfile.admin.mapper.DownloadLogMapper;
import im.zhaojun.zfile.admin.model.entity.DownloadLog;
import org.springframework.stereotype.Service;
/**
* 下载日志 Service
*
* @author zhaojun
*/
@Service
public class DownloadLogService extends ServiceImpl<DownloadLogMapper, DownloadLog> {
}

View File

@@ -0,0 +1,219 @@
package im.zhaojun.zfile.admin.service;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import im.zhaojun.zfile.admin.mapper.FilterConfigMapper;
import im.zhaojun.zfile.admin.model.entity.FilterConfig;
import im.zhaojun.zfile.common.exception.FileAccessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 存储源过滤器 Service
*
* @author zhaojun
*/
@Slf4j
@Service
public class FilterConfigService extends ServiceImpl<FilterConfigMapper, FilterConfig> {
@Resource
private FilterConfigMapper filterConfigMapper;
/**
* 存储源 ID -> 过滤器列表(全部)缓存.
*/
private final Map<Integer, List<FilterConfig>> baseCache = new HashMap<>();
/**
* 存储源 ID -> 过滤器列表(禁止访问)缓存.
*/
private final Map<Integer, List<FilterConfig>> inaccessibleCache = new HashMap<>();
/**
* 存储源 ID -> 过滤器列表(禁止下载)缓存.
*/
private final Map<Integer, List<FilterConfig>> disableDownloadCache = new HashMap<>();
/**
* 根据存储源 ID 获取存储源配置列表
*
* @param storageId
* 存储源 ID
*
* @return 存储源过滤器配置列表
*/
public List<FilterConfig> findByStorageId(Integer storageId){
if (baseCache.get(storageId) != null) {
return baseCache.get(storageId);
} else {
List<FilterConfig> dbResult = filterConfigMapper.findByStorageId(storageId);
baseCache.put(storageId, dbResult);
return dbResult;
}
}
/**
* 批量保存存储源过滤器配置, 会先删除之前的所有配置(在事务中运行)
*
* @param storageId
* 存储源 ID
*
* @param filterConfigList
* 存储源过滤器配置列表
*/
@Transactional(rollbackFor = Exception.class)
public void batchSave(Integer storageId, List<FilterConfig> filterConfigList) {
filterConfigMapper.deleteByStorageId(storageId);
super.saveBatch(filterConfigList);
baseCache.put(storageId, filterConfigList);
}
/**
* 判断访问的路径是否是不允许访问的
*
* @param storageId
* 存储源 ID
*
* @param path
* 请求路径
*
* @throws FileAccessException 如果没权限访问此目录, 则抛出异常
*
*/
public void checkPathPermission(Integer storageId, String path) {
List<FilterConfig> filterConfigList = findByStorageIdAndInaccessible(storageId);
if (testPattern(filterConfigList, path)) {
throw new FileAccessException("您没有权限访问该路径");
}
}
/**
* 获取所有类型为禁止访问的过滤规则
*
* @param storageId
* 存储 ID
*
* @return 禁止访问的过滤规则列表
*/
public List<FilterConfig> findByStorageIdAndInaccessible(Integer storageId){
if (inaccessibleCache.get(storageId) != null) {
return inaccessibleCache.get(storageId);
} else {
List<FilterConfig> dbResult = filterConfigMapper.findByStorageIdAndInaccessible(storageId);
inaccessibleCache.put(storageId, dbResult);
return dbResult;
}
}
/**
* 获取所有类型为禁止下载的过滤规则
*
* @param storageId
* 存储 ID
*
* @return 禁止下载的过滤规则列表
*/
public List<FilterConfig> findByStorageIdAndDisableDownload(Integer storageId){
if (disableDownloadCache.get(storageId) != null) {
return disableDownloadCache.get(storageId);
} else {
List<FilterConfig> dbResult = filterConfigMapper.findByStorageIdAndDisableDownload(storageId);
disableDownloadCache.put(storageId, dbResult);
return dbResult;
}
}
/**
* 指定存储源下的文件名称, 根据过滤表达式判断是否会显示, 如果符合任意一条表达式, 则返回 true, 反之则返回 false.
*
* @param storageId
* 存储源 ID
*
* @param fileName
* 文件名
*
* @return 是否显示
*/
public boolean filterResultIsHidden(Integer storageId, String fileName) {
List<FilterConfig> filterConfigList = findByStorageId(storageId);
return testPattern(filterConfigList, fileName);
}
/**
* 指定存储源下的文件名称, 根据过滤表达式判断文件名和所在路径是否禁止下载, 如果符合任意一条表达式, 则返回 true, 反之则返回 false.
*
* @param storageId
* 存储源 ID
*
* @param fileName
* 文件名
*
* @return 是否显示
*/
public boolean filterResultIsDisableDownload(Integer storageId, String fileName) {
List<FilterConfig> filterConfigList = findByStorageIdAndDisableDownload(storageId);
String filePath = FileUtil.getParent(fileName, 1);
if (StrUtil.isEmpty(filePath)) {
return testPattern(filterConfigList, fileName);
} else {
return testPattern(filterConfigList, fileName) || testPattern(filterConfigList, filePath);
}
}
/**
* 根据规则表达式和测试字符串进行匹配,如测试字符串和其中一个规则匹配上,则返回 true反之返回 false。
*
* @param patternList
* 规则列表
*
* @param test
*
* 测试字符串
*
* @return 是否显示
*/
private boolean testPattern(List<FilterConfig> patternList, String test) {
for (FilterConfig filterConfig : patternList) {
String expression = filterConfig.getExpression();
if (StrUtil.isEmpty(expression)) {
return false;
}
try {
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + expression);
boolean match = pathMatcher.matches(Paths.get(test));
if (match) {
return true;
}
log.debug("regex: {}, name {}, contains: {}", expression, test, match);
} catch (Exception e) {
log.debug("regex: {}, name {}, parse error, skip expression", expression, test);
}
}
return false;
}
}

View File

@@ -0,0 +1,159 @@
package im.zhaojun.zfile.admin.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import im.zhaojun.zfile.admin.mapper.PasswordConfigMapper;
import im.zhaojun.zfile.admin.model.entity.PasswordConfig;
import im.zhaojun.zfile.admin.model.verify.VerifyResult;
import im.zhaojun.zfile.common.util.AjaxJson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 存储源密码配置 Service
*
* @author zhaojun
*/
@Service
@Slf4j
public class PasswordConfigService extends ServiceImpl<PasswordConfigMapper, PasswordConfig> {
@Resource
private PasswordConfigMapper passwordConfigMapper;
/**
* 存储源 ID -> 密码规则列表(全部)缓存.
*/
private final Map<Integer, List<PasswordConfig>> baseCache = new HashMap<>();
/**
* 根据存储源 ID 查询密码规则列表
*
* @param storageId
* 存储源 ID
*
* @return 密码规则列表
*/
public List<PasswordConfig> findByStorageId(Integer storageId) {
if (baseCache.get(storageId) != null) {
return baseCache.get(storageId);
} else {
List<PasswordConfig> dbResult = passwordConfigMapper.findByStorageId(storageId);
baseCache.put(storageId, dbResult);
return dbResult;
}
}
/**
* 批量保存指定存储源 ID 的密码规则列表
*
* @param storageId
* 存储源 ID
*
* @param passwordConfigList
* 存储源类别
*/
@Transactional(rollbackFor = Exception.class)
public void batchSave(Integer storageId, List<PasswordConfig> passwordConfigList) {
passwordConfigMapper.deleteByStorageId(storageId);
super.saveBatch(passwordConfigList);
baseCache.put(storageId, passwordConfigList);
}
/**
* 校验密码
*
* @param storageId
* 存储源 ID
*
* @param path
* 请求路径
*
* @param inputPassword
* 用户输入的密码
*
* @return 是否校验通过
*/
public VerifyResult verifyPassword(Integer storageId, String path, String inputPassword) {
List<PasswordConfig> passwordConfigList = findByStorageId(storageId);
for (PasswordConfig passwordConfig : passwordConfigList) {
String expression = passwordConfig.getExpression();
if (StrUtil.isEmpty(expression)) {
continue;
}
try {
PathMatcher pathMatcher = FileSystems.getDefault()
.getPathMatcher("glob:" + expression);
// 判断当前请求路径是否和规则路径表达式匹配
boolean match = pathMatcher.matches(Paths.get(path));
// 如果匹配且输入了密码则校验
if (match) {
if (StrUtil.isEmpty(inputPassword)) {
return VerifyResult.fail("此文件夹需要密码.", AjaxJson.REQUIRED_PASSWORD);
}
String expectedPassword = passwordConfig.getPassword();
if (matchPassword(expectedPassword, inputPassword)) {
log.debug("匹配文件夹密码 path: {}, expression: {}, 用户输入: {}, 匹配成功", path, expression, inputPassword);
return VerifyResult.success(expression);
}
log.debug("匹配文件夹密码 path: {}, expression: {}, 用户输入: {}, 不匹配.", path, expression, inputPassword);
return VerifyResult.fail("此文件夹密码错误.", AjaxJson.INVALID_PASSWORD);
}
} catch (Exception e) {
log.warn("匹配文件夹密码 path: {}, expression: {}, 用户输入: {}, 解析错误, 跳过此规则.",
path, expression, inputPassword, e);
}
}
log.debug("校验文件夹密码 path: {}, 没有匹配的表达式, 不进行密码校验.", path);
return VerifyResult.success();
}
/**
* 校验两个密码是否相同, 忽略空白字符
*
* @param expectedPasswordContent
* 预期密码
*
* @param password
* 实际输入密码
*
* @return 是否匹配
*/
private boolean matchPassword(String expectedPasswordContent, String password) {
if (Objects.equals(expectedPasswordContent, password)) {
return true;
}
if (expectedPasswordContent == null) {
return false;
}
if (password == null) {
return false;
}
expectedPasswordContent = expectedPasswordContent.replace("\n", "").trim();
password = password.replace("\n", "").trim();
return Objects.equals(expectedPasswordContent, password);
}
}

View File

@@ -0,0 +1,110 @@
package im.zhaojun.zfile.admin.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import im.zhaojun.zfile.admin.mapper.ReadmeConfigMapper;
import im.zhaojun.zfile.admin.model.entity.ReadmeConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.List;
/**
* 存储源 readme 配置 Service
*
* @author zhaojun
*/
@Slf4j
@Service
public class ReadmeConfigService extends ServiceImpl<ReadmeConfigMapper, ReadmeConfig> {
@Resource
private ReadmeConfigMapper readmeConfigMapper;
/**
* 根据存储源 ID 查询文档配置
*
* @param storageId
* 存储源ID
*
* @return 存储源文档配置列表
*/
public List<ReadmeConfig> findByStorageId(Integer storageId){
return readmeConfigMapper.findByStorageId(storageId);
}
/**
* 批量保存存储源 readme 配置, 会先删除之前的所有配置(在事务中运行)
*
* @param storageId
* 存储源 ID
*
* @param filterConfigList
* 存储源 readme 配置列表
*/
@Transactional(rollbackFor = Exception.class)
public void batchSave(Integer storageId, List<ReadmeConfig> filterConfigList) {
readmeConfigMapper.deleteByStorageId(storageId);
super.saveBatch(filterConfigList);
}
/**
* 根据存储源指定路径下的 readme 配置
*
* @param storageId
* 存储源ID
*
* @param path
* 文件夹路径
*
* @return 存储源 readme 配置列表
*/
public ReadmeConfig findReadmeByPath(Integer storageId, String path) {
List<ReadmeConfig> readmeConfigList = readmeConfigMapper.findByStorageId(storageId);
return getReadmeByTestPattern(readmeConfigList, path);
}
/**
* 根据规则表达式和测试字符串进行匹配,如测试字符串和其中一个规则匹配上,则返回 true反之返回 false。
*
* @param patternList
* 规则列表
*
* @param test
* 测试字符串
*
* @return 是否显示
*/
private ReadmeConfig getReadmeByTestPattern(List<ReadmeConfig> patternList, String test) {
for (ReadmeConfig filterConfig : patternList) {
String expression = filterConfig.getExpression();
if (StrUtil.isEmpty(expression)) {
continue;
}
try {
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + expression);
boolean match = pathMatcher.matches(Paths.get(test));
if (match) {
return filterConfig;
}
log.debug("regex: {}, name {}, contains: {}", expression, test, match);
} catch (Exception e) {
log.debug("regex: {}, name {}, parse error, skip expression", expression, test);
}
}
return null;
}
}

View File

@@ -0,0 +1,116 @@
package im.zhaojun.zfile.admin.service;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import im.zhaojun.zfile.admin.mapper.ShortLinkMapper;
import im.zhaojun.zfile.admin.model.entity.ShortLink;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
/**
* 短链 Service
*
* @author zhaojun
*/
@Service
public class ShortLinkService extends ServiceImpl<ShortLinkMapper, ShortLink> implements IService<ShortLink> {
@Resource
private ShortLinkMapper shortLinkMapper;
@Resource
private StorageSourceService storageSourceService;
/**
* 根据短链接 key 查询短链接
*
* @param key
* 短链接 key
*
* @return 短链接信息
*/
public ShortLink findByKey(String key) {
return shortLinkMapper.findByKey(key);
}
/**
* 根据短链接 id 查询短链接
*
* @param id
* 短链接 id
*
* @return 短链接信息
*/
public ShortLink findById(Integer id) {
return shortLinkMapper.selectById(id);
}
/**
* 根据存储源 ID 和文件路径查询短链接
*
* @param storageId
* 存储源 ID
*
* @param url
* 短链接 url
*
* @return 短链接信息
*/
public ShortLink findByStorageIdAndUrl(Integer storageId, String url) {
return shortLinkMapper.findByStorageIdAndUrl(storageId, url);
}
/**
* 根据存储源 KEY 和文件路径查询短链接
*
* @param storageKey
* 存储源 KEY
*
* @param url
* 短链接 url
*
* @return 短链接信息
*/
public ShortLink findByStorageKeyAndUrl(String storageKey,String url) {
Integer storageId = storageSourceService.findIdByKey(storageKey);
return findByStorageIdAndUrl(storageId, url);
}
/**
* 为存储源指定路径生成短链接, 保证生成的短连接 key 是不同的
*
* @param storageId
* 存储源 id
*
* @param fullPath
* 存储源路径
*
* @return 生成后的短链接信息
*/
public ShortLink generatorShortLink(Integer storageId, String fullPath) {
ShortLink shortLink;
String randomKey;
do {
// 获取短链
randomKey = RandomUtil.randomString(6);
shortLink = findByKey(randomKey);
} while (shortLink != null);
shortLink = new ShortLink();
shortLink.setShortKey(randomKey);
shortLink.setUrl(fullPath);
shortLink.setCreateDate(new Date());
shortLink.setStorageId(storageId);
save(shortLink);
return shortLink;
}
}

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