Compare commits

...

22 Commits
3.2.5 ... 4.0.2

Author SHA1 Message Date
zhaojun
a7813ff7bc 🔖 发布 4.0.2 版本 2022-07-12 19:52:56 +08:00
zhaojun
ae500ef9dc ⬇️ 移除无用依赖,减小项目体积 2022-07-12 19:52:40 +08:00
zhaojun
29d5b84df2 📝 更新文档, 更换图片预览的示例图 2022-07-12 19:49:11 +08:00
zhaojun
28882b198c 💄 更新前端页面 2022-07-12 19:48:42 +08:00
zhaojun
d624c886fe war 打包方式 ipv6 支持 2022-07-12 19:36:45 +08:00
zhaojun
e08654a59e Merge remote-tracking branch 'origin/main' 2022-07-12 19:34:53 +08:00
zhaojun
8fa6a5c9b8 💄 更新前端页面 2022-07-12 19:34:20 +08:00
zhaojun
7620a578fb 服务器代理上传支持特殊字符的文件名,如 # 2022-07-12 19:33:49 +08:00
赵俊
9c9bf93f00 Update README.md 2022-07-11 19:17:54 +08:00
赵俊
912eb694cb Update README.md 2022-07-11 19:11:08 +08:00
zhaojun
78d5ba90cf 💄 更新前端页面 2022-07-11 17:33:53 +08:00
zhaojun
056f9d4449 💄 更新前端页面 2022-07-11 17:33:37 +08:00
zhaojun
9a6020dcc8 增加文件点击习惯设置, 支持设置为单击还是双击 2022-07-11 17:29:15 +08:00
zhaojun
6b3025f379 fix(直链): 统一下载日志 storage_key 字段长度, 防止存储源长度过长后无法正常记录下载日志. 2022-07-11 14:57:30 +08:00
zhaojun
9dffaaee25 fix(异常处理): 存储源不存在时进行提示,修复后提示存储源不存在 2022-07-11 14:56:27 +08:00
zhaojun
bda654a012 fix: 修复管理员修改密码无效的 bug 2022-07-11 14:54:26 +08:00
zhaojun
52271b5117 fix: 修复目录文档某些情况无法匹配成功的 bug 2022-07-11 14:53:44 +08:00
zhaojun
223a6c1970 去除无用的存储类型枚举 2022-07-11 14:53:04 +08:00
zhaojun
60c272b714 修复 SharePoint 无法直链下载的 bug 2022-07-11 14:52:45 +08:00
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
678 changed files with 18385 additions and 8899 deletions

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
}
}
```

161
README.md
View File

@@ -1,7 +1,7 @@
<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 等存储源,支持在线浏览图片、播放音视频,文本文件等文件类型。
基于 Java 的在线网盘程序,支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP、SFTP 等存储源,支持在线浏览图片、播放音视频,文本文件等文件类型。
<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">
@@ -31,25 +31,36 @@
## 系统特色
* 文件夹密码
* 目录 README 说明
* 文件直链(短链,永久直链,二维码)
* 支持在线浏览文本文件, 视频, 图片, 音乐. (支持 FLV 和 HLS)
* 图片模式
* 支持文件操作:上传, 删除, 重命名, 新建文件夹. 后续还会支持移动和复制文件(详见下方**后续计划**.
* 操作系统级的文件操作体验
1. 支持拖拽上传和 Ctrl + V 粘贴上传文件和文件夹
2. 支持 Ctrl + A 全选文件, 按 Esc 取消全选.
3. 支持拖拽批量选择文件
4. 支持按住 Shift 多选文件
5. 支持多选文件后按 Delete 键删除文件.
6. 按 Backspace 返回上级文件夹.
* 全新的 UI 风格, 更简洁易用.
* 支持给文件生成直链(短链,永久直链,二维码)
* 视频播放器支持调用本地软件进行下载如迅雷、Motrix. 支持调用本地播放器播放,更好的进行视频解码: PotPlayer IINA, VLC, nPlayer, MXPlayer(Free/Pro)
* 全新画廊模式, 支持按照瀑布流显示图片, 支持自定义 N 栏, 自定义每栏的间距
* 支持给文件夹配置 markdown 文档, 并配置显示方式, 如顶部、底部、弹窗
* 支持给文件夹设置密码
* 支持隐藏文件或文件夹
* 后台登录支持设置图片验证码和 2FA 身份认证,防止后台被暴力破解
* 支持自定义文件格式后缀, 避免系统内置的不完善导致文件无法预览.
* Docker 支持
* 隐藏指定文件夹(通配符支持)
* 自定义 JS, CSS
* 自定义目录 README 说明文件和密码文件名称
* 同时挂载多个存储策略
* 缓存动态开启, ~~缓存自动刷新 (v2.2 及以前版本支持)~~
* ~~全局搜索 (v2.2 及以前版本支持)~~
* 支持 S3 协议, 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
* 支持 S3 协议, 阿里云 OSS, FTP, SFTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
## 快速开始
<details>
<summary>普通安装 (点击展开)</summary>
安装依赖环境:
```bash
# CentOS系统
yum install -y java-1.8.0-openjdk unzip
```
@@ -69,8 +80,6 @@ apt update && apt install -y adoptopenjdk-8-hotspot-jre
```
> 如为更新程序, 则请先执行 `~/zfile/bin/stop.sh && rm -rf ~/zfile` 清理旧程序. 首次安装请忽略此选项.
下载项目:
```bash
@@ -81,46 +90,112 @@ 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`。
程序的目录结构为:
```
├── zfile
├── META-INF
├── WEB-INF
└── bin
├── start.sh # 启动脚本
└── stop.sh # 停止脚本
├── restart.sh # 重启脚本
```
启动项目:
```bash
~/zfile/bin/start.sh
```
</details>
---
<details>
<summary>Docker (点击展开)</summary>
```bash
docker run -d --name=zfile --restart=always \
-p 8080:8080 \
-v /root/zfile/db:/root/.zfile-v4/db \
-v /root/zfile/logs:/root/.zfile-v4/logs \
zhaojun1998/zfile
```
> 如需映射配置文件则需要先在宿主机下载配置文件,然后映射到容器内:
```bash
# 下载到 application.properties 文件到 /root 目录下, 此目录可自行更改, 如:
curl -o /root/application.properties https://c.jun6.net/ZFILE/application.properties
# 然后增加一个 -v 参数,将此源文件映射到容器内(如修改宿主机的 application.properties 为其他路径, 则下面命令也要一起修改),如:
docker run -d --name=zfile --restart=always \
-p 8080:8080 \
-v /root/zfile/db:/root/.zfile-v4/db \
-v /root/zfile/logs:/root/.zfile-v4/logs \
-v /root/application.properties:/root/application.properties \
zhaojun1998/zfile
```
</details>
---
<details>
<summary>Docker Compose (点击展开)</summary>
```yml
version: '3.3'
services:
zfile:
container_name: zfile
restart: always
ports:
- '8080:8080'
volumes:
- '/root/zfile/db:/root/.zfile-v4/db'
- '/root/zfile/logs:/root/.zfile-v4/logs'
image: zhaojun1998/zfile
```
> 如需映射配置文件则需要先在宿主机下载配置文件,然后映射到容器内:
下载到 application.properties 文件到 /root 目录下, 此目录可自行更改, 命令如:
```bash
curl -o /root/application.properties https://c.jun6.net/ZFILE/application.properties
```
> 然后增加一个 -v 参数,将此源文件映射到容器内(如修改宿主机的 application.properties 为其他路径, 则下面命令也要一起修改), 如:
```yml
version: '3.3'
services:
zfile:
container_name: zfile
restart: always
ports:
- '8080:8080'
volumes:
- '/root/zfile/db:/root/.zfile-v4/db'
- '/root/zfile/logs:/root/.zfile-v4/logs'
image: zhaojun1998/zfile
```
</details>
---
篇幅有限, 更详细的安装教程及介绍请参考: [ZFile 文档](https://docs.zfile.vip)
访问地址:
用户前台: http://127.0.0.1:8080/main
初始安装: http://127.0.0.1:8080/install
管理后台: http://127.0.0.1:8080/admin
## 预览
![前台首页](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/uPic/2022/07/11/eJU1B5.png)
![前台设置](https://cdn.jun6.net/uPic/2022/07/11/Y0fK7b.png)
![图片预览](https://cdn.jun6.net/uPic/2022/07/12/BP8wog.png)
![视频预览](https://cdn.jun6.net/uPic/2022/07/11/MsubMr.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)
![音频预览](https://cdn.jun6.net/uPic/2022/07/11/7U5IoK.png)
![管理登录](https://cdn.jun6.net/uPic/2022/07/11/U2XKcg.png)
![后台设置-站点设置](https://cdn.jun6.net/uPic/2022/07/11/9zsedD.png)
![后台设置-驱动器列表](https://cdn.jun6.net/uPic/2022/07/11/y2pFa1.png)
![后台设置-新增驱动器](https://cdn.jun6.net/uPic/2022/07/11/I1NWzF.png)
## 支持作者
@@ -134,4 +209,4 @@ chmod +x $ZFILE_INSTALL_PATH/bin/*.sh
## 开发工具赞助
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>
<a href="https://www.jetbrains.com/?from=zfile"><img src="https://cdn.jun6.net/2021/04/21/26e410d60b0b0.png?1=1" width="100px"></a>

203
pom.xml
View File

@@ -1,29 +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.5.4</version>
<relativePath/>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>im.zhaojun</groupId>
<artifactId>zfile</artifactId>
<version>3.2.5</version>
<version>4.0.2</version>
<name>zfile</name>
<packaging>war</packaging>
<description>一个在线的文件浏览系统</description>
<properties>
<java.version>1.8</java.version>
<log4j2.version>2.17.1</log4j2.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>
@@ -33,10 +34,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</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>
@@ -47,30 +44,29 @@
<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>
<version>1.4.197</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
<scope>runtime</scope>
<version>8.0.27</version>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.3</version>
<version>5.8.3</version>
</dependency>
<!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
@@ -82,7 +78,12 @@
<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>
@@ -91,41 +92,22 @@
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- WebDav -->
<dependency>
<groupId>io.milton</groupId>
<artifactId>milton-server-ce</artifactId>
<version>3.1.1.413</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<artifactId>json</artifactId>
<groupId>org.json</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- webdav client -->
<dependency>
<groupId>com.github.lookfirst</groupId>
<artifactId>sardine</artifactId>
<version>5.10</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>
@@ -135,15 +117,95 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.69</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>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<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.26.0</version>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</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>
@@ -153,6 +215,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>
@@ -170,10 +270,11 @@
<jvms>
<jvm>-Djava.security.egd=file:/dev/./urandom</jvm>
<jvm>-Dfile.encoding=utf-8</jvm>
<jvm>-Djava.net.preferIPv4Stack=false</jvm>
<jvm>-Djava.net.preferIPv4Addresses=true</jvm>
</jvms>
</configuration>
</plugin>
</plugins>
</build>
</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,22 +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.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @author zhaojun
*/
@EnableAsync
import javax.annotation.PostConstruct;
@SpringBootApplication
@EnableJpaRepositories("im.zhaojun.zfile.repository")
@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,109 @@
package im.zhaojun.zfile.admin.controller.setting;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
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);
String password = systemConfigDTO.getPassword();
if (StrUtil.isNotEmpty(password)) {
systemConfigDTO.setPassword(SecureUtil.md5(password));
}
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,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 FileClickModeEnum {
/**
* 单击打开文件/文件夹
*/
CLICK("click"),
/**
* 双击打开文件/文件夹
*/
DBCLICK("dbclick");
@EnumValue
@JsonValue
private final String value;
}

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,56 @@
package im.zhaojun.zfile.admin.model.request.setting;
import im.zhaojun.zfile.admin.model.enums.FileClickModeEnum;
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;
@ApiModelProperty(value = "默认文件点击习惯", example = "click")
private FileClickModeEnum fileClickMode;
}

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

@@ -1,9 +1,11 @@
package im.zhaojun.zfile.model.support;
package im.zhaojun.zfile.admin.model.verify;
import lombok.Data;
/**
* 用于表示校验结果的类
*
* @author zhaojun
*/
@Data
public class VerifyResult {
@@ -23,19 +25,26 @@ public class VerifyResult {
*/
private Integer code;
/**
* 表达式
*/
private String pattern;
public static VerifyResult success() {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(true);
return verifyResult;
}
public static VerifyResult fail(String msg) {
public static VerifyResult success(String pattern) {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(false);
verifyResult.setMsg(msg);
verifyResult.setPassed(true);
verifyResult.setPattern(pattern);
return verifyResult;
}
public static VerifyResult fail(String msg, Integer code) {
VerifyResult verifyResult = new VerifyResult();
verifyResult.setPassed(false);

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,114 @@
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 im.zhaojun.zfile.common.constant.ZFileConstant;
import im.zhaojun.zfile.common.util.StringUtils;
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) {
test = StringUtils.concat(test, ZFileConstant.PATH_SEPARATOR);
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