Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
a295e7024a | |||
b36eaf08f0 | |||
bb6e520ab5 | |||
9b64e2e045 | |||
ee7c12c30f | |||
96d6d58910 | |||
2bf235a5ac | |||
236f9969c0 | |||
09e63027d9 | |||
a15dae291e | |||
efaaeedfb8 | |||
190c8001a5 | |||
b8698700ef | |||
985b81826f | |||
74d8fa3919 | |||
43e4928bb9 | |||
03580fd76c | |||
6e8d551420 | |||
28998d6f8c | |||
1779617cb9 | |||
7dfe48339c | |||
9c5627a382 | |||
809850321a | |||
bdc1f68746 | |||
9aaef6c3a3 | |||
bb50c52d0e | |||
6041e5a0fa | |||
308a86c36e | |||
ba7c4fc230 | |||
d81ec0637d |
39
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
39
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
name: "Bug report"
|
||||||
|
description: Bug report
|
||||||
|
labels: [pending triage]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Alist Version / Alist 版本
|
||||||
|
description: What version of our software are you running?
|
||||||
|
placeholder: v2.0.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: bug-description
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug / 问题描述
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Reproduction / 复现链接
|
||||||
|
description: |
|
||||||
|
Please provide a link to a repo that can reproduce the problem you ran into.
|
||||||
|
请提供能复现此问题的链接
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: 日志 / Logs
|
||||||
|
description: |
|
||||||
|
Please copy and paste any relevant log output.
|
||||||
|
请复制粘贴错误日志,或者截图
|
||||||
|
render: shell
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Questions & Discussions & Feature request
|
||||||
|
url: https://github.com/Xhofe/alist/discussions
|
||||||
|
about: Use GitHub discussions for message-board style questions and discussions or feature request.
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,7 +21,7 @@ dist/
|
|||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
bin/*
|
bin/*
|
||||||
alist
|
/alist
|
||||||
*.json
|
*.json
|
||||||
public/index.html
|
public/index.html
|
||||||
public/assets/
|
public/assets/
|
||||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Xhofe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
67
README.md
Normal file → Executable file
67
README.md
Normal file → Executable file
@ -1,46 +1,61 @@
|
|||||||
<h2 align="center">Alist</h2>
|
<div align="center">
|
||||||
<p align="center">
|
<h3><a href="https://alist.nn.ci">Alist</a></h3>
|
||||||
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="Release version"></a>
|
<p><em>A file list program that supports multiple storage, powered by gin and React.</em></p>
|
||||||
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
||||||
|
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
||||||
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild"><img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build?style=flat-square" alt="Build status"></a>
|
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild"><img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build?style=flat-square" alt="Build status"></a>
|
||||||
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/downloads/Xhofe/alist/total?style=flat-square" alt="Downloads"></a>
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/downloads/Xhofe/alist/total?style=flat-square&color=%239F7AEA" alt="Downloads"></a>
|
||||||
<a href="https://github.com/Xhofe/alist/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Xhofe/alist?style=flat-square" alt="License"></a>
|
<a href="https://github.com/Xhofe/alist/blob/v2/LICENSE"><img src="https://img.shields.io/github/license/Xhofe/alist?style=flat-square" alt="License"></a>
|
||||||
<a href="https://pay.xhofe.top">
|
<a href="https://pay.xhofe.top">
|
||||||
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg?style=flat-square" alt="donate">
|
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg?style=flat-square" alt="donate">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 这是什么?
|
English | [中文](./README_cn.md)
|
||||||
|
|
||||||
一款支持多种存储的目录文件列表程序,后端基于`gin`,前端使用`react`。
|
## Features
|
||||||
|
|
||||||
### 前端项目地址
|
- [x] multiple storage
|
||||||
|
- [x] Local storage
|
||||||
|
- [x] [aliyundrive](https://www.aliyundrive.com/)
|
||||||
|
- [x] OneDrive / Sharepoint ([global](https://www.office.com/), [cn](https://portal.partner.microsoftonline.cn),de,us)
|
||||||
|
- [x] [189cloud](https://cloud.189.cn)
|
||||||
|
- [x] [GoogleDrive](https://drive.google.com/)
|
||||||
|
- [x] [123pan](https://www.123pan.com/)
|
||||||
|
- [x] [lanzou](https://pc.woozooo.com/)
|
||||||
|
- [x] File preview (PDF, markdown, code, plain text, ...)
|
||||||
|
- [x] Image preview in gallery mode
|
||||||
|
- [x] Video and audio preview (mp4, mp3, ...)
|
||||||
|
- [x] Office documents preview (docx, pptx, xlsx, ...)
|
||||||
|
- [x] `README.md` preview rendering
|
||||||
|
- [x] File permalink copy and direct file download
|
||||||
|
- [x] Dark mode
|
||||||
|
- [x] I18n
|
||||||
|
- [x] Protected routes (password protection and authentication)
|
||||||
|
- [x] WebDav (readonly)
|
||||||
|
- [x] [Docker Deploy](https://hub.docker.com/r/xhofe/alist)
|
||||||
|
|
||||||
- https://github.com/Xhofe/alist-web
|
## Discussion
|
||||||
|
|
||||||
### 演示地址
|
Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports only.**
|
||||||
|
|
||||||
- https://alist.nn.ci
|
## Demo
|
||||||
|
|
||||||
### 预览
|
Available at: <https://alist.nn.ci>.
|
||||||
|
|
||||||
<a href="https://alist.nn.ci/"><img src="https://store.heytapimage.com/cdo-portal/feedback/202111/03/695ef77854a144e928518efde38db97a.png"></a>
|

|
||||||
|
|
||||||
### 支持的存储
|
## Document
|
||||||
|
|
||||||
- 本地存储
|
<https://alist-doc.nn.ci/en/>
|
||||||
- 阿里云盘
|
|
||||||
- Onedrive/世纪互联
|
|
||||||
- 天翼云盘
|
|
||||||
- GoogleDrive
|
|
||||||
- 123pan
|
|
||||||
- ...
|
|
||||||
|
|
||||||
### 如何使用
|
## License
|
||||||
|
|
||||||
- https://alist-doc.nn.ci/
|
The `AList` is open-source software licensed under the MIT license.
|
||||||
|
|
||||||
### License
|
---
|
||||||
|
|
||||||
The `AList` is open-source software licensed under the MIT license.
|
> [@Blog](https://www.nn.ci/) · [@GitHub](https://github.com/Xhofe)
|
60
README_cn.md
Normal file
60
README_cn.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<div align="center">
|
||||||
|
<h3><a href="https://alist.nn.ci">Alist</a></h3>
|
||||||
|
<p><em>一个支持多存储的文件列表程序,使用 gin 和 React 。</em></p>
|
||||||
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
||||||
|
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
||||||
|
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild"><img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build?style=flat-square" alt="Build status"></a>
|
||||||
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/downloads/Xhofe/alist/total?style=flat-square&color=%239F7AEA" alt="Downloads"></a>
|
||||||
|
<a href="https://github.com/Xhofe/alist/blob/v2/LICENSE"><img src="https://img.shields.io/github/license/Xhofe/alist?style=flat-square" alt="License"></a>
|
||||||
|
<a href="https://pay.xhofe.top">
|
||||||
|
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg?style=flat-square" alt="donate">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[English](./README.md) | 中文
|
||||||
|
|
||||||
|
## 支持
|
||||||
|
|
||||||
|
- [x] 多种存储
|
||||||
|
- [x] 本地存储
|
||||||
|
- [x] [阿里云盘](https://www.aliyundrive.com/)
|
||||||
|
- [x] OneDrive / Sharepoint([国际版](https://www.office.com/), [世纪互联](https://portal.partner.microsoftonline.cn),de,us)
|
||||||
|
- [x] [天翼云盘](https://cloud.189.cn)
|
||||||
|
- [x] [GoogleDrive](https://drive.google.com/)
|
||||||
|
- [x] [123云盘](https://www.123pan.com/)
|
||||||
|
- [x] [蓝奏云](https://pc.woozooo.com/)
|
||||||
|
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
||||||
|
- [x] 画廊模式下的图像预览
|
||||||
|
- [x] 视频和音频预览(mp4、mp3 等)
|
||||||
|
- [x] Office 文档预览(docx、pptx、xlsx、...)
|
||||||
|
- [x] `README.md` 预览渲染
|
||||||
|
- [x] 文件永久链接复制和直接文件下载
|
||||||
|
- [x] 黑暗模式
|
||||||
|
- [x] 国际化
|
||||||
|
- [x] 受保护的路由(密码保护和身份验证)
|
||||||
|
- [x] WebDav(只读)
|
||||||
|
- [x] [Docker 部署](https://hub.docker.com/r/xhofe/alist)
|
||||||
|
|
||||||
|
## 讨论
|
||||||
|
|
||||||
|
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告。**
|
||||||
|
|
||||||
|
## 演示
|
||||||
|
|
||||||
|
<https://alist.nn.ci>。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
<https://alist-doc.nn.ci/>
|
||||||
|
|
||||||
|
## 许可
|
||||||
|
|
||||||
|
`AList` 是在 MIT 许可下许可的开源软件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> [@Blog](https://www.nn.ci/) · [@GitHub](https://github.com/Xhofe)
|
125
alist-proxy.js
Normal file
125
alist-proxy.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
const HOST = "YOUR_HOST";
|
||||||
|
const TOKEN = "YOUR_TOKEN";
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
|
||||||
|
"Access-Control-Max-Age": "86400",
|
||||||
|
};
|
||||||
|
|
||||||
|
!function(a){"use strict";function b(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c}function c(a,b){return a<<b|a>>>32-b}function d(a,d,e,f,g,h){return b(c(b(b(d,a),b(f,h)),g),e)}function e(a,b,c,e,f,g,h){return d(b&c|~b&e,a,b,f,g,h)}function f(a,b,c,e,f,g,h){return d(b&e|c&~e,a,b,f,g,h)}function g(a,b,c,e,f,g,h){return d(b^c^e,a,b,f,g,h)}function h(a,b,c,e,f,g,h){return d(c^(b|~e),a,b,f,g,h)}function i(a,c){a[c>>5]|=128<<c%32,a[(c+64>>>9<<4)+14]=c;var d,i,j,k,l,m=1732584193,n=-271733879,o=-1732584194,p=271733878;for(d=0;d<a.length;d+=16)i=m,j=n,k=o,l=p,m=e(m,n,o,p,a[d],7,-680876936),p=e(p,m,n,o,a[d+1],12,-389564586),o=e(o,p,m,n,a[d+2],17,606105819),n=e(n,o,p,m,a[d+3],22,-1044525330),m=e(m,n,o,p,a[d+4],7,-176418897),p=e(p,m,n,o,a[d+5],12,1200080426),o=e(o,p,m,n,a[d+6],17,-1473231341),n=e(n,o,p,m,a[d+7],22,-45705983),m=e(m,n,o,p,a[d+8],7,1770035416),p=e(p,m,n,o,a[d+9],12,-1958414417),o=e(o,p,m,n,a[d+10],17,-42063),n=e(n,o,p,m,a[d+11],22,-1990404162),m=e(m,n,o,p,a[d+12],7,1804603682),p=e(p,m,n,o,a[d+13],12,-40341101),o=e(o,p,m,n,a[d+14],17,-1502002290),n=e(n,o,p,m,a[d+15],22,1236535329),m=f(m,n,o,p,a[d+1],5,-165796510),p=f(p,m,n,o,a[d+6],9,-1069501632),o=f(o,p,m,n,a[d+11],14,643717713),n=f(n,o,p,m,a[d],20,-373897302),m=f(m,n,o,p,a[d+5],5,-701558691),p=f(p,m,n,o,a[d+10],9,38016083),o=f(o,p,m,n,a[d+15],14,-660478335),n=f(n,o,p,m,a[d+4],20,-405537848),m=f(m,n,o,p,a[d+9],5,568446438),p=f(p,m,n,o,a[d+14],9,-1019803690),o=f(o,p,m,n,a[d+3],14,-187363961),n=f(n,o,p,m,a[d+8],20,1163531501),m=f(m,n,o,p,a[d+13],5,-1444681467),p=f(p,m,n,o,a[d+2],9,-51403784),o=f(o,p,m,n,a[d+7],14,1735328473),n=f(n,o,p,m,a[d+12],20,-1926607734),m=g(m,n,o,p,a[d+5],4,-378558),p=g(p,m,n,o,a[d+8],11,-2022574463),o=g(o,p,m,n,a[d+11],16,1839030562),n=g(n,o,p,m,a[d+14],23,-35309556),m=g(m,n,o,p,a[d+1],4,-1530992060),p=g(p,m,n,o,a[d+4],11,1272893353),o=g(o,p,m,n,a[d+7],16,-155497632),n=g(n,o,p,m,a[d+10],23,-1094730640),m=g(m,n,o,p,a[d+13],4,681279174),p=g(p,m,n,o,a[d],11,-358537222),o=g(o,p,m,n,a[d+3],16,-722521979),n=g(n,o,p,m,a[d+6],23,76029189),m=g(m,n,o,p,a[d+9],4,-640364487),p=g(p,m,n,o,a[d+12],11,-421815835),o=g(o,p,m,n,a[d+15],16,530742520),n=g(n,o,p,m,a[d+2],23,-995338651),m=h(m,n,o,p,a[d],6,-198630844),p=h(p,m,n,o,a[d+7],10,1126891415),o=h(o,p,m,n,a[d+14],15,-1416354905),n=h(n,o,p,m,a[d+5],21,-57434055),m=h(m,n,o,p,a[d+12],6,1700485571),p=h(p,m,n,o,a[d+3],10,-1894986606),o=h(o,p,m,n,a[d+10],15,-1051523),n=h(n,o,p,m,a[d+1],21,-2054922799),m=h(m,n,o,p,a[d+8],6,1873313359),p=h(p,m,n,o,a[d+15],10,-30611744),o=h(o,p,m,n,a[d+6],15,-1560198380),n=h(n,o,p,m,a[d+13],21,1309151649),m=h(m,n,o,p,a[d+4],6,-145523070),p=h(p,m,n,o,a[d+11],10,-1120210379),o=h(o,p,m,n,a[d+2],15,718787259),n=h(n,o,p,m,a[d+9],21,-343485551),m=b(m,i),n=b(n,j),o=b(o,k),p=b(p,l);return[m,n,o,p]}function j(a){var b,c="";for(b=0;b<32*a.length;b+=8)c+=String.fromCharCode(a[b>>5]>>>b%32&255);return c}function k(a){var b,c=[];for(c[(a.length>>2)-1]=void 0,b=0;b<c.length;b+=1)c[b]=0;for(b=0;b<8*a.length;b+=8)c[b>>5]|=(255&a.charCodeAt(b/8))<<b%32;return c}function l(a){return j(i(k(a),8*a.length))}function m(a,b){var c,d,e=k(a),f=[],g=[];for(f[15]=g[15]=void 0,e.length>16&&(e=i(e,8*a.length)),c=0;16>c;c+=1)f[c]=909522486^e[c],g[c]=1549556828^e[c];return d=i(f.concat(k(b)),512+8*b.length),j(i(g.concat(d),640))}function n(a){var b,c,d="0123456789abcdef",e="";for(c=0;c<a.length;c+=1)b=a.charCodeAt(c),e+=d.charAt(b>>>4&15)+d.charAt(15&b);return e}function o(a){return unescape(encodeURIComponent(a))}function p(a){return l(o(a))}function q(a){return n(p(a))}function r(a,b){return m(o(a),o(b))}function s(a,b){return n(r(a,b))}function t(a,b,c){return b?c?r(b,a):s(b,a):c?p(a):q(a)}"function"==typeof define&&define.amd?define(function(){return t}):a.md5=t}(this);
|
||||||
|
|
||||||
|
async function handleRequest(request) {
|
||||||
|
const origin = request.headers.get("origin");
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const path = url.pathname;
|
||||||
|
const sign = url.searchParams.get("sign");
|
||||||
|
const name = path.split("/").pop();
|
||||||
|
const right = md5(`alist-${TOKEN}-${name}`).slice(8, 24);
|
||||||
|
if (sign !== right){
|
||||||
|
const resp = new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
code: 401,
|
||||||
|
message: `sign mismatch`,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json;charset=UTF-8",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
resp.headers.set("Access-Control-Allow-Origin", origin);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = await fetch(`${HOST}/api/admin/link`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json;charset=UTF-8",
|
||||||
|
Authorization: TOKEN,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
path: decodeURI(path),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
let res = await resp.json();
|
||||||
|
if (res.code !== 200) {
|
||||||
|
return new Response(JSON.stringify(res));
|
||||||
|
}
|
||||||
|
request = new Request(res.data.url, request);
|
||||||
|
if (res.data.headers) {
|
||||||
|
for(const header of res.data.headers){
|
||||||
|
request.headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let response = await fetch(request);
|
||||||
|
|
||||||
|
// Recreate the response so we can modify the headers
|
||||||
|
response = new Response(response.body, response);
|
||||||
|
|
||||||
|
// Set CORS headers
|
||||||
|
response.headers.set("Access-Control-Allow-Origin", origin);
|
||||||
|
|
||||||
|
// Append to/Add Vary header so browser will cache response correctly
|
||||||
|
response.headers.append("Vary", "Origin");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOptions(request) {
|
||||||
|
// Make sure the necessary headers are present
|
||||||
|
// for this to be a valid pre-flight request
|
||||||
|
let headers = request.headers;
|
||||||
|
if (
|
||||||
|
headers.get("Origin") !== null &&
|
||||||
|
headers.get("Access-Control-Request-Method") !== null
|
||||||
|
// && headers.get("Access-Control-Request-Headers") !== null
|
||||||
|
) {
|
||||||
|
// Handle CORS pre-flight request.
|
||||||
|
// If you want to check or reject the requested method + headers
|
||||||
|
// you can do that here.
|
||||||
|
let respHeaders = {
|
||||||
|
...corsHeaders,
|
||||||
|
// Allow all future content Request headers to go back to browser
|
||||||
|
// such as Authorization (Bearer) or X-Client-Name-Version
|
||||||
|
"Access-Control-Allow-Headers": request.headers.get(
|
||||||
|
"Access-Control-Request-Headers"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(null, {
|
||||||
|
headers: respHeaders,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Handle standard OPTIONS request.
|
||||||
|
// If you want to allow other HTTP Methods, you can do that here.
|
||||||
|
return new Response(null, {
|
||||||
|
headers: {
|
||||||
|
Allow: "GET, HEAD, POST, OPTIONS",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("fetch", (event) => {
|
||||||
|
const request = event.request;
|
||||||
|
// const url = new URL(request.url)
|
||||||
|
if (request.method === "OPTIONS") {
|
||||||
|
// Handle CORS preflight requests
|
||||||
|
event.respondWith(handleOptions(request));
|
||||||
|
} else if (
|
||||||
|
request.method === "GET" ||
|
||||||
|
request.method === "HEAD" ||
|
||||||
|
request.method === "POST"
|
||||||
|
) {
|
||||||
|
// Handle requests to the API server
|
||||||
|
event.respondWith(handleRequest(request));
|
||||||
|
} else {
|
||||||
|
event.respondWith(
|
||||||
|
new Response(null, {
|
||||||
|
status: 405,
|
||||||
|
statusText: "Method Not Allowed",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
12
alist.go
12
alist.go
@ -1,26 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/bootstrap"
|
"github.com/Xhofe/alist/bootstrap"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
_ "github.com/Xhofe/alist/drivers"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/server"
|
"github.com/Xhofe/alist/server"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.StringVar(&conf.ConfigFile, "conf", "data/config.json", "config file")
|
|
||||||
flag.BoolVar(&conf.Debug, "debug", false, "start with debug mode")
|
|
||||||
flag.BoolVar(&conf.Version, "version", false, "print version info")
|
|
||||||
flag.BoolVar(&conf.Password, "password", false, "print current password")
|
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Init() bool {
|
func Init() bool {
|
||||||
bootstrap.InitLog()
|
//bootstrap.InitLog()
|
||||||
bootstrap.InitConf()
|
bootstrap.InitConf()
|
||||||
bootstrap.InitCron()
|
bootstrap.InitCron()
|
||||||
bootstrap.InitModel()
|
bootstrap.InitModel()
|
||||||
|
@ -2,7 +2,7 @@ package bootstrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -15,7 +15,7 @@ func InitAccounts() {
|
|||||||
}
|
}
|
||||||
for i, account := range accounts {
|
for i, account := range accounts {
|
||||||
model.RegisterAccount(account)
|
model.RegisterAccount(account)
|
||||||
driver, ok := drivers.GetDriver(account.Type)
|
driver, ok := base.GetDriver(account.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Errorf("no [%s] driver", account.Type)
|
log.Errorf("no [%s] driver", account.Type)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -19,4 +20,13 @@ func InitLog() {
|
|||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
})
|
})
|
||||||
log.Infof("init log...")
|
log.Infof("init log...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&conf.ConfigFile, "conf", "data/config.json", "config file")
|
||||||
|
flag.BoolVar(&conf.Debug, "debug", false, "start with debug mode")
|
||||||
|
flag.BoolVar(&conf.Version, "version", false, "print version info")
|
||||||
|
flag.BoolVar(&conf.Password, "password", false, "print current password")
|
||||||
|
flag.Parse()
|
||||||
|
InitLog()
|
||||||
}
|
}
|
@ -15,9 +15,13 @@ func InitSettings() {
|
|||||||
Description: "version",
|
Description: "version",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Group: model.CONST,
|
Group: model.CONST,
|
||||||
|
Version: conf.GitTag,
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = model.SaveSetting(version)
|
err := model.SaveSetting(version)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed write setting: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
settings := []model.SettingItem{
|
settings := []model.SettingItem{
|
||||||
{
|
{
|
||||||
@ -36,14 +40,14 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "logo",
|
Key: "logo",
|
||||||
Value: "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png",
|
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
|
||||||
Description: "logo",
|
Description: "logo",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Group: model.PUBLIC,
|
Group: model.PUBLIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "favicon",
|
Key: "favicon",
|
||||||
Value: "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png",
|
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
|
||||||
Description: "favicon",
|
Description: "favicon",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Group: model.PUBLIC,
|
Group: model.PUBLIC,
|
||||||
@ -114,17 +118,34 @@ func InitSettings() {
|
|||||||
Group: model.PRIVATE,
|
Group: model.PRIVATE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "customize style",
|
Key: "customize head",
|
||||||
Value: "",
|
Value: `<style>
|
||||||
|
.chakra-ui-light{
|
||||||
|
background-image: linear-gradient(120deg,#e0c3fc 0%,#8ec5fc 100%) !important;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
.main-box {
|
||||||
|
border-radius: 15px !important;
|
||||||
|
}
|
||||||
|
.chakra-ui-light .main-box {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
.chakra-ui-light .readme-box {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
.readme-box {
|
||||||
|
border-radius: 15px !important;
|
||||||
|
}
|
||||||
|
</style>`,
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Description: "customize style, don't need add <style></style>",
|
Description: "Customize head, placed at the beginning of the head",
|
||||||
Group: model.PRIVATE,
|
Group: model.PRIVATE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "customize script",
|
Key: "customize body",
|
||||||
Value: "",
|
Value: "",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Description: "customize script, don't need add <script></script>",
|
Description: "Customize script, placed at the end of the body",
|
||||||
Group: model.PRIVATE,
|
Group: model.PRIVATE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -156,10 +177,22 @@ func InitSettings() {
|
|||||||
Group: model.PRIVATE,
|
Group: model.PRIVATE,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, v := range settings {
|
for i, _ := range settings {
|
||||||
_, err := model.GetSettingByKey(v.Key)
|
v := settings[i]
|
||||||
if err == gorm.ErrRecordNotFound {
|
v.Version = conf.GitTag
|
||||||
err = model.SaveSetting(v)
|
o, err := model.GetSettingByKey(v.Key)
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
err = model.SaveSetting(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed write setting: %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Fatal("can't get setting: %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
o.Version = conf.GitTag
|
||||||
|
err = model.SaveSetting(*o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed write setting: %s", err.Error())
|
log.Fatalf("failed write setting: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
10
conf/var.go
10
conf/var.go
@ -12,7 +12,7 @@ var (
|
|||||||
GoVersion string
|
GoVersion string
|
||||||
GitAuthor string
|
GitAuthor string
|
||||||
GitCommit string
|
GitCommit string
|
||||||
GitTag string
|
GitTag string = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -31,7 +31,7 @@ var (
|
|||||||
var (
|
var (
|
||||||
TextTypes = []string{"txt", "go", "md"}
|
TextTypes = []string{"txt", "go", "md"}
|
||||||
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
|
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
|
||||||
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb"}
|
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm"}
|
||||||
AudioTypes = []string{"mp3", "flac", "ogg", "m4a"}
|
AudioTypes = []string{"mp3", "flac", "ogg", "m4a"}
|
||||||
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg"}
|
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg"}
|
||||||
)
|
)
|
||||||
@ -41,11 +41,9 @@ var (
|
|||||||
RawIndexHtml string
|
RawIndexHtml string
|
||||||
IndexHtml string
|
IndexHtml string
|
||||||
CheckParent bool
|
CheckParent bool
|
||||||
//CustomizeStyle string
|
CheckDown bool
|
||||||
//CustomizeScript string
|
|
||||||
//Favicon string
|
|
||||||
CheckDown bool
|
|
||||||
|
|
||||||
|
Token string
|
||||||
DavUsername string
|
DavUsername string
|
||||||
DavPassword string
|
DavPassword string
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package _23
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -52,7 +53,7 @@ func (driver Pan123) Login(account *model.Account) error {
|
|||||||
var resp Pan123TokenResp
|
var resp Pan123TokenResp
|
||||||
_, err := pan123Client.R().
|
_, err := pan123Client.R().
|
||||||
SetResult(&resp).
|
SetResult(&resp).
|
||||||
SetBody(Json{
|
SetBody(base.Json{
|
||||||
"passport": account.Username,
|
"passport": account.Username,
|
||||||
"password": account.Password,
|
"password": account.Password,
|
||||||
}).Post("https://www.123pan.com/api/user/sign_in")
|
}).Post("https://www.123pan.com/api/user/sign_in")
|
||||||
@ -128,21 +129,21 @@ func (driver Pan123) GetFile(path string, account *model.Account) (*Pan123File,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
parentFiles_, _ := base.GetCache(dir, account)
|
||||||
parentFiles, _ := parentFiles_.([]Pan123File)
|
parentFiles, _ := parentFiles_.([]Pan123File)
|
||||||
for _, file := range parentFiles {
|
for _, file := range parentFiles {
|
||||||
if file.FileName == name {
|
if file.FileName == name {
|
||||||
if file.Type != conf.FOLDER {
|
if file.Type != conf.FOLDER {
|
||||||
return &file, err
|
return &file, err
|
||||||
} else {
|
} else {
|
||||||
return nil, NotFile
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&Pan123{})
|
base.RegisterDriver(&Pan123{})
|
||||||
pan123Client.SetRetryCount(3)
|
pan123Client.SetRetryCount(3)
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package _23
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -11,48 +12,48 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Pan123 struct {}
|
type Pan123 struct{}
|
||||||
|
|
||||||
func (driver Pan123) Config() DriverConfig {
|
func (driver Pan123) Config() base.DriverConfig {
|
||||||
return DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "123Pan",
|
Name: "123Pan",
|
||||||
OnlyProxy: false,
|
OnlyProxy: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Items() []Item {
|
func (driver Pan123) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
Label: "username",
|
Label: "username",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "account username/phone number",
|
Description: "account username/phone number",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Label: "password",
|
Label: "password",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "account password",
|
Description: "account password",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder file_id",
|
Label: "root folder file_id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_by",
|
Name: "order_by",
|
||||||
Label: "order_by",
|
Label: "order_by",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "name,fileId,updateAt,createAt",
|
Values: "name,fileId,updateAt,createAt",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
Label: "order_direction",
|
Label: "order_direction",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "asc,desc",
|
Values: "asc,desc",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
@ -89,13 +90,13 @@ func (driver Pan123) File(path string, account *model.Account) (*model.File, err
|
|||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Pan123) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
path = utils.ParsePath(path)
|
path = utils.ParsePath(path)
|
||||||
var rawFiles []Pan123File
|
var rawFiles []Pan123File
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
cache, err := base.GetCache(path, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawFiles, _ = cache.([]Pan123File)
|
rawFiles, _ = cache.([]Pan123File)
|
||||||
} else {
|
} else {
|
||||||
@ -108,7 +109,7 @@ func (driver Pan123) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(rawFiles) > 0 {
|
if len(rawFiles) > 0 {
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
_ = base.SetCache(path, rawFiles, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
@ -118,14 +119,14 @@ func (driver Pan123) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Link(path string, account *model.Account) (string, error) {
|
func (driver Pan123) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
file, err := driver.GetFile(utils.ParsePath(path), account)
|
file, err := driver.GetFile(utils.ParsePath(path), account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
var resp Pan123DownResp
|
var resp Pan123DownResp
|
||||||
_, err = pan123Client.R().SetResult(&resp).SetHeader("authorization", "Bearer "+account.AccessToken).
|
_, err = pan123Client.R().SetResult(&resp).SetHeader("authorization", "Bearer "+account.AccessToken).
|
||||||
SetBody(Json{
|
SetBody(base.Json{
|
||||||
"driveId": 0,
|
"driveId": 0,
|
||||||
"etag": file.Etag,
|
"etag": file.Etag,
|
||||||
"fileId": file.FileId,
|
"fileId": file.FileId,
|
||||||
@ -135,32 +136,35 @@ func (driver Pan123) Link(path string, account *model.Account) (string, error) {
|
|||||||
"type": file.Type,
|
"type": file.Type,
|
||||||
}).Post("https://www.123pan.com/api/file/download_info")
|
}).Post("https://www.123pan.com/api/file/download_info")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if resp.Code != 0 {
|
if resp.Code != 0 {
|
||||||
if resp.Code == 401 {
|
if resp.Code == 401 {
|
||||||
err := driver.Login(account)
|
err := driver.Login(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return driver.Link(path, account)
|
return driver.Link(path, account)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf(resp.Message)
|
return nil, fmt.Errorf(resp.Message)
|
||||||
}
|
}
|
||||||
u,err := url.Parse(resp.Data.DownloadUrl)
|
u, err := url.Parse(resp.Data.DownloadUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
u_ := fmt.Sprintf("https://%s%s",u.Host,u.Path)
|
u_ := fmt.Sprintf("https://%s%s", u.Host, u.Path)
|
||||||
res, err := NoRedirectClient.R().SetQueryParamsFromValues(u.Query()).Get(u_)
|
res, err := base.NoRedirectClient.R().SetQueryParamsFromValues(u.Query()).Get(u_)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debug(res.String())
|
log.Debug(res.String())
|
||||||
|
link := base.Link{}
|
||||||
if res.StatusCode() == 302 {
|
if res.StatusCode() == 302 {
|
||||||
return res.Header().Get("location"), nil
|
link.Url = res.Header().Get("location")
|
||||||
|
}else {
|
||||||
|
link.Url = resp.Data.DownloadUrl
|
||||||
}
|
}
|
||||||
return resp.Data.DownloadUrl, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver Pan123) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -170,8 +174,12 @@ func (driver Pan123) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if file.Type != conf.FOLDER {
|
if !file.IsDir() {
|
||||||
file.Url, _ = driver.Link(path, account)
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
file.Url = link.Url
|
||||||
return file, nil, nil
|
return file, nil, nil
|
||||||
}
|
}
|
||||||
files, err := driver.Files(path, account)
|
files, err := driver.Files(path, account)
|
||||||
@ -186,7 +194,27 @@ func (driver Pan123) Proxy(c *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*Pan123)(nil)
|
func (driver Pan123) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Pan123) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Pan123)(nil)
|
@ -1,4 +1,4 @@
|
|||||||
package drivers
|
package _89
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -51,7 +52,7 @@ func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
|
|||||||
//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) {
|
//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) {
|
||||||
// dir, name := filepath.Split(path)
|
// dir, name := filepath.Split(path)
|
||||||
// dir = utils.ParsePath(dir)
|
// dir = utils.ParsePath(dir)
|
||||||
// _, _, err := c.Path(dir, account)
|
// _, _, err := c.ParentPath(dir, account)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return nil, err
|
// return nil, err
|
||||||
// }
|
// }
|
||||||
@ -62,11 +63,11 @@ func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
|
|||||||
// if file.Size != -1 {
|
// if file.Size != -1 {
|
||||||
// return &file, err
|
// return &file, err
|
||||||
// } else {
|
// } else {
|
||||||
// return nil, NotFile
|
// return nil, ErrNotFile
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// return nil, PathNotFound
|
// return nil, ErrPathNotFound
|
||||||
//}
|
//}
|
||||||
|
|
||||||
type Cloud189Down struct {
|
type Cloud189Down struct {
|
||||||
@ -312,6 +313,6 @@ func b64tohex(a string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&Cloud189{})
|
base.RegisterDriver(&Cloud189{})
|
||||||
client189Map = make(map[string]*resty.Client, 0)
|
client189Map = make(map[string]*resty.Client, 0)
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package _89
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -12,46 +13,46 @@ import (
|
|||||||
|
|
||||||
type Cloud189 struct {}
|
type Cloud189 struct {}
|
||||||
|
|
||||||
func (driver Cloud189) Config() DriverConfig {
|
func (driver Cloud189) Config() base.DriverConfig {
|
||||||
return DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "189Cloud",
|
Name: "189Cloud",
|
||||||
OnlyProxy: false,
|
OnlyProxy: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Items() []Item {
|
func (driver Cloud189) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
Label: "username",
|
Label: "username",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "account username/phone number",
|
Description: "account username/phone number",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Label: "password",
|
Label: "password",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "account password",
|
Description: "account password",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder file_id",
|
Label: "root folder file_id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_by",
|
Name: "order_by",
|
||||||
Label: "order_by",
|
Label: "order_by",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "name,size,lastOpTime,createdDate",
|
Values: "name,size,lastOpTime,createdDate",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
Label: "desc",
|
Label: "desc",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "true,false",
|
Values: "true,false",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
@ -97,13 +98,13 @@ func (driver Cloud189) File(path string, account *model.Account) (*model.File, e
|
|||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Cloud189) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
path = utils.ParsePath(path)
|
path = utils.ParsePath(path)
|
||||||
var rawFiles []Cloud189File
|
var rawFiles []Cloud189File
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
cache, err := base.GetCache(path, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawFiles, _ = cache.([]Cloud189File)
|
rawFiles, _ = cache.([]Cloud189File)
|
||||||
} else {
|
} else {
|
||||||
@ -116,7 +117,7 @@ func (driver Cloud189) Files(path string, account *model.Account) ([]model.File,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(rawFiles) > 0 {
|
if len(rawFiles) > 0 {
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
_ = base.SetCache(path, rawFiles, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
@ -126,17 +127,17 @@ func (driver Cloud189) Files(path string, account *model.Account) ([]model.File,
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Link(path string, account *model.Account) (string, error) {
|
func (driver Cloud189) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
file, err := driver.File(utils.ParsePath(path), account)
|
file, err := driver.File(utils.ParsePath(path), account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if file.Type == conf.FOLDER {
|
if file.Type == conf.FOLDER {
|
||||||
return "", NotFile
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
client, ok := client189Map[account.Name]
|
client, ok := client189Map[account.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("can't find [%s] client", account.Name)
|
return nil, fmt.Errorf("can't find [%s] client", account.Name)
|
||||||
}
|
}
|
||||||
var e Cloud189Error
|
var e Cloud189Error
|
||||||
var resp Cloud189Down
|
var resp Cloud189Down
|
||||||
@ -147,28 +148,31 @@ func (driver Cloud189) Link(path string, account *model.Account) (string, error)
|
|||||||
"fileId": file.Id,
|
"fileId": file.Id,
|
||||||
}).Get("https://cloud.189.cn/api/open/file/getFileDownloadUrl.action")
|
}).Get("https://cloud.189.cn/api/open/file/getFileDownloadUrl.action")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.ErrorCode != "" {
|
if e.ErrorCode != "" {
|
||||||
if e.ErrorCode == "InvalidSessionKey" {
|
if e.ErrorCode == "InvalidSessionKey" {
|
||||||
err = driver.Login(account)
|
err = driver.Login(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return driver.Link(path, account)
|
return driver.Link(path, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resp.ResCode != 0 {
|
if resp.ResCode != 0 {
|
||||||
return "", fmt.Errorf(resp.ResMessage)
|
return nil, fmt.Errorf(resp.ResMessage)
|
||||||
}
|
}
|
||||||
res, err := NoRedirectClient.R().Get(resp.FileDownloadUrl)
|
res, err := base.NoRedirectClient.R().Get(resp.FileDownloadUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
link := base.Link{}
|
||||||
if res.StatusCode() == 302 {
|
if res.StatusCode() == 302 {
|
||||||
return res.Header().Get("location"), nil
|
link.Url = res.Header().Get("location")
|
||||||
|
}else {
|
||||||
|
link.Url = resp.FileDownloadUrl
|
||||||
}
|
}
|
||||||
return resp.FileDownloadUrl, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -178,8 +182,12 @@ func (driver Cloud189) Path(path string, account *model.Account) (*model.File, [
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if file.Type != conf.FOLDER {
|
if !file.IsDir() {
|
||||||
file.Url, _ = driver.Link(path, account)
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
file.Url = link.Url
|
||||||
return file, nil, nil
|
return file, nil, nil
|
||||||
}
|
}
|
||||||
files, err := driver.Files(path, account)
|
files, err := driver.Files(path, account)
|
||||||
@ -194,7 +202,28 @@ func (driver Cloud189) Proxy(ctx *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*Cloud189)(nil)
|
|
||||||
|
func (driver Cloud189) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Cloud189)(nil)
|
@ -1,250 +0,0 @@
|
|||||||
package drivers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
|
||||||
"github.com/Xhofe/alist/model"
|
|
||||||
"github.com/Xhofe/alist/utils"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AliDrive struct{}
|
|
||||||
|
|
||||||
func (driver AliDrive) Config() DriverConfig {
|
|
||||||
return DriverConfig{
|
|
||||||
Name: "AliDrive",
|
|
||||||
OnlyProxy: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Items() []Item {
|
|
||||||
return []Item{
|
|
||||||
{
|
|
||||||
Name: "order_by",
|
|
||||||
Label: "order_by",
|
|
||||||
Type: "select",
|
|
||||||
Values: "name,size,updated_at,created_at",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "order_direction",
|
|
||||||
Label: "order_direction",
|
|
||||||
Type: "select",
|
|
||||||
Values: "ASC,DESC",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "refresh_token",
|
|
||||||
Label: "refresh token",
|
|
||||||
Type: "string",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "root_folder",
|
|
||||||
Label: "root folder file_id",
|
|
||||||
Type: "string",
|
|
||||||
Required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "limit",
|
|
||||||
Label: "limit",
|
|
||||||
Type: "number",
|
|
||||||
Required: false,
|
|
||||||
Description: ">0 and <=200",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
|
|
||||||
if old != nil {
|
|
||||||
conf.Cron.Remove(cron.EntryID(old.CronId))
|
|
||||||
}
|
|
||||||
if account.RootFolder == "" {
|
|
||||||
account.RootFolder = "root"
|
|
||||||
}
|
|
||||||
if account.Limit == 0 {
|
|
||||||
account.Limit = 200
|
|
||||||
}
|
|
||||||
err := driver.RefreshToken(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var resp Json
|
|
||||||
_, _ = aliClient.R().SetResult(&resp).
|
|
||||||
SetBody("{}").
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
Post("https://api.aliyundrive.com/v2/user/get")
|
|
||||||
log.Debugf("user info: %+v", resp)
|
|
||||||
account.DriveId = resp["default_drive_id"].(string)
|
|
||||||
cronId, err := conf.Cron.AddFunc("@every 2h", func() {
|
|
||||||
name := account.Name
|
|
||||||
log.Debugf("ali account name: %s", name)
|
|
||||||
newAccount, ok := model.GetAccount(name)
|
|
||||||
log.Debugf("ali account: %+v", newAccount)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = driver.RefreshToken(&newAccount)
|
|
||||||
_ = model.SaveAccount(&newAccount)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
account.CronId = int(cronId)
|
|
||||||
err = model.SaveAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) File(path string, account *model.Account) (*model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
if path == "/" {
|
|
||||||
return &model.File{
|
|
||||||
Id: account.RootFolder,
|
|
||||||
Name: account.Name,
|
|
||||||
Size: 0,
|
|
||||||
Type: conf.FOLDER,
|
|
||||||
Driver: driver.Config().Name,
|
|
||||||
UpdatedAt: account.UpdatedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
dir, name := filepath.Split(path)
|
|
||||||
files, err := driver.Files(dir, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, file := range files {
|
|
||||||
if file.Name == name {
|
|
||||||
return &file, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, PathNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
var rawFiles []AliFile
|
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
|
||||||
if err == nil {
|
|
||||||
rawFiles, _ = cache.([]AliFile)
|
|
||||||
} else {
|
|
||||||
file, err := driver.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawFiles, err = driver.GetFiles(file.Id, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(rawFiles) > 0 {
|
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files := make([]model.File, 0)
|
|
||||||
for _, file := range rawFiles {
|
|
||||||
files = append(files, *driver.FormatFile(&file))
|
|
||||||
}
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Link(path string, account *model.Account) (string, error) {
|
|
||||||
file, err := driver.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var resp Json
|
|
||||||
var e AliRespError
|
|
||||||
_, err = aliClient.R().SetResult(&resp).
|
|
||||||
SetError(&e).
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
SetBody(Json{
|
|
||||||
"drive_id": account.DriveId,
|
|
||||||
"file_id": file.Id,
|
|
||||||
"expire_sec": 14400,
|
|
||||||
}).Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if e.Code != "" {
|
|
||||||
if e.Code == "AccessTokenInvalid" {
|
|
||||||
err = driver.RefreshToken(account)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
} else {
|
|
||||||
_ = model.SaveAccount(account)
|
|
||||||
return driver.Link(path, account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("%s", e.Message)
|
|
||||||
}
|
|
||||||
return resp["url"].(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
|
||||||
path = utils.ParsePath(path)
|
|
||||||
log.Debugf("ali path: %s", path)
|
|
||||||
file, err := driver.File(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if file.Type != conf.FOLDER {
|
|
||||||
file.Url, _ = driver.Link(path, account)
|
|
||||||
return file, nil, nil
|
|
||||||
}
|
|
||||||
files, err := driver.Files(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return nil, files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) {
|
|
||||||
c.Request.Header.Del("Origin")
|
|
||||||
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
|
||||||
file, err := driver.GetFile(path, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// office
|
|
||||||
var resp Json
|
|
||||||
var e AliRespError
|
|
||||||
var url string
|
|
||||||
req := Json{
|
|
||||||
"drive_id": account.DriveId,
|
|
||||||
"file_id": file.FileId,
|
|
||||||
}
|
|
||||||
switch file.Category {
|
|
||||||
case "doc":
|
|
||||||
{
|
|
||||||
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
|
||||||
req["access_token"] = account.AccessToken
|
|
||||||
}
|
|
||||||
case "video":
|
|
||||||
{
|
|
||||||
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
|
||||||
req["category"] = "live_transcoding"
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, NotSupport
|
|
||||||
}
|
|
||||||
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
|
||||||
SetBody(req).Post(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if e.Code != "" {
|
|
||||||
return nil, fmt.Errorf("%s", e.Message)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Driver = (*AliDrive)(nil)
|
|
@ -1,13 +1,16 @@
|
|||||||
package drivers
|
package alidrive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,7 +78,7 @@ func (driver AliDrive) GetFiles(fileId string, account *model.Account) ([]AliFil
|
|||||||
SetResult(&resp).
|
SetResult(&resp).
|
||||||
SetError(&e).
|
SetError(&e).
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
SetBody(Json{
|
SetBody(base.Json{
|
||||||
"drive_id": account.DriveId,
|
"drive_id": account.DriveId,
|
||||||
"fields": "*",
|
"fields": "*",
|
||||||
"image_thumbnail_process": "image/resize,w_400/format,jpeg",
|
"image_thumbnail_process": "image/resize,w_400/format,jpeg",
|
||||||
@ -116,7 +119,7 @@ func (driver AliDrive) GetFile(path string, account *model.Account) (*AliFile, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir))
|
parentFiles_, _ := base.GetCache(dir, account)
|
||||||
parentFiles, _ := parentFiles_.([]AliFile)
|
parentFiles, _ := parentFiles_.([]AliFile)
|
||||||
for _, file := range parentFiles {
|
for _, file := range parentFiles {
|
||||||
if file.Name == name {
|
if file.Name == name {
|
||||||
@ -127,16 +130,16 @@ func (driver AliDrive) GetFile(path string, account *model.Account) (*AliFile, e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver AliDrive) RefreshToken(account *model.Account) error {
|
func (driver AliDrive) RefreshToken(account *model.Account) error {
|
||||||
url := "https://auth.aliyundrive.com/v2/account/token"
|
url := "https://auth.aliyundrive.com/v2/account/token"
|
||||||
var resp TokenResp
|
var resp base.TokenResp
|
||||||
var e AliRespError
|
var e AliRespError
|
||||||
_, err := aliClient.R().
|
_, err := aliClient.R().
|
||||||
//ForceContentType("application/json").
|
//ForceContentType("application/json").
|
||||||
SetBody(Json{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}).
|
SetBody(base.Json{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}).
|
||||||
SetResult(&resp).
|
SetResult(&resp).
|
||||||
SetError(&e).
|
SetError(&e).
|
||||||
Post(url)
|
Post(url)
|
||||||
@ -155,11 +158,86 @@ func (driver AliDrive) RefreshToken(account *model.Account) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Rename(fileId, name string, account *model.Account) error {
|
||||||
|
var resp base.Json
|
||||||
|
var e AliRespError
|
||||||
|
_, err := aliClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"check_name_mode": "refuse",
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": fileId,
|
||||||
|
"name": name,
|
||||||
|
}).Post("https://api.aliyundrive.com/v3/file/update")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Rename(fileId, name, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
if resp["name"] == name {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%+v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Batch(srcId,dstId string, account *model.Account) error {
|
||||||
|
var e AliRespError
|
||||||
|
res, err := aliClient.R().SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"requests": []base.Json{
|
||||||
|
{
|
||||||
|
"headers": base.Json{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
"method":"POST",
|
||||||
|
"id":srcId,
|
||||||
|
"body":base.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id":srcId,
|
||||||
|
"to_drive_id":account.DriveId,
|
||||||
|
"to_parent_file_id":dstId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"resource": "file",
|
||||||
|
}).Post("https://api.aliyundrive.com/v3/batch")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Batch(srcId, dstId, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
if strings.Contains(res.String(), `"status":200`) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(res.String())
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&AliDrive{})
|
base.RegisterDriver(&AliDrive{})
|
||||||
aliClient.
|
aliClient.
|
||||||
SetRetryCount(3).
|
SetRetryCount(3).
|
||||||
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
||||||
SetHeader("content-type", "application/json").
|
SetHeader("content-type", "application/json").
|
||||||
SetHeader("origin", "https://aliyundrive.com")
|
SetHeader("origin", "https://www.aliyundrive.com")
|
||||||
}
|
}
|
479
drivers/alidrive/driver.go
Normal file
479
drivers/alidrive/driver.go
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
package alidrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliDrive struct{}
|
||||||
|
|
||||||
|
func (driver AliDrive) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "AliDrive",
|
||||||
|
OnlyProxy: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "name,size,updated_at,created_at",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "ASC,DESC",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "refresh_token",
|
||||||
|
Label: "refresh token",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder file_id",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "limit",
|
||||||
|
Label: "limit",
|
||||||
|
Type: base.TypeNumber,
|
||||||
|
Required: false,
|
||||||
|
Description: ">0 and <=200",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if old != nil {
|
||||||
|
conf.Cron.Remove(cron.EntryID(old.CronId))
|
||||||
|
}
|
||||||
|
if account.RootFolder == "" {
|
||||||
|
account.RootFolder = "root"
|
||||||
|
}
|
||||||
|
if account.Limit == 0 {
|
||||||
|
account.Limit = 200
|
||||||
|
}
|
||||||
|
err := driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var resp base.Json
|
||||||
|
_, _ = aliClient.R().SetResult(&resp).
|
||||||
|
SetBody("{}").
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
Post("https://api.aliyundrive.com/v2/user/get")
|
||||||
|
log.Debugf("user info: %+v", resp)
|
||||||
|
account.DriveId = resp["default_drive_id"].(string)
|
||||||
|
cronId, err := conf.Cron.AddFunc("@every 2h", func() {
|
||||||
|
name := account.Name
|
||||||
|
log.Debugf("ali account name: %s", name)
|
||||||
|
newAccount, ok := model.GetAccount(name)
|
||||||
|
log.Debugf("ali account: %+v", newAccount)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = driver.RefreshToken(&newAccount)
|
||||||
|
_ = model.SaveAccount(&newAccount)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
account.CronId = int(cronId)
|
||||||
|
err = model.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: account.RootFolder,
|
||||||
|
Name: account.Name,
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
files, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var rawFiles []AliFile
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
rawFiles, _ = cache.([]AliFile)
|
||||||
|
} else {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rawFiles, err = driver.GetFiles(file.Id, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rawFiles) > 0 {
|
||||||
|
_ = base.SetCache(path, rawFiles, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
for _, file := range rawFiles {
|
||||||
|
files = append(files, *driver.FormatFile(&file))
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp base.Json
|
||||||
|
var e AliRespError
|
||||||
|
_, err = aliClient.R().SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": file.Id,
|
||||||
|
"expire_sec": 14400,
|
||||||
|
}).Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Link(path, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
return &base.Link{
|
||||||
|
Url: resp["url"].(string),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("ali path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
file.Url = link.Url
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) {
|
||||||
|
c.Request.Header.Del("Origin")
|
||||||
|
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
file, err := driver.GetFile(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// office
|
||||||
|
var resp base.Json
|
||||||
|
var e AliRespError
|
||||||
|
var url string
|
||||||
|
req := base.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": file.FileId,
|
||||||
|
}
|
||||||
|
switch file.Category {
|
||||||
|
case "doc":
|
||||||
|
{
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
||||||
|
req["access_token"] = account.AccessToken
|
||||||
|
}
|
||||||
|
case "video":
|
||||||
|
{
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
||||||
|
req["category"] = "live_transcoding"
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(req).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
return nil, fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) MakeDir(path string, account *model.Account) error {
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
parentFile, err := driver.File(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !parentFile.IsDir() {
|
||||||
|
return base.ErrNotFolder
|
||||||
|
}
|
||||||
|
var resp base.Json
|
||||||
|
var e AliRespError
|
||||||
|
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"check_name_mode": "refuse",
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"name": name,
|
||||||
|
"parent_file_id": parentFile.Id,
|
||||||
|
"type": "folder",
|
||||||
|
}).Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders")
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.MakeDir(path, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
if resp["file_name"] == name {
|
||||||
|
_ = base.DeleteCache(dir, account)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%+v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Move(src string, dst string, account *model.Account) error {
|
||||||
|
srcDir, _ := filepath.Split(src)
|
||||||
|
dstDir, dstName := filepath.Split(dst)
|
||||||
|
srcFile, err := driver.File(src, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// rename
|
||||||
|
if srcDir == dstDir {
|
||||||
|
err = driver.Rename(srcFile.Id, dstName, account)
|
||||||
|
} else {
|
||||||
|
// move
|
||||||
|
dstDirFile, err := driver.File(dstDir, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = driver.Batch(srcFile.Id, dstDirFile.Id, account)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_ = base.DeleteCache(srcDir, account)
|
||||||
|
_ = base.DeleteCache(dstDir, account)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Delete(path string, account *model.Account) error {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var e AliRespError
|
||||||
|
res, err := aliClient.R().SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": file.Id,
|
||||||
|
}).Post("https://api.aliyundrive.com/v2/recyclebin/trash")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Delete(path, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
if res.StatusCode() == 204 {
|
||||||
|
_ = base.DeleteCache(utils.Dir(path), account)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(res.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadResp struct {
|
||||||
|
FileId string `json:"file_id"`
|
||||||
|
UploadId string `json:"upload_id"`
|
||||||
|
PartInfoList []struct {
|
||||||
|
UploadUrl string `json:"upload_url"`
|
||||||
|
} `json:"part_info_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
const DEFAULT uint64 = 10485760
|
||||||
|
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||||
|
var finish uint64 = 0
|
||||||
|
parentFile, err := driver.File(file.ParentPath, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var resp UploadResp
|
||||||
|
var e AliRespError
|
||||||
|
partInfoList := make([]base.Json, 0)
|
||||||
|
var i int64
|
||||||
|
for i = 0; i < count; i++ {
|
||||||
|
partInfoList = append(partInfoList, base.Json{
|
||||||
|
"part_number": i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err = aliClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"check_name_mode": "auto_rename",
|
||||||
|
// content_hash
|
||||||
|
"content_hash_name": "none",
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"name": file.GetFileName(),
|
||||||
|
"parent_file_id": parentFile.Id,
|
||||||
|
"part_info_list": partInfoList,
|
||||||
|
//proof_code
|
||||||
|
"proof_version": "v1",
|
||||||
|
"size": file.GetSize(),
|
||||||
|
"type": "file",
|
||||||
|
}).Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders") // /v2/file/create_with_proof
|
||||||
|
//log.Debugf("%+v\n%+v", resp, e)
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return driver.Upload(file, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
var byteSize uint64
|
||||||
|
for i = 0; i < count; i++ {
|
||||||
|
byteSize = file.GetSize() - finish
|
||||||
|
if DEFAULT < byteSize {
|
||||||
|
byteSize = DEFAULT
|
||||||
|
}
|
||||||
|
log.Debugf("%d,%d", byteSize, finish)
|
||||||
|
byteData := make([]byte, byteSize)
|
||||||
|
n, err := io.ReadFull(file, byteData)
|
||||||
|
//n, err := file.Read(byteData)
|
||||||
|
//byteData, err := io.ReadAll(file)
|
||||||
|
//n := len(byteData)
|
||||||
|
log.Debug(err, n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
finish += uint64(n)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", resp.PartInfoList[i].UploadUrl, bytes.NewBuffer(byteData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("%+v", res)
|
||||||
|
//res, err := base.BaseClient.R().
|
||||||
|
// SetHeader("Content-Type","").
|
||||||
|
// SetBody(byteData).Put(resp.PartInfoList[i].UploadUrl)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//log.Debugf("put to %s : %d,%s", resp.PartInfoList[i].UploadUrl, res.StatusCode(),res.String())
|
||||||
|
}
|
||||||
|
var resp2 base.Json
|
||||||
|
_, err = aliClient.R().SetResult(&resp2).SetError(&e).
|
||||||
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"drive_id": account.DriveId,
|
||||||
|
"file_id": resp.FileId,
|
||||||
|
"upload_id": resp.UploadId,
|
||||||
|
}).Post("https://api.aliyundrive.com/v2/file/complete")
|
||||||
|
if e.Code != "" {
|
||||||
|
//if e.Code == "AccessTokenInvalid" {
|
||||||
|
// err = driver.RefreshToken(account)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// } else {
|
||||||
|
// _ = model.SaveAccount(account)
|
||||||
|
// return driver.Upload(file, account)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
return fmt.Errorf("%s", e.Message)
|
||||||
|
}
|
||||||
|
if resp2["file_id"] == resp.FileId {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%+v", resp2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*AliDrive)(nil)
|
40
drivers/alist/alist.go
Normal file
40
drivers/alist/alist.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package alist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseResp struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathResp struct {
|
||||||
|
BaseResp
|
||||||
|
Data []model.File `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreviewResp struct {
|
||||||
|
BaseResp
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *Alist) Login(account *model.Account) error {
|
||||||
|
var resp BaseResp
|
||||||
|
_, err := base.RestyClient.R().SetResult(&resp).
|
||||||
|
SetHeader("Authorization", account.AccessToken).
|
||||||
|
Get(account.SiteUrl+"/api/admin/login")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return errors.New(resp.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Alist{})
|
||||||
|
}
|
182
drivers/alist/driver.go
Normal file
182
drivers/alist/driver.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package alist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Alist struct{}
|
||||||
|
|
||||||
|
func (driver Alist) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "Alist",
|
||||||
|
OnlyProxy: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "site_url",
|
||||||
|
Label: "alist site url",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "access_token",
|
||||||
|
Label: "token",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Description: "admin token",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder path",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Save(account *model.Account, old *model.Account) error {
|
||||||
|
account.SiteUrl = strings.TrimRight(account.SiteUrl, "/")
|
||||||
|
if account.RootFolder == "" {
|
||||||
|
account.RootFolder = "/"
|
||||||
|
}
|
||||||
|
err := driver.Login(account)
|
||||||
|
if err == nil {
|
||||||
|
account.Status = "work"
|
||||||
|
} else {
|
||||||
|
account.Status = err.Error()
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
now := time.Now()
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: "root",
|
||||||
|
Name: "root",
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
_, files, err := driver.Path(utils.Dir(path), account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if files == nil {
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
name := utils.Base(path)
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
//return nil, base.ErrNotImplement
|
||||||
|
_, files, err := driver.Path(utils.Dir(path), account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if files == nil {
|
||||||
|
return nil, base.ErrNotFolder
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
name := utils.Base(path)
|
||||||
|
flag := "d"
|
||||||
|
if utils.GetFileType(filepath.Ext(path)) == conf.TEXT {
|
||||||
|
flag = "p"
|
||||||
|
}
|
||||||
|
link := base.Link{}
|
||||||
|
link.Url = fmt.Sprintf("%s/%s%s?sign=%s", account.SiteUrl, flag, path, utils.SignWithToken(name, conf.Token))
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
files := cache.([]model.File)
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
var resp PathResp
|
||||||
|
_, err = base.RestyClient.R().SetResult(&resp).
|
||||||
|
SetHeader("Authorization", account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"path": path,
|
||||||
|
}).Post(account.SiteUrl + "/api/public/path")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return nil, nil, errors.New(resp.Message)
|
||||||
|
}
|
||||||
|
if resp.Message == "file" {
|
||||||
|
return &resp.Data[0], nil, nil
|
||||||
|
}
|
||||||
|
if len(resp.Data) > 0 {
|
||||||
|
_ = base.SetCache(path, resp.Data, account)
|
||||||
|
}
|
||||||
|
return nil, resp.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Proxy(c *gin.Context, account *model.Account) {}
|
||||||
|
|
||||||
|
func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
var resp PathResp
|
||||||
|
_, err := base.RestyClient.R().SetResult(&resp).
|
||||||
|
SetHeader("Authorization", account.AccessToken).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"path": path,
|
||||||
|
}).Post(account.SiteUrl + "/api/public/preview")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return nil, errors.New(resp.Message)
|
||||||
|
}
|
||||||
|
return resp.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Alist) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Alist)(nil)
|
12
drivers/all.go
Normal file
12
drivers/all.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/Xhofe/alist/drivers/123"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/189"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/alidrive"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/alist"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/google"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/lanzou"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/native"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/onedrive"
|
||||||
|
)
|
25
drivers/base/cache.go
Normal file
25
drivers/base/cache.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func KeyCache(path string, account *model.Account) string {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
return fmt.Sprintf("%s%s", account.Name, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCache(path string, obj interface{}, account *model.Account) error {
|
||||||
|
return conf.Cache.Set(conf.Ctx, KeyCache(path, account), obj, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCache(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return conf.Cache.Get(conf.Ctx, KeyCache(path, account))
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteCache(path string, account *model.Account) error {
|
||||||
|
return conf.Cache.Delete(conf.Ctx, KeyCache(path, account))
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package drivers
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
@ -9,8 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DriverConfig struct {
|
type DriverConfig struct {
|
||||||
Name string
|
Name string
|
||||||
OnlyProxy bool
|
OnlyProxy bool
|
||||||
|
NoLink bool // 必须本机返回的
|
||||||
}
|
}
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
@ -19,16 +20,17 @@ type Driver interface {
|
|||||||
Save(account *model.Account, old *model.Account) error
|
Save(account *model.Account, old *model.Account) error
|
||||||
File(path string, account *model.Account) (*model.File, error)
|
File(path string, account *model.Account) (*model.File, error)
|
||||||
Files(path string, account *model.Account) ([]model.File, error)
|
Files(path string, account *model.Account) ([]model.File, error)
|
||||||
Link(path string, account *model.Account) (string, error)
|
Link(path string, account *model.Account) (*Link, error)
|
||||||
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
||||||
Proxy(c *gin.Context, account *model.Account)
|
Proxy(c *gin.Context, account *model.Account)
|
||||||
Preview(path string, account *model.Account) (interface{}, error)
|
Preview(path string, account *model.Account) (interface{}, error)
|
||||||
// TODO
|
// TODO
|
||||||
//Search(path string, keyword string, account *model.Account) ([]*model.File, error)
|
//Search(path string, keyword string, account *model.Account) ([]*model.File, error)
|
||||||
//MakeDir(path string, account *model.Account) error
|
MakeDir(path string, account *model.Account) error
|
||||||
//Move(src string, des string, account *model.Account) error
|
Move(src string, dst string, account *model.Account) error
|
||||||
//Delete(path string) error
|
Copy(src string, dst string, account *model.Account) error
|
||||||
//Upload(file *fs.File, path string, account *model.Account) error
|
Delete(path string, account *model.Account) error
|
||||||
|
Upload(file *model.FileStream, account *model.Account) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
@ -40,11 +42,6 @@ type Item struct {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenResp struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var driversMap = map[string]Driver{}
|
var driversMap = map[string]Driver{}
|
||||||
|
|
||||||
func RegisterDriver(driver Driver) {
|
func RegisterDriver(driver Driver) {
|
||||||
@ -64,29 +61,46 @@ func GetDrivers() map[string][]Item {
|
|||||||
res[k] = v.Items()
|
res[k] = v.Items()
|
||||||
} else {
|
} else {
|
||||||
res[k] = append([]Item{
|
res[k] = append([]Item{
|
||||||
|
//{
|
||||||
|
// Name: "allow_proxy",
|
||||||
|
// Label: "allow_proxy",
|
||||||
|
// Type: TypeBool,
|
||||||
|
// Required: true,
|
||||||
|
// Description: "allow proxy",
|
||||||
|
//},
|
||||||
{
|
{
|
||||||
Name: "proxy",
|
Name: "proxy",
|
||||||
Label: "proxy",
|
Label: "proxy",
|
||||||
Type: "bool",
|
Type: TypeBool,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "allow proxy",
|
Description: "web proxy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "webdav_proxy",
|
Name: "webdav_proxy",
|
||||||
Label: "webdav proxy",
|
Label: "webdav proxy",
|
||||||
Type: "bool",
|
Type: TypeBool,
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: "Transfer the WebDAV of this account through the server",
|
Description: "Transfer the WebDAV of this account through the server",
|
||||||
},
|
},
|
||||||
}, v.Items()...)
|
}, v.Items()...)
|
||||||
}
|
}
|
||||||
|
// 不支持给本地文件添加中转
|
||||||
|
if v.Config().Name != "Native" {
|
||||||
|
res[k] = append(res[k], Item{
|
||||||
|
Name: "proxy_url",
|
||||||
|
Label: "proxy_url",
|
||||||
|
Type: TypeString,
|
||||||
|
Required: false,
|
||||||
|
Description: "proxy url",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
type Json map[string]interface{}
|
|
||||||
|
|
||||||
var NoRedirectClient *resty.Client
|
var NoRedirectClient *resty.Client
|
||||||
|
var RestyClient = resty.New()
|
||||||
|
var HttpClient = &http.Client{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
NoRedirectClient = resty.New().SetRedirectPolicy(
|
NoRedirectClient = resty.New().SetRedirectPolicy(
|
37
drivers/base/types.go
Normal file
37
drivers/base/types.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPathNotFound = errors.New("path not found")
|
||||||
|
ErrNotFile = errors.New("not file")
|
||||||
|
ErrNotImplement = errors.New("not implement")
|
||||||
|
ErrNotSupport = errors.New("not support")
|
||||||
|
ErrNotFolder = errors.New("not a folder")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeString = "string"
|
||||||
|
TypeSelect = "select"
|
||||||
|
TypeBool = "bool"
|
||||||
|
TypeNumber = "number"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Json map[string]interface{}
|
||||||
|
|
||||||
|
type TokenResp struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Header struct{
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Headers []Header `json:"headers"`
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package google
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -12,37 +13,37 @@ import (
|
|||||||
|
|
||||||
type GoogleDrive struct{}
|
type GoogleDrive struct{}
|
||||||
|
|
||||||
func (driver GoogleDrive) Config() DriverConfig {
|
func (driver GoogleDrive) Config() base.DriverConfig {
|
||||||
return DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "GoogleDrive",
|
Name: "GoogleDrive",
|
||||||
OnlyProxy: true,
|
OnlyProxy: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Items() []Item {
|
func (driver GoogleDrive) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "client_id",
|
Name: "client_id",
|
||||||
Label: "client id",
|
Label: "client id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "client_secret",
|
Name: "client_secret",
|
||||||
Label: "client secret",
|
Label: "client secret",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "refresh_token",
|
Name: "refresh_token",
|
||||||
Label: "refresh token",
|
Label: "refresh token",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder file_id",
|
Label: "root folder file_id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -86,13 +87,13 @@ func (driver GoogleDrive) File(path string, account *model.Account) (*model.File
|
|||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
path = utils.ParsePath(path)
|
path = utils.ParsePath(path)
|
||||||
var rawFiles []GoogleFile
|
var rawFiles []GoogleFile
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
cache, err := base.GetCache(path, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawFiles, _ = cache.([]GoogleFile)
|
rawFiles, _ = cache.([]GoogleFile)
|
||||||
} else {
|
} else {
|
||||||
@ -105,7 +106,7 @@ func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.Fi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(rawFiles) > 0 {
|
if len(rawFiles) > 0 {
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
_ = base.SetCache(path, rawFiles, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
@ -115,31 +116,40 @@ func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.Fi
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Link(path string, account *model.Account) (string, error) {
|
func (driver GoogleDrive) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
file, err := driver.File(path, account)
|
file, err := driver.File(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if file.Type == conf.FOLDER {
|
if file.Type == conf.FOLDER {
|
||||||
return "", NotFile
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
link := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.Id)
|
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.Id)
|
||||||
var e GoogleError
|
var e GoogleError
|
||||||
_, _ = googleClient.R().SetError(&e).
|
_, _ = googleClient.R().SetError(&e).
|
||||||
SetHeader("Authorization", "Bearer "+account.AccessToken).
|
SetHeader("Authorization", "Bearer "+account.AccessToken).
|
||||||
Get(link)
|
Get(url)
|
||||||
if e.Error.Code != 0 {
|
if e.Error.Code != 0 {
|
||||||
if e.Error.Code == 401 {
|
if e.Error.Code == 401 {
|
||||||
err = driver.RefreshToken(account)
|
err = driver.RefreshToken(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = model.SaveAccount(account)
|
_ = model.SaveAccount(account)
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return driver.Link(path, account)
|
return driver.Link(path, account)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
||||||
}
|
}
|
||||||
return link + "&alt=media", nil
|
link := base.Link{
|
||||||
|
Url: url + "&alt=media",
|
||||||
|
Headers: []base.Header{
|
||||||
|
{
|
||||||
|
Name: "Authorization",
|
||||||
|
Value: "Bearer " + account.AccessToken,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -165,7 +175,27 @@ func (driver GoogleDrive) Proxy(c *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*GoogleDrive)(nil)
|
func (driver GoogleDrive) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver GoogleDrive) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver GoogleDrive) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver GoogleDrive) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver GoogleDrive) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*GoogleDrive)(nil)
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package google
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -20,7 +21,7 @@ type GoogleTokenError struct {
|
|||||||
|
|
||||||
func (driver GoogleDrive) RefreshToken(account *model.Account) error {
|
func (driver GoogleDrive) RefreshToken(account *model.Account) error {
|
||||||
url := "https://www.googleapis.com/oauth2/v4/token"
|
url := "https://www.googleapis.com/oauth2/v4/token"
|
||||||
var resp TokenResp
|
var resp base.TokenResp
|
||||||
var e GoogleTokenError
|
var e GoogleTokenError
|
||||||
_, err := googleClient.R().SetResult(&resp).SetError(&e).
|
_, err := googleClient.R().SetResult(&resp).SetError(&e).
|
||||||
SetFormData(map[string]string{
|
SetFormData(map[string]string{
|
||||||
@ -133,7 +134,7 @@ func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]GoogleF
|
|||||||
//func (driver GoogleDrive) GetFile(path string, account *model.Account) (*GoogleFile, error) {
|
//func (driver GoogleDrive) GetFile(path string, account *model.Account) (*GoogleFile, error) {
|
||||||
// dir, name := filepath.Split(path)
|
// dir, name := filepath.Split(path)
|
||||||
// dir = utils.ParsePath(dir)
|
// dir = utils.ParsePath(dir)
|
||||||
// _, _, err := driver.Path(dir, account)
|
// _, _, err := driver.ParentPath(dir, account)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return nil, err
|
// return nil, err
|
||||||
// }
|
// }
|
||||||
@ -144,14 +145,14 @@ func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]GoogleF
|
|||||||
// if !driver.IsDir(file.MimeType) {
|
// if !driver.IsDir(file.MimeType) {
|
||||||
// return &file, err
|
// return &file, err
|
||||||
// } else {
|
// } else {
|
||||||
// return nil, drivers.NotFile
|
// return nil, drivers.ErrNotFile
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// return nil, drivers.PathNotFound
|
// return nil, drivers.ErrPathNotFound
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&GoogleDrive{})
|
base.RegisterDriver(&GoogleDrive{})
|
||||||
googleClient.SetRetryCount(3)
|
googleClient.SetRetryCount(3)
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package drivers
|
package lanzou
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -12,42 +12,42 @@ import (
|
|||||||
|
|
||||||
type Lanzou struct{}
|
type Lanzou struct{}
|
||||||
|
|
||||||
func (driver Lanzou) Config() DriverConfig {
|
func (driver Lanzou) Config() base.DriverConfig {
|
||||||
return DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "Lanzou",
|
Name: "Lanzou",
|
||||||
OnlyProxy: false,
|
OnlyProxy: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Items() []Item {
|
func (driver Lanzou) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "onedrive_type",
|
Name: "onedrive_type",
|
||||||
Label: "lanzou type",
|
Label: "lanzou type",
|
||||||
Type: SELECT,
|
Type: base.TypeSelect,
|
||||||
Required: true,
|
Required: true,
|
||||||
Values: "cookie,url",
|
Values: "cookie,url",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "access_token",
|
Name: "access_token",
|
||||||
Label: "cookie",
|
Label: "cookie",
|
||||||
Type: STRING,
|
Type: base.TypeString,
|
||||||
Description: "about 15 days valid",
|
Description: "about 15 days valid",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder file_id",
|
Label: "root folder file_id",
|
||||||
Type: STRING,
|
Type: base.TypeString,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "site_url",
|
Name: "site_url",
|
||||||
Label: "share url",
|
Label: "share url",
|
||||||
Type: STRING,
|
Type: base.TypeString,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Label: "share password",
|
Label: "share password",
|
||||||
Type: STRING,
|
Type: base.TypeString,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,13 +85,13 @@ func (driver Lanzou) File(path string, account *model.Account) (*model.File, err
|
|||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Lanzou) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
path = utils.ParsePath(path)
|
path = utils.ParsePath(path)
|
||||||
var rawFiles []LanZouFile
|
var rawFiles []LanZouFile
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
cache, err := base.GetCache(path, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rawFiles, _ = cache.([]LanZouFile)
|
rawFiles, _ = cache.([]LanZouFile)
|
||||||
} else {
|
} else {
|
||||||
@ -104,7 +104,7 @@ func (driver Lanzou) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(rawFiles) > 0 {
|
if len(rawFiles) > 0 {
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil)
|
_ = base.SetCache(path, rawFiles, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
@ -114,24 +114,27 @@ func (driver Lanzou) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Link(path string, account *model.Account) (string, error) {
|
func (driver Lanzou) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
file, err := driver.File(path, account)
|
file, err := driver.File(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("down file: %+v", file)
|
log.Debugf("down file: %+v", file)
|
||||||
downId := file.Id
|
downId := file.Id
|
||||||
if account.OnedriveType == "cookie" {
|
if account.OnedriveType == "cookie" {
|
||||||
downId, err = driver.GetDownPageId(file.Id, account)
|
downId, err = driver.GetDownPageId(file.Id, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
link, err := driver.GetLink(downId)
|
url, err := driver.GetLink(downId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return link, nil
|
link := base.Link{
|
||||||
|
Url: url,
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -141,8 +144,12 @@ func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if file.Type != conf.FOLDER {
|
if !file.IsDir() {
|
||||||
file.Url, _ = driver.Link(path, account)
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
file.Url = link.Url
|
||||||
return file, nil, nil
|
return file, nil, nil
|
||||||
}
|
}
|
||||||
files, err := driver.Files(path, account)
|
files, err := driver.Files(path, account)
|
||||||
@ -157,7 +164,27 @@ func (driver Lanzou) Proxy(c *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*Lanzou)(nil)
|
func (driver *Lanzou) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *Lanzou) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *Lanzou) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *Lanzou) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *Lanzou) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Lanzou)(nil)
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package lanzou
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -231,7 +232,7 @@ func (driver *Lanzou) GetLink(downId string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&Lanzou{})
|
base.RegisterDriver(&Lanzou{})
|
||||||
lanzouClient.
|
lanzouClient.
|
||||||
SetRetryCount(3).
|
SetRetryCount(3).
|
||||||
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
|
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
|
@ -1,5 +0,0 @@
|
|||||||
package drivers
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterDriver(&Native{})
|
|
||||||
}
|
|
@ -1,12 +1,14 @@
|
|||||||
package drivers
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -15,32 +17,33 @@ import (
|
|||||||
|
|
||||||
type Native struct{}
|
type Native struct{}
|
||||||
|
|
||||||
func (driver Native) Config() DriverConfig {
|
func (driver Native) Config() base.DriverConfig {
|
||||||
return DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "Native",
|
Name: "Native",
|
||||||
OnlyProxy: true,
|
OnlyProxy: true,
|
||||||
|
NoLink: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Items() []Item {
|
func (driver Native) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder path",
|
Label: "root folder path",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_by",
|
Name: "order_by",
|
||||||
Label: "order_by",
|
Label: "order_by",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "name,size,updated_at",
|
Values: "name,size,updated_at",
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
Label: "order_direction",
|
Label: "order_direction",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "ASC,DESC",
|
Values: "ASC,DESC",
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
@ -66,7 +69,7 @@ func (driver Native) Save(account *model.Account, old *model.Account) error {
|
|||||||
func (driver Native) File(path string, account *model.Account) (*model.File, error) {
|
func (driver Native) File(path string, account *model.Account) (*model.File, error) {
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
if !utils.Exists(fullPath) {
|
if !utils.Exists(fullPath) {
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
f, err := os.Stat(fullPath)
|
f, err := os.Stat(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,7 +93,7 @@ func (driver Native) File(path string, account *model.Account) (*model.File, err
|
|||||||
func (driver Native) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Native) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
if !utils.Exists(fullPath) {
|
if !utils.Exists(fullPath) {
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
rawFiles, err := ioutil.ReadDir(fullPath)
|
rawFiles, err := ioutil.ReadDir(fullPath)
|
||||||
@ -120,16 +123,19 @@ func (driver Native) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Link(path string, account *model.Account) (string, error) {
|
func (driver Native) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
s, err := os.Stat(fullPath)
|
s, err := os.Stat(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if s.IsDir() {
|
if s.IsDir() {
|
||||||
return "", fmt.Errorf("can't down folder")
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
return fullPath, nil
|
link := base.Link{
|
||||||
|
Url: fullPath,
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver Native) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -138,7 +144,7 @@ func (driver Native) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if file.Type != conf.FOLDER {
|
if !file.IsDir() {
|
||||||
//file.Url, _ = driver.Link(path, account)
|
//file.Url, _ = driver.Link(path, account)
|
||||||
return file, nil, nil
|
return file, nil, nil
|
||||||
}
|
}
|
||||||
@ -155,7 +161,74 @@ func (driver Native) Proxy(c *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*Native)(nil)
|
func (driver Native) MakeDir(path string, account *model.Account) error {
|
||||||
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
|
err := os.MkdirAll(fullPath, 0700)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Native) Move(src string, dst string, account *model.Account) error {
|
||||||
|
fullSrc := filepath.Join(account.RootFolder, src)
|
||||||
|
fullDst := filepath.Join(account.RootFolder, dst)
|
||||||
|
return os.Rename(fullSrc, fullDst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Native) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
fullSrc := filepath.Join(account.RootFolder, src)
|
||||||
|
fullDst := filepath.Join(account.RootFolder, dst)
|
||||||
|
srcFile, err := driver.File(src, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstFile, err := driver.File(dst, account)
|
||||||
|
if err == nil {
|
||||||
|
if !dstFile.IsDir() {
|
||||||
|
return base.ErrNotSupport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if srcFile.IsDir() {
|
||||||
|
return driver.CopyDir(fullSrc, fullDst)
|
||||||
|
}
|
||||||
|
return driver.CopyFile(fullSrc, fullDst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Native) Delete(path string, account *model.Account) error {
|
||||||
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if file.IsDir() {
|
||||||
|
return os.RemoveAll(fullPath)
|
||||||
|
}
|
||||||
|
return os.Remove(fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Native) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
|
||||||
|
_, err := driver.File(filepath.Join(file.ParentPath, file.Name), account)
|
||||||
|
if err == nil {
|
||||||
|
// TODO overwrite?
|
||||||
|
}
|
||||||
|
basePath := filepath.Dir(fullPath)
|
||||||
|
if !utils.Exists(basePath) {
|
||||||
|
err := os.MkdirAll(basePath, 0744)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, err := os.Create(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = out.Close()
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(out, file)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Native)(nil)
|
74
drivers/native/native.go
Normal file
74
drivers/native/native.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File copies a single file from src to dst
|
||||||
|
func (driver *Native) CopyFile(src, dst string) error {
|
||||||
|
var err error
|
||||||
|
var srcfd *os.File
|
||||||
|
var dstfd *os.File
|
||||||
|
var srcinfo os.FileInfo
|
||||||
|
|
||||||
|
if srcfd, err = os.Open(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer srcfd.Close()
|
||||||
|
|
||||||
|
if dstfd, err = os.Create(dst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dstfd.Close()
|
||||||
|
|
||||||
|
if _, err = io.Copy(dstfd, srcfd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if srcinfo, err = os.Stat(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chmod(dst, srcinfo.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dir copies a whole directory recursively
|
||||||
|
func (driver *Native) CopyDir(src string, dst string) error {
|
||||||
|
var err error
|
||||||
|
var fds []os.FileInfo
|
||||||
|
var srcinfo os.FileInfo
|
||||||
|
|
||||||
|
if srcinfo, err = os.Stat(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fds, err = ioutil.ReadDir(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, fd := range fds {
|
||||||
|
srcfp := path.Join(src, fd.Name())
|
||||||
|
dstfp := path.Join(dst, fd.Name())
|
||||||
|
|
||||||
|
if fd.IsDir() {
|
||||||
|
if err = driver.CopyDir(srcfp, dstfp); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = driver.CopyFile(srcfp, dstfp); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Native{})
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package onedrive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -13,19 +14,20 @@ import (
|
|||||||
|
|
||||||
type Onedrive struct{}
|
type Onedrive struct{}
|
||||||
|
|
||||||
func (driver Onedrive) Config() DriverConfig {
|
|
||||||
return DriverConfig{
|
func (driver Onedrive) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
Name: "Onedrive",
|
Name: "Onedrive",
|
||||||
OnlyProxy: false,
|
OnlyProxy: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Items() []Item {
|
func (driver Onedrive) Items() []base.Item {
|
||||||
return []Item{
|
return []base.Item{
|
||||||
{
|
{
|
||||||
Name: "zone",
|
Name: "zone",
|
||||||
Label: "zone",
|
Label: "zone",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Required: true,
|
Required: true,
|
||||||
Values: "global,cn,us,de",
|
Values: "global,cn,us,de",
|
||||||
Description: "",
|
Description: "",
|
||||||
@ -33,57 +35,57 @@ func (driver Onedrive) Items() []Item {
|
|||||||
{
|
{
|
||||||
Name: "onedrive_type",
|
Name: "onedrive_type",
|
||||||
Label: "onedrive type",
|
Label: "onedrive type",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Required: true,
|
Required: true,
|
||||||
Values: "onedrive,sharepoint",
|
Values: "onedrive,sharepoint",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "client_id",
|
Name: "client_id",
|
||||||
Label: "client id",
|
Label: "client id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "client_secret",
|
Name: "client_secret",
|
||||||
Label: "client secret",
|
Label: "client secret",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "redirect_uri",
|
Name: "redirect_uri",
|
||||||
Label: "redirect uri",
|
Label: "redirect uri",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "refresh_token",
|
Name: "refresh_token",
|
||||||
Label: "refresh token",
|
Label: "refresh token",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "site_id",
|
Name: "site_id",
|
||||||
Label: "site id",
|
Label: "site id",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder path",
|
Label: "root folder path",
|
||||||
Type: "string",
|
Type: base.TypeString,
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_by",
|
Name: "order_by",
|
||||||
Label: "order_by",
|
Label: "order_by",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "name,size,lastModifiedDateTime",
|
Values: "name,size,lastModifiedDateTime",
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
Label: "order_direction",
|
Label: "order_direction",
|
||||||
Type: "select",
|
Type: base.TypeSelect,
|
||||||
Values: "asc,desc",
|
Values: "asc,desc",
|
||||||
Required: false,
|
Required: false,
|
||||||
},
|
},
|
||||||
@ -147,12 +149,12 @@ func (driver Onedrive) File(path string, account *model.Account) (*model.File, e
|
|||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, PathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Onedrive) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
path = utils.ParsePath(path)
|
path = utils.ParsePath(path)
|
||||||
cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path))
|
cache, err := base.GetCache(path, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
files, _ := cache.([]model.File)
|
files, _ := cache.([]model.File)
|
||||||
return files, nil
|
return files, nil
|
||||||
@ -166,20 +168,23 @@ func (driver Onedrive) Files(path string, account *model.Account) ([]model.File,
|
|||||||
files = append(files, *driver.FormatFile(&file))
|
files = append(files, *driver.FormatFile(&file))
|
||||||
}
|
}
|
||||||
if len(files) > 0 {
|
if len(files) > 0 {
|
||||||
_ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), files, nil)
|
_ = base.SetCache(path, files, account)
|
||||||
}
|
}
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Link(path string, account *model.Account) (string, error) {
|
func (driver Onedrive) Link(path string, account *model.Account) (*base.Link, error) {
|
||||||
file, err := driver.GetFile(account, path)
|
file, err := driver.GetFile(account, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if file.File.MimeType == "" {
|
if file.File.MimeType == "" {
|
||||||
return "", fmt.Errorf("can't down folder")
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
return file.Url, nil
|
link := base.Link{
|
||||||
|
Url: file.Url,
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
func (driver Onedrive) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
@ -188,7 +193,7 @@ func (driver Onedrive) Path(path string, account *model.Account) (*model.File, [
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if file.Type != conf.FOLDER {
|
if !file.IsDir() {
|
||||||
//file.Url, _ = driver.Link(path, account)
|
//file.Url, _ = driver.Link(path, account)
|
||||||
return file, nil, nil
|
return file, nil, nil
|
||||||
}
|
}
|
||||||
@ -204,7 +209,27 @@ func (driver Onedrive) Proxy(c *gin.Context, account *model.Account) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, NotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Driver = (*Onedrive)(nil)
|
func (driver Onedrive) MakeDir(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Onedrive) Move(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Onedrive) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Onedrive) Delete(path string, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Onedrive) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
return base.ErrNotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Onedrive)(nil)
|
@ -1,8 +1,9 @@
|
|||||||
package drivers
|
package onedrive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -73,7 +74,7 @@ type OneTokenErr struct {
|
|||||||
|
|
||||||
func (driver Onedrive) RefreshToken(account *model.Account) error {
|
func (driver Onedrive) RefreshToken(account *model.Account) error {
|
||||||
url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
|
url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
|
||||||
var resp TokenResp
|
var resp base.TokenResp
|
||||||
var e OneTokenErr
|
var e OneTokenErr
|
||||||
_, err := oneClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
|
_, err := oneClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
|
||||||
"grant_type": "refresh_token",
|
"grant_type": "refresh_token",
|
||||||
@ -185,6 +186,6 @@ func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDriver(&Onedrive{})
|
base.RegisterDriver(&Onedrive{})
|
||||||
oneClient.SetRetryCount(3)
|
oneClient.SetRetryCount(3)
|
||||||
}
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package drivers
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
var (
|
|
||||||
PathNotFound = fmt.Errorf("path not found")
|
|
||||||
NotFile = fmt.Errorf("not file")
|
|
||||||
NotImplement = fmt.Errorf("not implement")
|
|
||||||
NotSupport = fmt.Errorf("not support")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
STRING = "string"
|
|
||||||
SELECT = "select"
|
|
||||||
BOOL = "bool"
|
|
||||||
)
|
|
@ -7,22 +7,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
ID uint `json:"id" gorm:"primaryKey"` // 唯一ID
|
||||||
Name string `json:"name" gorm:"unique" binding:"required"`
|
Name string `json:"name" gorm:"unique" binding:"required"` // 唯一名称
|
||||||
Index int `json:"index"`
|
Index int `json:"index"` // 序号 用于排序
|
||||||
Type string `json:"type"`
|
Type string `json:"type"` // 类型,即driver
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
RootFolder string `json:"root_folder"`
|
RootFolder string `json:"root_folder"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"` // 状态
|
||||||
CronId int
|
CronId int
|
||||||
DriveId string
|
DriveId string
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
OrderBy string `json:"order_by"`
|
OrderBy string `json:"order_by"`
|
||||||
OrderDirection string `json:"order_direction"`
|
OrderDirection string `json:"order_direction"`
|
||||||
Proxy bool `json:"proxy"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
Search bool `json:"search"`
|
Search bool `json:"search"`
|
||||||
ClientId string `json:"client_id"`
|
ClientId string `json:"client_id"`
|
||||||
@ -33,6 +32,9 @@ type Account struct {
|
|||||||
SiteId string `json:"site_id"`
|
SiteId string `json:"site_id"`
|
||||||
OnedriveType string `json:"onedrive_type"`
|
OnedriveType string `json:"onedrive_type"`
|
||||||
WebdavProxy bool `json:"webdav_proxy"`
|
WebdavProxy bool `json:"webdav_proxy"`
|
||||||
|
Proxy bool `json:"proxy"` // 是否中转
|
||||||
|
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
|
||||||
|
ProxyUrl string `json:"proxy_url"` // 用于中转下载服务的URL
|
||||||
}
|
}
|
||||||
|
|
||||||
var accountsMap = map[string]Account{}
|
var accountsMap = map[string]Account{}
|
||||||
|
35
model/file_stream.go
Normal file
35
model/file_stream.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type FileStream struct {
|
||||||
|
File io.ReadCloser
|
||||||
|
Size uint64
|
||||||
|
ParentPath string
|
||||||
|
Name string
|
||||||
|
MIMEType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) Read(p []byte) (n int, err error) {
|
||||||
|
return file.File.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) GetMIMEType() string {
|
||||||
|
return file.MIMEType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) GetSize() uint64 {
|
||||||
|
return file.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) Close() error {
|
||||||
|
return file.File.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) GetFileName() string {
|
||||||
|
return file.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileStream) GetParentPath() string {
|
||||||
|
return file.ParentPath
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ type SettingItem struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Group int `json:"group"`
|
Group int `json:"group"`
|
||||||
Values string `json:"values"`
|
Values string `json:"values"`
|
||||||
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveSettings(items []SettingItem) error {
|
func SaveSettings(items []SettingItem) error {
|
||||||
@ -44,6 +47,13 @@ func GetSettings() (*[]SettingItem, error) {
|
|||||||
return &items, nil
|
return &items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteSetting(key string) error {
|
||||||
|
setting := SettingItem{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
return conf.DB.Delete(&setting).Error
|
||||||
|
}
|
||||||
|
|
||||||
func GetSettingByKey(key string) (*SettingItem, error) {
|
func GetSettingByKey(key string) (*SettingItem, error) {
|
||||||
var items SettingItem
|
var items SettingItem
|
||||||
if err := conf.DB.Where("`key` = ?", key).First(&items).Error; err != nil {
|
if err := conf.DB.Where("`key` = ?", key).First(&items).Error; err != nil {
|
||||||
@ -61,7 +71,7 @@ func LoadSettings() {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
conf.CheckParent = checkParent.Value == "true"
|
conf.CheckParent = checkParent.Value == "true"
|
||||||
}
|
}
|
||||||
checkDown,err := GetSettingByKey("check down link")
|
checkDown, err := GetSettingByKey("check down link")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
conf.CheckDown = checkDown.Value == "true"
|
conf.CheckDown = checkDown.Value == "true"
|
||||||
}
|
}
|
||||||
@ -72,18 +82,20 @@ func LoadSettings() {
|
|||||||
}
|
}
|
||||||
title, err := GetSettingByKey("title")
|
title, err := GetSettingByKey("title")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//conf.CustomizeStyle = customizeStyle.Value
|
|
||||||
conf.IndexHtml = strings.Replace(conf.IndexHtml, "Loading...", title.Value, 1)
|
conf.IndexHtml = strings.Replace(conf.IndexHtml, "Loading...", title.Value, 1)
|
||||||
}
|
}
|
||||||
customizeStyle, err := GetSettingByKey("customize style")
|
customizeHead, err := GetSettingByKey("customize head")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//conf.CustomizeStyle = customizeStyle.Value
|
conf.IndexHtml = strings.Replace(conf.IndexHtml, "<!-- customize head -->", customizeHead.Value, 1)
|
||||||
conf.IndexHtml = strings.Replace(conf.IndexHtml, "/* customize-style */", customizeStyle.Value, 1)
|
|
||||||
}
|
}
|
||||||
customizeScript, err := GetSettingByKey("customize script")
|
customizeBody, err := GetSettingByKey("customize body")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//conf.CustomizeStyle = customizeScript.Value
|
conf.IndexHtml = strings.Replace(conf.IndexHtml, "<!-- customize body -->", customizeBody.Value, 1)
|
||||||
conf.IndexHtml = strings.Replace(conf.IndexHtml, "// customize-js", customizeScript.Value, 1)
|
}
|
||||||
|
|
||||||
|
adminPassword, err := GetSettingByKey("password")
|
||||||
|
if err == nil {
|
||||||
|
conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
davUsername, err := GetSettingByKey("WebDAV username")
|
davUsername, err := GetSettingByKey("WebDAV username")
|
||||||
|
@ -1,45 +1,17 @@
|
|||||||
package server
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Auth(c *gin.Context) {
|
|
||||||
token := c.GetHeader("Authorization")
|
|
||||||
password, err := model.GetSettingByKey("password")
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
ErrorResp(c, fmt.Errorf("password not set"), 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if token != utils.GetMD5Encode(password.Value) {
|
|
||||||
ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Login(c *gin.Context) {
|
func Login(c *gin.Context) {
|
||||||
SuccessResp(c)
|
SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckAccount(c *gin.Context) {
|
|
||||||
if model.AccountsCount() == 0 {
|
|
||||||
ErrorResp(c, fmt.Errorf("no accounts,please add one first"), 1001)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckParent(path string, password string) bool {
|
func CheckParent(path string, password string) bool {
|
||||||
meta, err := model.GetMetaByPath(path)
|
meta, err := model.GetMetaByPath(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -55,7 +27,7 @@ func CheckParent(path string, password string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckDownLink(path string, passwordMd5 string) bool {
|
func CheckDownLink(path string, passwordMd5 string, name string) bool {
|
||||||
if !conf.CheckDown {
|
if !conf.CheckDown {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -63,7 +35,7 @@ func CheckDownLink(path string, passwordMd5 string) bool {
|
|||||||
log.Debugf("check down path: %s", path)
|
log.Debugf("check down path: %s", path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugf("check down link: %s,%s", meta.Password, passwordMd5)
|
log.Debugf("check down link: %s,%s", meta.Password, passwordMd5)
|
||||||
if meta.Password != "" && utils.Get16MD5Encode(meta.Password) != passwordMd5 {
|
if meta.Password != "" && utils.SignWithPassword(name, meta.Password) != passwordMd5 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -74,6 +46,6 @@ func CheckDownLink(path string, passwordMd5 string) bool {
|
|||||||
if path == "/" {
|
if path == "/" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return CheckDownLink(utils.Dir(path), passwordMd5)
|
return CheckDownLink(utils.Dir(path), passwordMd5, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package server
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/drivers"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -15,7 +15,12 @@ type Resp struct {
|
|||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePath(rawPath string) (*model.Account, string, drivers.Driver, error) {
|
type PathReq struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
|
||||||
var path, name string
|
var path, name string
|
||||||
switch model.AccountsCount() {
|
switch model.AccountsCount() {
|
||||||
case 0:
|
case 0:
|
||||||
@ -32,7 +37,7 @@ func ParsePath(rawPath string) (*model.Account, string, drivers.Driver, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
||||||
}
|
}
|
||||||
driver, ok := drivers.GetDriver(account.Type)
|
driver, ok := base.GetDriver(account.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
|
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package server
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/drivers"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -13,52 +14,52 @@ import (
|
|||||||
func GetAccounts(c *gin.Context) {
|
func GetAccounts(c *gin.Context) {
|
||||||
accounts, err := model.GetAccounts()
|
accounts, err := model.GetAccounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c, accounts)
|
common.SuccessResp(c, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAccount(c *gin.Context) {
|
func CreateAccount(c *gin.Context) {
|
||||||
var req model.Account
|
var req model.Account
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
driver, ok := drivers.GetDriver(req.Type)
|
driver, ok := base.GetDriver(req.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
|
common.ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
req.UpdatedAt = &now
|
req.UpdatedAt = &now
|
||||||
if err := model.CreateAccount(&req); err != nil {
|
if err := model.CreateAccount(&req); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("new account: %+v", req)
|
log.Debugf("new account: %+v", req)
|
||||||
err = driver.Save(&req, nil)
|
err = driver.Save(&req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveAccount(c *gin.Context) {
|
func SaveAccount(c *gin.Context) {
|
||||||
var req model.Account
|
var req model.Account
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
driver, ok := drivers.GetDriver(req.Type)
|
driver, ok := base.GetDriver(req.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
|
common.ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
old, err := model.GetAccountById(req.ID)
|
old, err := model.GetAccountById(req.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
@ -67,15 +68,15 @@ func SaveAccount(c *gin.Context) {
|
|||||||
model.DeleteAccountFromMap(old.Name)
|
model.DeleteAccountFromMap(old.Name)
|
||||||
}
|
}
|
||||||
if err := model.SaveAccount(&req); err != nil {
|
if err := model.SaveAccount(&req); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("save account: %+v", req)
|
log.Debugf("save account: %+v", req)
|
||||||
err = driver.Save(&req, old)
|
err = driver.Save(&req, old)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +84,12 @@ func DeleteAccount(c *gin.Context) {
|
|||||||
idStr := c.Query("id")
|
idStr := c.Query("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := model.DeleteAccount(uint(id)); err != nil {
|
if err := model.DeleteAccount(uint(id)); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
@ -1,15 +1,16 @@
|
|||||||
package server
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ClearCache(c *gin.Context) {
|
func ClearCache(c *gin.Context) {
|
||||||
err := conf.Cache.Clear(conf.Ctx)
|
err := conf.Cache.Clear(conf.Ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
143
server/controllers/down.go
Normal file
143
server/controllers/down.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Down(c *gin.Context) {
|
||||||
|
rawPath := c.Param("path")
|
||||||
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
log.Debugf("down: %s", rawPath)
|
||||||
|
account, path, driver, err := common.ParsePath(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if driver.Config().OnlyProxy || account.Proxy {
|
||||||
|
Proxy(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Redirect(302, link.Url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Proxy(c *gin.Context) {
|
||||||
|
rawPath := c.Param("path")
|
||||||
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
log.Debugf("proxy: %s", rawPath)
|
||||||
|
account, path, driver, err := common.ParsePath(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 只有三种情况允许中转:
|
||||||
|
// 1. 账号开启中转
|
||||||
|
// 2. driver只能中转
|
||||||
|
// 3. 是文本类型文件
|
||||||
|
if !account.Proxy && !driver.Config().OnlyProxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("[%s] not allowed proxy", account.Name), 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 中转时有中转机器使用中转机器
|
||||||
|
if account.ProxyUrl != "" {
|
||||||
|
name := utils.Base(rawPath)
|
||||||
|
link := fmt.Sprintf("%s%s?sign=%s", account.ProxyUrl, rawPath, utils.SignWithToken(name, conf.Token))
|
||||||
|
c.Redirect(302, link)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
link, err := driver.Link(path, account)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 本机文件直接返回文件
|
||||||
|
if account.Type == "Native" {
|
||||||
|
// 对于名称为index.html的文件需要特殊处理
|
||||||
|
if utils.Base(rawPath) == "index.html" {
|
||||||
|
file, err := os.Open(link.Url)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
fileStat, err := os.Stat(link.Url)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.File(link.Url)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
|
||||||
|
Text(c, link)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
driver.Proxy(c, account)
|
||||||
|
r := c.Request
|
||||||
|
w := c.Writer
|
||||||
|
target, err := url.Parse(link.Url)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
protocol := "http://"
|
||||||
|
if strings.HasPrefix(link.Url, "https://") {
|
||||||
|
protocol = "https://"
|
||||||
|
}
|
||||||
|
targetHost, err := url.Parse(fmt.Sprintf("%s%s", protocol, target.Host))
|
||||||
|
proxy := httputil.NewSingleHostReverseProxy(targetHost)
|
||||||
|
r.URL = target
|
||||||
|
r.Host = target.Host
|
||||||
|
proxy.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var client *resty.Client
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
client = resty.New()
|
||||||
|
client.SetRetryCount(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Text(c *gin.Context, link *base.Link) {
|
||||||
|
res, err := client.R().Get(link.Url)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text := res.String()
|
||||||
|
t := utils.GetStrCoding(res.Body())
|
||||||
|
log.Debugf("text type: %s", t)
|
||||||
|
if t != utils.UTF8 {
|
||||||
|
body, err := utils.GbkToUtf8(res.Body())
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = string(body)
|
||||||
|
}
|
||||||
|
c.String(200, text)
|
||||||
|
}
|
11
server/controllers/driver.go
Normal file
11
server/controllers/driver.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDrivers(c *gin.Context) {
|
||||||
|
common.SuccessResp(c, base.GetDrivers())
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package server
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -10,37 +11,37 @@ import (
|
|||||||
func GetMetas(c *gin.Context) {
|
func GetMetas(c *gin.Context) {
|
||||||
metas,err := model.GetMetas()
|
metas,err := model.GetMetas()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c,err,500)
|
common.ErrorResp(c,err,500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c, metas)
|
common.SuccessResp(c, metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateMeta(c *gin.Context) {
|
func CreateMeta(c *gin.Context) {
|
||||||
var req model.Meta
|
var req model.Meta
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Path = utils.ParsePath(req.Path)
|
req.Path = utils.ParsePath(req.Path)
|
||||||
if err := model.CreateMeta(req); err != nil {
|
if err := model.CreateMeta(req); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveMeta(c *gin.Context) {
|
func SaveMeta(c *gin.Context) {
|
||||||
var req model.Meta
|
var req model.Meta
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Path = utils.ParsePath(req.Path)
|
req.Path = utils.ParsePath(req.Path)
|
||||||
if err := model.SaveMeta(req); err != nil {
|
if err := model.SaveMeta(req); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,13 +49,13 @@ func DeleteMeta(c *gin.Context) {
|
|||||||
idStr := c.Query("id")
|
idStr := c.Query("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//path = utils.ParsePath(path)
|
//path = utils.ParsePath(path)
|
||||||
if err := model.DeleteMeta(uint(id)); err != nil {
|
if err := model.DeleteMeta(uint(id)); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
@ -1,74 +1,59 @@
|
|||||||
package server
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PathReq struct {
|
|
||||||
Path string `json:"Path"`
|
|
||||||
Password string `json:"Password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Path(c *gin.Context) {
|
func Path(c *gin.Context) {
|
||||||
var req PathReq
|
reqV, _ := c.Get("req")
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
req := reqV.(common.PathReq)
|
||||||
ErrorResp(c, err, 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Path = utils.ParsePath(req.Path)
|
|
||||||
log.Debugf("path: %s", req.Path)
|
|
||||||
meta, err := model.GetMetaByPath(req.Path)
|
|
||||||
if err == nil {
|
|
||||||
if meta.Password != "" && meta.Password != req.Password {
|
|
||||||
ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO hide or ignore?
|
|
||||||
} else if conf.CheckParent {
|
|
||||||
if !CheckParent(utils.Dir(req.Path), req.Password) {
|
|
||||||
ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if model.AccountsCount() > 1 && req.Path == "/" {
|
if model.AccountsCount() > 1 && req.Path == "/" {
|
||||||
files, err := model.GetAccountFiles()
|
files, err := model.GetAccountFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, Resp{
|
c.JSON(200, common.Resp{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
Message: "folder",
|
Message: "folder",
|
||||||
Data: files,
|
Data: files,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
account, path, driver, err := ParsePath(req.Path)
|
account, path, driver, err := common.ParsePath(req.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file, files, err := driver.Path(path, account)
|
file, files, err := driver.Path(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if file != nil {
|
if file != nil {
|
||||||
if account.Type == "Native" {
|
// 对于中转文件或只能中转,将链接修改为中转链接
|
||||||
file.Url = fmt.Sprintf("//%s/d%s", c.Request.Host, req.Path)
|
if driver.Config().OnlyProxy || account.Proxy {
|
||||||
|
if account.ProxyUrl != "" {
|
||||||
|
file.Url = fmt.Sprintf("%s%s?sign=%s", account.ProxyUrl, req.Path, utils.SignWithToken(file.Name, conf.Token))
|
||||||
|
} else {
|
||||||
|
file.Url = fmt.Sprintf("//%s/d%s", c.Request.Host, req.Path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.JSON(200, Resp{
|
c.JSON(200, common.Resp{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
Message: "file",
|
Message: "file",
|
||||||
Data: []*model.File{file},
|
Data: []*model.File{file},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
meta, _ := model.GetMetaByPath(req.Path)
|
||||||
if meta != nil && meta.Hide != "" {
|
if meta != nil && meta.Hide != "" {
|
||||||
tmpFiles := make([]model.File, 0)
|
tmpFiles := make([]model.File, 0)
|
||||||
hideFiles := strings.Split(meta.Hide, ",")
|
hideFiles := strings.Split(meta.Hide, ",")
|
||||||
@ -79,7 +64,7 @@ func Path(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
files = tmpFiles
|
files = tmpFiles
|
||||||
}
|
}
|
||||||
c.JSON(200, Resp{
|
c.JSON(200, common.Resp{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
Message: "folder",
|
Message: "folder",
|
||||||
Data: files,
|
Data: files,
|
||||||
@ -87,56 +72,53 @@ func Path(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回真实的链接,且携带头,只提供给中转程序使用
|
||||||
func Link(c *gin.Context) {
|
func Link(c *gin.Context) {
|
||||||
var req PathReq
|
var req common.PathReq
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
req.Path = utils.ParsePath(req.Path)
|
||||||
rawPath := req.Path
|
rawPath := req.Path
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
log.Debugf("link: %s", rawPath)
|
log.Debugf("link: %s", rawPath)
|
||||||
account, path, driver, err := ParsePath(rawPath)
|
account, path, driver, err := common.ParsePath(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
link, err := driver.Link(path, account)
|
link, err := driver.Link(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if account.Type == "Native" {
|
if driver.Config().NoLink {
|
||||||
SuccessResp(c, gin.H{
|
common.SuccessResp(c, base.Link{
|
||||||
"url": fmt.Sprintf("//%s/d%s", c.Request.Host, req.Path),
|
Url: fmt.Sprintf("//%s/d%s?sign=%s", c.Request.Host, req.Path, utils.SignWithToken(utils.Base(rawPath), conf.Token)),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
SuccessResp(c, gin.H{
|
common.SuccessResp(c, link)
|
||||||
"url": link,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Preview(c *gin.Context) {
|
func Preview(c *gin.Context) {
|
||||||
var req PathReq
|
reqV, _ := c.Get("req")
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
req := reqV.(common.PathReq)
|
||||||
ErrorResp(c, err, 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rawPath := req.Path
|
rawPath := req.Path
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
log.Debugf("preview: %s", rawPath)
|
log.Debugf("preview: %s", rawPath)
|
||||||
account, path, driver, err := ParsePath(rawPath)
|
account, path, driver, err := common.ParsePath(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := driver.Preview(path, account)
|
data, err := driver.Preview(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
SuccessResp(c, data)
|
common.SuccessResp(c, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,38 +1,48 @@
|
|||||||
package server
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SaveSettings(c *gin.Context) {
|
func SaveSettings(c *gin.Context) {
|
||||||
var req []model.SettingItem
|
var req []model.SettingItem
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := model.SaveSettings(req); err != nil {
|
if err := model.SaveSettings(req); err != nil {
|
||||||
ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
model.LoadSettings()
|
model.LoadSettings()
|
||||||
SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSettings(c *gin.Context) {
|
func GetSettings(c *gin.Context) {
|
||||||
settings, err := model.GetSettings()
|
settings, err := model.GetSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c, settings)
|
common.SuccessResp(c, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSettingsPublic(c *gin.Context) {
|
func GetSettingsPublic(c *gin.Context) {
|
||||||
settings, err := model.GetSettingsPublic()
|
settings, err := model.GetSettingsPublic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SuccessResp(c, settings)
|
common.SuccessResp(c, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSetting(c *gin.Context) {
|
||||||
|
key := c.Query("key")
|
||||||
|
if err := model.DeleteSetting(key); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
}
|
}
|
124
server/down.go
124
server/down.go
@ -1,124 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
|
||||||
"github.com/Xhofe/alist/utils"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Down(c *gin.Context) {
|
|
||||||
rawPath := c.Param("path")
|
|
||||||
rawPath = utils.ParsePath(rawPath)
|
|
||||||
log.Debugf("down: %s", rawPath)
|
|
||||||
pw := c.Query("pw")
|
|
||||||
if !CheckDownLink(utils.Dir(rawPath), pw) {
|
|
||||||
ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
account, path, driver, err := ParsePath(rawPath)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if account.Type == "GoogleDrive" {
|
|
||||||
Proxy(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
link, err := driver.Link(path, account)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if account.Type == "Native" {
|
|
||||||
c.File(link)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
c.Redirect(302, link)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Proxy(c *gin.Context) {
|
|
||||||
rawPath := c.Param("path")
|
|
||||||
rawPath = utils.ParsePath(rawPath)
|
|
||||||
log.Debugf("proxy: %s", rawPath)
|
|
||||||
pw := c.Query("pw")
|
|
||||||
if !CheckDownLink(utils.Dir(rawPath), pw) {
|
|
||||||
ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
account, path, driver, err := ParsePath(rawPath)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !account.Proxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT {
|
|
||||||
ErrorResp(c, fmt.Errorf("[%s] not allowed proxy", account.Name), 403)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
link, err := driver.Link(path, account)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if account.Type == "Native" {
|
|
||||||
c.File(link)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
|
|
||||||
Text(c, link)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
driver.Proxy(c, account)
|
|
||||||
r := c.Request
|
|
||||||
w := c.Writer
|
|
||||||
target, err := url.Parse(link)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
protocol := "http://"
|
|
||||||
if strings.HasPrefix(link, "https://") {
|
|
||||||
protocol = "https://"
|
|
||||||
}
|
|
||||||
targetHost, err := url.Parse(fmt.Sprintf("%s%s", protocol, target.Host))
|
|
||||||
proxy := httputil.NewSingleHostReverseProxy(targetHost)
|
|
||||||
r.URL = target
|
|
||||||
r.Host = target.Host
|
|
||||||
proxy.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var client *resty.Client
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
client = resty.New()
|
|
||||||
client.SetRetryCount(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Text(c *gin.Context, link string) {
|
|
||||||
res, err := client.R().Get(link)
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text := res.String()
|
|
||||||
t := utils.GetStrCoding(res.Body())
|
|
||||||
log.Debugf("text type: %s", t)
|
|
||||||
if t != utils.UTF8 {
|
|
||||||
body, err := utils.GbkToUtf8(res.Body())
|
|
||||||
if err != nil {
|
|
||||||
ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text = string(body)
|
|
||||||
}
|
|
||||||
c.String(200, text)
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Xhofe/alist/drivers"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetDrivers(c *gin.Context) {
|
|
||||||
SuccessResp(c, drivers.GetDrivers())
|
|
||||||
}
|
|
16
server/middlewares/account.go
Normal file
16
server/middlewares/account.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckAccount(c *gin.Context) {
|
||||||
|
if model.AccountsCount() == 0 {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("no accounts,please add one first"), 1001)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
27
server/middlewares/auth.go
Normal file
27
server/middlewares/auth.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Auth(c *gin.Context) {
|
||||||
|
token := c.GetHeader("Authorization")
|
||||||
|
//password, err := model.GetSettingByKey("password")
|
||||||
|
//if err != nil {
|
||||||
|
// if err == gorm.ErrRecordNotFound {
|
||||||
|
// common.ErrorResp(c, fmt.Errorf("password not set"), 400)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// common.ErrorResp(c, err, 500)
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
//if token != utils.GetMD5Encode(password.Value) {
|
||||||
|
if token != conf.Token {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
27
server/middlewares/down.go
Normal file
27
server/middlewares/down.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DownCheck(c *gin.Context) {
|
||||||
|
sign := c.Query("sign")
|
||||||
|
rawPath := c.Param("path")
|
||||||
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
name := utils.Base(rawPath)
|
||||||
|
if sign == utils.SignWithToken(name, conf.Token) {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pw := c.Query("pw")
|
||||||
|
if !common.CheckDownLink(utils.Dir(rawPath), pw, utils.Base(rawPath)) {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
40
server/middlewares/path.go
Normal file
40
server/middlewares/path.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PathCheck(c *gin.Context) {
|
||||||
|
var req common.PathReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Path = utils.ParsePath(req.Path)
|
||||||
|
c.Set("req",req)
|
||||||
|
token := c.GetHeader("Authorization")
|
||||||
|
if token == conf.Token {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
meta, err := model.GetMetaByPath(req.Path)
|
||||||
|
if err == nil {
|
||||||
|
if meta.Password != "" && meta.Password != req.Password {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if conf.CheckParent {
|
||||||
|
if !common.CheckParent(utils.Dir(req.Path), req.Password) {
|
||||||
|
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/server/controllers"
|
||||||
|
"github.com/Xhofe/alist/server/middlewares"
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -9,35 +12,42 @@ func InitApiRouter(r *gin.Engine) {
|
|||||||
|
|
||||||
// TODO from settings
|
// TODO from settings
|
||||||
Cors(r)
|
Cors(r)
|
||||||
r.GET("/d/*path", Down)
|
r.GET("/d/*path", middlewares.DownCheck, controllers.Down)
|
||||||
r.GET("/p/*path", Proxy)
|
r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy)
|
||||||
|
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
public := api.Group("/public")
|
public := api.Group("/public")
|
||||||
{
|
{
|
||||||
public.POST("/path", CheckAccount, Path)
|
path := public.Group("", middlewares.PathCheck, middlewares.CheckAccount)
|
||||||
public.POST("/preview", CheckAccount, Preview)
|
path.POST("/path", controllers.Path)
|
||||||
public.GET("/settings", GetSettingsPublic)
|
path.POST("/preview", controllers.Preview)
|
||||||
public.POST("/link", CheckAccount, Link)
|
|
||||||
|
//path.POST("/link",middlewares.Auth, controllers.Link)
|
||||||
|
|
||||||
|
public.GET("/settings", controllers.GetSettingsPublic)
|
||||||
}
|
}
|
||||||
|
|
||||||
admin := api.Group("/admin")
|
admin := api.Group("/admin")
|
||||||
{
|
{
|
||||||
admin.Use(Auth)
|
admin.Use(middlewares.Auth)
|
||||||
admin.GET("/login", Login)
|
admin.GET("/login", common.Login)
|
||||||
admin.GET("/settings", GetSettings)
|
admin.GET("/settings", controllers.GetSettings)
|
||||||
admin.POST("/settings", SaveSettings)
|
admin.POST("/settings", controllers.SaveSettings)
|
||||||
admin.POST("/account/create", CreateAccount)
|
admin.DELETE("/setting", controllers.DeleteSetting)
|
||||||
admin.POST("/account/save", SaveAccount)
|
|
||||||
admin.GET("/accounts", GetAccounts)
|
|
||||||
admin.DELETE("/account", DeleteAccount)
|
|
||||||
admin.GET("/drivers", GetDrivers)
|
|
||||||
admin.GET("/clear_cache", ClearCache)
|
|
||||||
|
|
||||||
admin.GET("/metas", GetMetas)
|
admin.POST("/account/create", controllers.CreateAccount)
|
||||||
admin.POST("/meta/create", CreateMeta)
|
admin.POST("/account/save", controllers.SaveAccount)
|
||||||
admin.POST("/meta/save", SaveMeta)
|
admin.GET("/accounts", controllers.GetAccounts)
|
||||||
admin.DELETE("/meta", DeleteMeta)
|
admin.DELETE("/account", controllers.DeleteAccount)
|
||||||
|
admin.GET("/drivers", controllers.GetDrivers)
|
||||||
|
admin.GET("/clear_cache", controllers.ClearCache)
|
||||||
|
|
||||||
|
admin.GET("/metas", controllers.GetMetas)
|
||||||
|
admin.POST("/meta/create", controllers.CreateMeta)
|
||||||
|
admin.POST("/meta/save", controllers.SaveMeta)
|
||||||
|
admin.DELETE("/meta", controllers.DeleteMeta)
|
||||||
|
|
||||||
|
admin.POST("/link", controllers.Link)
|
||||||
}
|
}
|
||||||
Static(r)
|
Static(r)
|
||||||
WebDav(r)
|
WebDav(r)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -21,28 +21,28 @@ import (
|
|||||||
|
|
||||||
type FileSystem struct{}
|
type FileSystem struct{}
|
||||||
|
|
||||||
func ParsePath(rawPath string) (*model.Account, string, drivers.Driver, error) {
|
func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
|
||||||
var path, name string
|
var internalPath, name string
|
||||||
switch model.AccountsCount() {
|
switch model.AccountsCount() {
|
||||||
case 0:
|
case 0:
|
||||||
return nil, "", nil, fmt.Errorf("no accounts,please add one first")
|
return nil, "", nil, fmt.Errorf("no accounts,please add one first")
|
||||||
case 1:
|
case 1:
|
||||||
path = rawPath
|
internalPath = rawPath
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
paths := strings.Split(rawPath, "/")
|
paths := strings.Split(rawPath, "/")
|
||||||
path = "/" + strings.Join(paths[2:], "/")
|
internalPath = "/" + strings.Join(paths[2:], "/")
|
||||||
name = paths[1]
|
name = paths[1]
|
||||||
}
|
}
|
||||||
account, ok := model.GetAccount(name)
|
account, ok := model.GetAccount(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
||||||
}
|
}
|
||||||
driver, ok := drivers.GetDriver(account.Type)
|
driver, ok := base.GetDriver(account.Type)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
|
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
|
||||||
}
|
}
|
||||||
return &account, path, driver, nil
|
return &account, internalPath, driver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
||||||
@ -80,26 +80,26 @@ func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
|
|||||||
return driver.Files(path_, account)
|
return driver.Files(path_, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPW(path string) string {
|
//func GetPW(path string, name string) string {
|
||||||
if !conf.CheckDown {
|
// if !conf.CheckDown {
|
||||||
return ""
|
// return ""
|
||||||
}
|
// }
|
||||||
meta, err := model.GetMetaByPath(path)
|
// meta, err := model.GetMetaByPath(path)
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
if meta.Password != "" {
|
// if meta.Password != "" {
|
||||||
utils.Get16MD5Encode(meta.Password)
|
// return utils.SignWithPassword(name, meta.Password)
|
||||||
}
|
// }
|
||||||
return ""
|
// return ""
|
||||||
} else {
|
// } else {
|
||||||
if !conf.CheckParent {
|
// if !conf.CheckParent {
|
||||||
return ""
|
// return ""
|
||||||
}
|
// }
|
||||||
if path == "/" {
|
// if path == "/" {
|
||||||
return ""
|
// return ""
|
||||||
}
|
// }
|
||||||
return GetPW(utils.Dir(path))
|
// return GetPW(utils.Dir(path), name)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
@ -119,18 +119,73 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
|||||||
if driver.Config().OnlyProxy || account.WebdavProxy {
|
if driver.Config().OnlyProxy || account.WebdavProxy {
|
||||||
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
||||||
if conf.CheckDown {
|
if conf.CheckDown {
|
||||||
pw := GetPW(utils.Dir(rawPath))
|
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
|
||||||
link += "?pw" + pw
|
link += "?sign" + sign
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
link, err = driver.Link(path_, account)
|
link_, err := driver.Link(path_, account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
link = link_.Url
|
||||||
}
|
}
|
||||||
log.Debugf("webdav get link: %s", link)
|
log.Debugf("webdav get link: %s", link)
|
||||||
return link, err
|
return link, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) CreateDirectory(ctx context.Context, reqPath string) (interface{}, error) {
|
func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error {
|
||||||
return nil, nil
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
if rawPath == "/" {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
if model.AccountsCount() > 1 && len(strings.Split(rawPath, "/")) < 2 {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
account, path_, driver, err := ParsePath(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return driver.MakeDir(path_,account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) error {
|
||||||
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
account, path_, driver, err := ParsePath(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//fileSize, err := strconv.ParseUint(r.Header.Get("Content-Length"), 10, 64)
|
||||||
|
fileSize := uint64(r.ContentLength)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
filePath, fileName := filepath.Split(path_)
|
||||||
|
fileData := model.FileStream{
|
||||||
|
MIMEType: r.Header.Get("Content-Type"),
|
||||||
|
File: r.Body,
|
||||||
|
Size: fileSize,
|
||||||
|
Name: fileName,
|
||||||
|
ParentPath: filePath,
|
||||||
|
}
|
||||||
|
return driver.Upload(&fileData, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) Delete(rawPath string) error {
|
||||||
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
if rawPath == "/" {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
if model.AccountsCount() > 1 && len(strings.Split(rawPath, "/")) < 2 {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
account, path_, driver, err := ParsePath(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return driver.Delete(path_, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
// slashClean is equivalent to but slightly more efficient than
|
// slashClean is equivalent to but slightly more efficient than
|
||||||
@ -145,16 +200,55 @@ func slashClean(name string) string {
|
|||||||
// moveFiles moves files and/or directories from src to dst.
|
// moveFiles moves files and/or directories from src to dst.
|
||||||
//
|
//
|
||||||
// See section 9.9.4 for when various HTTP status codes apply.
|
// See section 9.9.4 for when various HTTP status codes apply.
|
||||||
func moveFiles(ctx context.Context, fs *FileSystem, src FileInfo, dst string, overwrite bool) (status int, err error) {
|
func moveFiles(ctx context.Context, fs *FileSystem, src string, dst string, overwrite bool) (status int, err error) {
|
||||||
|
src = utils.ParsePath(src)
|
||||||
|
dst = utils.ParsePath(dst)
|
||||||
|
if src == dst {
|
||||||
|
return http.StatusMethodNotAllowed, errDestinationEqualsSource
|
||||||
|
}
|
||||||
|
srcAccount, srcPath, driver, err := ParsePath(src)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
|
}
|
||||||
|
dstAccount, dstPath, _, err := ParsePath(dst)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
|
}
|
||||||
|
if srcAccount.Name != dstAccount.Name {
|
||||||
|
return http.StatusMethodNotAllowed, errInvalidDestination
|
||||||
|
}
|
||||||
|
err = driver.Move(srcPath,dstPath,srcAccount)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
return http.StatusNoContent, nil
|
return http.StatusNoContent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyFiles copies files and/or directories from src to dst.
|
// copyFiles copies files and/or directories from src to dst.
|
||||||
//
|
//
|
||||||
// See section 9.8.5 for when various HTTP status codes apply.
|
// See section 9.8.5 for when various HTTP status codes apply.
|
||||||
func copyFiles(ctx context.Context, fs *FileSystem, src FileInfo, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
|
func copyFiles(ctx context.Context, fs *FileSystem, src string, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
|
||||||
|
src = utils.ParsePath(src)
|
||||||
|
dst = utils.ParsePath(dst)
|
||||||
|
if src == dst {
|
||||||
|
return http.StatusMethodNotAllowed, errDestinationEqualsSource
|
||||||
|
}
|
||||||
|
srcAccount, srcPath, driver, err := ParsePath(src)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
|
}
|
||||||
|
dstAccount, dstPath, _, err := ParsePath(dst)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
|
}
|
||||||
|
if srcAccount.Name != dstAccount.Name {
|
||||||
|
// TODO 跨账号复制
|
||||||
|
return http.StatusMethodNotAllowed, errInvalidDestination
|
||||||
|
}
|
||||||
|
err = driver.Copy(srcPath,dstPath,srcAccount)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
return http.StatusNoContent, nil
|
return http.StatusNoContent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,26 +253,11 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileS
|
|||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
err = fs.Delete(reqPath)
|
||||||
//ctx := r.Context()
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
//// 尝试作为文件删除
|
}
|
||||||
//if ok, file := fs.IsFileExist(reqPath); ok {
|
return http.StatusNoContent, nil
|
||||||
// if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false); err != nil {
|
|
||||||
// return http.StatusMethodNotAllowed, err
|
|
||||||
// }
|
|
||||||
// return http.StatusNoContent, nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// 尝试作为目录删除
|
|
||||||
//if ok, folder := fs.IsPathExist(reqPath); ok {
|
|
||||||
// if err := fs.Delete(ctx, []uint{folder.ID}, []uint{}, false); err != nil {
|
|
||||||
// return http.StatusMethodNotAllowed, err
|
|
||||||
// }
|
|
||||||
// return http.StatusNoContent, nil
|
|
||||||
//}
|
|
||||||
|
|
||||||
return http.StatusNotFound, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK
|
// OK
|
||||||
@ -291,7 +276,13 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
etag, err := findETag(ctx, fs, h.LockSystem, reqPath, nil)
|
err = fs.Upload(ctx, r, reqPath)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusMethodNotAllowed, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, fi := isPathExist(ctx, fs, reqPath)
|
||||||
|
etag, err := findETag(ctx, fs, h.LockSystem, reqPath, fi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
@ -321,7 +312,7 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSy
|
|||||||
// ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true)
|
// ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true)
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
if _, err := fs.CreateDirectory(ctx, reqPath); err != nil {
|
if err := fs.CreateDirectory(ctx, reqPath); err != nil {
|
||||||
return http.StatusConflict, err
|
return http.StatusConflict, err
|
||||||
}
|
}
|
||||||
return http.StatusCreated, nil
|
return http.StatusCreated, nil
|
||||||
@ -361,7 +352,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
|
|||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
isExist, target := isPathExist(ctx, fs, src)
|
isExist, _ := isPathExist(ctx, fs, src)
|
||||||
|
|
||||||
if !isExist {
|
if !isExist {
|
||||||
return http.StatusNotFound, nil
|
return http.StatusNotFound, nil
|
||||||
@ -390,7 +381,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
|
|||||||
return http.StatusBadRequest, errInvalidDepth
|
return http.StatusBadRequest, errInvalidDepth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0)
|
return copyFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") != "F", depth, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// windows下,某些情况下(网盘根目录下)Office保存文件时附带的锁token只包含源文件,
|
// windows下,某些情况下(网盘根目录下)Office保存文件时附带的锁token只包含源文件,
|
||||||
@ -409,7 +400,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
|
|||||||
return http.StatusBadRequest, errInvalidDepth
|
return http.StatusBadRequest, errInvalidDepth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return moveFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") == "T")
|
return moveFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") == "T")
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK
|
// OK
|
||||||
|
@ -105,4 +105,12 @@ func Dir(path string) string {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
return path[:idx]
|
return path[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Base(path string) string {
|
||||||
|
idx := strings.LastIndex(path, "/")
|
||||||
|
if idx == -1 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return path[idx+1:]
|
||||||
}
|
}
|
@ -3,6 +3,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetMD5Encode
|
// GetMD5Encode
|
||||||
@ -16,3 +17,11 @@ func GetMD5Encode(data string) string {
|
|||||||
func Get16MD5Encode(data string) string {
|
func Get16MD5Encode(data string) string {
|
||||||
return GetMD5Encode(data)[8:24]
|
return GetMD5Encode(data)[8:24]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SignWithPassword(name, password string) string {
|
||||||
|
return Get16MD5Encode(fmt.Sprintf("alist-%s-%s", password, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignWithToken(name, token string) string {
|
||||||
|
return Get16MD5Encode(fmt.Sprintf("alist-%s-%s", token, name))
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user