Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f873a4aeb9 | ||
|
|
a49495268f | ||
|
|
389b1bdd2e | ||
|
|
09c903ec2d | ||
|
|
b4c0455f70 | ||
|
|
6f59fe2b37 | ||
|
|
365853fade | ||
|
|
837ce742e0 | ||
|
|
11bf29fad7 | ||
|
|
350e898bda | ||
|
|
3c29788980 | ||
|
|
09ff58362b | ||
|
|
7cb6c148dd | ||
|
|
e8cca6ea15 | ||
|
|
a067234c64 | ||
|
|
b74463d9e5 | ||
|
|
28f85b4d45 | ||
|
|
e8567524ac | ||
|
|
c0dcee45d4 | ||
|
|
d51343ba8d | ||
|
|
777a1961d0 | ||
|
|
706cb057bc | ||
|
|
31c0356acc | ||
|
|
47de9769d0 | ||
|
|
ebb7ad0207 | ||
|
|
750c0983f1 | ||
|
|
f005466293 | ||
|
|
aacf9e05b9 | ||
|
|
62fba81406 | ||
|
|
c644757135 | ||
|
|
53a0dcce2f | ||
|
|
b1a948dd2a | ||
|
|
e19fdb2ad2 | ||
|
|
b9e2f2cf48 | ||
|
|
2de7e6b2d0 | ||
|
|
a2716ed8b8 | ||
|
|
73d60a0907 | ||
|
|
15c26a6e1f | ||
|
|
09ce9b811e | ||
|
|
05737663a2 | ||
|
|
9820a1e1f3 | ||
|
|
f4965c8256 | ||
|
|
4892083ee9 | ||
|
|
8f8295cb68 | ||
|
|
0be4513349 | ||
|
|
c7219a7516 | ||
|
|
f5704f470a | ||
|
|
9b19fd1e9d | ||
|
|
3e81d886f6 | ||
|
|
f1d31d1c10 | ||
|
|
44b811507d | ||
|
|
3c28140c32 | ||
|
|
a12148671a | ||
|
|
3a9199e176 | ||
|
|
3d5c444db0 | ||
|
|
6c385014cc | ||
|
|
f0a1c0ab06 | ||
|
|
4ebf15f3c1 | ||
|
|
b325a3e9a0 | ||
|
|
c3dd34dac9 | ||
|
|
3eceb3c719 | ||
|
|
f1cbde414f | ||
|
|
9bc02bdfcb | ||
|
|
e34a26c009 | ||
|
|
3fc032daa9 | ||
|
|
006554cd0a | ||
|
|
42468e9711 | ||
|
|
da5b04cdb3 | ||
|
|
66486b8810 | ||
|
|
c9883cafb9 | ||
|
|
a83b5fa489 | ||
|
|
49f44a00b2 | ||
|
|
807a860d07 | ||
|
|
3df445e98c | ||
|
|
e53abb7c79 | ||
|
|
4ca8bc06d8 | ||
|
|
76d4752776 | ||
|
|
95fcaada2b | ||
|
|
327af9f3fa | ||
|
|
066ae11ad7 | ||
|
|
f5c377dab8 | ||
|
|
963b7fdb82 | ||
|
|
8f66b5e04b | ||
|
|
0b98c6acc0 | ||
|
|
dfae93bbed | ||
|
|
1a440e1f67 | ||
|
|
270e2253a4 | ||
|
|
a9a3b524eb | ||
|
|
e4799f0045 | ||
|
|
9e6d2c1043 | ||
|
|
691fb2a380 | ||
|
|
f39bf33922 | ||
|
|
14ff3fea9a | ||
|
|
246829869a | ||
|
|
5f2fa2f033 | ||
|
|
6c52e87396 | ||
|
|
a4b0bb3e9c | ||
|
|
44b79ab946 | ||
|
|
1a39ed726d | ||
|
|
4c4fa9b2c7 | ||
|
|
708110c498 | ||
|
|
227efb5471 | ||
|
|
17cbb4b663 | ||
|
|
35ecc4c894 | ||
|
|
d976abdb84 | ||
|
|
bd78b87d6f | ||
|
|
a0869708aa | ||
|
|
bf53df1dc3 | ||
|
|
6f63e1c837 | ||
|
|
3e2234c1cd | ||
|
|
42208ac101 | ||
|
|
b247364d16 | ||
|
|
d8b86bd302 | ||
|
|
1ec66adda0 | ||
|
|
d3d0ee73a0 | ||
|
|
e757b471fd | ||
|
|
60c8843734 | ||
|
|
f779053040 | ||
|
|
6becac74d0 | ||
|
|
e64a73eb67 | ||
|
|
cffa82226d | ||
|
|
6626afa6b6 | ||
|
|
6d35063027 | ||
|
|
09d6db68ff | ||
|
|
f33145d02a | ||
|
|
a74e4f1a13 | ||
|
|
9344a831be | ||
|
|
4fd7e89ff8 | ||
|
|
d0a2774f65 | ||
|
|
fb563396fe | ||
|
|
73ad9f49ef | ||
|
|
10e6bf81f9 | ||
|
|
274b49d66f | ||
|
|
ab6749056a | ||
|
|
3be2e354c4 | ||
|
|
0c825c65d4 | ||
|
|
c6836f3654 | ||
|
|
c6a5db7740 | ||
|
|
179f34fad2 | ||
|
|
161096fbb2 | ||
|
|
69412300b6 | ||
|
|
329751350d | ||
|
|
90675e3ae8 | ||
|
|
238125eb8d | ||
|
|
d30f02b1c0 | ||
|
|
d9ec47f8ea | ||
|
|
6020915993 | ||
|
|
07ed4f7922 | ||
|
|
13b6d7f98d | ||
|
|
3752d42de8 | ||
|
|
847bcd746c | ||
|
|
e1e46457e7 | ||
|
|
a2751deac6 | ||
|
|
74c11e7539 | ||
|
|
cb0ee30c6b | ||
|
|
c518c0c1e6 | ||
|
|
8827c45df1 | ||
|
|
b91518136b | ||
|
|
b46cb2501b | ||
|
|
c1823e7a83 | ||
|
|
84d5f4e5e2 | ||
|
|
ff7416f858 | ||
|
|
b6b4cb1ce4 | ||
|
|
617d5df294 | ||
|
|
ff87ea8e39 | ||
|
|
36581ded16 | ||
|
|
333682325c | ||
|
|
1a1d9e8adf | ||
|
|
020e7b0944 | ||
|
|
e15bd00524 | ||
|
|
de1e2dbcbb | ||
|
|
7b24f1dedf | ||
|
|
ce94a35ed7 | ||
|
|
8a0c66ceff | ||
|
|
a92e172d26 | ||
|
|
a7bcc4d4fb | ||
|
|
19cfd3daf4 | ||
|
|
b82bac40fc | ||
|
|
21d61d9cd2 | ||
|
|
76e7359c69 | ||
|
|
82047f4504 | ||
|
|
c970acbe8b | ||
|
|
988d2caced | ||
|
|
c5111929a3 | ||
|
|
b6b72e0273 | ||
|
|
0d43439200 | ||
|
|
da0e68f95c | ||
|
|
fbea823ba1 | ||
|
|
7adf56ce0a | ||
|
|
7f394b327b | ||
|
|
7fe673068b | ||
|
|
f16d48b432 | ||
|
|
192df21da4 | ||
|
|
02e0f10016 | ||
|
|
24e8dc4e9f | ||
|
|
dbeef1cee4 | ||
|
|
096eb7c73e | ||
|
|
69820bfcb9 | ||
|
|
3c3b7c4389 | ||
|
|
822ebf4888 | ||
|
|
3b6c43ee23 | ||
|
|
c711423ef9 | ||
|
|
948c37c039 | ||
|
|
1f8e3cfb7a | ||
|
|
7b875c81d1 | ||
|
|
3fb1d874d5 | ||
|
|
601c60fca7 | ||
|
|
d052f75a1c | ||
|
|
51c81e19af | ||
|
|
480cbb73ea | ||
|
|
4a1e4c1d68 | ||
|
|
76c763b84d | ||
|
|
ed0aa017db | ||
|
|
a314d4dc54 | ||
|
|
4bfae38176 | ||
|
|
1f047e25c7 | ||
|
|
ff81448611 | ||
|
|
83cf140fa1 | ||
|
|
8fc1cd0430 | ||
|
|
7ff7907e11 | ||
|
|
4611defe36 | ||
|
|
eb89807cf1 | ||
|
|
bea9a0a56d | ||
|
|
fc2df7e501 | ||
|
|
bab3e80aec | ||
|
|
a556813b01 | ||
|
|
e90f922b58 | ||
|
|
7671564ea3 | ||
|
|
402dc5d970 | ||
|
|
51b0668b5d | ||
|
|
8686551e71 | ||
|
|
437e2c602a | ||
|
|
fe00d39bc1 | ||
|
|
5cb7998f12 | ||
|
|
6cec43bee6 | ||
|
|
6e81d130ed | ||
|
|
e8ba2326aa | ||
|
|
a070df069b | ||
|
|
fe133d3ec2 | ||
|
|
ddfeb37114 |
@@ -8,3 +8,5 @@ tmp
|
||||
tank
|
||||
tank.exe
|
||||
|
||||
# mac
|
||||
.DS_Store
|
||||
@@ -12,10 +12,7 @@
|
||||
3. core/config.go
|
||||
VERSION = x.x.x
|
||||
|
||||
4. tank-front 修改 vuex/index.js
|
||||
versionName: 'x.x.x'
|
||||
|
||||
5. readme.md 修改 eyeblue/tank:x.x.x
|
||||
4. readme.md 修改 eyeblue/tank:x.x.x
|
||||
eyeblue/tank:x.x.x
|
||||
|
||||
|
||||
@@ -30,8 +27,8 @@ docker container prune
|
||||
# 构建新版本 (最后的.不能少)
|
||||
docker build -t eyeblue/tank:x.x.x .
|
||||
# 本地测试
|
||||
docker run --name dockermysql -p 13306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=tank -e MYSQL_USER=tank -e MYSQL_PASSWORD=tank123 -v ~/data/dockermysql:/var/lib/mysql -d mysql:5.7
|
||||
docker run --name tank -p 6010:6010 --link dockermysql:mysql -v ~/data/dockermatter:/data/matter -d eyeblue/tank:x.x.x
|
||||
docker run --name dockermysql -p 13306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=tank -e MYSQL_USER=tank -e MYSQL_PASSWORD=tank123 -v ~/data/dockermysql:/var/lib/mysql -e TZ=Asia/Shanghai -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
docker run --name tank -p 6010:6010 --link dockermysql:mysql -v ~/data/dockermatter:/data/build/matter -d eyeblue/tank:x.x.x
|
||||
# 没问题后,推送到远程
|
||||
docker push eyeblue/tank:x.x.x
|
||||
```
|
||||
@@ -50,6 +47,170 @@ c. 在github上发布新版本。
|
||||
|
||||
3.更新tank-doc 下载内容
|
||||
|
||||
## useful commands
|
||||
go get xxx/xxx
|
||||
go mod tidy
|
||||
|
||||
tank-4.1.1 2024-10-6
|
||||
1. 修复已知问题
|
||||
|
||||
tank-4.1.0 2024-7-27
|
||||
1. 删除用户后,空间一并删除。 #222
|
||||
2. 增加批量绑定空间成员功能。 #227
|
||||
3. 共享空间增加回收站逻辑。 #232
|
||||
4. 修复用户修改空间单位问题。 #233 #253
|
||||
5. 增强搜索功能,会进行dfs深度优先搜索。 #239
|
||||
|
||||
|
||||
1. When delete user, it's space will be deleted #222
|
||||
2. Batch creating space member #227
|
||||
3. Add feature of recycle bin for space #232
|
||||
4. Fix issue of changing unit #233 #253
|
||||
5. Enhance the search ability, using dfs #239
|
||||
|
||||
|
||||
tank-4.0.4 2024-4-29
|
||||
1. 修复已知问题
|
||||
|
||||
tank-4.0.3 2024-3-26
|
||||
1. 修复已知问题
|
||||
|
||||
tank-4.0.2 2023-11-10
|
||||
1. 修复BUG https://github.com/eyebluecn/tank/issues/229
|
||||
2. 新增了分享提取码快速提取
|
||||
3. 分享文件默认按照创建时间倒序排列。
|
||||
|
||||
1. Fix BUG https://github.com/eyebluecn/tank/issues/229
|
||||
2. Fast share using code.
|
||||
3. Share list order by create time desc.
|
||||
|
||||
|
||||
tank-4.0.1 2023-09-09
|
||||
1.修复一些文件扫描的bug https://github.com/eyebluecn/tank/issues/221 https://github.com/eyebluecn/tank/issues/220
|
||||
|
||||
1.Fix scan bug. https://github.com/eyebluecn/tank/issues/221 https://github.com/eyebluecn/tank/issues/220
|
||||
|
||||
|
||||
tank-4.0.0 2023-08-13
|
||||
1.增加空间概念,可以多人共同维护一个共享空间中的文件 #201
|
||||
2.新增粘贴上传文件功能 #188
|
||||
3.文件大小单位选择体验优化 #179
|
||||
4.支持使用url的方式抓取文件
|
||||
5.一些UI优化
|
||||
|
||||
1.Add space, supports multi users maintaining a shared space. #201
|
||||
2.Add the feature of uploading by pasting files. #188
|
||||
3.Human readable file size unit. #179
|
||||
4.Add the feature of uploading by url.
|
||||
6.Some user experience enhancement.
|
||||
|
||||
|
||||
tank-4.0.0.beta1 2023-07-22
|
||||
1. Shared space
|
||||
2. Copy and paste upload
|
||||
3. Crawl upload
|
||||
4. Space size selection control upgrade
|
||||
5. Some style modifications
|
||||
|
||||
|
||||
1. 共享空间
|
||||
2. 文件复制粘贴上传
|
||||
3. 文件抓取上传
|
||||
4. 空间大小选择控件升级
|
||||
5. 一些样式修改
|
||||
|
||||
|
||||
|
||||
tank-3.1.5 2022-03-21
|
||||
1. Support Sqlite.
|
||||
2. Upgrade webpack.
|
||||
|
||||
1. 支持Sqlite
|
||||
2. 升级了前端的webpack
|
||||
|
||||
|
||||
tank-3.1.4 2022-03-20
|
||||
1. Support Sqlite.
|
||||
2. Upgrade webpack.
|
||||
|
||||
1. 支持Sqlite
|
||||
2. 升级了前端的webpack
|
||||
|
||||
|
||||
|
||||
tank-3.1.3 2022-03-10
|
||||
1. Add kkfileview 3.x support
|
||||
2. Fix search bug https://github.com/eyebluecn/tank/issues/148
|
||||
3. Fix bug of totalSize in dashboard
|
||||
|
||||
1. 添加了kkfileview 3.x的预览支持
|
||||
2. 修复了一个搜索的bug https://github.com/eyebluecn/tank/issues/148
|
||||
3. 修复了文件大小统计的bug
|
||||
|
||||
|
||||
tank-3.1.2 2021-01-02
|
||||
1. Add scan manually once feature
|
||||
2. Add disable recycle bin feature
|
||||
3. WebDAV use hard delete now
|
||||
4. Support Chinese Username
|
||||
5. Normal user cannot change itself space limit
|
||||
6. Support the order sort by filename, size and update time
|
||||
7. Hide the side menu on phone by default
|
||||
8. Friendly hint for large file upload.
|
||||
9. Cancel during uploading.
|
||||
|
||||
1. 添加了手动扫描一次的功能
|
||||
2. 添加了禁用回收站功能
|
||||
3. WebDAV中的删除采用硬删除
|
||||
4. 支持中文的用户名
|
||||
5. 普通用户不能再修改自己的空间限制
|
||||
6. 文件支持按照名称排序,按照大小排序,按照修改日期排序
|
||||
7. 手机端默认隐藏左边菜单
|
||||
8. 大文件上传友好性提示
|
||||
9. 可以取消文件上传
|
||||
|
||||
|
||||
tank-3.1.1 2020-09-08
|
||||
1. Add upload folder feature.
|
||||
2. Fix the share auth bug.
|
||||
|
||||
1. 添加了文件夹上传功能
|
||||
2. 修复了分享时需要登陆的问题
|
||||
|
||||
tank-3.1.0
|
||||
1. Reconstruct frontend by typescript+react+antd.
|
||||
2. Enhance the webdav ability.
|
||||
3. Add charset in mysql config.
|
||||
4. Add preview_config.
|
||||
5. Add delete user feature.
|
||||
6. Add Scan config.
|
||||
7. Add recycle bin feature.
|
||||
|
||||
1. 前后进行了一次完全的重构。 采用Typescript+React+Antd
|
||||
2. 增强了WebDav的能力,现在能够适配更多的WebDav客户端了
|
||||
3. 数据库配置项中添加了编码支持,可以支持emoji了
|
||||
4. 添加了预览引擎配置,用户可以使用自己的引擎来预览特定格式文件
|
||||
5. 添加了删除用户的功能,删除用户后会清空用户所有的文件和痕迹
|
||||
6. 添加了扫描功能,可以定时同步物理文件到蓝眼云盘中
|
||||
7. 增加回收站功能
|
||||
|
||||
|
||||
2019-10-13
|
||||
tank-3.0.6
|
||||
1. Fix the Bug of limit size.
|
||||
2. Add the drag feature for uploading files.
|
||||
3. Fix the page bug of list.
|
||||
|
||||
2019-08-13
|
||||
tank-3.0.5
|
||||
1. Fix the Bug of sharing. [#56](https://github.com/eyebluecn/tank/issues/56)
|
||||
|
||||
2019-06-11
|
||||
tank-3.0.4
|
||||
1. Use relative assets in tank-front.
|
||||
2. Fix the total size check bug.
|
||||
3. Add feature. Creating user.
|
||||
|
||||
2019-05-31
|
||||
tank-3.0.3
|
||||
1. Add default utf8 charset when creating tables.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# use golang 1.12
|
||||
FROM golang:1.12
|
||||
# use golang 1.22.5
|
||||
FROM golang:1.22.5
|
||||
|
||||
# maintainer. Author's name and email.
|
||||
MAINTAINER eyeblue "eyebluecn@126.com"
|
||||
@@ -14,10 +14,14 @@ COPY . /data
|
||||
VOLUME /data/build/matter
|
||||
|
||||
# proxy
|
||||
ENV GOPROXY=https://athens.azurefd.net
|
||||
ENV GOPROXY=https://goproxy.cn
|
||||
|
||||
# Handle time-zone
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN apt-get install -y tzdata
|
||||
|
||||
# prepare the config file
|
||||
RUN go build -mod=readonly \
|
||||
RUN go build \
|
||||
&& cp -r /data/tank /data/build
|
||||
|
||||
# use 6010 as default.
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
[](https://github.com/eyebluecn/tank)
|
||||
[](https://github.com/eyebluecn/tank)
|
||||
|
||||
[English Version](./README_EN.md)
|
||||
[English Version](https://tank-doc.eyeblue.cn/en)
|
||||
|
||||
# 蓝眼云盘(3.0.3)
|
||||
# 蓝眼云盘(4.1.2)
|
||||
[在线Demo](https://tanker.eyeblue.cn) (体验账号: demo 密码:123456)
|
||||
|
||||
[在线Demo](https://tank.eyeblue.cn) (体验账号: demo 密码:123456)
|
||||
蓝眼云盘是蓝眼开源系列代表作品之一,致力于打造精致,优雅,简约的云盘。核心功能如下:
|
||||
1. 文件列表
|
||||
2. 文件/文件夹上传 + 拖拽上传
|
||||
3. 共享空间
|
||||
3. 文件分享
|
||||
4. 文件监控统计
|
||||
5. 回收站
|
||||
6. 多用户
|
||||
7. [WebDav](https://tank-doc.eyeblue.cn/advance/webdav.html)
|
||||
8. [扫描磁盘任务](https://tank-doc.eyeblue.cn/advance/scan.html)
|
||||
9. [在线预览及自定义配置预览引擎](https://tank-doc.eyeblue.cn/advance/preview.html)
|
||||
10. 支持Sqlite和Mysql数据库用以存储文件元信息,Sqlite可以做到开箱即用。
|
||||
|
||||
[文档](https://tank-doc.eyeblue.cn/)
|
||||
|
||||
[更多内容请移步至文档](https://tank-doc.eyeblue.cn/)
|
||||
|
||||
### 软件截图
|
||||
|
||||
@@ -18,27 +31,22 @@
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 手机端截图
|
||||
|
||||

|
||||
|
||||
|
||||
#### [安装文档](https://tank-doc.eyeblue.cn/zh/basic/install.html)
|
||||
## [安装文档](https://tank-doc.eyeblue.cn/basic/install.html)
|
||||
|
||||
|
||||
### 相关文档
|
||||
|
||||
[蓝眼云盘后端api](https://github.com/eyebluecn/tank/blob/master/build/doc/api_zh.md)
|
||||
|
||||
[蓝眼云盘编程接口](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md)
|
||||
|
||||
[快速使用Let's Encrypt开启个人网站的https](https://blog.eyeblue.cn/home/article/9f580b3f-5679-4a9d-be6f-4d9f0dd417af)
|
||||
|
||||
[Docker 化你的开源项目](https://blog.eyeblue.cn/home/article/510f9316-9ca1-40fe-b1b3-5285505a527d)
|
||||
### 快速运行
|
||||
安装依赖
|
||||
```shell
|
||||
go mod tidy
|
||||
```
|
||||
运行main文件即可:
|
||||
```shell
|
||||
./main.go
|
||||
```
|
||||
|
||||
### Contribution
|
||||
|
||||
@@ -46,9 +54,18 @@
|
||||
|
||||
如果您也想参与进来,请尽情的fork, star, post issue, pull requests
|
||||
|
||||
当然你可以加入钉钉群一起直接交流
|
||||
当然你可以加入钉钉群(一群已满员)一起直接交流,在钉钉群中可以获取最新beta版本。
|
||||
|
||||

|
||||
|
||||
群号:44754005
|
||||
|
||||
|
||||
### Support
|
||||
IDE for this project is supported by [Jetbrains](https://jb.gg/OpenSourceSupport).
|
||||
|
||||
[](https://jb.gg/OpenSourceSupport)
|
||||
|
||||

|
||||
|
||||
### License
|
||||
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
[](https://github.com/eyebluecn/tank)
|
||||
|
||||
[中文版](./README_EN.md)
|
||||
|
||||
# Eyeblue Cloud Disk(2.0.0)
|
||||
|
||||
[Online Demo](https://tank.eyeblue.cn) (demo@tank.eyeblue.cn 123456)
|
||||
|
||||
 [tank backend](https://github.com/eyebluecn/tank)
|
||||
|
||||
 [tank-front frontend](https://github.com/eyebluecn/tank-front)
|
||||
|
||||
### Description
|
||||
Eyeblue Cloud Disk is one of [Eyeblue Softwares](https://github.com/eyebluecn), and it is the basic software for other eyeblue softwares.Eyeblue Cloud Disk has the following features.
|
||||
|
||||
**1. Easy to use**
|
||||
|
||||
- Docker available
|
||||
|
||||
- [Install package](https://github.com/eyebluecn/tank/releases) is about 10M. Cross platform.
|
||||
|
||||
- Unpack the install zip, and run it with one command.
|
||||
|
||||
- Installation Guide
|
||||
|
||||
- Dashboard to view UV and PV.
|
||||
|
||||
- Preview usual files (doc,ppt,xls,pdf,mp3,mp4,jpg,png)
|
||||
|
||||
**2. Core features all in readiness**
|
||||
|
||||
- Manage your files on cloud.
|
||||
|
||||
- Responsive on PC and mobile.
|
||||
|
||||
- Normal user and administrator
|
||||
|
||||
**3. Custom Api supported**
|
||||
|
||||
- Support [Custom Api](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md)
|
||||
|
||||
- Manage files by api.
|
||||
|
||||
If this project is helpful for you, star ⭐ it.
|
||||
|
||||
### Screen Shot
|
||||
|
||||
#### PC
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### Mobile
|
||||
|
||||

|
||||
|
||||
|
||||
### Installation by Docker
|
||||
|
||||
##### a) Run
|
||||
```
|
||||
docker run --name tank -p 6010:6010 -d eyeblue/tank:2.0.0
|
||||
```
|
||||
If you'd like to start you mysql by docker, use the following command.
|
||||
```
|
||||
docker run --name mysql4tank -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=tank -e MYSQL_USER=tank -e MYSQL_PASSWORD=tank123 -v ~/data/mysqldemo1:/var/lib/mysql -d mysql:5.7
|
||||
```
|
||||
|
||||
##### b) Validation
|
||||
Open http://127.0.0.1:6010 it succeed if you see the installation guide.
|
||||
|
||||
### Installation by installation package (windows/linux almost the same)
|
||||
|
||||
##### a) Prepare
|
||||
|
||||
1. [Mysql](https://www.mysql.com/downloads/) (Use utf8 charset)
|
||||
|
||||
2. [Here](https://github.com/eyebluecn/tank/releases) download installation package.
|
||||
|
||||
3. Unzip on server
|
||||
|
||||
##### b) Run
|
||||
|
||||
- Double click `tank.exe` on windows
|
||||
|
||||
- Run the following command on linux
|
||||
|
||||
```
|
||||
cd ApplicationDirectory
|
||||
./tank
|
||||
```
|
||||
|
||||
If you'd like to run in daemon mode.
|
||||
```shell
|
||||
# Run the application
|
||||
cd ApplicationDirectory/service
|
||||
./startup.sh
|
||||
|
||||
# Stop the application
|
||||
cd ApplicationDirectory/service
|
||||
./shutdown.sh
|
||||
|
||||
```
|
||||
|
||||
##### c) Validation
|
||||
|
||||
Open http://127.0.0.1:6010 it succeed if you see the installation guide.
|
||||
|
||||
### Reference
|
||||
|
||||
[api](https://github.com/eyebluecn/tank/blob/master/build/doc/api_zh.md)
|
||||
|
||||
[Custom API](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md)
|
||||
|
||||
### Contribution
|
||||
|
||||
Thanks these authors [@zicla](https://github.com/zicla),[@seaheart](https://github.com/seaheart),[@yemuhe](https://github.com/yemuhe),[@hxsherry](https://github.com/hxsherry)
|
||||
|
||||
You can join us by scanning the following qcode.
|
||||
|
||||

|
||||
|
||||
### License
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2017-present, eyeblue.cn
|
||||
@@ -0,0 +1,16 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| master | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We strongly encourage you to report security vulnerabilities to
|
||||
our private security mailing list: security@cilium.io - first, before
|
||||
disclosing them in any public forums.
|
||||
|
||||
This is a private mailing list where only members of the Cilium internal
|
||||
security team are subscribed to, and is treated as top priority.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Eyeblue Tank
|
||||
|
||||
> The document is comming...
|
||||
|
||||
|
||||
go build -mod=readonly
|
||||
|
||||
|
||||
|
||||
### Program Arguments
|
||||
|
||||
-mode=mirror -username=admin -password=123456 -src=/Users/fusu/d/temp -dest=/morning
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 蓝眼云盘
|
||||
|
||||
> 详细文档正在撰写中,敬请期待...
|
||||
@@ -1,132 +0,0 @@
|
||||
# 蓝眼云盘编程接口
|
||||
|
||||
- 蓝眼云盘提供了[编程接口](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md),实现了云存储(如:[七牛云](https://www.qiniu.com),[阿里云OSS](https://www.aliyun.com/product/oss))的核心功能,可以使用编程接口上传文件,作为其他网站、系统、app的资源存储器。可以在下载图片时对图片做缩放裁剪处理,可以有效地节省客户端流量。
|
||||
|
||||
- 蓝眼系列开源软件之二的[《蓝眼博客》](https://github.com/eyebluecn/blog)正是使用蓝眼博客作为第三方资源存储器。蓝眼博客中的所有图片,附件均是存储在蓝眼云盘中。
|
||||
|
||||
所有的编程接口均定义在`alien_controller`中,主要包括以下几个接口:
|
||||
|
||||
`/api/alien/fetch/upload/token` 获取上传token
|
||||
|
||||
`/api/alien/fetch/download/token` 获取下载token
|
||||
|
||||
`/api/alien/confirm` 来蓝眼云盘确认文件
|
||||
|
||||
`/api/alien/upload` 使用form表单上传文件
|
||||
|
||||
`/api/alien/download/{uuid}/{filename}` 下载文件
|
||||
|
||||
### 上传时序图
|
||||
|
||||

|
||||
|
||||
### 下载时序图
|
||||
|
||||

|
||||
|
||||
### 接口详情
|
||||
|
||||
#### /api/alien/fetch/upload/token
|
||||
功能:一个蓝眼云盘受信任的用户请求一个`UploadToken`,用于给另一个用户向蓝眼云盘上传文件。
|
||||
|
||||
一般的使用场景是`应用服务器`向`蓝眼云盘`请求`UploadToken`,然后将此`UploadToken`交由`浏览器`去向`蓝眼云盘`上传文件。
|
||||
|
||||
参数 | 类型 | 描述
|
||||
--------- | ---- | -----------
|
||||
email | `string` | 【必填】邮箱,用于确定请求者身份
|
||||
password | `string` | 【必填】密码,用于确定请求者身份
|
||||
filename | `string` | 【必填】即将上传的文件名,不能包含以下特殊符号:`< > \| * ? / \`
|
||||
expire | `int` | 【选填】UploadToken过期时间,单位:s。默认 86400s 即24h
|
||||
privacy | `bool` | 【必填】文件的共有性。`true`表示文件私有,下载时必须要DownloadToken. `false`表示文件公有,任何人可以通过下载链接直接下载
|
||||
size | `int` | 【必填】文件的大小。单位:byte
|
||||
dir | `string` | 【必填】文件存放的路径。不能为空,必须以`/`开头,不能出现连续的`//`,不能包含以下特殊符号:`< > \| * ? \`。举例:`/app/blog/20180101121212001`
|
||||
|
||||
#### /api/alien/upload
|
||||
|
||||
功能:浏览器拿着`UploadToken`通过FormData向蓝眼云盘上传文件。
|
||||
|
||||
一般的使用场景是`应用服务器`向`蓝眼云盘`请求`UploadToken`,然后将此`UploadToken`交由`浏览器`去向`蓝眼云盘`上传文件。由于在请求`UploadToken`的时候已经传入了文件元信息,因此这里的文件信息必须要和`/api/alien/fetch/upload/token`传入的参数信息一致。
|
||||
|
||||
参数 | 类型 | 描述
|
||||
--------- | ---- | -----------
|
||||
uploadTokenUuid | `string` | 【必填】uploadToken标识,`/api/alien/fetch/upload/token`请求返回对象中的`uuid`
|
||||
file | `file` | 【必填】文件,在浏览器中是通过`<input type="file" name="file"/>`来选择的
|
||||
|
||||
#### /api/alien/confirm
|
||||
|
||||
功能:`应用服务器`向蓝眼云盘确认某个文件是否确实已经上传好了。
|
||||
|
||||
参数 | 类型 | 描述
|
||||
--------- | ---- | -----------
|
||||
email | `string` | 【必填】邮箱,用于确定请求者身份
|
||||
password | `string` | 【必填】密码,用于确定请求者身份
|
||||
matterUuid | `string` | 【必填】浏览器上传完毕后,蓝眼云盘返回给浏览器的`uuid`
|
||||
|
||||
#### /api/alien/fetch/download/token
|
||||
|
||||
功能:一个蓝眼云盘受信任的用户请求一个`DownloadToken`,用于给另一个用户下载蓝眼云盘上的私有文件。
|
||||
|
||||
一般的使用场景是`应用服务器`向`蓝眼云盘`请求`DownloadToken`,然后将此`DownloadToken`交由`浏览器`去向`蓝眼云盘`下载文件。
|
||||
|
||||
参数 | 类型 | 描述
|
||||
--------- | ---- | -----------
|
||||
email | `string` | 【必填】邮箱,用于确定请求者身份
|
||||
password | `string` | 【必填】密码,用于确定请求者身份
|
||||
matterUuid | `string` | 【必填】文件uuid,要想下载的文件`uuid`
|
||||
expire | `int` | 【选填】UploadToken过期时间,单位:s。默认 86400s 即24h
|
||||
|
||||
|
||||
#### /api/alien/download/{uuid}/{filename}
|
||||
|
||||
功能:在浏览器中下载文件
|
||||
|
||||
这个接口既可以下载公有文件,又可以下载私有文件。同时对于图片文件还可以做裁剪缩放等操作。
|
||||
|
||||
参数 | 类型 | 描述
|
||||
--------- | ---- | -----------
|
||||
uuid | `string` | 【必填】文件的uuid,该参数放在url的路径中
|
||||
filename | `string` | 【必填】文件的名称,该参数放在url的路径中
|
||||
downloadTokenUuid | `string` | 【选填】download的uuid,如果是私有文件该参数必须,公有文件无需填写。
|
||||
|
||||
该接口同时还可以对图片进行缩放预处理
|
||||
> 图片缩放支持的格式有:".jpg", ".jpeg", ".png", ".tif", ".tiff", ".bmp", ".gif"
|
||||
|
||||
##### 额外参数
|
||||
|
||||
| 参数 | 类型 | 描述 | 取值范围 |
|
||||
| ------------ | ---- | ------------ | ------------ |
|
||||
| imageProcess | `string` | 指定图片处理的方式,对于图片缩放固定为`resize` | 固定为`resize` |
|
||||
| imageResizeM | `string` | 指定图片缩放的策略,有三种策略,`fit` 表示固定一边,另一边按比例缩放;`fill`表示先将图片延伸出指定W与H的矩形框外,然后进行居中裁剪;`fixed`表示直接按照指定的W和H缩放图片,这种方式可能导致图片变形 | [`fit`,`fill`,`fixed`] 不填默认`fit` |
|
||||
| imageResizeW | `int` | 指定的宽度,对于`fit`可以不指定 | 1 ~ 4096 |
|
||||
| imageResizeH | `int` | 指定的高度,对于`fit`可以不指定 | 1 ~ 4096 |
|
||||
|
||||
##### 示例
|
||||
|
||||
原图:
|
||||
|
||||

|
||||
|
||||
1. 将宽度指定为200,高度等比例缩放
|
||||
|
||||

|
||||
|
||||
[http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fit&imageResizeW=200](http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fit&imageResizeW=200)
|
||||
|
||||
2. 将高度指定为200,宽度等比例缩放
|
||||
|
||||

|
||||
|
||||
[http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fit&imageResizeH=200](http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fit&imageResizeH=200)
|
||||
|
||||
|
||||
3. 图片自动填充在200*200的大小中 (这种情况用得最多)
|
||||
|
||||

|
||||
|
||||
[http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fill&imageResizeW=200&imageResizeH=200](http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fill&imageResizeW=200&imageResizeH=200)
|
||||
|
||||
4. 图片固定大小200*200 (一般会导致变形)
|
||||
|
||||

|
||||
|
||||
[http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fixed&imageResizeW=200&imageResizeH=200](http://tank.eyeblue.cn/api/alien/download/3f4b3090-e688-4d63-7705-93a120690505/horse.jpg?imageProcess=resize&imageResizeM=fixed&imageResizeW=200&imageResizeH=200)
|
||||
@@ -1,798 +0,0 @@
|
||||
|
||||
# 蓝眼云盘api接口
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、实体
|
||||
|
||||
在详细介绍各controller中的接口前,有必要先介绍蓝眼云盘中的各实体,所有的实体基类均为`Base`
|
||||
|
||||
#### Base
|
||||
|
||||
`Base`定义如下,所有会在数据库中持久化的实体均会继承`Base`,`Controller`在返回实体给前端时,会将字段和值序列化成json字符串,其中键就和每个实体字段后面的`json`标签一致,下文也会有详细例子介绍
|
||||
|
||||
```
|
||||
type Base struct {
|
||||
//唯一标识
|
||||
Uuid string `gorm:"primary_key" json:"uuid"`
|
||||
//排序用的字段,一般是时间戳来表示序号先后
|
||||
Sort int64 `json:"sort"`
|
||||
//修改时间
|
||||
ModifyTime time.Time `json:"modifyTime"`
|
||||
//创建时间
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Preference
|
||||
|
||||
`Preference`是整个网站的偏好设置,网站的名称,logo,favicon,版权,备案号等信息均由这个实体负责。定义如下:
|
||||
|
||||
```
|
||||
type Preference struct {
|
||||
//继承Base,也就是说Base中的Uuid,Sort,ModifyTime,CreateTime这里也会有
|
||||
Base
|
||||
//网站名称
|
||||
Name string `json:"name"`
|
||||
//网站的logo url
|
||||
LogoUrl string `json:"logoUrl"`
|
||||
//网站的favicon.ico url
|
||||
FaviconUrl string `json:"faviconUrl"`
|
||||
//网站下方第一行信息,一般是版权信息
|
||||
FooterLine1 string `json:"footerLine1"`
|
||||
//网站下方的第二行信息,一般是本案信息
|
||||
FooterLine2 string `json:"footerLine2"`
|
||||
//当前运行的蓝眼博客版本,这个字段不可修改,每次发版时硬编码
|
||||
Version string `json:"version"`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Matter
|
||||
|
||||
`Matter`是代表文件(文件夹是一种特殊的文件),为了和系统的`file`重复,这里使用`matter`,这个实体是蓝眼云盘最重要的实体:
|
||||
|
||||
```
|
||||
type Matter struct {
|
||||
//继承Base,也就是说Base中的Uuid,Sort,ModifyTime,CreateTime这里也会有
|
||||
Base
|
||||
//所在的文件夹的uuid,如果在根目录下,这个字段为 root
|
||||
Puuid string `json:"puuid"`
|
||||
//创建这个文件的用户uuid
|
||||
UserUuid string `json:"userUuid"`
|
||||
//该文件是否是文件夹
|
||||
Dir bool `json:"dir"`
|
||||
//该文件是否是应用文件,比如用户上传的头像这个字段就为true. 一般上传的文件就为false
|
||||
Alien bool `json:"alien"`
|
||||
//文件名,带后缀名。例如:avatar.jpg
|
||||
Name string `json:"name"`
|
||||
//文件的md5值,目前该功能尚未实现,作为保留字段
|
||||
Md5 string `json:"md5"`
|
||||
//文件大小,单位 byte。比如某个文件1M大,那么这里就为: 1048576
|
||||
Size int64 `json:"size"`
|
||||
//文件是否为私有,如果true则该文件只能作者或超级管理员可以下载,如果false所有人均可以通过下载链接下载。
|
||||
Privacy bool `json:"privacy"`
|
||||
//文件在磁盘中的路径,前端无需关心这个字段。但是后端在寻找文件时这个字段非常关键。
|
||||
Path string `json:"path"`
|
||||
//该文件的父级matter,该字段不会持久化到数据集,属于获取matter详情时临时组装出来的。
|
||||
Parent *Matter `gorm:"-" json:"parent"`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### User
|
||||
|
||||
`User`是代表用户:
|
||||
|
||||
```
|
||||
type User struct {
|
||||
//继承Base,也就是说Base中的Uuid,Sort,ModifyTime,CreateTime这里也会有
|
||||
Base
|
||||
//角色,有以下枚举值:GUEST(游客,不会持久化到数据库),USER(普通用户),ADMINISTRATOR(超级管理员)
|
||||
Role string `json:"role"`
|
||||
//用户名,在Matter的path字段中很有用。
|
||||
Username string `json:"username"`
|
||||
//密码,默认不会返回给前端
|
||||
Password string `json:"-"`
|
||||
//邮箱,作为登录时的账户
|
||||
Email string `json:"email"`
|
||||
//手机
|
||||
Phone string `json:"phone"`
|
||||
//性别,有以下枚举值:MALE(男性),FEMALE(女性),UNKNOWN(未知)
|
||||
Gender string `json:"gender"`
|
||||
//城市
|
||||
City string `json:"city"`
|
||||
//头像Url
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
//上次登录时的ip
|
||||
LastIp string `json:"lastIp"`
|
||||
//上次登录的时间
|
||||
LastTime time.Time `json:"lastTime"`
|
||||
//该用户允许上传的单文件最大大小
|
||||
SizeLimit int64 `json:"sizeLimit"`
|
||||
//状态,有以下枚举值:OK(正常),DISABLED(被禁用)
|
||||
Status string `json:"status"`
|
||||
}
|
||||
```
|
||||
|
||||
### UploadToken
|
||||
|
||||
用于给陌生人上传的token
|
||||
|
||||
```
|
||||
type UploadToken struct {
|
||||
//继承Base,也就是说Base中的Uuid,Sort,ModifyTime,CreateTime这里也会有
|
||||
Base
|
||||
//颁发该token的用户,系统中任何用户都能颁发token
|
||||
UserUuid string `json:"userUuid"`
|
||||
//使用这个token上传文件就必须上传在这个文件夹下
|
||||
FolderUuid string `json:"folderUuid"`
|
||||
//陌生人上传好了的文件uuid
|
||||
MatterUuid string `json:"matterUuid"`
|
||||
//过期时间
|
||||
ExpireTime time.Time `json:"expireTime"`
|
||||
//使用这个token上传文件就必须是这个文件名
|
||||
Filename string `json:"filename"`
|
||||
//使用这个token上传文件就必须是这个公私有性
|
||||
Privacy bool `json:"privacy"`
|
||||
//使用这个token上传文件就必须这个大小
|
||||
Size int64 `json:"size"`
|
||||
//使用这个token上传文件陌生人的ip
|
||||
Ip string `json:"ip"`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### DownloadToken
|
||||
|
||||
用于给陌生人下载的token,一个matter如果Privacy=true,那么就意味着只有自己或者超级管理员可以下载,如果让某些自己信任的用户也能下载,那么就需要生成`DownloadToken`给这些用户来下载。
|
||||
|
||||
```
|
||||
type DownloadToken struct {
|
||||
//继承Base,也就是说Base中的Uuid,Sort,ModifyTime,CreateTime这里也会有
|
||||
Base
|
||||
//颁发该token的用户
|
||||
UserUuid string `json:"userUuid"`
|
||||
//该token只能下载这个文件
|
||||
MatterUuid string `json:"matterUuid"`
|
||||
//有效期截止
|
||||
ExpireTime time.Time `json:"expireTime"`
|
||||
//下载者的ip
|
||||
Ip string `json:"ip"`
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Pager
|
||||
|
||||
在前端请求一个列表时,通常返回的都是一个`Pager`,`Pager`中就是装的各个实体的列表。
|
||||
|
||||
```
|
||||
type Pager struct {
|
||||
//当前页数,0基
|
||||
Page int `json:"page"`
|
||||
//每页的大小
|
||||
PageSize int `json:"pageSize"`
|
||||
//总的条目数
|
||||
TotalItems int `json:"totalItems"`
|
||||
//总的页数
|
||||
TotalPages int `json:"totalPages"`
|
||||
//实体的数组
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
```
|
||||
|
||||
### WebResult
|
||||
|
||||
`WebResult`并不是会持久化到数据库中实体,`WebResult`是在`controller`返回数据给前端时包装的一层,有了`WebResult`后每个接口返回的数据会更加统一,方便了前端的统一处理。
|
||||
|
||||
```
|
||||
type WebResult struct {
|
||||
//状态码,具体每个码的意义参考下文
|
||||
Code int `json:"code"`
|
||||
//一句话描述请求结果,通常会是出错时指明出错原因,或者修改权限等小操作时提示的`操作成功`
|
||||
Msg string `json:"msg"`
|
||||
//内容可能是一个实体,也可能是一个 Pager.
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
状态码对应关系如下:
|
||||
|
||||
```
|
||||
const (
|
||||
//正常
|
||||
RESULT_CODE_OK = 200
|
||||
//未登录
|
||||
RESULT_CODE_LOGIN = -400
|
||||
//没有权限
|
||||
RESULT_CODE_UNAUTHORIZED = -401
|
||||
//请求错误
|
||||
RESULT_CODE_BAD_REQUEST = -402
|
||||
//没有找到
|
||||
RESULT_CODE_NOT_FOUND = -404
|
||||
//登录过期
|
||||
RESULT_CODE_LOGIN_EXPIRED = -405
|
||||
//该登录用户不是有效用户
|
||||
RESULT_CODE_LOGIN_INVALID = -406
|
||||
//提交的表单验证不通过
|
||||
RESULT_CODE_FORM_INVALID = -410
|
||||
//请求太频繁
|
||||
RESULT_CODE_FREQUENCY = -420
|
||||
//服务器出错。
|
||||
RESULT_CODE_SERVER_ERROR = -500
|
||||
//远程服务不可用
|
||||
RESULT_CODE_NOT_AVAILABLE = -501
|
||||
//并发异常
|
||||
RESULT_CODE_CONCURRENCY = -511
|
||||
//远程微服务没有找到
|
||||
RESULT_CODE_SERVICE_NOT_FOUND = -600
|
||||
//远程微服务连接超时
|
||||
RESULT_CODE_SERVICE_TIME_OUT = -610
|
||||
//通用的异常
|
||||
RESULT_CODE_UTIL_EXCEPTION = -700
|
||||
)
|
||||
```
|
||||
|
||||
## 二、返回规范
|
||||
|
||||
蓝眼云盘采用前后端分离的模式,前端调用后端接口时,url均以`/api`开头,返回均是json字符串。
|
||||
|
||||
- 返回的json字符串的key均为小写开头的驼峰法,具体参考实体类中`json`标签
|
||||
|
||||
- 返回的时间格式均为 `YYYY-MM-dd HH:mm:ss`(例如:2018-01-06 17:57:00)
|
||||
|
||||
返回内容均是由`WebResult`进行包装,因此具有高度的统一性,在这里我们约定一些说法,后面介绍`Controller`时便不再赘述。
|
||||
|
||||
1. 返回一个`XX`实体
|
||||
|
||||
指的是`WebResult`的`Code=200`, `Data=一个XX实体对象`。
|
||||
|
||||
例:返回一个User,则前端会收到以下json字符串:
|
||||
```
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"uuid": "eed2c66d-1de6-47ff-645e-b67beaa10365",
|
||||
"sort": 1514803034507,
|
||||
"modifyTime": "2018-01-06 18:00:58",
|
||||
"createTime": "2018-01-01 18:37:15",
|
||||
"role": "USER",
|
||||
"username": "demo",
|
||||
"email": "demo@tank.eyeblue.cn",
|
||||
"phone": "",
|
||||
"gender": "UNKNOWN",
|
||||
"city": "",
|
||||
"avatarUrl": "/api/alien/download/ea490cb6-368e-436d-71c0-fcfb08854c80/1180472.png",
|
||||
"lastIp": "124.78.220.82",
|
||||
"lastTime": "2018-01-06 18:00:58",
|
||||
"sizeLimit": 1048576,
|
||||
"status": "OK"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 返回`XX`的`Pager`
|
||||
|
||||
指的是`WebResult`的`Code=200`, `Data=XX的Pager`。
|
||||
|
||||
例:返回`User`的`Pager`,则前端会收到以下json字符串:
|
||||
```
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"page": 0,
|
||||
"pageSize": 10,
|
||||
"totalItems": 2,
|
||||
"totalPages": 1,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "6a661938-8289-4957-4096-5a1b584bf371",
|
||||
"sort": 1515057859613,
|
||||
"modifyTime": "2018-01-04 17:26:01",
|
||||
"createTime": "2018-01-04 17:24:20",
|
||||
"role": "ADMINISTRATOR",
|
||||
"username": "simba",
|
||||
"email": "y******5@163.com",
|
||||
"phone": "",
|
||||
"gender": "MALE",
|
||||
"city": "",
|
||||
"avatarUrl": "/api/alien/download/d1e453cb-3170-4bdb-73f2-fa0372ee017b/1180480.png",
|
||||
"lastIp": "180.173.103.207",
|
||||
"lastTime": "2018-01-04 17:26:01",
|
||||
"sizeLimit": -1,
|
||||
"status": "OK"
|
||||
},
|
||||
{
|
||||
"uuid": "e59be6a3-f806-463e-553a-4c5892eedf78",
|
||||
"sort": 1514881002975,
|
||||
"modifyTime": "2018-01-02 16:16:43",
|
||||
"createTime": "2018-01-02 16:16:43",
|
||||
"role": "USER",
|
||||
"username": "blog_dev",
|
||||
"email": "b*****@tank.eyeblue.cn",
|
||||
"phone": "",
|
||||
"gender": "MALE",
|
||||
"city": "",
|
||||
"avatarUrl": "/api/alien/download/fdca6eee-d009-4eb3-5ad4-15ba3701cb2e/jump.jpg",
|
||||
"lastIp": "",
|
||||
"lastTime": "2018-01-02 16:16:43",
|
||||
"sizeLimit": 1048576,
|
||||
"status": "OK"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
3. 返回错误信息:yyy
|
||||
|
||||
指的是`WebResult`的`Code=-400`, `Msg=yyy`。(这里的Code具体值参考上文的code表)
|
||||
|
||||
例:返回错误信息:"【新建文件夹】已经存在了,请使用其他名称。",则前端会收到以下json字符串:
|
||||
```
|
||||
{
|
||||
"code": -700,
|
||||
"msg": "【新建文件夹】已经存在了,请使用其他名称。",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
4. 返回成功信息:zzz
|
||||
|
||||
指的是`WebResult`的`Code=200`, `Msg=zzz`。(这里的Code具体值参考上文的code表)
|
||||
|
||||
例:返回成功信息:"删除成功。",则前端会收到以下json字符串:
|
||||
```
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "删除成功。",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 三、接口
|
||||
|
||||
蓝眼云盘所有的接口均定义在`controller`中,总共定义了以下`controller`:
|
||||
|
||||
名称 | 所在文件 | 描述
|
||||
--------- | ---- | -----------
|
||||
PreferenceController | `preference_controller.go` | 网站标题,logo,版权说明等信息的增删改查
|
||||
MatterController | `matter_controller.go` | 站内创建文件夹,上传文件,删除文件,修改权限等
|
||||
UserController | `user_controller.go` | 登录,管理操作站内用户
|
||||
AlienController | `alien_controller.go` | 第三方授权上传,下载,预处理
|
||||
|
||||
每个接口都有不同的访问级别,系统中定义了三种访问级别,分别是:
|
||||
|
||||
`游客` < `注册用户` < `管理员`
|
||||
|
||||
### PreferenceController
|
||||
|
||||
该Controller负责网站中的偏好设置,主要操作`Preference`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/preference/fetch
|
||||
|
||||
**功能**:读取网站偏好设置,网站名称,logo,版权,备案信息均从此接口读取。
|
||||
|
||||
**访问级别**:`游客`,`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:无
|
||||
|
||||
**返回**: 一个`Preference`实体
|
||||
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/preference/edit
|
||||
|
||||
**功能**:编辑网站偏好设置,修改网站名称,logo,版权,备案信息。
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
name | `string` | 必填 | 网站名称
|
||||
logoUrl | `string` | 选填 | 网站logoUrl,如果不填默认使用蓝眼云盘logo
|
||||
faviconUrl | `string` | 选填 | 网站faviconUrl,如果不填默认使用蓝眼云盘favicon.ico
|
||||
footerLine1 | `string` | 选填 | 网站版权所有信息
|
||||
footerLine2 | `string` | 选填 | 网站备案信息
|
||||
|
||||
**返回**: 一个`Preference`实体
|
||||
|
||||
----------
|
||||
|
||||
### MatterController
|
||||
|
||||
该Controller负责站内创建文件夹,上传文件,删除文件,修改权限等,主要操作`Matter`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/matter/create/directory
|
||||
|
||||
**功能**:创建文件夹
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
puuid | `string` | 必填 | 准备创建的目录所在的目录,如果在根目录下创建传`root`
|
||||
name | `string` | 必填 | 文件夹名称, 不能包含以下特殊符号:`< > \| * ? / \`
|
||||
|
||||
**返回**: 新建的这个文件夹的`Matter`实体
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/matter/upload
|
||||
|
||||
**功能**:上传文件
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
userUuid | `string` | 选填 | 该文件的归属人,如果不填则使用当前登录的用户uuid;如果是管理员要给用户A上传文件,那么这里应该填A的uuid.
|
||||
alien | `bool` | 选填 | 是否是应用数据,true表示应用数据,这时会自动放置在`应用数据`这个文件夹下,比如上传头像。false表示非应用数据,这时`puuid`字段为必填
|
||||
puuid | `bool` | 选填 | 文件上传到哪个目录下。 如果`alien`=false,这个字段必填。
|
||||
privacy | `bool` | 选填 | 文件的私有性,默认`false`
|
||||
file | `file` | 必填 | 文件,在浏览器中是通过`<input type="file" name="file"/>`来选择的
|
||||
|
||||
|
||||
**返回**: 刚上传的这个文件的`Matter`实体
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/matter/delete
|
||||
|
||||
**功能**:删除文件或者文件夹
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 待删除的文件或文件夹的uuid
|
||||
|
||||
|
||||
**返回**: 成功信息“删除成功”
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/matter/delete/batch
|
||||
|
||||
**功能**:批量删除文件或文件夹
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuids | `string` | 必填 | 待删除的文件或文件夹的uuids,用逗号(,)分隔。
|
||||
|
||||
**返回**: 成功信息“删除成功”
|
||||
|
||||
----------
|
||||
|
||||
#### /api/matter/rename
|
||||
|
||||
**功能**:重命名文件或文件夹
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 文件的uuid
|
||||
name | `string` | 必填 | 新名字,不能包含以下特殊符号:`< > \| * ? / \`
|
||||
|
||||
**返回**: 刚重命名的这个文件的`Matter`实体
|
||||
|
||||
----------
|
||||
#### /api/matter/change/privacy
|
||||
|
||||
**功能**:改变文件的公私有属性
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 文件的uuid
|
||||
privacy | `bool` | 选填 | 文件的私有性,默认`false`
|
||||
|
||||
**返回**: 成功信息“设置成功”
|
||||
|
||||
----------
|
||||
|
||||
#### /api/matter/move
|
||||
|
||||
**功能**:将一个文件夹或者文件移入到另一个文件夹下。
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
srcUuids | `string` | 必填 | 待移动的文件或文件夹的uuids,用逗号(,)分隔。
|
||||
destUuid | `string` | 必填 | 目标文件夹,根目录用`root`
|
||||
|
||||
**返回**: 成功信息“设置成功”
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/matter/detail
|
||||
|
||||
**功能**:产看文件详情
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 该文件的uuid。
|
||||
destUuid | `string` | 必填 | 目标文件夹,根目录用`root`
|
||||
|
||||
**返回**: 这个文件的`Matter`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/matter/page
|
||||
|
||||
**功能**:按照分页的方式获取某个文件夹下文件和子文件夹的列表
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
puuid | `string` | 选填 | 文件夹uuid,如果根目录填`root`
|
||||
page | `int` | 选填 | 当前页数,0基,默认0
|
||||
pageSize | `int` | 选填 | 每页条目数,默认200
|
||||
userUuid | `string` | 选填 | 筛选文件拥有者,对于普通用户使用当前登录的用户uuid.
|
||||
name | `string` | 选填 | 模糊筛选文件名
|
||||
dir | `bool` | 选填 | 筛选是否为文件夹
|
||||
orderDir | `DESC`或`ASC` | 选填 | 按文件夹排序,`DESC`降序排,`ASC`升序排
|
||||
orderCreateTime | `DESC`或`ASC` | 选填 | 按创建时间排序,`DESC`降序排,`ASC`升序排
|
||||
orderSize | `DESC`或`ASC` | 选填 | 按文件大小排序,`DESC`降序排,`ASC`升序排
|
||||
orderName | `DESC`或`ASC` | 选填 | 按名称排序,`DESC`降序排,`ASC`升序排
|
||||
extensions | `string` | 选填 | 按文件后缀名筛选,逗号(,)分隔。例:`jpg,png,pdf`
|
||||
|
||||
|
||||
**返回**: `Matter`的`Pager`
|
||||
|
||||
----------
|
||||
|
||||
#### /api/alien/download/{uuid}/{filename}
|
||||
|
||||
这个接口的定义在在`AlienController`中的,具体使用请参考[蓝眼云盘编程接口](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md) 这里只做简单的下载使用。
|
||||
|
||||
**功能**:下载文件。
|
||||
|
||||
**访问级别**:`游客`,`注册用户`,`管理员`
|
||||
|
||||
**请求参数**: 均是放置在url中
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 要下载的文件uuid
|
||||
filename | `string` | 必填 | 要下载的文件名
|
||||
|
||||
**返回**: 二进制的文件
|
||||
|
||||
----------
|
||||
|
||||
### UserController
|
||||
|
||||
该Controller负责站内创建文件夹,上传文件,删除文件,修改权限等,主要操作`Matter`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/create
|
||||
|
||||
**功能**:创建用户
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
username | `string` | 必填 | 用户名,且只能包含字母,数字和'_'
|
||||
password | `string` | 必填 | 密码,密码长度至少为6位
|
||||
email | `string` | 必填 | 邮箱,作为用户登录的凭证
|
||||
avatarUrl | `string` | 选填 | 头像
|
||||
phone | `string` | 选填 | 电话
|
||||
gender | `string` | 选填 | 性别
|
||||
role | `string` | 选填 | 角色
|
||||
city | `string` | 选填 | 城市
|
||||
sizeLimit | `string` | 选填 | 用户上传单文件限制,单位byte. 如果负数表示无限制。
|
||||
|
||||
|
||||
**返回**: 新建的`User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/edit
|
||||
|
||||
**功能**:编辑用户
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 待编辑的用户uuid
|
||||
avatarUrl | `string` | 选填 | 头像
|
||||
phone | `string` | 选填 | 电话
|
||||
gender | `string` | 选填 | 性别
|
||||
role | `string` | 选填 | 角色
|
||||
city | `string` | 选填 | 城市
|
||||
sizeLimit | `string` | 选填 | 用户上传单文件限制,单位byte. 如果负数表示无限制。
|
||||
|
||||
|
||||
**返回**: 编辑的`User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/change/password
|
||||
|
||||
**功能**:用户修改密码
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
oldPassword | `string` | 必填 | 旧密码
|
||||
newPassword | `string` | 必填 | 新密码
|
||||
|
||||
**返回**: 修改密码的`User`实体
|
||||
|
||||
----------
|
||||
|
||||
|
||||
#### /api/user/change/password
|
||||
|
||||
**功能**:管理员重置用户密码
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
userUuid | `string` | 必填 | 待重置密码的用户uuid
|
||||
password | `string` | 必填 | 密码
|
||||
|
||||
**返回**: 修改密码的`User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/login
|
||||
|
||||
**功能**:登录
|
||||
|
||||
**访问级别**:`游客`,`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
email | `string` | 必填 | 邮箱
|
||||
password | `string` | 必填 | 密码
|
||||
|
||||
**返回**: 当前登录的`User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/logout
|
||||
|
||||
**功能**:登录
|
||||
|
||||
**访问级别**:`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:无
|
||||
|
||||
**返回**: 成功信息"退出成功!"
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/detail
|
||||
|
||||
**功能**:查看用户详情
|
||||
|
||||
**访问级别**:`游客`,`注册用户`,`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 待查看的用户uuid
|
||||
|
||||
**返回**: `User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/page
|
||||
|
||||
**功能**:查看用户列表
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
page | `int` | 选填 | 当前页数,0基,默认0
|
||||
pageSize | `int` | 选填 | 每页条目数,默认200
|
||||
username | `string` | 选填 | 模糊筛选用户名
|
||||
email | `string` | 选填 | 模糊筛选邮箱
|
||||
phone | `string` | 选填 | 模糊筛选电话
|
||||
orderLastTime | `DESC`或`ASC` | 选填 | 按上次登录时间排序,`DESC`降序排,`ASC`升序排
|
||||
orderCreateTime | `DESC`或`ASC` | 选填 | 按创建时间排序,`DESC`降序排,`ASC`升序排
|
||||
|
||||
**返回**: `User`实体的`Pager`
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/disable
|
||||
|
||||
**功能**:禁用用户
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 待操作的用户
|
||||
|
||||
**返回**: 被操作的用户`User`实体
|
||||
|
||||
----------
|
||||
|
||||
#### /api/user/enable
|
||||
|
||||
**功能**:启用用户
|
||||
|
||||
**访问级别**:`管理员`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
名称 | 类型 | 必填性 | 描述
|
||||
--------- | ---- | ---- | -----------
|
||||
uuid | `string` | 必填 | 待操作的用户
|
||||
|
||||
**返回**: 被操作的用户`User`实体
|
||||
|
||||
----------
|
||||
|
||||
### AlienController
|
||||
|
||||
这个Controller主要暴露给第三方使用,详情请参考:[蓝眼云盘编程接口](https://github.com/eyebluecn/tank/blob/master/build/doc/alien_zh.md)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Package dependency
|
||||
|
||||
```
|
||||
main
|
||||
├── bin
|
||||
├── pkg
|
||||
├── src
|
||||
│ ├── github.com
|
||||
│ ├── golang.org
|
||||
│ ├── tank
|
||||
│ │ ├── build
|
||||
│ │ │ ├── conf
|
||||
│ │ │ ├── doc
|
||||
```
|
||||
|
After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,193 @@
|
||||
CREATE TABLE `tank31_bridge` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`share_uuid` char(36) DEFAULT NULL,
|
||||
`matter_uuid` char(36) DEFAULT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_dashboard` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`invoke_num` bigint(20) NOT NULL,
|
||||
`total_invoke_num` bigint(20) NOT NULL DEFAULT '0',
|
||||
`uv` bigint(20) NOT NULL DEFAULT '0',
|
||||
`total_uv` bigint(20) NOT NULL DEFAULT '0',
|
||||
`matter_num` bigint(20) NOT NULL DEFAULT '0',
|
||||
`total_matter_num` bigint(20) NOT NULL DEFAULT '0',
|
||||
`file_size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`total_file_size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`avg_cost` bigint(20) NOT NULL DEFAULT '0',
|
||||
`dt` varchar(45) NOT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `idx_dt` (`dt`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_download_token` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`user_uuid` char(36) NOT NULL,
|
||||
`matter_uuid` char(36) NOT NULL,
|
||||
`expire_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`ip` varchar(128) NOT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `idx_mu` (`matter_uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_footprint` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`user_uuid` char(36) DEFAULT NULL,
|
||||
`ip` varchar(128) NOT NULL,
|
||||
`host` varchar(45) NOT NULL,
|
||||
`uri` varchar(255) NOT NULL,
|
||||
`params` text,
|
||||
`cost` bigint(20) NOT NULL DEFAULT '0',
|
||||
`success` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_image_cache` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`user_uuid` char(36) DEFAULT NULL,
|
||||
`username` varchar(45) NOT NULL,
|
||||
`matter_uuid` char(36) DEFAULT NULL,
|
||||
`matter_name` varchar(255) NOT NULL,
|
||||
`mode` varchar(512) DEFAULT NULL,
|
||||
`md5` varchar(45) DEFAULT NULL,
|
||||
`size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`path` varchar(512) DEFAULT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `idx_mu` (`matter_uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_matter` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`puuid` char(36) DEFAULT NULL,
|
||||
`user_uuid` char(36) DEFAULT NULL,
|
||||
`username` varchar(45) NOT NULL,
|
||||
`dir` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`md5` varchar(45) DEFAULT NULL,
|
||||
`size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`privacy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`path` varchar(1024) DEFAULT NULL,
|
||||
`times` bigint(20) NOT NULL DEFAULT '0',
|
||||
`prop` varchar(1024) NOT NULL DEFAULT '{}',
|
||||
`visit_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`delete_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
KEY `idx_puuid` (`puuid`),
|
||||
KEY `idx_uu` (`user_uuid`),
|
||||
KEY `idx_del` (`deleted`),
|
||||
KEY `idx_delt` (`delete_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_preference` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`name` varchar(45) DEFAULT NULL,
|
||||
`logo_url` varchar(255) DEFAULT NULL,
|
||||
`favicon_url` varchar(255) DEFAULT NULL,
|
||||
`copyright` varchar(1024) DEFAULT NULL,
|
||||
`record` varchar(1024) DEFAULT NULL,
|
||||
`download_dir_max_size` bigint(20) NOT NULL DEFAULT '-1',
|
||||
`download_dir_max_num` bigint(20) NOT NULL DEFAULT '-1',
|
||||
`default_total_size_limit` bigint(20) NOT NULL DEFAULT '-1',
|
||||
`allow_register` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`preview_config` text,
|
||||
`scan_config` text,
|
||||
`deleted_keep_days` bigint(20) NOT NULL DEFAULT '7',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_session` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`user_uuid` char(36) DEFAULT NULL,
|
||||
`ip` varchar(128) NOT NULL,
|
||||
`expire_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_share` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`share_type` varchar(45) DEFAULT NULL,
|
||||
`username` varchar(45) DEFAULT NULL,
|
||||
`user_uuid` char(36) DEFAULT NULL,
|
||||
`download_times` bigint(20) NOT NULL DEFAULT '0',
|
||||
`code` varchar(45) NOT NULL,
|
||||
`expire_infinity` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`expire_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_upload_token` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`user_uuid` char(36) NOT NULL,
|
||||
`folder_uuid` char(36) NOT NULL,
|
||||
`matter_uuid` char(36) NOT NULL,
|
||||
`expire_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`filename` varchar(255) NOT NULL,
|
||||
`privacy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`ip` varchar(128) NOT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `tank31_user` (
|
||||
`uuid` char(36) NOT NULL DEFAULT '',
|
||||
`sort` bigint(20) NOT NULL,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`create_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`role` varchar(45) DEFAULT NULL,
|
||||
`username` varchar(45) NOT NULL,
|
||||
`password` varchar(255) DEFAULT NULL,
|
||||
`avatar_url` varchar(255) DEFAULT NULL,
|
||||
`last_ip` varchar(128) DEFAULT NULL,
|
||||
`last_time` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00',
|
||||
`size_limit` bigint(20) NOT NULL DEFAULT '-1',
|
||||
`total_size_limit` bigint(20) NOT NULL DEFAULT '-1',
|
||||
`total_size` bigint(20) NOT NULL DEFAULT '0',
|
||||
`status` varchar(45) DEFAULT NULL,
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `username` (`username`),
|
||||
UNIQUE KEY `uuid` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -1,267 +0,0 @@
|
||||
#### PROPFIND 列出目录情况
|
||||
request method
|
||||
```
|
||||
PROPFIND
|
||||
```
|
||||
|
||||
request header
|
||||
```
|
||||
Authorization=Basic YWRtaW46YWRtaW4=
|
||||
Content-Type=text/xml
|
||||
Accept-Encoding=gzip
|
||||
Depth=infinity
|
||||
```
|
||||
|
||||
request body
|
||||
```
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:propfind xmlns:D="DAV:">
|
||||
<D:prop>
|
||||
<D:resourcetype />
|
||||
<D:getcontentlength />
|
||||
<D:creationdate />
|
||||
<D:getlastmodified />
|
||||
</D:prop>
|
||||
</D:propfind>
|
||||
```
|
||||
|
||||
response body
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<D:multistatus xmlns:D="DAV:">
|
||||
<D:response>
|
||||
<D:href>/api/dav/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname>dav</D:displayname>
|
||||
<D:getlastmodified>Tue, 16 Apr 2019 17:50:59 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/api/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname>api</D:displayname>
|
||||
<D:getlastmodified>Tue, 16 Apr 2019 17:51:03 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/api/dav/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname>dav</D:displayname>
|
||||
<D:getlastmodified>Tue, 16 Apr 2019 17:51:38 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/api/dav/body.txt</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname>body.txt</D:displayname>
|
||||
<D:getlastmodified>Tue, 16 Apr 2019 17:51:38 GMT</D:getlastmodified>
|
||||
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||
<D:getetag>"159605ccc1d0f3c410"</D:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:resourcetype></D:resourcetype>
|
||||
<D:getcontentlength>16</D:getcontentlength>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/api/dav/cat.txt</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype></D:resourcetype>
|
||||
<D:getcontentlength>24</D:getcontentlength>
|
||||
<D:getlastmodified>Tue, 16 Apr 2019 17:51:19 GMT</D:getlastmodified>
|
||||
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||
<D:getetag>"159605c862b0d64c18"</D:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:displayname>cat.txt</D:displayname>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/cat/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getlastmodified>Sat, 13 Apr 2019 16:55:54 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:displayname>cat</D:displayname>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/cat/dog/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getlastmodified>Sat, 13 Apr 2019 16:55:58 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:displayname>dog</D:displayname>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/cat/dog/pig/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:collection xmlns:D="DAV:"/>
|
||||
</D:resourcetype>
|
||||
<D:getlastmodified>Sat, 13 Apr 2019 16:56:08 GMT</D:getlastmodified>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:displayname>pig</D:displayname>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/cat/dog/pig/hi.txt</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getlastmodified>Sat, 13 Apr 2019 16:56:08 GMT</D:getlastmodified>
|
||||
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||
<D:getetag>"15951707dc1116d87"</D:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
<D:displayname>hi.txt</D:displayname>
|
||||
<D:resourcetype></D:resourcetype>
|
||||
<D:getcontentlength>7</D:getcontentlength>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/api/dav/morning.txt</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype></D:resourcetype>
|
||||
<D:getcontentlength>13</D:getcontentlength>
|
||||
<D:displayname>morning.txt</D:displayname>
|
||||
<D:getlastmodified>Sat, 13 Apr 2019 16:52:08 GMT</D:getlastmodified>
|
||||
<D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
|
||||
<D:getetag>"159516cfe790beecd"</D:getetag>
|
||||
<D:supportedlock>
|
||||
<D:lockentry xmlns:D="DAV:">
|
||||
<D:lockscope>
|
||||
<D:exclusive/>
|
||||
</D:lockscope>
|
||||
<D:locktype>
|
||||
<D:write/>
|
||||
</D:locktype>
|
||||
</D:lockentry>
|
||||
</D:supportedlock>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
</D:multistatus>
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.8f121d4f.css",
|
||||
"main.js": "/static/js/main.e27b1761.js",
|
||||
"static/media/logo.png": "/static/media/logo.847e54ef7fb4b744fad4.png",
|
||||
"static/media/empty.svg": "/static/media/empty.087b383305fa7993cbf2fd9ccfd2cdaf.svg",
|
||||
"static/media/psd.svg": "/static/media/psd.d0769f3468941f83b2cd1491e6ac7d69.svg",
|
||||
"static/media/xls.svg": "/static/media/xls.7a882f471ff07b1015705abf4945d6a3.svg",
|
||||
"static/media/ppt.svg": "/static/media/ppt.b987e0e201d3a6ebb0f83c5444521626.svg",
|
||||
"static/media/doc.svg": "/static/media/doc.bc5d5331ff681915068a120f067a9dec.svg",
|
||||
"static/media/pdf.svg": "/static/media/pdf.c92b8a844f37db16d4a72bf62a73138c.svg",
|
||||
"static/media/video.svg": "/static/media/video.a1b07956cad84a3efc508d024286b30f.svg",
|
||||
"static/media/file.svg": "/static/media/file.9f6f3fda1dd60a03fe595532e845852c.svg",
|
||||
"static/media/text.svg": "/static/media/text.8aeb382b647dad745f0e9c2f11c349d2.svg",
|
||||
"static/media/audio.svg": "/static/media/audio.0d111f7f828e8ea78c83b47d23e771a2.svg",
|
||||
"static/media/image.svg": "/static/media/image.333c8c7c7df48b383c852de5ce99c33c.svg",
|
||||
"static/media/folder.svg": "/static/media/folder.fce5913565dc18a92c7bbbe19ba6c4d0.svg",
|
||||
"static/media/archive.svg": "/static/media/archive.982c48675c38139800a2f45d7c86a661.svg",
|
||||
"index.html": "/index.html",
|
||||
"main.8f121d4f.css.map": "/static/css/main.8f121d4f.css.map",
|
||||
"main.e27b1761.js.map": "/static/js/main.e27b1761.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.8f121d4f.css",
|
||||
"static/js/main.e27b1761.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189353878" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1197" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M9.367 0h992.97v341.333H9.366z" fill="#55C7F7" p-id="1198"></path><path d="M9.367 341.333h992.97v341.334H9.366z" fill="#F95F5D" p-id="1199"></path><path d="M9.367 682.667h992.97V1024H9.366z" fill="#7ECF3B" p-id="1200"></path><path d="M350.7 0h310.303v1024H350.7z" fill="#FDAF42" p-id="1201"></path><path d="M304.154 386.638V646.05h403.394V386.638H304.154z m356.849 215.97H350.7V429.46h310.303v173.15z" fill="#FFFFFF" p-id="1202"></path></svg>
|
||||
|
Before Width: | Height: | Size: 828 B |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543321997742" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3887" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 32c-12 0-24.8 4.8-33.6 14.4S112 68 112 80v864c0 12 4.8 24.8 14.4 33.6C136 987.2 148 992 160 992h704c12 0 24.8-4.8 33.6-14.4C907.2 968 912 956 912 944V304L640 32H160z" fill="#FF5562" p-id="3888"></path><path d="M912 304H688c-12 0-24.8-4.8-33.6-14.4-9.6-8.8-14.4-21.6-14.4-33.6V32l272 272z" fill="#FFBBC0" p-id="3889"></path><path d="M669.6 491.2c0-1.6 0.8-4 0-6.4V369.6c0-4.8-2.4-8.8-5.6-12-4-3.2-8-4-12.8-3.2l-250.4 70.4c-7.2 1.6-12 7.2-12 14.4v275.2c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8 6.4 23.2 30.4 37.6 60.8 37.6 8.8 0 17.6-1.6 26.4-3.2 36.8-10.4 60.8-36.8 60-63.2 0.8-1.6 0.8-3.2 0.8-5.6V570.4l220-61.6v136c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8C528 737.6 552 752 582.4 752c8.8 0 17.6-0.8 26.4-3.2 36-9.6 60-36 60-62.4 0.8-1.6 0.8-3.2 0.8-5.6V491.2z m-250.4 48v-53.6l220-61.6v53.6l-220 61.6z" fill="#FFFFFF" p-id="3890"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
@@ -1 +0,0 @@
|
||||
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300132402" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2107" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M533.947184 38.36489h65.880106v88.119185c117.859409 0.660894 235.60867-1.211639 353.35793 0.660894 25.102953-2.533427 44.81962 16.962943 42.506492 42.07691 1.872533 216.993492-0.440596 434.097133 1.112505 651.090626-1.112505 22.250094 2.191965 46.923466-10.695466 66.640133-16.081751 11.455494-36.899909 10.023557-55.625236 10.904749-110.25913-0.550745-220.408111-0.330447-330.656225-0.330447v88.119185h-68.843113c-167.657764-30.621417-335.745108-58.709407-503.722304-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 168.858388-29.519927 337.826924-58.268811 506.685311-88.339483z" fill="#2A5699" p-id="2108"></path><path d="M599.816275 159.528769h363.491637v704.953477h-363.491637v-88.119185h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-55.07449h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-77.104286zM254.829667 354.162018c20.928306-1.211639 41.856613-2.092831 62.784919-3.19432 14.649814 74.350562 29.619061 148.590975 45.491529 222.61109 12.446835-76.443393 26.215457-152.666487 39.543484-228.999731 22.029796-0.771043 44.059592-1.982682 65.97924-3.30447-24.89367 106.734362-46.703168 214.349917-73.909966 320.423385-18.39488 9.582961-45.932125-0.440596-67.741623 1.10149-14.649814-72.90761-31.722906-145.38564-44.819621-218.645727-12.898446 71.156242-29.641091 141.651589-44.390039 212.367235a4187.754101 4187.754101 0 0 1-63.666111-3.866229c-18.284731-96.931103-39.763782-193.190297-56.836874-290.341698 18.835476-0.881192 37.792115-1.652235 56.627591-2.313129 11.33433 70.164901 24.221761 139.999355 34.146184 210.274404 15.508976-72.037433 31.37043-144.074867 46.791287-216.1123z" fill="#FFFFFF" p-id="2109"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189511483" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6709" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M676.297143 0H145.609143C130.834286 0 118.857143 11.977143 118.857143 35.218286V1005.714286c0 6.308571 11.977143 18.285714 26.752 18.285714h732.781714c14.774857 0 26.752-11.977143 26.752-18.285714V237.312c0-12.726857-1.700571-16.822857-4.699428-19.84L687.670857 4.699429A16.164571 16.164571 0 0 0 676.297143 0z" fill="#E9E9E0" p-id="6710"></path><path d="M685.714286 2.761143V219.428571h216.667428z" fill="#D9D7CA" p-id="6711"></path><path d="M878.390857 1024H145.609143A26.752 26.752 0 0 1 118.857143 997.248V713.142857h786.285714v284.105143c0 14.774857-11.977143 26.752-26.752 26.752z" fill="#C8BDB8" p-id="6712"></path><path d="M338.285714 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6713"></path><path d="M521.142857 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6714"></path><path d="M704 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6715"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10648" xmlns:xlink="http://www.w3.org/1999/xlink" width="238.0859375" height="200"><defs><style type="text/css"></style></defs><path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z" fill="#23B7E5" p-id="10649"></path></svg>
|
||||
|
Before Width: | Height: | Size: 943 B |
|
Before Width: | Height: | Size: 434 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514190539077" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14917" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M128 0h597.3l227.6 227.9v739.2c0 31.4-25.5 56.9-56.9 56.9H128c-31.4 0-56.9-25.5-56.9-56.9V56.9C71.1 25.5 96.6 0 128 0z" fill="#13BE93" p-id="14918"></path><path d="M630.5 1024H128c-25.7 0-47.4-17-54.4-40.4l218.1-218.1c22.2-22.2 58.2-22.2 80.5 0L630.5 1024z" fill="#6BD8BD" p-id="14919"></path><path d="M952.9 858v109.1c0 31.4-25.5 56.9-56.9 56.9H322.4l-4.7-4.7 358-358c22.2-22.2 58.2-22.2 80.5 0L952.9 858z" fill="#9CF9E2" p-id="14920"></path><path d="M725.3 0l227.6 227.6H782.2c-31.4 0-56.9-25.5-56.9-56.9V0z" fill="#69DCC0" p-id="14921"></path><path d="M327.1 483.6m-85.3 0a85.3 85.3 0 1 0 170.6 0 85.3 85.3 0 1 0-170.6 0Z" fill="#E9FFFA" p-id="14922"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514284130163" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1551" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M915.114667 1024H108.885333V0h587.776v218.112H914.773333V1024z m-785.066667-20.821333h763.904V238.933333H675.84V20.821333H129.706667v982.357334z" fill="#F90E09" p-id="1552"></path><path d="M708.266667 10.581333L904.533333 206.848h-196.266666z" fill="#F90E09" p-id="1553"></path><path d="M282.965333 792.576c33.450667-23.210667 55.637333-57.344 76.8-91.136-30.378667 25.941333-59.050667 54.954667-76.8 91.136z m396.629334-169.642667c31.402667 15.018667 65.877333 31.061333 101.376 21.504-24.576-29.696-67.584-21.504-101.376-21.504z m-246.101334 23.210667c45.397333-16.725333 91.818667-29.696 138.922667-39.594667-30.378667-26.965333-55.978667-58.368-76.458667-93.525333-16.725333 46.08-38.912 90.112-62.464 133.12z m67.925334-243.370667c14.677333-37.546667 19.114667-83.285333-5.802667-117.418666-7.168 38.912-4.096 79.189333 5.802667 117.418666z m-45.397334-105.130666c4.096-24.917333 35.84-29.696 55.637334-22.186667 12.288 7.850667 18.432 22.869333 18.773333 37.205333 1.365333 44.032-6.485333 87.381333-12.629333 130.730667-3.413333 20.138667 8.874667 38.229333 17.749333 55.296 21.845333 40.277333 52.224 76.458667 92.16 99.669333 48.128-5.802667 98.645333-12.970667 145.749333 2.389334 35.157333 11.946667 26.965333 77.141333-11.264 77.824-53.589333 2.730667-102.058667-23.893333-150.186666-43.349334-66.218667 4.778667-130.730667 24.917333-194.218667 44.032-28.672 46.762667-54.613333 98.986667-101.034667 130.730667-23.210667 18.090667-76.458667 3.072-64.512-32.085333 20.138667-49.152 70.314667-76.117333 114.346667-100.693334 12.629333-6.485333 19.797333-18.773333 26.282667-31.061333 30.037333-57.685333 60.074667-115.370667 84.309333-175.786667-12.629333-56.661333-31.061333-114.688-21.162667-172.714666z" fill="#F90E09" p-id="1554"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300178225" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5733" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M537.372818 38.36489h62.454472v99.134083c117.738246 0.660894 235.60867-1.211639 353.236766 0.550745 25.334266-2.423278 45.172097 17.29339 42.627656 42.627655 1.872533 205.758296-0.440596 411.626741 1.10149 617.506201-1.10149 22.250094 2.20298 46.923466-10.574302 66.640134-16.081751 11.664777-36.999043 10.133706-55.834519 11.014898-110.148981-0.550745-220.297962-0.330447-330.557091-0.330447v110.148981h-68.523681c-167.856032-30.621417-336.06454-58.599258-504.041736-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 169.970892-29.420793 339.93077-59.381316 510.110945-88.350498z" fill="#D24625" p-id="5734"></path><path d="M599.82729 170.543667h363.491636v671.908783h-363.491636v-88.119185h264.357554v-44.059592h-264.357554v-55.074491h264.357554v-44.059592h-264.247405c-0.110149-21.5892-0.110149-43.1784-0.231313-64.767601 43.630011 13.53731 93.307202 13.217878 132.299941-13.008594 42.18706-25.003819 64.216856-72.698327 67.851772-120.271672-48.366417-0.330447-96.710805-0.220298-144.956059-0.220298-0.121164-47.914807 0.550745-95.829613-0.991341-143.634271-18.075448 3.524767-36.128866 7.269833-54.083149 11.125047v-109.818534z" fill="#FFFFFF" p-id="5735"></path><path d="M677.041725 246.216017c76.553542 3.524767 140.880546 67.961921 144.956059 144.295165-48.366417 0.550745-96.710805 0.330447-145.066208 0.330447 0-48.245254-0.121164-96.490507 0.110149-144.625612z" fill="#D24625" p-id="5736"></path><path d="M203.940838 329.378497c59.810897 2.863874 132.288926-23.682031 181.855967 21.148605 47.485226 57.607917 34.917227 163.571236-35.467972 198.047867-25.003819 12.777282-53.642554 11.014898-80.849351 10.133706-0.110149 43.068251-0.220298 86.136503-0.110149 129.19374-21.820513-1.872533-43.74016-3.745065-65.538644-5.507449-0.980326-117.628097-1.211639-235.377357 0.110149-353.016469z" fill="#FFFFFF" p-id="5737"></path><path d="M269.380347 388.969096c21.699349-0.991341 48.575701-4.956704 63.214501 15.420857 12.567999 21.5892 11.89609 50.007637 1.442951 72.257732-12.567999 22.69069-40.986436 20.48771-62.895068 23.131286-2.313129-36.899909-2.092831-73.799817-1.762384-110.809875z" fill="#D24625" p-id="5738"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1545064197737" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14227" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M759.725253 0H134.464646v1024h817.131314V191.870707z" fill="#02007A" p-id="14228"></path><path d="M72.40404 708.525253h687.838384v258.585858H72.40404z" fill="#230091" p-id="14229"></path><path d="M82.747475 718.868687h667.151515v237.89899H82.747475zM755.070707 5.171717l191.353535 191.353536h-191.353535z" fill="#FFFFFF" p-id="14230"></path><path d="M506.828283 791.272727v93.090909h25.858586c28.444444 0 41.373737-12.929293 41.373737-45.511111s-12.929293-47.579798-41.373737-47.579798h-25.858586z m-25.858586-25.858586h51.717172c42.408081 0 67.232323 29.99596 67.232323 74.989899S575.612121 910.222222 532.686869 910.222222H480.969697v-144.808081z m-132.39596 104.468687c1.034343 26.375758 22.755556 42.408081 55.337374 42.408081 34.650505 0 55.854545-17.066667 55.854546-43.959596 0-21.20404-12.412121-33.09899-40.856566-39.822222l-16.549495-3.620202c-17.583838-4.137374-24.824242-9.826263-24.824242-19.135354 0-11.894949 10.860606-19.652525 26.892929-19.652525s27.410101 7.757576 28.444444 21.204041h24.307071c-0.517172-24.824242-21.20404-41.890909-52.751515-41.89091S351.676768 782.480808 351.676768 807.822222c0 20.169697 12.412121 33.09899 38.787878 38.787879l18.618182 4.137374c18.10101 4.137374 25.341414 10.343434 25.341414 20.169697 0 11.894949-11.894949 20.169697-28.961616 20.169697s-30.513131-8.791919-32.064646-21.721212l-24.824243 0.517171zM258.585859 788.686869v53.785858h20.686868c18.10101 0 31.030303-9.826263 31.030303-26.892929s-12.412121-26.892929-31.030303-26.892929h-20.686868z m-25.858586-23.272728h56.888889c28.444444 0 46.545455 20.686869 46.545454 49.131314s-17.583838 49.131313-46.545454 49.131313h-31.030303v46.545454h-25.858586v-144.808081z" fill="#230091" p-id="14231"></path><path d="M415.806061 443.216162h-31.030303v137.567676H321.163636V196.525253h93.090909c91.022222 0 140.670707 35.684848 140.670708 122.052525 0.517172 103.434343-92.573737 124.638384-139.119192 124.638384z m-5.688889-192.905051c-6.206061 0-14.99798 0.517172-26.89293 1.551515V387.878788h36.202021C461.834343 387.878788 491.313131 366.157576 491.313131 312.888889c0-40.856566-32.064646-62.577778-81.195959-62.577778z m258.068687 337.19596c70.852525 0 96.711111-50.165657 96.711111-84.816162 0-54.820202-35.684848-67.749495-66.19798-87.40202-25.858586-17.066667-49.648485-23.272727-49.648485-40.856566 0-22.755556 15.515152-27.410101 36.20202-27.410101 25.858586 0 54.820202 2.068687 67.232323 16.032323V306.682828c-12.929293-10.343434-37.236364-13.446465-68.783838-13.446464-67.749495 0-93.608081 44.993939-93.608081 84.29899 0 50.682828 33.616162 65.680808 66.19798 83.781818 21.20404 11.894949 46.545455 20.686869 46.545455 44.476767 0 13.963636-8.791919 27.927273-37.753536 27.927273-26.892929 0-59.991919-10.343434-74.989899-23.789899v59.991919c13.446465 10.343434 40.339394 17.583838 78.09293 17.583839z" fill="#7AB7FF" p-id="14232"></path></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1544085034574" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1828" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M0 512A512 512 0 1 0 512 0 512 512 0 0 0 0 512" fill="#36D49F" p-id="1829"></path><path d="M450.37037 552.580741l-115.105185-87.798519-50.820741 39.822222L483.365926 701.62963C517.499259 616.296296 625.777778 449.232593 758.518519 330.714074L746.382222 303.407407a1264.071111 1264.071111 0 0 0-296.011852 249.173334" fill="#FFFFFF" p-id="1830"></path></svg>
|
||||
|
Before Width: | Height: | Size: 743 B |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189544355" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8186" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M176 1024l672 0c26.464 0 48-21.536 48-48l0-672c0-0.544-0.256-0.992-0.288-1.504-0.064-0.736-0.256-1.376-0.416-2.08-0.544-2.272-1.472-4.32-2.88-6.112-0.224-0.288-0.192-0.64-0.416-0.928l-256-288c-0.096-0.096-0.224-0.096-0.32-0.192-1.92-2.048-4.352-3.456-7.136-4.288-0.608-0.192-1.152-0.256-1.792-0.352-0.928-0.16-1.792-0.544-2.752-0.544l-448 0c-26.464 0-48 21.536-48 48l0 928c0 26.464 21.536 48 48 48zM640 58.08l204.384 229.92-188.384 0c-7.04 0-16-13.44-16-24l0-205.92zM160 48c0-8.832 7.2-16 16-16l432 0 0 232c0 25.888 20.96 56 48 56l208 0 0 656c0 8.832-7.2 16-16 16l-672 0c-8.96 0-16-7.04-16-16l0-928zM304 448l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 576l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 320l192 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-192 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 704l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 832l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16z" p-id="8187"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189554639" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8871" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M80 34.4h864v960H80z" fill="#8095FF" p-id="8872"></path><path d="M176 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8873"></path><path d="M176 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8874"></path><path d="M176 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8875"></path><path d="M176 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8876"></path><path d="M176 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8877"></path><path d="M176 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8878"></path><path d="M864 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8879"></path><path d="M864 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8880"></path><path d="M864 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8881"></path><path d="M864 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8882"></path><path d="M864 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8883"></path><path d="M864 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8884"></path><path d="M648 508L436 362.4c-4.8-3.2-11.2-4-16.8-0.8-5.6 3.2-8.8 8.8-8.8 14.4v290.4c0 5.6 3.2 11.2 8.8 14.4 5.6 3.2 12 2.4 16.8-0.8L648 533.6c4.8-2.4 7.2-8 7.2-12.8 0-4.8-3.2-9.6-7.2-12.8z" fill="#FFFFFF" p-id="8885"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300142282" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2233" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M534.310676 38.36489h65.428495v88.119185c110.25913 0 220.507245 0.220298 330.766374-0.330447 18.615178 0.771043 39.113903-0.550745 55.305803 10.574302 11.345345 16.302049 10.023557 37.010058 10.783586 55.746399-0.550745 191.218631-0.319432 382.327112-0.220298 573.435594-0.550745 32.053353 2.974022 64.767601-3.745066 96.380358-4.405959 22.910988-31.943204 23.461733-50.338084 24.232776-114.114344 0.330447-228.338837-0.220298-342.56333 0v99.134083h-68.402517c-167.867047-30.511268-336.075555-58.709407-504.151885-88.119185v-770.932716c169.0897-29.420793 338.168386-58.389975 507.136922-88.240349z" fill="#207245" p-id="2234"></path><path d="M599.739171 159.528769h363.491636v693.938579h-363.491636v-66.089389h88.119184v-77.104286h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-66.089388z" fill="#FFFFFF" p-id="2235"></path><path d="M731.917948 225.618157h154.208573v77.104287h-154.208573v-77.104287z" fill="#207245" p-id="2236"></path><path d="M353.44605 328.827752c24.89367-1.762384 49.897488-3.304469 74.901307-4.516108-29.409778 60.251492-58.929705 120.502985-88.879213 180.53418 30.29097 61.683429 61.242833 122.926263 91.632937 184.609692-26.545904-1.553101-52.98166-3.19432-79.527564-5.066854-18.725327-45.932125-41.526166-90.322164-54.964342-138.358134-14.969246 44.731501-36.349164 86.918561-53.521389 130.74684-24.122627-0.330447-48.256268-1.321788-72.367881-2.313129 28.308288-55.404937 55.625235-111.250471 84.814716-166.324961-24.783521-56.726725-52.001334-112.35196-77.544883-168.748238 24.232776-1.431937 48.454537-2.863874 72.698327-4.185662 16.412198 43.068251 34.366482 85.585758 47.925822 129.7555 14.506621-46.802302 36.216985-90.861894 54.832163-136.133126z" fill="#FFFFFF" p-id="2237"></path><path d="M731.917948 346.782036h154.208573v77.104287h-154.208573v-77.104287zM731.917948 467.945915h154.208573v77.104287h-154.208573v-77.104287zM731.917948 589.109794h154.208573v77.104287h-154.208573v-77.104287zM731.917948 710.273673h154.208573v77.104286h-154.208573v-77.104286z" fill="#207245" p-id="2238"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>EyeblueTank</title><link href=/css/app.b8a22cc6.css rel=preload as=style><link href=/css/chunk-vendors.cb22afd2.css rel=preload as=style><link href=/js/app.ec9d36fc.js rel=preload as=script><link href=/js/chunk-vendors.220ccae9.js rel=preload as=script><link href=/css/chunk-vendors.cb22afd2.css rel=stylesheet><link href=/css/app.b8a22cc6.css rel=stylesheet></head><body><noscript><strong>We're sorry but tank-front-tmp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.220ccae9.js></script><script src=/js/app.ec9d36fc.js></script></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="eyeblue tank"/><title>EyeblueTank</title><script defer="defer" src="/static/js/main.e27b1761.js"></script><link href="/static/css/main.8f121d4f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run EyeblueTank.</noscript><div id="root"></div></body></html>
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*! VelocityJS.org (1.5.2). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */
|
||||
|
||||
/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */
|
||||
|
||||
/** @license React v0.20.2
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
//! moment.js
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514189353878" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1197" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M9.367 0h992.97v341.333H9.366z" fill="#55C7F7" p-id="1198"></path>
|
||||
<path d="M9.367 341.333h992.97v341.334H9.366z" fill="#F95F5D" p-id="1199"></path>
|
||||
<path d="M9.367 682.667h992.97V1024H9.366z" fill="#7ECF3B" p-id="1200"></path>
|
||||
<path d="M350.7 0h310.303v1024H350.7z" fill="#FDAF42" p-id="1201"></path>
|
||||
<path d="M304.154 386.638V646.05h403.394V386.638H304.154z m356.849 215.97H350.7V429.46h310.303v173.15z"
|
||||
fill="#FFFFFF" p-id="1202"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 855 B |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1543321997742" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3887" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M160 32c-12 0-24.8 4.8-33.6 14.4S112 68 112 80v864c0 12 4.8 24.8 14.4 33.6C136 987.2 148 992 160 992h704c12 0 24.8-4.8 33.6-14.4C907.2 968 912 956 912 944V304L640 32H160z"
|
||||
fill="#FF5562" p-id="3888"></path>
|
||||
<path d="M912 304H688c-12 0-24.8-4.8-33.6-14.4-9.6-8.8-14.4-21.6-14.4-33.6V32l272 272z" fill="#FFBBC0"
|
||||
p-id="3889"></path>
|
||||
<path d="M669.6 491.2c0-1.6 0.8-4 0-6.4V369.6c0-4.8-2.4-8.8-5.6-12-4-3.2-8-4-12.8-3.2l-250.4 70.4c-7.2 1.6-12 7.2-12 14.4v275.2c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8 6.4 23.2 30.4 37.6 60.8 37.6 8.8 0 17.6-1.6 26.4-3.2 36.8-10.4 60.8-36.8 60-63.2 0.8-1.6 0.8-3.2 0.8-5.6V570.4l220-61.6v136c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8C528 737.6 552 752 582.4 752c8.8 0 17.6-0.8 26.4-3.2 36-9.6 60-36 60-62.4 0.8-1.6 0.8-3.2 0.8-5.6V491.2z m-250.4 48v-53.6l220-61.6v53.6l-220 61.6z"
|
||||
fill="#FFFFFF" p-id="3890"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1543300132402" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2107" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M533.947184 38.36489h65.880106v88.119185c117.859409 0.660894 235.60867-1.211639 353.35793 0.660894 25.102953-2.533427 44.81962 16.962943 42.506492 42.07691 1.872533 216.993492-0.440596 434.097133 1.112505 651.090626-1.112505 22.250094 2.191965 46.923466-10.695466 66.640133-16.081751 11.455494-36.899909 10.023557-55.625236 10.904749-110.25913-0.550745-220.408111-0.330447-330.656225-0.330447v88.119185h-68.843113c-167.657764-30.621417-335.745108-58.709407-503.722304-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 168.858388-29.519927 337.826924-58.268811 506.685311-88.339483z"
|
||||
fill="#2A5699" p-id="2108"></path>
|
||||
<path d="M599.816275 159.528769h363.491637v704.953477h-363.491637v-88.119185h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-55.07449h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-77.104286zM254.829667 354.162018c20.928306-1.211639 41.856613-2.092831 62.784919-3.19432 14.649814 74.350562 29.619061 148.590975 45.491529 222.61109 12.446835-76.443393 26.215457-152.666487 39.543484-228.999731 22.029796-0.771043 44.059592-1.982682 65.97924-3.30447-24.89367 106.734362-46.703168 214.349917-73.909966 320.423385-18.39488 9.582961-45.932125-0.440596-67.741623 1.10149-14.649814-72.90761-31.722906-145.38564-44.819621-218.645727-12.898446 71.156242-29.641091 141.651589-44.390039 212.367235a4187.754101 4187.754101 0 0 1-63.666111-3.866229c-18.284731-96.931103-39.763782-193.190297-56.836874-290.341698 18.835476-0.881192 37.792115-1.652235 56.627591-2.313129 11.33433 70.164901 24.221761 139.999355 34.146184 210.274404 15.508976-72.037433 31.37043-144.074867 46.791287-216.1123z"
|
||||
fill="#FFFFFF" p-id="2109"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1582644951614" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3842"
|
||||
width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M692.906667 385.024c-11.605333-2.730667-23.210667-6.144-34.816-11.605333l-63.488 25.258666-5.461334 2.048h-5.461333c-8.874667 0-17.749333-4.096-23.210667-11.605333-5.461333-7.509333-6.826667-17.066667-4.778666-25.258667l8.874666-30.72-97.621333-19.797333v400.725333L901.12 402.090667l-105.130667-21.162667c-18.432 5.461333-38.229333 8.874667-59.392 8.874667-13.653333 0-28.672-1.365333-43.690666-4.778667z"
|
||||
fill="#A6DAFC" p-id="3843"></path>
|
||||
<path d="M512 502.442667v467.626666l389.802667-100.352V402.090667zM111.274667 413.696l356.352-100.352V580.266667z"
|
||||
fill="#568EC8" p-id="3844"></path>
|
||||
<path d="M111.274667 413.696v456.021333L512 970.069333V502.442667z" fill="#A6DAFC" p-id="3845"></path>
|
||||
<path d="M512 502.442667l389.802667-100.352L1024 546.816 645.802667 658.090667z" fill="#78C5FD" p-id="3846"></path>
|
||||
<path d="M111.274667 413.696l400.725333 88.746667-122.197333 167.253333L0 569.344z" fill="#CFF0FF"
|
||||
p-id="3847"></path>
|
||||
<path d="M886.784 160.426667c-2.730667-6.826667-6.144-13.653333-9.557333-20.48-1.365333-2.048-2.730667-4.096-4.096-6.826667-4.096-6.144-8.192-12.970667-13.653334-18.432s-10.24-11.605333-16.384-16.384c-2.048-2.048-4.096-3.413333-6.144-4.778667-8.192-6.826667-17.066667-12.288-25.941333-17.749333-2.048-1.365333-4.778667-2.730667-6.826667-3.413333-4.778667-2.048-9.557333-4.778667-15.018666-6.144-7.509333-2.730667-15.701333-5.461333-23.893334-6.826667-2.730667-0.682667-6.144-1.365333-8.874666-2.048-0.682667 0-2.048 0-2.730667-0.682667-2.048 0-4.096-0.682667-6.144-0.682666-1.365333 0-2.048 0-3.413333-0.682667-2.048 0-3.413333 0-5.461334-0.682667H727.04c-4.096 0-8.192 0-11.605333 0.682667h-2.048c-4.096 0.682667-8.192 0.682667-12.288 1.365333h-0.682667c-4.096 0.682667-7.509333 1.365333-11.605333 2.048-0.682667 0-1.365333 0-1.365334 0.682667-4.096 0.682667-7.509333 2.048-10.922666 3.413333-0.682667 0-1.365333 0.682667-2.048 0.682667-3.413333 1.365333-7.509333 2.730667-10.922667 4.096-0.682667 0-0.682667 0.682667-1.365333 0.682667-3.413333 1.365333-6.826667 3.413333-10.24 4.778666 0 0-0.682667 0-0.682667 0.682667-3.413333 2.048-6.826667 3.413333-10.24 5.461333-0.682667 0.682667-1.365333 0.682667-2.048 1.365334-3.413333 2.048-6.144 4.096-9.557333 6.144-0.682667 0.682667-1.365333 0.682667-1.365334 1.365333-3.413333 2.048-6.144 4.778667-8.874666 7.509333l-8.192 8.192-1.365334 1.365334c-2.730667 2.730667-4.778667 5.461333-7.509333 8.192-0.682667 0.682667-0.682667 1.365333-1.365333 1.365333-2.048 2.730667-4.778667 6.144-6.826667 8.874667 0 0.682667-0.682667 0.682667-0.682667 1.365333-2.048 3.413333-4.096 6.144-5.461333 9.557333 0 0.682667-0.682667 0.682667-0.682667 1.365334-2.048 3.413333-3.413333 6.826667-4.778666 10.24 0 0.682667-0.682667 1.365333-0.682667 2.048-1.365333 3.413333-2.730667 6.826667-4.096 10.922666 0 0.682667-0.682667 1.365333-0.682667 2.048-1.365333 4.096-2.048 7.509333-3.413333 11.605334-10.24 46.421333 2.730667 92.16 32.085333 127.658666L593.92 339.968l-9.557333 32.768v0.682667h0.682666l58.709334-23.210667 16.384-6.826667c6.144 3.413333 12.970667 6.144 19.797333 8.192s13.653333 4.778667 21.162667 6.144c2.730667 0.682667 6.144 1.365333 8.874666 2.048 0.682667 0 2.048 0 2.730667 0.682667 2.048 0 4.096 0.682667 6.144 0.682667 1.365333 0 2.048 0 3.413333 0.682666 2.048 0 3.413333 0 5.461334 0.682667H739.328c4.096 0 8.192 0 11.605333-0.682667h2.048c4.096-0.682667 8.192-0.682667 12.288-1.365333h0.682667c4.096-0.682667 7.509333-1.365333 11.605333-2.048 0.682667 0 1.365333 0 2.048-0.682667 4.096-0.682667 7.509333-2.048 10.922667-3.413333 0.682667 0 1.365333-0.682667 2.048-0.682667 3.413333-1.365333 7.509333-2.730667 10.922667-4.096 0.682667 0 0.682667-0.682667 1.365333-0.682666 3.413333-1.365333 6.826667-3.413333 10.24-4.778667 0 0 0.682667 0 0.682667-0.682667 3.413333-2.048 6.826667-3.413333 10.24-5.461333 0.682667-0.682667 1.365333-0.682667 2.048-1.365333 3.413333-2.048 6.144-4.096 9.557333-6.144 0.682667-0.682667 1.365333-0.682667 1.365333-1.365334 3.413333-2.048 6.144-4.778667 8.874667-7.509333l8.192-8.192 1.365333-1.365333c2.730667-2.730667 4.778667-5.461333 7.509334-8.192 0.682667-0.682667 0.682667-1.365333 1.365333-2.048 2.048-2.730667 4.778667-6.144 6.826667-8.874667 0-0.682667 0.682667-0.682667 0.682666-1.365333 2.048-3.413333 4.096-6.144 5.461334-9.557334 0-0.682667 0.682667-0.682667 0.682666-1.365333 2.048-3.413333 3.413333-6.826667 4.778667-10.24 0-0.682667 0.682667-1.365333 0.682667-2.048 1.365333-3.413333 2.730667-6.826667 4.096-10.922667 0-0.682667 0.682667-1.365333 0.682666-2.048 1.365333-4.096 2.048-7.509333 3.413334-11.605333 5.461333-28.672 2.730667-57.344-6.826667-83.285333z"
|
||||
fill="#F9DA3E" p-id="3848"></path>
|
||||
<path d="M739.328 167.936h48.469333v27.989333h-13.653333v-15.701333h-83.968v15.701333h-13.653333v-27.989333h48.469333c-1.365333-3.413333-3.413333-6.826667-4.778667-10.24l14.336-2.048c2.048 3.413333 3.413333 7.509333 4.778667 12.288z m37.546667 52.565333v12.288h-37.546667v25.258667h48.469333v12.970667H677.205333v-12.970667h48.469334v-25.258667h-37.546667v-12.288h88.746667z m-51.882667-24.576c-10.24 8.874667-22.528 16.384-37.546667 22.528l-5.461333-12.288c14.336-5.461333 25.941333-11.605333 34.816-19.797333l8.192 9.557333z m56.661333 10.922667l-7.509333 11.605333c-11.605333-9.557333-23.210667-17.066667-35.498667-22.528l6.826667-10.24c13.653333 6.144 25.258667 12.970667 36.181333 21.162667z"
|
||||
fill="#FFFFFF" p-id="3849"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514189511483" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="6709" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M676.297143 0H145.609143C130.834286 0 118.857143 11.977143 118.857143 35.218286V1005.714286c0 6.308571 11.977143 18.285714 26.752 18.285714h732.781714c14.774857 0 26.752-11.977143 26.752-18.285714V237.312c0-12.726857-1.700571-16.822857-4.699428-19.84L687.670857 4.699429A16.164571 16.164571 0 0 0 676.297143 0z"
|
||||
fill="#E9E9E0" p-id="6710"></path>
|
||||
<path d="M685.714286 2.761143V219.428571h216.667428z" fill="#D9D7CA" p-id="6711"></path>
|
||||
<path d="M878.390857 1024H145.609143A26.752 26.752 0 0 1 118.857143 997.248V713.142857h786.285714v284.105143c0 14.774857-11.977143 26.752-26.752 26.752z"
|
||||
fill="#C8BDB8" p-id="6712"></path>
|
||||
<path d="M338.285714 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
|
||||
fill="#FFFFFF" p-id="6713"></path>
|
||||
<path d="M521.142857 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
|
||||
fill="#FFFFFF" p-id="6714"></path>
|
||||
<path d="M704 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
|
||||
fill="#FFFFFF" p-id="6715"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="10648" width="238.0859375" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z"
|
||||
fill="#23B7E5" p-id="10649"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 949 B |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514190539077" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="14917" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M128 0h597.3l227.6 227.9v739.2c0 31.4-25.5 56.9-56.9 56.9H128c-31.4 0-56.9-25.5-56.9-56.9V56.9C71.1 25.5 96.6 0 128 0z"
|
||||
fill="#13BE93" p-id="14918"></path>
|
||||
<path d="M630.5 1024H128c-25.7 0-47.4-17-54.4-40.4l218.1-218.1c22.2-22.2 58.2-22.2 80.5 0L630.5 1024z"
|
||||
fill="#6BD8BD" p-id="14919"></path>
|
||||
<path d="M952.9 858v109.1c0 31.4-25.5 56.9-56.9 56.9H322.4l-4.7-4.7 358-358c22.2-22.2 58.2-22.2 80.5 0L952.9 858z"
|
||||
fill="#9CF9E2" p-id="14920"></path>
|
||||
<path d="M725.3 0l227.6 227.6H782.2c-31.4 0-56.9-25.5-56.9-56.9V0z" fill="#69DCC0" p-id="14921"></path>
|
||||
<path d="M327.1 483.6m-85.3 0a85.3 85.3 0 1 0 170.6 0 85.3 85.3 0 1 0-170.6 0Z" fill="#E9FFFA" p-id="14922"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514284130163" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1551" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M915.114667 1024H108.885333V0h587.776v218.112H914.773333V1024z m-785.066667-20.821333h763.904V238.933333H675.84V20.821333H129.706667v982.357334z"
|
||||
fill="#F90E09" p-id="1552"></path>
|
||||
<path d="M708.266667 10.581333L904.533333 206.848h-196.266666z" fill="#F90E09" p-id="1553"></path>
|
||||
<path d="M282.965333 792.576c33.450667-23.210667 55.637333-57.344 76.8-91.136-30.378667 25.941333-59.050667 54.954667-76.8 91.136z m396.629334-169.642667c31.402667 15.018667 65.877333 31.061333 101.376 21.504-24.576-29.696-67.584-21.504-101.376-21.504z m-246.101334 23.210667c45.397333-16.725333 91.818667-29.696 138.922667-39.594667-30.378667-26.965333-55.978667-58.368-76.458667-93.525333-16.725333 46.08-38.912 90.112-62.464 133.12z m67.925334-243.370667c14.677333-37.546667 19.114667-83.285333-5.802667-117.418666-7.168 38.912-4.096 79.189333 5.802667 117.418666z m-45.397334-105.130666c4.096-24.917333 35.84-29.696 55.637334-22.186667 12.288 7.850667 18.432 22.869333 18.773333 37.205333 1.365333 44.032-6.485333 87.381333-12.629333 130.730667-3.413333 20.138667 8.874667 38.229333 17.749333 55.296 21.845333 40.277333 52.224 76.458667 92.16 99.669333 48.128-5.802667 98.645333-12.970667 145.749333 2.389334 35.157333 11.946667 26.965333 77.141333-11.264 77.824-53.589333 2.730667-102.058667-23.893333-150.186666-43.349334-66.218667 4.778667-130.730667 24.917333-194.218667 44.032-28.672 46.762667-54.613333 98.986667-101.034667 130.730667-23.210667 18.090667-76.458667 3.072-64.512-32.085333 20.138667-49.152 70.314667-76.117333 114.346667-100.693334 12.629333-6.485333 19.797333-18.773333 26.282667-31.061333 30.037333-57.685333 60.074667-115.370667 84.309333-175.786667-12.629333-56.661333-31.061333-114.688-21.162667-172.714666z"
|
||||
fill="#F90E09" p-id="1554"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1543300178225" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="5733" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M537.372818 38.36489h62.454472v99.134083c117.738246 0.660894 235.60867-1.211639 353.236766 0.550745 25.334266-2.423278 45.172097 17.29339 42.627656 42.627655 1.872533 205.758296-0.440596 411.626741 1.10149 617.506201-1.10149 22.250094 2.20298 46.923466-10.574302 66.640134-16.081751 11.664777-36.999043 10.133706-55.834519 11.014898-110.148981-0.550745-220.297962-0.330447-330.557091-0.330447v110.148981h-68.523681c-167.856032-30.621417-336.06454-58.599258-504.041736-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 169.970892-29.420793 339.93077-59.381316 510.110945-88.350498z"
|
||||
fill="#D24625" p-id="5734"></path>
|
||||
<path d="M599.82729 170.543667h363.491636v671.908783h-363.491636v-88.119185h264.357554v-44.059592h-264.357554v-55.074491h264.357554v-44.059592h-264.247405c-0.110149-21.5892-0.110149-43.1784-0.231313-64.767601 43.630011 13.53731 93.307202 13.217878 132.299941-13.008594 42.18706-25.003819 64.216856-72.698327 67.851772-120.271672-48.366417-0.330447-96.710805-0.220298-144.956059-0.220298-0.121164-47.914807 0.550745-95.829613-0.991341-143.634271-18.075448 3.524767-36.128866 7.269833-54.083149 11.125047v-109.818534z"
|
||||
fill="#FFFFFF" p-id="5735"></path>
|
||||
<path d="M677.041725 246.216017c76.553542 3.524767 140.880546 67.961921 144.956059 144.295165-48.366417 0.550745-96.710805 0.330447-145.066208 0.330447 0-48.245254-0.121164-96.490507 0.110149-144.625612z"
|
||||
fill="#D24625" p-id="5736"></path>
|
||||
<path d="M203.940838 329.378497c59.810897 2.863874 132.288926-23.682031 181.855967 21.148605 47.485226 57.607917 34.917227 163.571236-35.467972 198.047867-25.003819 12.777282-53.642554 11.014898-80.849351 10.133706-0.110149 43.068251-0.220298 86.136503-0.110149 129.19374-21.820513-1.872533-43.74016-3.745065-65.538644-5.507449-0.980326-117.628097-1.211639-235.377357 0.110149-353.016469z"
|
||||
fill="#FFFFFF" p-id="5737"></path>
|
||||
<path d="M269.380347 388.969096c21.699349-0.991341 48.575701-4.956704 63.214501 15.420857 12.567999 21.5892 11.89609 50.007637 1.442951 72.257732-12.567999 22.69069-40.986436 20.48771-62.895068 23.131286-2.313129-36.899909-2.092831-73.799817-1.762384-110.809875z"
|
||||
fill="#D24625" p-id="5738"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1545064197737" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="14227" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M759.725253 0H134.464646v1024h817.131314V191.870707z" fill="#02007A" p-id="14228"></path>
|
||||
<path d="M72.40404 708.525253h687.838384v258.585858H72.40404z" fill="#230091" p-id="14229"></path>
|
||||
<path d="M82.747475 718.868687h667.151515v237.89899H82.747475zM755.070707 5.171717l191.353535 191.353536h-191.353535z"
|
||||
fill="#FFFFFF" p-id="14230"></path>
|
||||
<path d="M506.828283 791.272727v93.090909h25.858586c28.444444 0 41.373737-12.929293 41.373737-45.511111s-12.929293-47.579798-41.373737-47.579798h-25.858586z m-25.858586-25.858586h51.717172c42.408081 0 67.232323 29.99596 67.232323 74.989899S575.612121 910.222222 532.686869 910.222222H480.969697v-144.808081z m-132.39596 104.468687c1.034343 26.375758 22.755556 42.408081 55.337374 42.408081 34.650505 0 55.854545-17.066667 55.854546-43.959596 0-21.20404-12.412121-33.09899-40.856566-39.822222l-16.549495-3.620202c-17.583838-4.137374-24.824242-9.826263-24.824242-19.135354 0-11.894949 10.860606-19.652525 26.892929-19.652525s27.410101 7.757576 28.444444 21.204041h24.307071c-0.517172-24.824242-21.20404-41.890909-52.751515-41.89091S351.676768 782.480808 351.676768 807.822222c0 20.169697 12.412121 33.09899 38.787878 38.787879l18.618182 4.137374c18.10101 4.137374 25.341414 10.343434 25.341414 20.169697 0 11.894949-11.894949 20.169697-28.961616 20.169697s-30.513131-8.791919-32.064646-21.721212l-24.824243 0.517171zM258.585859 788.686869v53.785858h20.686868c18.10101 0 31.030303-9.826263 31.030303-26.892929s-12.412121-26.892929-31.030303-26.892929h-20.686868z m-25.858586-23.272728h56.888889c28.444444 0 46.545455 20.686869 46.545454 49.131314s-17.583838 49.131313-46.545454 49.131313h-31.030303v46.545454h-25.858586v-144.808081z"
|
||||
fill="#230091" p-id="14231"></path>
|
||||
<path d="M415.806061 443.216162h-31.030303v137.567676H321.163636V196.525253h93.090909c91.022222 0 140.670707 35.684848 140.670708 122.052525 0.517172 103.434343-92.573737 124.638384-139.119192 124.638384z m-5.688889-192.905051c-6.206061 0-14.99798 0.517172-26.89293 1.551515V387.878788h36.202021C461.834343 387.878788 491.313131 366.157576 491.313131 312.888889c0-40.856566-32.064646-62.577778-81.195959-62.577778z m258.068687 337.19596c70.852525 0 96.711111-50.165657 96.711111-84.816162 0-54.820202-35.684848-67.749495-66.19798-87.40202-25.858586-17.066667-49.648485-23.272727-49.648485-40.856566 0-22.755556 15.515152-27.410101 36.20202-27.410101 25.858586 0 54.820202 2.068687 67.232323 16.032323V306.682828c-12.929293-10.343434-37.236364-13.446465-68.783838-13.446464-67.749495 0-93.608081 44.993939-93.608081 84.29899 0 50.682828 33.616162 65.680808 66.19798 83.781818 21.20404 11.894949 46.545455 20.686869 46.545455 44.476767 0 13.963636-8.791919 27.927273-37.753536 27.927273-26.892929 0-59.991919-10.343434-74.989899-23.789899v59.991919c13.446465 10.343434 40.339394 17.583838 78.09293 17.583839z"
|
||||
fill="#7AB7FF" p-id="14232"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514189544355" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="8186" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M176 1024l672 0c26.464 0 48-21.536 48-48l0-672c0-0.544-0.256-0.992-0.288-1.504-0.064-0.736-0.256-1.376-0.416-2.08-0.544-2.272-1.472-4.32-2.88-6.112-0.224-0.288-0.192-0.64-0.416-0.928l-256-288c-0.096-0.096-0.224-0.096-0.32-0.192-1.92-2.048-4.352-3.456-7.136-4.288-0.608-0.192-1.152-0.256-1.792-0.352-0.928-0.16-1.792-0.544-2.752-0.544l-448 0c-26.464 0-48 21.536-48 48l0 928c0 26.464 21.536 48 48 48zM640 58.08l204.384 229.92-188.384 0c-7.04 0-16-13.44-16-24l0-205.92zM160 48c0-8.832 7.2-16 16-16l432 0 0 232c0 25.888 20.96 56 48 56l208 0 0 656c0 8.832-7.2 16-16 16l-672 0c-8.96 0-16-7.04-16-16l0-928zM304 448l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 576l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 320l192 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-192 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 704l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 832l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16z"
|
||||
p-id="8187"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1514189554639" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="8871" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M80 34.4h864v960H80z" fill="#8095FF" p-id="8872"></path>
|
||||
<path d="M176 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8873"></path>
|
||||
<path d="M176 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8874"></path>
|
||||
<path d="M176 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8875"></path>
|
||||
<path d="M176 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8876"></path>
|
||||
<path d="M176 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8877"></path>
|
||||
<path d="M176 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8878"></path>
|
||||
<path d="M864 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8879"></path>
|
||||
<path d="M864 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8880"></path>
|
||||
<path d="M864 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8881"></path>
|
||||
<path d="M864 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8882"></path>
|
||||
<path d="M864 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8883"></path>
|
||||
<path d="M864 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8884"></path>
|
||||
<path d="M648 508L436 362.4c-4.8-3.2-11.2-4-16.8-0.8-5.6 3.2-8.8 8.8-8.8 14.4v290.4c0 5.6 3.2 11.2 8.8 14.4 5.6 3.2 12 2.4 16.8-0.8L648 533.6c4.8-2.4 7.2-8 7.2-12.8 0-4.8-3.2-9.6-7.2-12.8z"
|
||||
fill="#FFFFFF" p-id="8885"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1543300142282" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2233" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M534.310676 38.36489h65.428495v88.119185c110.25913 0 220.507245 0.220298 330.766374-0.330447 18.615178 0.771043 39.113903-0.550745 55.305803 10.574302 11.345345 16.302049 10.023557 37.010058 10.783586 55.746399-0.550745 191.218631-0.319432 382.327112-0.220298 573.435594-0.550745 32.053353 2.974022 64.767601-3.745066 96.380358-4.405959 22.910988-31.943204 23.461733-50.338084 24.232776-114.114344 0.330447-228.338837-0.220298-342.56333 0v99.134083h-68.402517c-167.867047-30.511268-336.075555-58.709407-504.151885-88.119185v-770.932716c169.0897-29.420793 338.168386-58.389975 507.136922-88.240349z"
|
||||
fill="#207245" p-id="2234"></path>
|
||||
<path d="M599.739171 159.528769h363.491636v693.938579h-363.491636v-66.089389h88.119184v-77.104286h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-66.089388z"
|
||||
fill="#FFFFFF" p-id="2235"></path>
|
||||
<path d="M731.917948 225.618157h154.208573v77.104287h-154.208573v-77.104287z" fill="#207245" p-id="2236"></path>
|
||||
<path d="M353.44605 328.827752c24.89367-1.762384 49.897488-3.304469 74.901307-4.516108-29.409778 60.251492-58.929705 120.502985-88.879213 180.53418 30.29097 61.683429 61.242833 122.926263 91.632937 184.609692-26.545904-1.553101-52.98166-3.19432-79.527564-5.066854-18.725327-45.932125-41.526166-90.322164-54.964342-138.358134-14.969246 44.731501-36.349164 86.918561-53.521389 130.74684-24.122627-0.330447-48.256268-1.321788-72.367881-2.313129 28.308288-55.404937 55.625235-111.250471 84.814716-166.324961-24.783521-56.726725-52.001334-112.35196-77.544883-168.748238 24.232776-1.431937 48.454537-2.863874 72.698327-4.185662 16.412198 43.068251 34.366482 85.585758 47.925822 129.7555 14.506621-46.802302 36.216985-90.861894 54.832163-136.133126z"
|
||||
fill="#FFFFFF" p-id="2237"></path>
|
||||
<path d="M731.917948 346.782036h154.208573v77.104287h-154.208573v-77.104287zM731.917948 467.945915h154.208573v77.104287h-154.208573v-77.104287zM731.917948 589.109794h154.208573v77.104287h-154.208573v-77.104287zM731.917948 710.273673h154.208573v77.104286h-154.208573v-77.104286z"
|
||||
fill="#207245" p-id="2238"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -11,10 +11,10 @@
|
||||
@REM prepare the variables.
|
||||
|
||||
@REM version name
|
||||
SET VERSION_NAME=tank-3.0.3
|
||||
SET VERSION_NAME=tank-4.1.2
|
||||
ECHO VERSION_NAME: %VERSION_NAME%
|
||||
@REM golang proxy
|
||||
SET GOPROXY=https://athens.azurefd.net
|
||||
SET GOPROXY=https://goproxy.cn
|
||||
ECHO GOPROXY: %GOPROXY%
|
||||
@REM assign variable like Linux GOARCH=$(go env GOARCH) eg. amd64
|
||||
FOR /f %%i IN ('go env GOARCH') DO SET GOARCH=%%i
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
###########################################################################
|
||||
#
|
||||
@@ -9,10 +10,10 @@
|
||||
#prepare the variables.
|
||||
|
||||
# version name
|
||||
VERSION_NAME=tank-3.0.3
|
||||
VERSION_NAME=tank-4.1.2
|
||||
echo "VERSION_NAME: ${VERSION_NAME}"
|
||||
# golang proxy
|
||||
GOPROXY=https://athens.azurefd.net
|
||||
GOPROXY=https://goproxy.cn
|
||||
echo "GOPROXY: ${GOPROXY}"
|
||||
# eg. amd64
|
||||
GOARCH=$(go env GOARCH)
|
||||
@@ -47,8 +48,8 @@ echo "DIST_PATH: ${DIST_PATH}"
|
||||
|
||||
cd ${PROJECT_DIR}
|
||||
|
||||
echo "go build -mod=readonly"
|
||||
go build -mod=readonly
|
||||
echo "go build"
|
||||
go build
|
||||
|
||||
# if a directory
|
||||
if [[ -d COMPONENT_DIR ]] ; then
|
||||
|
||||
@@ -8,7 +8,7 @@ EXE_PATH=$TANK_DIR/tank
|
||||
|
||||
if [ -f "$EXE_PATH" ]; then
|
||||
nohup $EXE_PATH >/dev/null 2>&1 &
|
||||
echo 'Start tank successfully! http://127.0.0.1:6010'
|
||||
echo 'Start tank successfully! Default value http://127.0.0.1:6010'
|
||||
else
|
||||
echo 'Cannot find $EXE_PATH.'
|
||||
exit 1
|
||||
|
||||
@@ -6,10 +6,10 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
DynamicUser=yes
|
||||
#DynamicUser=yes
|
||||
ExecStart=/data/program/tank/tank
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package core
|
||||
|
||||
import "gorm.io/gorm/schema"
|
||||
|
||||
const (
|
||||
//authentication key of cookie
|
||||
COOKIE_AUTH_KEY = "_ak"
|
||||
@@ -9,19 +11,25 @@ const (
|
||||
|
||||
DEFAULT_SERVER_PORT = 6010
|
||||
|
||||
//db table's prefix. tank30_ means current version is tank:3.0.x
|
||||
TABLE_PREFIX = "tank30_"
|
||||
//db table's prefix. tank41_ means current version is tank:4.1.x
|
||||
TABLE_PREFIX = "tank41_"
|
||||
|
||||
VERSION = "3.0.3"
|
||||
VERSION = "4.1.2"
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
Installed() bool
|
||||
ServerPort() int
|
||||
//get the db type
|
||||
DbType() string
|
||||
//get the mysql url. eg. tank:tank123@tcp(127.0.0.1:3306)/tank?charset=utf8&parseTime=True&loc=Local
|
||||
MysqlUrl() string
|
||||
//get the sqlite path
|
||||
SqliteFolder() string
|
||||
//files storage location.
|
||||
MatterPath() string
|
||||
//table name strategy
|
||||
NamingStrategy() schema.NamingStrategy
|
||||
//when installed by user. Write configs to tank.json
|
||||
FinishInstall(mysqlPort int, mysqlHost string, mysqlSchema string, mysqlUsername string, mysqlPassword string)
|
||||
FinishInstall(dbType string, mysqlPort int, mysqlHost string, mysqlSchema string, mysqlUsername string, mysqlPassword string, mysqlCharset string)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/tool/cache"
|
||||
"github.com/jinzhu/gorm"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,14 +12,16 @@ import (
|
||||
|
||||
type AlienController struct {
|
||||
BaseController
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
alienService *AlienService
|
||||
shareService *ShareService
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
matterDao *MatterDao
|
||||
spaceDao *SpaceDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
alienService *AlienService
|
||||
shareService *ShareService
|
||||
spaceMemberService *SpaceMemberService
|
||||
}
|
||||
|
||||
func (this *AlienController) Init() {
|
||||
@@ -40,6 +42,11 @@ func (this *AlienController) Init() {
|
||||
this.matterDao = c
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.spaceDao)
|
||||
if c, ok := b.(*SpaceDao); ok {
|
||||
this.spaceDao = c
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.matterService)
|
||||
if c, ok := b.(*MatterService); ok {
|
||||
this.matterService = c
|
||||
@@ -64,6 +71,11 @@ func (this *AlienController) Init() {
|
||||
if c, ok := b.(*ShareService); ok {
|
||||
this.shareService = c
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.spaceMemberService)
|
||||
if b, ok := b.(*SpaceMemberService); ok {
|
||||
this.spaceMemberService = b
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AlienController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
|
||||
@@ -80,7 +92,7 @@ func (this *AlienController) RegisterRoutes() map[string]func(writer http.Respon
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//handle some special routes, eg. params in the url.
|
||||
// handle some special routes, eg. params in the url.
|
||||
func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
|
||||
path := request.URL.Path
|
||||
@@ -108,7 +120,7 @@ func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *h
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//fetch a upload token for guest. Guest can upload file with this token.
|
||||
// fetch a upload token for guest. Guest can upload file with this token.
|
||||
func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
filename := request.FormValue("filename")
|
||||
@@ -151,7 +163,8 @@ func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, reques
|
||||
}
|
||||
|
||||
user := this.checkUser(request)
|
||||
dirMatter := this.matterService.CreateDirectories(request, user, dirPath)
|
||||
space := this.spaceDao.CheckByUuid(user.SpaceUuid)
|
||||
dirMatter := this.matterService.CreateDirectories(request, user, space, dirPath)
|
||||
|
||||
uploadToken := &UploadToken{
|
||||
UserUuid: user.Uuid,
|
||||
@@ -170,7 +183,7 @@ func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, reques
|
||||
|
||||
}
|
||||
|
||||
//user confirm a file whether uploaded successfully.
|
||||
// user confirm a file whether uploaded successfully.
|
||||
func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
matterUuid := request.FormValue("matterUuid")
|
||||
@@ -188,7 +201,7 @@ func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.R
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//a guest upload a file with a upload token.
|
||||
// a guest upload a file with a upload token.
|
||||
func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
//allow cors.
|
||||
this.allowCORS(writer)
|
||||
@@ -216,6 +229,7 @@ func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Re
|
||||
}
|
||||
|
||||
user := this.userDao.CheckByUuid(uploadToken.UserUuid)
|
||||
space := this.spaceDao.CheckByUuid(user.SpaceUuid)
|
||||
|
||||
err = request.ParseMultipartForm(32 << 20)
|
||||
this.PanicError(err)
|
||||
@@ -228,9 +242,9 @@ func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Re
|
||||
panic(result.BadRequest("file size doesn't the one in uploadToken"))
|
||||
}
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user)
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, space)
|
||||
|
||||
matter := this.matterService.Upload(request, file, user, dirMatter, uploadToken.Filename, uploadToken.Privacy)
|
||||
matter := this.matterService.Upload(request, file, handler, user, space, dirMatter, uploadToken.Filename, uploadToken.Privacy)
|
||||
|
||||
//expire the upload token.
|
||||
uploadToken.ExpireTime = time.Now()
|
||||
@@ -239,7 +253,7 @@ func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Re
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//crawl a url with uploadToken. guest can visit this method.
|
||||
// crawl a url with uploadToken. guest can visit this method.
|
||||
func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//allow cors.
|
||||
@@ -263,10 +277,11 @@ func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *htt
|
||||
}
|
||||
|
||||
user := this.userDao.CheckByUuid(uploadToken.UserUuid)
|
||||
space := this.spaceDao.CheckByUuid(user.SpaceUuid)
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user)
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, space)
|
||||
|
||||
matter := this.matterService.AtomicCrawl(request, url, uploadToken.Filename, user, dirMatter, uploadToken.Privacy)
|
||||
matter := this.matterService.AtomicCrawl(request, url, uploadToken.Filename, user, space, dirMatter, uploadToken.Privacy)
|
||||
|
||||
//expire the upload token.
|
||||
uploadToken.ExpireTime = time.Now()
|
||||
@@ -275,7 +290,7 @@ func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *htt
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//crawl a url directly. only user can visit this method.
|
||||
// crawl a url directly. only user can visit this method.
|
||||
func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
filename := request.FormValue("filename")
|
||||
@@ -291,14 +306,15 @@ func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *ht
|
||||
}
|
||||
|
||||
user := this.checkUser(request)
|
||||
dirMatter := this.matterService.CreateDirectories(request, user, dirPath)
|
||||
space := this.spaceDao.CheckByUuid(user.SpaceUuid)
|
||||
dirMatter := this.matterService.CreateDirectories(request, user, space, dirPath)
|
||||
|
||||
matter := this.matterService.AtomicCrawl(request, url, filename, user, dirMatter, privacy)
|
||||
matter := this.matterService.AtomicCrawl(request, url, filename, user, space, dirMatter, privacy)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//fetch a download token for guest. Guest can download file with this token.
|
||||
// fetch a download token for guest. Guest can download file with this token.
|
||||
func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
matterUuid := request.FormValue("matterUuid")
|
||||
@@ -311,8 +327,9 @@ func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, requ
|
||||
user := this.checkUser(request)
|
||||
|
||||
matter := this.matterDao.CheckByUuid(matterUuid)
|
||||
if matter.UserUuid != user.Uuid {
|
||||
panic(result.BadRequest("matter not belong to you"))
|
||||
canRead := this.spaceMemberService.canRead(user, matter.SpaceUuid)
|
||||
if !canRead {
|
||||
panic(result.BadRequest("no auth to visit this file."))
|
||||
}
|
||||
|
||||
var expireTime time.Time
|
||||
@@ -338,14 +355,14 @@ func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, requ
|
||||
|
||||
}
|
||||
|
||||
//preview a file.
|
||||
// preview a file.
|
||||
func (this *AlienController) Preview(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
|
||||
|
||||
this.alienService.PreviewOrDownload(writer, request, uuid, filename, false)
|
||||
matter := this.alienService.ValidMatter(writer, request, uuid, filename)
|
||||
this.alienService.PreviewOrDownload(writer, request, matter, false)
|
||||
}
|
||||
|
||||
//download a file.
|
||||
// download a file.
|
||||
func (this *AlienController) Download(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
|
||||
|
||||
this.alienService.PreviewOrDownload(writer, request, uuid, filename, true)
|
||||
matter := this.alienService.ValidMatter(writer, request, uuid, filename)
|
||||
this.alienService.PreviewOrDownload(writer, request, matter, true)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//@Service
|
||||
// @Service
|
||||
type AlienService struct {
|
||||
BaseBean
|
||||
matterDao *MatterDao
|
||||
@@ -19,6 +19,7 @@ type AlienService struct {
|
||||
shareService *ShareService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
spaceService *SpaceService
|
||||
}
|
||||
|
||||
func (this *AlienService) Init() {
|
||||
@@ -63,14 +64,18 @@ func (this *AlienService) Init() {
|
||||
if c, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = c
|
||||
}
|
||||
b = core.CONTEXT.GetBean(this.spaceService)
|
||||
if c, ok := b.(*SpaceService); ok {
|
||||
this.spaceService = c
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AlienService) PreviewOrDownload(
|
||||
// check whether the request params ok.
|
||||
func (this *AlienService) ValidMatter(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uuid string,
|
||||
filename string,
|
||||
withContentDisposition bool) {
|
||||
filename string) *Matter {
|
||||
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
@@ -94,8 +99,10 @@ func (this *AlienService) PreviewOrDownload(
|
||||
}
|
||||
|
||||
tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid)
|
||||
if matter.UserUuid != tokenUser.Uuid {
|
||||
panic(result.UNAUTHORIZED)
|
||||
|
||||
if matter.SpaceUuid != tokenUser.SpaceUuid {
|
||||
//whether user has the space's read auth.
|
||||
this.spaceService.CheckReadableByUuid(request, tokenUser, matter.SpaceUuid)
|
||||
}
|
||||
|
||||
//TODO: expire the download token. If download by chunk, do this later.
|
||||
@@ -104,18 +111,28 @@ func (this *AlienService) PreviewOrDownload(
|
||||
|
||||
} else {
|
||||
|
||||
//whether this is myself's matter.
|
||||
operator := this.findUser(request)
|
||||
if operator == nil {
|
||||
panic(result.BadRequest("no auth"))
|
||||
}
|
||||
|
||||
//use share code to auth.
|
||||
shareUuid := request.FormValue("shareUuid")
|
||||
shareCode := request.FormValue("shareCode")
|
||||
shareRootUuid := request.FormValue("shareRootUuid")
|
||||
|
||||
this.shareService.ValidateMatter(request, shareUuid, shareCode, operator, shareRootUuid, matter)
|
||||
if matter.SpaceUuid != operator.SpaceUuid {
|
||||
//whether user has the space's read auth.
|
||||
this.spaceService.CheckReadableByUuid(request, operator, matter.SpaceUuid)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return matter
|
||||
}
|
||||
|
||||
func (this *AlienService) PreviewOrDownload(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
matter *Matter,
|
||||
withContentDisposition bool,
|
||||
) {
|
||||
//download directory
|
||||
if matter.Dir {
|
||||
|
||||
@@ -135,7 +152,7 @@ func (this *AlienService) PreviewOrDownload(
|
||||
}
|
||||
|
||||
//download the cache image file.
|
||||
this.matterService.DownloadFile(writer, request, GetUserCacheRootDir(imageCache.Username)+imageCache.Path, imageCache.Name, withContentDisposition)
|
||||
this.matterService.DownloadFile(writer, request, GetSpaceCacheRootDir(imageCache.Username)+imageCache.Path, imageCache.Name, withContentDisposition)
|
||||
|
||||
} else {
|
||||
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, withContentDisposition)
|
||||
@@ -145,7 +162,6 @@ func (this *AlienService) PreviewOrDownload(
|
||||
|
||||
//async increase the download times.
|
||||
go core.RunWithRecovery(func() {
|
||||
this.matterDao.TimesIncrement(uuid)
|
||||
this.matterDao.TimesIncrement(matter.Uuid)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ import (
|
||||
|
||||
type BaseController struct {
|
||||
BaseBean
|
||||
userDao *UserDao
|
||||
sessionDao *SessionDao
|
||||
userDao *UserDao
|
||||
spaceDao *SpaceDao
|
||||
spaceService *SpaceService
|
||||
sessionDao *SessionDao
|
||||
}
|
||||
|
||||
func (this *BaseController) Init() {
|
||||
@@ -26,6 +28,16 @@ func (this *BaseController) Init() {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.spaceDao)
|
||||
if b, ok := b.(*SpaceDao); ok {
|
||||
this.spaceDao = b
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.spaceService)
|
||||
if b, ok := b.(*SpaceService); ok {
|
||||
this.spaceService = b
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.sessionDao)
|
||||
if b, ok := b.(*SessionDao); ok {
|
||||
this.sessionDao = b
|
||||
@@ -38,12 +50,12 @@ func (this *BaseController) RegisterRoutes() map[string]func(writer http.Respons
|
||||
return make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||
}
|
||||
|
||||
//handle some special routes, eg. params in the url.
|
||||
// handle some special routes, eg. params in the url.
|
||||
func (this *BaseController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//wrap the handle method.
|
||||
// wrap the handle method.
|
||||
func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *http.Request) *result.WebResult, qualifiedRole string) func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
@@ -87,7 +99,50 @@ func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *htt
|
||||
}
|
||||
}
|
||||
|
||||
//response a success result. 1.string 2. WebResult 3.nil pointer 4.any type
|
||||
// wrap the handle method without result.
|
||||
func (this *BaseController) WrapPure(f func(writer http.ResponseWriter, request *http.Request), qualifiedRole string) func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
var webResult *result.WebResult = nil
|
||||
|
||||
//if the api not annotated with GUEST. login is required.
|
||||
if qualifiedRole != USER_ROLE_GUEST {
|
||||
user := this.checkUser(request)
|
||||
|
||||
if user.Status == USER_STATUS_DISABLED {
|
||||
//check user's status
|
||||
webResult = result.CustomWebResultI18n(request, result.USER_DISABLED, i18n.UserDisabled)
|
||||
} else {
|
||||
if qualifiedRole == USER_ROLE_ADMINISTRATOR && user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
webResult = result.ConstWebResult(result.UNAUTHORIZED)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//if webResult not nil. response a json. if webResult is nil, return empty body or binary content.
|
||||
if webResult != nil {
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
||||
|
||||
b, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult)
|
||||
|
||||
this.PanicError(err)
|
||||
|
||||
writer.WriteHeader(result.FetchHttpStatus(webResult.Code))
|
||||
|
||||
_, err = fmt.Fprintf(writer, string(b))
|
||||
this.PanicError(err)
|
||||
}
|
||||
|
||||
//no error.
|
||||
f(writer, request)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// response a success result. 1.string 2. WebResult 3.nil pointer 4.any type
|
||||
func (this *BaseController) Success(data interface{}) *result.WebResult {
|
||||
var webResult *result.WebResult = nil
|
||||
if value, ok := data.(string); ok {
|
||||
@@ -106,7 +161,7 @@ func (this *BaseController) Success(data interface{}) *result.WebResult {
|
||||
return webResult
|
||||
}
|
||||
|
||||
//allow cors.
|
||||
// allow cors.
|
||||
func (this *BaseController) allowCORS(writer http.ResponseWriter) {
|
||||
util.AllowCORS(writer)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package rest
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -11,26 +10,11 @@ const (
|
||||
|
||||
DIRECTION_ASC = "ASC"
|
||||
DIRECTION_DESC = "DESC"
|
||||
|
||||
EMPTY_JSON_MAP = "{}"
|
||||
EMPTY_JSON_ARRAY = "[]"
|
||||
)
|
||||
|
||||
type IBase interface {
|
||||
//name of db table
|
||||
TableName() string
|
||||
}
|
||||
|
||||
// Mysql 5.5 only support one CURRENT_TIMESTAMP
|
||||
// so we use 2018-01-01 00:00:00 as default, which is the first release date of EyeblueTank
|
||||
type Base struct {
|
||||
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
|
||||
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
|
||||
UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
|
||||
CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
}
|
||||
|
||||
func (this *Base) TableName() string {
|
||||
panic("you should overwrite TableName()")
|
||||
}
|
||||
|
||||
//pager
|
||||
type Pager struct {
|
||||
Page int `json:"page"`
|
||||
|
||||
@@ -4,9 +4,8 @@ import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
"github.com/eyebluecn/tank/code/tool/builder"
|
||||
"github.com/eyebluecn/tank/code/tool/result"
|
||||
"github.com/jinzhu/gorm"
|
||||
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"github.com/eyebluecn/tank/code/tool/uuid"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -59,7 +58,7 @@ func (this *BridgeDao) CheckByShareUuidAndMatterUuid(shareUuid string, matterUui
|
||||
}
|
||||
|
||||
//get pager
|
||||
func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray []builder.OrderPair) *Pager {
|
||||
func (this *BridgeDao) PlainPage(page int, pageSize int, shareUuid string, sortArray []builder.OrderPair) (int, []*Bridge) {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
@@ -70,13 +69,21 @@ func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray
|
||||
var conditionDB *gorm.DB
|
||||
conditionDB = core.CONTEXT.GetDB().Model(&Bridge{}).Where(wp.Query, wp.Args...)
|
||||
|
||||
count := 0
|
||||
var count int64 = 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var bridges []*Bridge
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&bridges)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return int(count), bridges
|
||||
}
|
||||
|
||||
//get pager
|
||||
func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
count, bridges := this.PlainPage(page, pageSize, shareUuid, sortArray)
|
||||
pager := NewPager(page, pageSize, count, bridges)
|
||||
|
||||
return pager
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
)
|
||||
import "time"
|
||||
|
||||
/**
|
||||
* the link table for Share and Matter.
|
||||
*/
|
||||
type Bridge struct {
|
||||
Base
|
||||
ShareUuid string `json:"shareUuid" gorm:"type:char(36)"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36)"`
|
||||
}
|
||||
|
||||
func (this *Bridge) TableName() string {
|
||||
return core.TABLE_PREFIX + "bridge"
|
||||
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
|
||||
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
|
||||
UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
|
||||
CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
ShareUuid string `json:"shareUuid" gorm:"type:char(36)"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36)"`
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ func (this *DashboardController) RegisterRoutes() map[string]func(writer http.Re
|
||||
|
||||
routeMap["/api/dashboard/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/dashboard/active/ip/top10"] = this.Wrap(this.ActiveIpTop10, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/dashboard/etl"] = this.Wrap(this.Etl, USER_ROLE_ADMINISTRATOR)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
@@ -89,3 +90,9 @@ func (this *DashboardController) ActiveIpTop10(writer http.ResponseWriter, reque
|
||||
list := this.dashboardDao.ActiveIpTop10()
|
||||
return this.Success(list)
|
||||
}
|
||||
|
||||
func (this *DashboardController) Etl(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
this.dashboardService.Etl()
|
||||
return this.Success("OK")
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package rest
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
"github.com/eyebluecn/tank/code/tool/builder"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"github.com/eyebluecn/tank/code/tool/uuid"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -61,14 +61,14 @@ func (this *DashboardDao) Page(page int, pageSize int, dt string, sortArray []bu
|
||||
var conditionDB *gorm.DB
|
||||
conditionDB = core.CONTEXT.GetDB().Model(&Dashboard{}).Where(wp.Query, wp.Args...)
|
||||
|
||||
count := 0
|
||||
var count int64 = 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var dashboards []*Dashboard
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&dashboards)
|
||||
this.PanicError(db.Error)
|
||||
pager := NewPager(page, pageSize, count, dashboards)
|
||||
pager := NewPager(page, pageSize, int(count), dashboards)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
package rest
|
||||
|
||||
import "github.com/eyebluecn/tank/code/core"
|
||||
import "time"
|
||||
|
||||
/**
|
||||
* application's dashboard.
|
||||
*/
|
||||
type Dashboard struct {
|
||||
Base
|
||||
InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //api invoke num.
|
||||
TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //total invoke num up to now.
|
||||
Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //today's uv
|
||||
TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //total uv
|
||||
MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //file's num
|
||||
TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //file's total number
|
||||
FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //today's file size
|
||||
TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //total file's size
|
||||
AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //api time cost in ms
|
||||
Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //date
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (this *Dashboard) TableName() string {
|
||||
return core.TABLE_PREFIX + "dashboard"
|
||||
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
|
||||
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
|
||||
UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
|
||||
CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //api invoke num.
|
||||
TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //total invoke num up to now.
|
||||
Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //today's uv
|
||||
TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //total uv
|
||||
MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //file's num
|
||||
TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //file's total number
|
||||
FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //today's file size
|
||||
TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //total file's size
|
||||
AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //api time cost in ms
|
||||
Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dashboard_dt"` //date. index should unique globally for sqlite.
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@ package rest
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
"github.com/eyebluecn/tank/code/tool/util"
|
||||
"github.com/robfig/cron"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -49,25 +48,24 @@ func (this *DashboardService) Init() {
|
||||
|
||||
func (this *DashboardService) Bootstrap() {
|
||||
|
||||
this.logger.Info("[cron job] Everyday 00:05 ETL dashboard data.")
|
||||
expression := "0 5 0 * * ?"
|
||||
cronJob := cron.New()
|
||||
err := cronJob.AddFunc(expression, this.etl)
|
||||
core.PanicError(err)
|
||||
cronJob.Start()
|
||||
this.logger.Info("Immediately ETL dashboard data.")
|
||||
|
||||
//do the etl method now.
|
||||
go core.RunWithRecovery(this.etl)
|
||||
go core.RunWithRecovery(this.Etl)
|
||||
}
|
||||
|
||||
func (this *DashboardService) Etl() {
|
||||
this.etlOneDay(util.Yesterday())
|
||||
this.etlOneDay(time.Now())
|
||||
}
|
||||
|
||||
// handle the dashboard data.
|
||||
func (this *DashboardService) etl() {
|
||||
func (this *DashboardService) etlOneDay(thenTime time.Time) {
|
||||
|
||||
this.logger.Info("ETL dashboard data.")
|
||||
|
||||
startTime := util.FirstSecondOfDay(util.Yesterday())
|
||||
endTime := util.LastSecondOfDay(util.Yesterday())
|
||||
startTime := util.FirstSecondOfDay(thenTime)
|
||||
endTime := util.LastSecondOfDay(thenTime)
|
||||
dt := util.ConvertTimeToDateString(startTime)
|
||||
now := time.Now()
|
||||
longTimeAgo := time.Now()
|
||||
longTimeAgo = longTimeAgo.AddDate(-20, 0, 0)
|
||||
|
||||
@@ -81,19 +79,19 @@ func (this *DashboardService) etl() {
|
||||
}
|
||||
|
||||
invokeNum := this.footprintDao.CountBetweenTime(startTime, endTime)
|
||||
totalInvokeNum := this.footprintDao.CountBetweenTime(longTimeAgo, endTime)
|
||||
totalInvokeNum := this.footprintDao.CountBetweenTime(longTimeAgo, now)
|
||||
uv := this.footprintDao.UvBetweenTime(startTime, endTime)
|
||||
totalUv := this.footprintDao.UvBetweenTime(longTimeAgo, endTime)
|
||||
totalUv := this.footprintDao.UvBetweenTime(longTimeAgo, now)
|
||||
matterNum := this.matterDao.CountBetweenTime(startTime, endTime)
|
||||
totalMatterNum := this.matterDao.CountBetweenTime(longTimeAgo, endTime)
|
||||
totalMatterNum := this.matterDao.CountBetweenTime(longTimeAgo, now)
|
||||
|
||||
matterSize := this.matterDao.SizeBetweenTime(startTime, endTime)
|
||||
|
||||
totalMatterSize := this.matterDao.SizeBetweenTime(longTimeAgo, endTime)
|
||||
totalMatterSize := this.matterDao.SizeBetweenTime(longTimeAgo, now)
|
||||
|
||||
cacheSize := this.imageCacheDao.SizeBetweenTime(startTime, endTime)
|
||||
|
||||
totalCacheSize := this.imageCacheDao.SizeBetweenTime(longTimeAgo, endTime)
|
||||
totalCacheSize := this.imageCacheDao.SizeBetweenTime(longTimeAgo, now)
|
||||
|
||||
avgCost := this.footprintDao.AvgCostBetweenTime(startTime, endTime)
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@ import (
|
||||
* WebDav document
|
||||
* https://tools.ietf.org/html/rfc4918
|
||||
* http://www.webdav.org/specs/rfc4918.html
|
||||
*
|
||||
* test machine: http://www.webdav.org/neon/litmus/
|
||||
*/
|
||||
|
||||
type DavController struct {
|
||||
BaseController
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
spaceDao *SpaceDao
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
@@ -50,6 +51,11 @@ func (this *DavController) Init() {
|
||||
this.matterDao = c
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.spaceDao)
|
||||
if c, ok := b.(*SpaceDao); ok {
|
||||
this.spaceDao = c
|
||||
}
|
||||
|
||||
b = core.CONTEXT.GetBean(this.matterService)
|
||||
if c, ok := b.(*MatterService); ok {
|
||||
this.matterService = c
|
||||
@@ -71,7 +77,7 @@ func (this *DavController) Init() {
|
||||
}
|
||||
}
|
||||
|
||||
//Auth user by BasicAuth
|
||||
// Auth user by BasicAuth
|
||||
func (this *DavController) CheckCurrentUser(writer http.ResponseWriter, request *http.Request) *User {
|
||||
|
||||
username, password, ok := request.BasicAuth()
|
||||
@@ -100,7 +106,7 @@ func (this *DavController) RegisterRoutes() map[string]func(writer http.Response
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//handle some special routes, eg. params in the url.
|
||||
// handle some special routes, eg. params in the url.
|
||||
func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
|
||||
path := request.URL.Path
|
||||
@@ -111,10 +117,16 @@ func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *htt
|
||||
strs := reg.FindStringSubmatch(path)
|
||||
if len(strs) == 2 {
|
||||
var f = func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
if err := recover(); err != nil {
|
||||
this.logger.Error("occur error in webdav: %v", err)
|
||||
}
|
||||
|
||||
subPath := strs[1]
|
||||
//guarantee subPath not end with /
|
||||
subPath = strings.TrimSuffix(subPath, "/")
|
||||
this.Index(writer, request, subPath)
|
||||
|
||||
}
|
||||
return f, true
|
||||
}
|
||||
@@ -145,7 +157,7 @@ func (this *DavController) debug(writer http.ResponseWriter, request *http.Reque
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
fmt.Println("读取body时出错" + err.Error())
|
||||
fmt.Println("occur error when reading body: " + err.Error())
|
||||
}
|
||||
fmt.Println(string(bodyBytes))
|
||||
|
||||
@@ -162,10 +174,12 @@ func (this *DavController) debug(writer http.ResponseWriter, request *http.Reque
|
||||
|
||||
func (this *DavController) Index(writer http.ResponseWriter, request *http.Request, subPath string) {
|
||||
|
||||
//when debugging. open it.
|
||||
//this.debug(writer, request, subPath)
|
||||
|
||||
user := this.CheckCurrentUser(writer, request)
|
||||
space := this.spaceDao.CheckByUuid(user.SpaceUuid)
|
||||
|
||||
this.davService.HandleDav(writer, request, user, subPath)
|
||||
this.davService.HandleDav(writer, request, user, space, subPath)
|
||||
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//webdav url prefix.
|
||||
// webdav url prefix.
|
||||
var WEBDAV_PREFIX = "/api/dav"
|
||||
|
||||
//live prop.
|
||||
// live prop.
|
||||
type LiveProp struct {
|
||||
findFn func(user *User, matter *Matter) string
|
||||
findFn func(space *Space, matter *Matter) string
|
||||
dir bool
|
||||
}
|
||||
|
||||
//all live prop map.
|
||||
// all live prop map.
|
||||
var LivePropMap = map[xml.Name]LiveProp{
|
||||
{Space: "DAV:", Local: "resourcetype"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
if matter.Dir {
|
||||
return `<D:collection xmlns:D="DAV:"/>`
|
||||
} else {
|
||||
@@ -31,7 +31,7 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "displayname"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
if path.Clean("/"+matter.Name) == "/" {
|
||||
return ""
|
||||
} else {
|
||||
@@ -41,13 +41,13 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "getcontentlength"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
return strconv.FormatInt(matter.Size, 10)
|
||||
},
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getlastmodified"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
return matter.UpdateTime.UTC().Format(http.TimeFormat)
|
||||
},
|
||||
// http://webdav.org/specs/rfc4918.html#PROPERTY_getlastmodified
|
||||
@@ -68,7 +68,7 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getcontenttype"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
if matter.Dir {
|
||||
return ""
|
||||
} else {
|
||||
@@ -78,7 +78,7 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getetag"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
return fmt.Sprintf(`"%x%x"`, matter.UpdateTime.UnixNano(), matter.Size)
|
||||
},
|
||||
// findETag implements ETag as the concatenated hex values of a file's
|
||||
@@ -91,7 +91,7 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
// active locks on a resource.
|
||||
{Space: "DAV:", Local: "lockdiscovery"}: {},
|
||||
{Space: "DAV:", Local: "supportedlock"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
return `` +
|
||||
`<D:lockentry xmlns:D="DAV:">` +
|
||||
`<D:lockscope><D:exclusive/></D:lockscope>` +
|
||||
@@ -101,11 +101,11 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "quota-available-bytes"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
var size int64 = 0
|
||||
if user.TotalSizeLimit >= 0 {
|
||||
if user.TotalSizeLimit-user.TotalSize > 0 {
|
||||
size = user.TotalSizeLimit - user.TotalSize
|
||||
if space.TotalSizeLimit >= 0 {
|
||||
if space.TotalSizeLimit-space.TotalSize > 0 {
|
||||
size = space.TotalSizeLimit - space.TotalSize
|
||||
} else {
|
||||
size = 0
|
||||
}
|
||||
@@ -118,8 +118,8 @@ var LivePropMap = map[xml.Name]LiveProp{
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "quota-used-bytes"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
return fmt.Sprintf(`%d`, user.TotalSize)
|
||||
findFn: func(space *Space, matter *Matter) string {
|
||||
return fmt.Sprintf(`%d`, space.TotalSize)
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"github.com/eyebluecn/tank/code/tool/dav/xml"
|
||||
"github.com/eyebluecn/tank/code/tool/result"
|
||||
"github.com/eyebluecn/tank/code/tool/util"
|
||||
"github.com/eyebluecn/tank/code/tool/webdav"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -26,6 +29,7 @@ type DavService struct {
|
||||
BaseBean
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
lockSystem webdav.LockSystem
|
||||
}
|
||||
|
||||
func (this *DavService) Init() {
|
||||
@@ -41,9 +45,11 @@ func (this *DavService) Init() {
|
||||
this.matterService = b
|
||||
}
|
||||
|
||||
// init the webdav lock system.
|
||||
this.lockSystem = webdav.NewMemLS()
|
||||
}
|
||||
|
||||
//get the depth in header. Not support infinity yet.
|
||||
// get the depth in header. Not support infinity yet.
|
||||
func (this *DavService) ParseDepth(request *http.Request) int {
|
||||
|
||||
depth := 1
|
||||
@@ -82,36 +88,63 @@ func (this *DavService) makePropstatResponse(href string, pstats []dav.Propstat)
|
||||
return &resp
|
||||
}
|
||||
|
||||
//fetch a matter's []dav.Propstat
|
||||
func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNames []xml.Name) []dav.Propstat {
|
||||
// fetch a matter's []dav.Propstat
|
||||
func (this *DavService) PropstatsFromXmlNames(user *User, space *Space, matter *Matter, xmlNames []xml.Name) []dav.Propstat {
|
||||
|
||||
propstats := make([]dav.Propstat, 0)
|
||||
|
||||
var properties []dav.Property
|
||||
var okProperties []dav.Property
|
||||
var notFoundProperties []dav.Property
|
||||
|
||||
for _, xmlName := range xmlNames {
|
||||
//TODO: deadprops not implement yet.
|
||||
|
||||
// Otherwise, it must either be a live property or we don't know it.
|
||||
if liveProp := LivePropMap[xmlName]; liveProp.findFn != nil && (liveProp.dir || !matter.Dir) {
|
||||
innerXML := liveProp.findFn(user, matter)
|
||||
innerXML := liveProp.findFn(space, matter)
|
||||
|
||||
properties = append(properties, dav.Property{
|
||||
okProperties = append(okProperties, dav.Property{
|
||||
XMLName: xmlName,
|
||||
InnerXML: []byte(innerXML),
|
||||
})
|
||||
} else {
|
||||
this.logger.Info("%s %s cannot finish.", matter.Path, xmlName.Local)
|
||||
this.logger.Info("handle props %s %s.", matter.Path, xmlName.Local)
|
||||
|
||||
propMap := matter.FetchPropMap()
|
||||
if value, isPresent := propMap[xmlName.Local]; isPresent {
|
||||
okProperties = append(okProperties, dav.Property{
|
||||
XMLName: xmlName,
|
||||
InnerXML: []byte(value),
|
||||
})
|
||||
} else {
|
||||
|
||||
//only accept Space not null.
|
||||
if xmlName.Space != "" {
|
||||
|
||||
//collect not found props
|
||||
notFoundProperties = append(notFoundProperties, dav.Property{
|
||||
XMLName: xmlName,
|
||||
InnerXML: []byte(""),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(properties) == 0 {
|
||||
if len(okProperties) == 0 && len(notFoundProperties) == 0 {
|
||||
panic(result.BadRequest("cannot parse request properties"))
|
||||
}
|
||||
|
||||
okPropstat := dav.Propstat{Status: http.StatusOK, Props: properties}
|
||||
if len(okProperties) != 0 {
|
||||
okPropstat := dav.Propstat{Status: http.StatusOK, Props: okProperties}
|
||||
propstats = append(propstats, okPropstat)
|
||||
}
|
||||
|
||||
propstats = append(propstats, okPropstat)
|
||||
if len(notFoundProperties) != 0 {
|
||||
notFoundPropstat := dav.Propstat{Status: http.StatusNotFound, Props: notFoundProperties}
|
||||
propstats = append(propstats, notFoundPropstat)
|
||||
}
|
||||
|
||||
return propstats
|
||||
|
||||
@@ -129,7 +162,7 @@ func (this *DavService) AllPropXmlNames(matter *Matter) []xml.Name {
|
||||
return pnames
|
||||
}
|
||||
|
||||
func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Propfind) []dav.Propstat {
|
||||
func (this *DavService) Propstats(user *User, space *Space, matter *Matter, propfind *dav.Propfind) []dav.Propstat {
|
||||
|
||||
propstats := make([]dav.Propstat, 0)
|
||||
if propfind.Propname != nil {
|
||||
@@ -139,34 +172,35 @@ func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Prop
|
||||
//TODO: if include other things. add to it.
|
||||
xmlNames := this.AllPropXmlNames(matter)
|
||||
|
||||
propstats = this.PropstatsFromXmlNames(user, matter, xmlNames)
|
||||
propstats = this.PropstatsFromXmlNames(user, space, matter, xmlNames)
|
||||
|
||||
} else {
|
||||
propstats = this.PropstatsFromXmlNames(user, matter, propfind.Prop)
|
||||
propstats = this.PropstatsFromXmlNames(user, space, matter, propfind.Prop)
|
||||
}
|
||||
|
||||
return propstats
|
||||
|
||||
}
|
||||
|
||||
//list the directory.
|
||||
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// list the directory.
|
||||
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("PROPFIND %s\n", subPath)
|
||||
|
||||
// read depth
|
||||
depth := this.ParseDepth(request)
|
||||
|
||||
propfind := dav.ReadPropfind(request.Body)
|
||||
|
||||
//find the matter, if subPath is null, means the root directory.
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user, space)
|
||||
|
||||
var matters []*Matter
|
||||
if depth == 0 {
|
||||
matters = []*Matter{matter}
|
||||
} else {
|
||||
// len(matters) == 0 means empty directory
|
||||
matters = this.matterDao.FindByPuuidAndUserUuid(matter.Uuid, user.Uuid, nil)
|
||||
matters = this.matterDao.FindByPuuidAndUserUuidAndDeleted(matter.Uuid, user.Uuid, FALSE, nil)
|
||||
|
||||
//add this matter to head.
|
||||
matters = append([]*Matter{matter}, matters...)
|
||||
@@ -179,7 +213,7 @@ func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http
|
||||
|
||||
fmt.Printf("handle Matter %s\n", matter.Path)
|
||||
|
||||
propstats := this.Propstats(user, matter, propfind)
|
||||
propstats := this.Propstats(user, space, matter, propfind)
|
||||
visitPath := fmt.Sprintf("%s%s", WEBDAV_PREFIX, matter.Path)
|
||||
response := this.makePropstatResponse(visitPath, propstats)
|
||||
|
||||
@@ -194,16 +228,84 @@ func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http
|
||||
|
||||
}
|
||||
|
||||
//handle download
|
||||
func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// change the file's property
|
||||
func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("PROPPATCH %s\n", subPath)
|
||||
|
||||
// handle the lock feature.
|
||||
reqPath, status, err := this.stripPrefix(request.URL.Path)
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
release, status, err := this.confirmLocks(request, reqPath, "")
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
matter := this.matterDao.checkByUserUuidAndPath(user.Uuid, subPath)
|
||||
|
||||
patches, status, err := webdav.ReadProppatch(request.Body)
|
||||
this.PanicError(err)
|
||||
|
||||
fmt.Printf("status:%v\n", status)
|
||||
|
||||
//prepare a multiStatusWriter.
|
||||
multiStatusWriter := &dav.MultiStatusWriter{Writer: writer}
|
||||
|
||||
propstats := make([]dav.Propstat, 0)
|
||||
propMap := matter.FetchPropMap()
|
||||
for _, patch := range patches {
|
||||
propStat := dav.Propstat{Status: http.StatusOK}
|
||||
if patch.Remove {
|
||||
|
||||
if len(patch.Props) > 0 {
|
||||
property := patch.Props[0]
|
||||
if _, isPresent := propMap[property.XMLName.Local]; isPresent {
|
||||
//delete the prop.
|
||||
delete(propMap, property.XMLName.Local)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, prop := range patch.Props {
|
||||
propMap[prop.XMLName.Local] = string(prop.InnerXML)
|
||||
|
||||
propStat.Props = append(propStat.Props, dav.Property{XMLName: xml.Name{Space: prop.XMLName.Space, Local: prop.XMLName.Local}})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
propstats = append(propstats, propStat)
|
||||
|
||||
}
|
||||
matter.SetPropMap(propMap)
|
||||
// update the matter
|
||||
this.matterDao.Save(matter)
|
||||
|
||||
visitPath := fmt.Sprintf("%s%s", WEBDAV_PREFIX, matter.Path)
|
||||
response := this.makePropstatResponse(visitPath, propstats)
|
||||
|
||||
err1 := multiStatusWriter.Write(response)
|
||||
this.PanicError(err1)
|
||||
|
||||
err2 := multiStatusWriter.Close()
|
||||
this.PanicError(err2)
|
||||
|
||||
}
|
||||
|
||||
// handle download
|
||||
func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("GET %s\n", subPath)
|
||||
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user, space)
|
||||
|
||||
//if this is a Directory, it means Propfind
|
||||
if matter.Dir {
|
||||
this.HandlePropfind(writer, request, user, subPath)
|
||||
this.HandlePropfind(writer, request, user, space, subPath)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -212,56 +314,115 @@ func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *h
|
||||
|
||||
}
|
||||
|
||||
//upload a file
|
||||
func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// upload a file
|
||||
func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("PUT %s\n", subPath)
|
||||
|
||||
// handle the lock feature.
|
||||
reqPath, status, err := this.stripPrefix(request.URL.Path)
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
release, status, err := this.confirmLocks(request, reqPath, "")
|
||||
if err != nil {
|
||||
|
||||
//if status == http.StatusLocked {
|
||||
// status = http.StatusPreconditionFailed
|
||||
//}
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
filename := util.GetFilenameOfPath(subPath)
|
||||
dirPath := util.GetDirOfPath(subPath)
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
|
||||
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user, space)
|
||||
|
||||
//if exist delete it.
|
||||
srcMatter := this.matterDao.findByUserUuidAndPath(user.Uuid, subPath)
|
||||
if srcMatter != nil {
|
||||
this.matterService.AtomicDelete(request, srcMatter, user)
|
||||
this.matterService.AtomicDelete(request, srcMatter, user, space)
|
||||
}
|
||||
|
||||
this.matterService.Upload(request, request.Body, user, dirMatter, filename, true)
|
||||
this.matterService.Upload(request, request.Body, nil, user, space, dirMatter, filename, true)
|
||||
|
||||
//set the status code 201
|
||||
writer.WriteHeader(http.StatusCreated)
|
||||
|
||||
}
|
||||
|
||||
//delete file
|
||||
func (this *DavService) HandleDelete(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// delete file
|
||||
func (this *DavService) HandleDelete(w http.ResponseWriter, r *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("DELETE %s\n", subPath)
|
||||
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
reqPath, status, err := this.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
release, status, err := this.confirmLocks(r, reqPath, "")
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
this.matterService.AtomicDelete(request, matter, user)
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user, space)
|
||||
|
||||
this.matterService.AtomicDelete(r, matter, user, space)
|
||||
}
|
||||
|
||||
//crate a directory
|
||||
func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// crate a directory
|
||||
func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("MKCOL %s\n", subPath)
|
||||
|
||||
//the body of MKCOL request MUST be empty. (RFC2518:8.3.1)
|
||||
bodyBytes, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
fmt.Println("occur error when reading body: " + err.Error())
|
||||
} else {
|
||||
if len(bodyBytes) != 0 {
|
||||
//throw conflict error
|
||||
panic(result.CustomWebResult(result.UNSUPPORTED_MEDIA_TYPE, fmt.Sprintf("%s MKCOL should NO body", subPath)))
|
||||
}
|
||||
}
|
||||
|
||||
thisDirName := util.GetFilenameOfPath(subPath)
|
||||
dirPath := util.GetDirOfPath(subPath)
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
|
||||
dirMatter := this.matterDao.FindWithRootByPath(dirPath, user, space)
|
||||
if dirMatter == nil {
|
||||
//throw conflict error
|
||||
panic(result.CustomWebResult(result.CONFLICT, fmt.Sprintf("%s not exist", dirPath)))
|
||||
}
|
||||
|
||||
this.matterService.AtomicCreateDirectory(request, dirMatter, thisDirName, user)
|
||||
//check whether col exists. (RFC2518:8.3.1)
|
||||
dbMatter := this.matterDao.FindBySpaceNameAndPuuidAndDirAndName(space.Name, dirMatter.Uuid, TRUE, thisDirName)
|
||||
if dbMatter != nil {
|
||||
panic(result.CustomWebResult(result.METHOD_NOT_ALLOWED, fmt.Sprintf("%s already exists", dirPath)))
|
||||
}
|
||||
|
||||
//check whether file exists. (RFC2518:8.3.1)
|
||||
fileMatter := this.matterDao.FindBySpaceNameAndPuuidAndDirAndName(space.Name, dirMatter.Uuid, FALSE, thisDirName)
|
||||
if fileMatter != nil {
|
||||
panic(result.CustomWebResult(result.METHOD_NOT_ALLOWED, fmt.Sprintf("%s file already exists", dirPath)))
|
||||
}
|
||||
|
||||
this.matterService.AtomicCreateDirectory(request, dirMatter, thisDirName, user, space)
|
||||
|
||||
}
|
||||
|
||||
//cors options
|
||||
func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
|
||||
// cors options
|
||||
func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("OPTIONS %s\n", subPath)
|
||||
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user, space)
|
||||
|
||||
allow := "OPTIONS, LOCK, PUT, MKCOL"
|
||||
if matter.Dir {
|
||||
@@ -278,11 +439,11 @@ func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, us
|
||||
|
||||
}
|
||||
|
||||
//prepare for moving or copying
|
||||
// prepare for moving or copying
|
||||
func (this *DavService) prepareMoveCopy(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
user *User, subPath string) (
|
||||
user *User, space *Space, subPath string) (
|
||||
srcMatter *Matter,
|
||||
destDirMatter *Matter,
|
||||
srcDirPath string,
|
||||
@@ -345,106 +506,348 @@ func (this *DavService) prepareMoveCopy(
|
||||
}
|
||||
|
||||
//source matter
|
||||
srcMatter = this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
srcMatter = this.matterDao.CheckWithRootByPath(subPath, user, space)
|
||||
|
||||
//if source matter is root.
|
||||
if srcMatter.Uuid == MATTER_ROOT {
|
||||
panic(result.BadRequest("you cannot move the root directory"))
|
||||
}
|
||||
|
||||
destDirMatter = this.matterDao.CheckWithRootByPath(destinationDirPath, user)
|
||||
destDirMatter = this.matterDao.FindWithRootByPath(destinationDirPath, user, space)
|
||||
if destDirMatter == nil {
|
||||
//throw conflict error
|
||||
panic(result.CustomWebResult(result.CONFLICT, fmt.Sprintf("%s not exist", destinationDirPath)))
|
||||
}
|
||||
|
||||
return srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite
|
||||
|
||||
}
|
||||
|
||||
//move or rename.
|
||||
func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// move or rename.
|
||||
func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("MOVE %s\n", subPath)
|
||||
|
||||
srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
|
||||
// handle the lock feature.
|
||||
reqPath, status, err := this.stripPrefix(request.URL.Path)
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
release, status, err := this.confirmLocks(request, reqPath, "")
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, space, subPath)
|
||||
|
||||
//move to the new directory
|
||||
if destinationDirPath == srcDirPath {
|
||||
//if destination path not change. it means rename.
|
||||
this.matterService.AtomicRename(request, srcMatter, destinationName, user)
|
||||
this.matterService.AtomicRename(request, srcMatter, destinationName, overwrite, user, space)
|
||||
} else {
|
||||
this.matterService.AtomicMove(request, srcMatter, destDirMatter, overwrite, user)
|
||||
this.matterService.AtomicMove(request, srcMatter, destDirMatter, overwrite, user, space)
|
||||
}
|
||||
|
||||
this.logger.Info("finish moving %s => %s", subPath, destDirMatter.Path)
|
||||
|
||||
if overwrite {
|
||||
//overwrite old. set the status code 204
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
} else {
|
||||
//copy new. set the status code 201
|
||||
writer.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
}
|
||||
|
||||
//copy file/directory
|
||||
func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// copy file/directory
|
||||
func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
fmt.Printf("COPY %s\n", subPath)
|
||||
|
||||
srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
|
||||
srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, space, subPath)
|
||||
|
||||
// handle the lock feature.
|
||||
release, status, err := this.confirmLocks(request, destDirMatter.Path+"/"+destinationName, "")
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
//copy to the new directory
|
||||
this.matterService.AtomicCopy(request, srcMatter, destDirMatter, destinationName, overwrite, user)
|
||||
this.matterService.AtomicCopy(request, srcMatter, destDirMatter, destinationName, overwrite, user, space)
|
||||
|
||||
this.logger.Info("finish copying %s => %s", subPath, destDirMatter.Path)
|
||||
|
||||
if overwrite {
|
||||
//overwrite old. set the status code 204
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
} else {
|
||||
//copy new. set the status code 201
|
||||
writer.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//lock.
|
||||
func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
panic(result.BadRequest("not support LOCK yet."))
|
||||
func (h *DavService) stripPrefix(p string) (string, int, error) {
|
||||
if r := strings.TrimPrefix(p, WEBDAV_PREFIX); len(r) < len(p) {
|
||||
return r, http.StatusOK, nil
|
||||
}
|
||||
return p, http.StatusNotFound, webdav.ErrPrefixMismatch
|
||||
}
|
||||
|
||||
//unlock
|
||||
func (this *DavService) HandleUnlock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
panic(result.BadRequest("not support UNLOCK yet."))
|
||||
func (h *DavService) lock(now time.Time, root string) (token string, status int, err error) {
|
||||
token, err = h.lockSystem.Create(now, webdav.LockDetails{
|
||||
Root: root,
|
||||
Duration: webdav.InfiniteTimeout,
|
||||
ZeroDepth: true,
|
||||
})
|
||||
if err != nil {
|
||||
if err == webdav.ErrLocked {
|
||||
return "", webdav.StatusLocked, err
|
||||
}
|
||||
return "", http.StatusInternalServerError, err
|
||||
}
|
||||
return token, 0, nil
|
||||
}
|
||||
|
||||
//change the file's property
|
||||
func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
func (h *DavService) confirmLocks(r *http.Request, src, dst string) (release func(), status int, err error) {
|
||||
hdr := r.Header.Get("If")
|
||||
if hdr == "" {
|
||||
// An empty If header means that the client hasn't previously created locks.
|
||||
// Even if this client doesn't care about locks, we still need to check that
|
||||
// the resources aren't locked by another client, so we create temporary
|
||||
// locks that would conflict with another client's locks. These temporary
|
||||
// locks are unlocked at the end of the HTTP request.
|
||||
now, srcToken, dstToken := time.Now(), "", ""
|
||||
if src != "" {
|
||||
srcToken, status, err = h.lock(now, src)
|
||||
if err != nil {
|
||||
return nil, status, err
|
||||
}
|
||||
}
|
||||
if dst != "" {
|
||||
dstToken, status, err = h.lock(now, dst)
|
||||
if err != nil {
|
||||
if srcToken != "" {
|
||||
h.lockSystem.Unlock(now, srcToken)
|
||||
}
|
||||
return nil, status, err
|
||||
}
|
||||
}
|
||||
|
||||
panic(result.BadRequest("not support PROPPATCH yet."))
|
||||
return func() {
|
||||
if dstToken != "" {
|
||||
h.lockSystem.Unlock(now, dstToken)
|
||||
}
|
||||
if srcToken != "" {
|
||||
h.lockSystem.Unlock(now, srcToken)
|
||||
}
|
||||
}, 0, nil
|
||||
}
|
||||
|
||||
ih, ok := webdav.ParseIfHeader(hdr)
|
||||
if !ok {
|
||||
return nil, http.StatusBadRequest, webdav.ErrInvalidIfHeader
|
||||
}
|
||||
// ih is a disjunction (OR) of ifLists, so any IfList will do.
|
||||
for _, l := range ih.Lists {
|
||||
lsrc := l.ResourceTag
|
||||
if lsrc == "" {
|
||||
lsrc = src
|
||||
} else {
|
||||
u, err := url.Parse(lsrc)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if u.Host != r.Host {
|
||||
continue
|
||||
}
|
||||
lsrc, status, err = h.stripPrefix(u.Path)
|
||||
if err != nil {
|
||||
return nil, status, err
|
||||
}
|
||||
}
|
||||
release, err = h.lockSystem.Confirm(time.Now(), lsrc, dst, l.Conditions...)
|
||||
if err == webdav.ErrConfirmationFailed {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, err
|
||||
}
|
||||
return release, 0, nil
|
||||
}
|
||||
// Section 10.4.1 says that "If this header is evaluated and all state lists
|
||||
// fail, then the request must fail with a 412 (Precondition Failed) status."
|
||||
// We follow the spec even though the cond_put_corrupt_token test case from
|
||||
// the litmus test warns on seeing a 412 instead of a 423 (Locked).
|
||||
return nil, http.StatusLocked, webdav.ErrLocked
|
||||
}
|
||||
|
||||
//hanle all the request.
|
||||
func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
// lock.
|
||||
func (this *DavService) HandleLock(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
|
||||
|
||||
duration, err := webdav.ParseTimeout(r.Header.Get("Timeout"))
|
||||
if err != nil {
|
||||
panic(result.BadRequest(err.Error()))
|
||||
}
|
||||
li, status, err := webdav.ReadLockInfo(r.Body)
|
||||
if err != nil {
|
||||
panic(result.BadRequest(fmt.Sprintf("error:%s, status=%d", err.Error(), status)))
|
||||
}
|
||||
|
||||
token, ld, now, created := "", webdav.LockDetails{}, time.Now(), false
|
||||
if li == (webdav.LockInfo{}) {
|
||||
// An empty LockInfo means to refresh the lock.
|
||||
ih, ok := webdav.ParseIfHeader(r.Header.Get("If"))
|
||||
if !ok {
|
||||
panic(result.BadRequest(webdav.ErrInvalidIfHeader.Error()))
|
||||
}
|
||||
if len(ih.Lists) == 1 && len(ih.Lists[0].Conditions) == 1 {
|
||||
token = ih.Lists[0].Conditions[0].Token
|
||||
}
|
||||
if token == "" {
|
||||
panic(result.BadRequest(webdav.ErrInvalidLockToken.Error()))
|
||||
}
|
||||
ld, err = this.lockSystem.Refresh(now, token, duration)
|
||||
if err != nil {
|
||||
if err == webdav.ErrNoSuchLock {
|
||||
panic(result.StatusCodeWebResult(http.StatusPreconditionFailed, err.Error()))
|
||||
}
|
||||
panic(result.StatusCodeWebResult(http.StatusInternalServerError, err.Error()))
|
||||
}
|
||||
|
||||
} else {
|
||||
// Section 9.10.3 says that "If no Depth header is submitted on a LOCK request,
|
||||
// then the request MUST act as if a "Depth:infinity" had been submitted."
|
||||
depth := webdav.InfiniteDepth
|
||||
if hdr := r.Header.Get("Depth"); hdr != "" {
|
||||
depth = webdav.ParseDepth(hdr)
|
||||
if depth != 0 && depth != webdav.InfiniteDepth {
|
||||
// Section 9.10.3 says that "Values other than 0 or infinity must not be
|
||||
// used with the Depth header on a LOCK method".
|
||||
panic(result.StatusCodeWebResult(http.StatusBadRequest, webdav.ErrInvalidDepth.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
reqPath, status, err := this.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
panic(result.StatusCodeWebResult(status, err.Error()))
|
||||
}
|
||||
|
||||
ld = webdav.LockDetails{
|
||||
Root: reqPath,
|
||||
Duration: duration,
|
||||
OwnerXML: li.Owner.InnerXML,
|
||||
ZeroDepth: depth == 0,
|
||||
}
|
||||
token, err = this.lockSystem.Create(now, ld)
|
||||
if err != nil {
|
||||
if err == webdav.ErrLocked {
|
||||
panic(result.StatusCodeWebResult(http.StatusLocked, err.Error()))
|
||||
}
|
||||
panic(result.StatusCodeWebResult(http.StatusInternalServerError, err.Error()))
|
||||
}
|
||||
defer func() {
|
||||
//when error occur, rollback.
|
||||
//this.lockSystem.Unlock(now, token)
|
||||
}()
|
||||
|
||||
// Create the resource if it didn't previously exist.
|
||||
// ctx := r.Context()
|
||||
//if _, err := this.FileSystem.Stat(ctx, subPath); err != nil {
|
||||
// f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
// if err != nil {
|
||||
// // TODO: detect missing intermediate dirs and return http.StatusConflict?
|
||||
// return http.StatusInternalServerError, err
|
||||
// }
|
||||
// f.Close()
|
||||
// created = true
|
||||
//}
|
||||
|
||||
// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
|
||||
// Lock-Token value is a Coded-URL. We add angle brackets.
|
||||
w.Header().Set("Lock-Token", "<"+token+">")
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/xml; charset=utf-8")
|
||||
if created {
|
||||
// This is "w.WriteHeader(http.StatusCreated)" and not "return
|
||||
// http.StatusCreated, nil" because we write our own (XML) response to w
|
||||
// and Handler.ServeHTTP would otherwise write "Created".
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
_, _ = webdav.WriteLockInfo(w, token, ld)
|
||||
|
||||
}
|
||||
|
||||
// unlock
|
||||
func (this *DavService) HandleUnlock(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
|
||||
|
||||
// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
|
||||
// Lock-Token value is a Coded-URL. We strip its angle brackets.
|
||||
t := r.Header.Get("Lock-Token")
|
||||
if len(t) < 2 || t[0] != '<' || t[len(t)-1] != '>' {
|
||||
panic(result.StatusCodeWebResult(http.StatusBadRequest, webdav.ErrInvalidLockToken.Error()))
|
||||
}
|
||||
t = t[1 : len(t)-1]
|
||||
|
||||
switch err := this.lockSystem.Unlock(time.Now(), t); err {
|
||||
case nil:
|
||||
panic(result.StatusCodeWebResult(http.StatusNoContent, ""))
|
||||
case webdav.ErrForbidden:
|
||||
panic(result.StatusCodeWebResult(http.StatusForbidden, err.Error()))
|
||||
case webdav.ErrLocked:
|
||||
panic(result.StatusCodeWebResult(http.StatusLocked, err.Error()))
|
||||
case webdav.ErrNoSuchLock:
|
||||
panic(result.StatusCodeWebResult(http.StatusConflict, err.Error()))
|
||||
default:
|
||||
panic(result.StatusCodeWebResult(http.StatusInternalServerError, err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// hanle all the request.
|
||||
func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, space *Space, subPath string) {
|
||||
|
||||
method := request.Method
|
||||
if method == "OPTIONS" {
|
||||
|
||||
//cors option
|
||||
this.HandleOptions(writer, request, user, subPath)
|
||||
this.HandleOptions(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "GET" || method == "HEAD" || method == "POST" {
|
||||
|
||||
//get the detail of file. download
|
||||
this.HandleGetHeadPost(writer, request, user, subPath)
|
||||
this.HandleGetHeadPost(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "DELETE" {
|
||||
|
||||
//delete file
|
||||
this.HandleDelete(writer, request, user, subPath)
|
||||
this.HandleDelete(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "PUT" {
|
||||
|
||||
//upload file
|
||||
this.HandlePut(writer, request, user, subPath)
|
||||
this.HandlePut(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "MKCOL" {
|
||||
|
||||
//crate directory
|
||||
this.HandleMkcol(writer, request, user, subPath)
|
||||
this.HandleMkcol(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "COPY" {
|
||||
|
||||
//copy file/directory
|
||||
this.HandleCopy(writer, request, user, subPath)
|
||||
this.HandleCopy(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "MOVE" {
|
||||
|
||||
//move/rename a file or directory
|
||||
this.HandleMove(writer, request, user, subPath)
|
||||
this.HandleMove(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "LOCK" {
|
||||
|
||||
@@ -459,7 +862,7 @@ func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Requ
|
||||
} else if method == "PROPFIND" {
|
||||
|
||||
//list a directory
|
||||
this.HandlePropfind(writer, request, user, subPath)
|
||||
this.HandlePropfind(writer, request, user, space, subPath)
|
||||
|
||||
} else if method == "PROPPATCH" {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package rest
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
"github.com/eyebluecn/tank/code/tool/result"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"github.com/eyebluecn/tank/code/tool/uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -58,6 +58,13 @@ func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken
|
||||
return downloadToken
|
||||
}
|
||||
|
||||
func (this *DownloadTokenDao) DeleteByUserUuid(userUuid string) {
|
||||
|
||||
db := core.CONTEXT.GetDB().Where("user_uuid = ?", userUuid).Delete(DownloadToken{})
|
||||
this.PanicError(db.Error)
|
||||
|
||||
}
|
||||
|
||||
func (this *DownloadTokenDao) Cleanup() {
|
||||
this.logger.Info("[DownloadTokenDao] clean up. Delete all DownloadToken")
|
||||
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(DownloadToken{})
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/eyebluecn/tank/code/core"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DownloadToken struct {
|
||||
Base
|
||||
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
|
||||
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
|
||||
UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
|
||||
CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36) not null"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36) not null;index:idx_mu"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36) not null;index:idx_download_token_mu"` //index should unique globally for sqlite
|
||||
ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
Ip string `json:"ip" gorm:"type:varchar(128) not null"`
|
||||
}
|
||||
|
||||
func (this *DownloadToken) TableName() string {
|
||||
return core.TABLE_PREFIX + "download_token"
|
||||
}
|
||||
|
||||