Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
e6be11c17f | |||
b52e1e8be3 | |||
948bbe9136 | |||
ced61da33a | |||
a0f4383d41 | |||
5a527dfa2c | |||
49fc475f9f | |||
83c377270e | |||
7ffaef0de6 | |||
dd151480a8 | |||
ad3121d367 | |||
c1525ebc69 | |||
30277cd81f | |||
466ec27ffe | |||
85c757b035 | |||
712687370a | |||
b68ba22df3 | |||
d9652e2a0b | |||
a5b757b251 | |||
0bc05a60b0 | |||
db275f885a | |||
9e483d902f | |||
801f843f8a | |||
77ffb93cbe | |||
bf73ea7f5d | |||
9b23d0ab29 | |||
908cdd2c78 | |||
f4f61a5787 | |||
6db09a2736 | |||
b21801d505 | |||
2dbedc245c | |||
58426613f6 | |||
ef19e851e3 | |||
5a1b16a601 | |||
4eef9cd9bc | |||
79b5c018ea |
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -5,7 +5,8 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report, please confirm that your issue is not a duplicate issue
|
||||
Thanks for taking the time to fill out this bug report, please **confirm that your issue is not a duplicate issue and not because of your operation or version issues**
|
||||
感谢您花时间填写此错误报告,请**务必确认您的issue不是重复的且不是因为您的操作或版本问题**
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
@ -28,11 +29,11 @@ body:
|
||||
Please provide a link to a repo that can reproduce the problem you ran into.
|
||||
请提供能复现此问题的链接
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 日志 / Logs
|
||||
label: Logs / 日志
|
||||
description: |
|
||||
Please copy and paste any relevant log output.
|
||||
请复制粘贴错误日志,或者截图
|
||||
|
25
.github/workflows/issue_invalid.yml
vendored
Normal file
25
.github/workflows/issue_invalid.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Issue Invalid
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
create-comment:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'invalid'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}, your issue is invalid and will be closed.
|
||||
你好 @${{ github.event.issue.user.login }},你的issue无效,将被关闭。
|
||||
- name: Close issue
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
2
.github/workflows/issue_question.yml
vendored
2
.github/workflows/issue_question.yml
vendored
@ -17,4 +17,4 @@ jobs:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}, please input issue by template and add detail. Issues labeled by `question` will be closed if no activities in 7 days.
|
||||
你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 并详细说明问题/复现步骤/实现思路或提供更多信息等, 7天内未回复issue自动关闭。
|
||||
你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 并详细说明问题/复现步骤/复现链接/实现思路或提供更多信息等, 7天内未回复issue自动关闭。
|
15
README.md
15
README.md
@ -21,7 +21,7 @@ English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contri
|
||||
- [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] [189cloud](https://cloud.189.cn) (Personal, Family)
|
||||
- [x] [GoogleDrive](https://drive.google.com/)
|
||||
- [x] [123pan](https://www.123pan.com/)
|
||||
- [x] [Lanzou](https://pc.woozooo.com/)
|
||||
@ -69,10 +69,21 @@ Available at: <https://alist.nn.ci>.
|
||||
|
||||
<https://alist-doc.nn.ci/en/>
|
||||
|
||||
## Special sponsors
|
||||
- [Find Resources - Aliyundrive Resource Search Engine](https://zhaoziyuan.la/)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
||||
## License
|
||||
|
||||
The `AList` is open-source software licensed under the AGPL-3.0 license.
|
||||
|
||||
## Disclaimer
|
||||
- This program is a free and open source project. It is designed to share files on the network disk, which is convenient for downloading and learning golang. Please abide by relevant laws and regulations when using it, and do not abuse it;
|
||||
- This program is implemented by calling the official sdk/interface, without destroying the official interface behavior;
|
||||
- This program only does 302 redirect/traffic forwarding, and does not intercept, store, or tamper with any user data;
|
||||
- Before using this program, you should understand and bear the corresponding risks, including but not limited to account ban, download speed limit, etc., which is none of this program's business;
|
||||
- If there is any infringement, please contact me by [email](mailto:i@nn.ci), and it will be dealt with in time.
|
||||
|
||||
---
|
||||
|
||||
> [@Blog](https://www.nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@QQGroup](https://jq.qq.com/?_wv=1027&k=OVPJcv2b)
|
||||
> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@QQGroup](https://jq.qq.com/?_wv=1027&k=OVPJcv2b)
|
15
README_cn.md
15
README_cn.md
@ -21,7 +21,7 @@
|
||||
- [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] [天翼云盘](https://cloud.189.cn) (个人云, 家庭云)
|
||||
- [x] [GoogleDrive](https://drive.google.com/)
|
||||
- [x] [123云盘](https://www.123pan.com/)
|
||||
- [x] [蓝奏云](https://pc.woozooo.com/)
|
||||
@ -69,10 +69,21 @@
|
||||
|
||||
<https://alist-doc.nn.ci/>
|
||||
|
||||
## 特别赞助
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
||||
## 许可
|
||||
|
||||
`AList` 是在 AGPL-3.0 许可下许可的开源软件。
|
||||
|
||||
## 免责声明
|
||||
- 本程序为免费开源项目,旨在分享网盘文件,方便下载以及学习golang,使用时请遵守相关法律法规,请勿滥用;
|
||||
- 本程序通过调用官方sdk/接口实现,无破坏官方接口行为;
|
||||
- 本程序仅做302重定向/流量转发,不拦截、存储、篡改任何用户数据;
|
||||
- 在使用本程序之前,你应了解并承担相应的风险,包括但不限于账号被ban,下载限速等,与本程序无关;
|
||||
- 如有侵权,请通过[邮件](mailto:i@nn.ci)与我联系,会及时处理。
|
||||
|
||||
---
|
||||
|
||||
> [@Blog](https://www.nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@QQGroup](https://jq.qq.com/?_wv=1027&k=OVPJcv2b)
|
||||
> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@QQGroup](https://jq.qq.com/?_wv=1027&k=OVPJcv2b)
|
@ -3,6 +3,7 @@ package bootstrap
|
||||
import (
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/drivers/operate"
|
||||
"github.com/Xhofe/alist/model"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -20,7 +21,8 @@ func InitAccounts() {
|
||||
log.Errorf("no [%s] driver", account.Type)
|
||||
} else {
|
||||
log.Infof("start init account: [%s], type: [%s]", account.Name, account.Type)
|
||||
err := driver.Save(&accounts[i], nil)
|
||||
//err := driver.Save(&accounts[i], nil)
|
||||
err := operate.Save(driver, &accounts[i], nil)
|
||||
if err != nil {
|
||||
log.Errorf("init account [%s] error:[%s]", account.Name, err.Error())
|
||||
} else {
|
||||
|
@ -50,8 +50,8 @@ func InitModel() {
|
||||
}
|
||||
case "mysql":
|
||||
{
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name)
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
|
||||
databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name, databaseConfig.SslMode)
|
||||
db, err := gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect database:%s", err.Error())
|
||||
|
@ -73,6 +73,20 @@ func InitSettings() {
|
||||
Description: "text type extensions",
|
||||
Group: model.FRONT,
|
||||
},
|
||||
{
|
||||
Key: "audio types",
|
||||
Value: strings.Join(conf.AudioTypes, ","),
|
||||
Type: "string",
|
||||
Description: "audio type extensions",
|
||||
Group: model.FRONT,
|
||||
},
|
||||
{
|
||||
Key: "video types",
|
||||
Value: strings.Join(conf.VideoTypes, ","),
|
||||
Type: "string",
|
||||
Description: "video type extensions",
|
||||
Group: model.FRONT,
|
||||
},
|
||||
{
|
||||
Key: "d_proxy types",
|
||||
Value: strings.Join(conf.DProxyTypes, ","),
|
||||
|
@ -45,7 +45,6 @@ func DefaultConfig() *Config {
|
||||
Port: 0,
|
||||
TablePrefix: "x_",
|
||||
DBFile: "data/data.db",
|
||||
SslMode: "disable",
|
||||
},
|
||||
Cache: CacheConfig{
|
||||
Expiration: 60,
|
||||
|
@ -140,7 +140,7 @@ func (driver Pan123) Link(args base.Args, account *model.Account) (*base.Link, e
|
||||
}
|
||||
var resp Pan123DownResp
|
||||
var headers map[string]string
|
||||
if args.IP != "" && args.IP != "::1" {
|
||||
if !utils.IsLocalIPAddr(args.IP) {
|
||||
headers = map[string]string{
|
||||
//"X-Real-IP": "1.1.1.1",
|
||||
"X-Forwarded-For": args.IP,
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -105,6 +106,9 @@ func (driver Cloud189) Login(account *model.Account) error {
|
||||
client.SetRetryCount(3)
|
||||
client.SetHeader("Referer", "https://cloud.189.cn/")
|
||||
}
|
||||
// clear cookie
|
||||
jar, _ := cookiejar.New(nil)
|
||||
client.SetCookieJar(jar)
|
||||
url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||
b := ""
|
||||
lt := ""
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Cloud189 struct{}
|
||||
@ -198,6 +199,7 @@ func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link,
|
||||
} else {
|
||||
link.Url = resp.FileDownloadUrl
|
||||
}
|
||||
link.Url = strings.Replace(link.Url, "http://", "https://", 1)
|
||||
return &link, nil
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/google/uuid"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -110,7 +109,7 @@ func (s *State) login(account *model.Account) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toUrl := jsoniter.Get(res.Body(), "toUrl").ToString()
|
||||
toUrl := utils.Json.Get(res.Body(), "toUrl").ToString()
|
||||
if toUrl == "" {
|
||||
log.Error(res.String())
|
||||
return fmt.Errorf(res.String())
|
||||
@ -179,10 +178,10 @@ func (s *State) getLoginParam() (*LoginParam, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
return nil, errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||
if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString())
|
||||
}
|
||||
param.vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||
param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString()
|
||||
log.Debugln("code: ", param.vCodeRS)
|
||||
}
|
||||
return param, nil
|
||||
@ -308,9 +307,9 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
|
||||
}
|
||||
|
||||
if account != nil {
|
||||
switch jsoniter.Get(res.Body(), "res_code").ToInt64() {
|
||||
switch utils.Json.Get(res.Body(), "res_code").ToInt64() {
|
||||
case 11, 18:
|
||||
if err := s.refreshSession(account); err != nil {
|
||||
if err := s.RefreshSession(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.Request(method, fullUrl, params, callback, account)
|
||||
@ -324,8 +323,8 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
|
||||
}
|
||||
}
|
||||
|
||||
if jsoniter.Get(res.Body(), "res_code").ToInt64() != 0 {
|
||||
return res, fmt.Errorf(jsoniter.Get(res.Body(), "res_message").ToString())
|
||||
if utils.Json.Get(res.Body(), "res_code").ToInt64() != 0 {
|
||||
return res, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package _189
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -64,9 +63,10 @@ func (driver Cloud189) Items() []base.Item {
|
||||
Values: "Personal,Family",
|
||||
},
|
||||
{
|
||||
Name: "site_id",
|
||||
Label: "family id",
|
||||
Type: base.TypeString,
|
||||
Name: "site_id",
|
||||
Label: "family id",
|
||||
Type: base.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "order_by",
|
||||
@ -96,13 +96,41 @@ func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
|
||||
|
||||
state := GetState(account)
|
||||
if !state.IsLogin() {
|
||||
return state.Login(account)
|
||||
if err := state.Login(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isFamily(account) {
|
||||
list, err := driver.getFamilyInfoList(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range list {
|
||||
if account.SiteId == "" {
|
||||
account.SiteId = fmt.Sprint(l.FamilyID)
|
||||
}
|
||||
log.Infof("天翼家庭云 用户名:%s FamilyID %d\n", l.RemarkName, l.FamilyID)
|
||||
}
|
||||
}
|
||||
|
||||
account.Status = "work"
|
||||
model.SaveAccount(account)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver Cloud189) getFamilyInfoList(account *model.Account) ([]FamilyInfoResp, error) {
|
||||
var resp FamilyInfoListResp
|
||||
_, err := GetState(account).Request("GET", API_URL+"/family/manage/getFamilyList.action", nil, func(r *resty.Request) {
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetResult(&resp)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.FamilyInfoResp, nil
|
||||
}
|
||||
|
||||
func (driver Cloud189) File(path string, account *model.Account) (*model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
if path == "/" {
|
||||
@ -329,7 +357,7 @@ func (driver Cloud189) Move(src string, dst string, account *model.Account) erro
|
||||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "MOVE",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
@ -445,7 +473,7 @@ func (driver Cloud189) Copy(src string, dst string, account *model.Account) erro
|
||||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "COPY",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
@ -475,7 +503,7 @@ func (driver Cloud189) Delete(path string, account *model.Account) error {
|
||||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "DELETE",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
@ -546,7 +574,7 @@ func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.Fi
|
||||
}
|
||||
|
||||
if createUpload.FileDataExists != 1 {
|
||||
if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -601,7 +629,7 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
|
||||
}
|
||||
|
||||
if createUpload.FileDataExists != 1 {
|
||||
if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -610,7 +638,7 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetFormData(map[string]string{
|
||||
"uploadFileId": fmt.Sprint(createUpload.UploadFileId),
|
||||
"opertype": "1", //5 覆盖
|
||||
"opertype": "5", //5 覆盖 1 重命名
|
||||
"ResumePolicy": "1",
|
||||
"isLog": "0",
|
||||
})
|
||||
@ -618,40 +646,39 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) error {
|
||||
var uploadFileState *UploadFileStatusResult
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
if uploadFileState, err = driver.getUploadFileState(createUpload.UploadFileId, account); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) {
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetHeaders(map[string]string{
|
||||
"ResumePolicy": "1",
|
||||
"Edrive-UploadFileId": fmt.Sprint(createUpload.UploadFileId),
|
||||
"Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size),
|
||||
"Expect": "100-continue",
|
||||
})
|
||||
if isFamily(account) {
|
||||
r.SetHeader("FamilyId", account.SiteId)
|
||||
}
|
||||
r.SetBody(tempFile)
|
||||
}, account)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) (int64, error) {
|
||||
uploadFileState, err := driver.getUploadFileState(createUpload.UploadFileId, account)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return err
|
||||
|
||||
if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) {
|
||||
return uploadFileState.UploadFileId, nil
|
||||
}
|
||||
|
||||
if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) {
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetHeaders(map[string]string{
|
||||
"Content-Type": "application/octet-stream",
|
||||
"ResumePolicy": "1",
|
||||
"Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size),
|
||||
"Expect": "100-continue",
|
||||
})
|
||||
if isFamily(account) {
|
||||
r.SetHeaders(map[string]string{
|
||||
"familyId": account.SiteId,
|
||||
"UploadFileId": fmt.Sprint(uploadFileState.UploadFileId),
|
||||
})
|
||||
} else {
|
||||
r.SetHeader("Edrive-UploadFileId", fmt.Sprint(uploadFileState.UploadFileId))
|
||||
}
|
||||
r.SetBody(tempFile)
|
||||
}, account)
|
||||
return uploadFileState.UploadFileId, err
|
||||
}
|
||||
|
||||
func (driver Cloud189) getUploadFileState(uploadFileId int64, account *model.Account) (*UploadFileStatusResult, error) {
|
||||
|
@ -60,6 +60,19 @@ type appSessionResp struct {
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
type FamilyInfoListResp struct {
|
||||
FamilyInfoResp []FamilyInfoResp `json:"familyInfoResp"`
|
||||
}
|
||||
type FamilyInfoResp struct {
|
||||
Count int `json:"count"`
|
||||
CreateTime string `json:"createTime"`
|
||||
FamilyID int `json:"familyId"`
|
||||
RemarkName string `json:"remarkName"`
|
||||
Type int `json:"type"`
|
||||
UseFlag int `json:"useFlag"`
|
||||
UserRole int `json:"userRole"`
|
||||
}
|
||||
|
||||
/*文件部分*/
|
||||
// 文件
|
||||
type Cloud189File struct {
|
||||
|
@ -228,7 +228,7 @@ func (driver AliDrive) batch(srcId, dstId string, url string, account *model.Acc
|
||||
}
|
||||
return fmt.Errorf("%s", e.Message)
|
||||
}
|
||||
status := jsoniter.Get(res.Body(), "status").ToInt()
|
||||
status := jsoniter.Get(res.Body(), "responses", 0, "status").ToInt()
|
||||
if status < 400 && status >= 100 {
|
||||
return nil
|
||||
}
|
||||
|
@ -94,15 +94,15 @@ func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
|
||||
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)
|
||||
id := account.ID
|
||||
log.Debugf("ali account id: %d", id)
|
||||
newAccount, err := model.GetAccountById(id)
|
||||
log.Debugf("ali account: %+v", newAccount)
|
||||
if !ok {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = driver.RefreshToken(&newAccount)
|
||||
_ = model.SaveAccount(&newAccount)
|
||||
err = driver.RefreshToken(newAccount)
|
||||
_ = model.SaveAccount(newAccount)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -412,7 +412,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := file.Read(buf[:])
|
||||
reqBody := base.Json{
|
||||
"check_name_mode": "auto_rename",
|
||||
"check_name_mode": "overwrite",
|
||||
"drive_id": account.DriveId,
|
||||
"name": file.GetFileName(),
|
||||
"parent_file_id": parentFile.Id,
|
||||
|
@ -76,6 +76,9 @@ func (driver Baidu) Items() []base.Item {
|
||||
}
|
||||
|
||||
func (driver Baidu) Save(account *model.Account, old *model.Account) error {
|
||||
if account == nil {
|
||||
return nil
|
||||
}
|
||||
return driver.RefreshToken(account)
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ func GetDrivers() map[string][]Item {
|
||||
{
|
||||
Name: "down_proxy_url",
|
||||
Label: "down_proxy_url",
|
||||
Type: TypeString,
|
||||
Type: TypeText,
|
||||
},
|
||||
{
|
||||
Name: "extract_folder",
|
||||
|
@ -21,6 +21,7 @@ const (
|
||||
TypeSelect = "select"
|
||||
TypeBool = "bool"
|
||||
TypeNumber = "number"
|
||||
TypeText = "text"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -73,8 +73,8 @@ func (driver GoogleDrive) FormatFile(file *File, account *model.Account) *model.
|
||||
f.Type = utils.GetFileType(filepath.Ext(file.Name))
|
||||
}
|
||||
if file.ThumbnailLink != "" {
|
||||
if account.DownProxyUrl != "" {
|
||||
f.Thumbnail = fmt.Sprintf("%s/%s", account.DownProxyUrl, file.ThumbnailLink)
|
||||
if account.APIProxyUrl != "" {
|
||||
f.Thumbnail = fmt.Sprintf("%s/%s", account.APIProxyUrl, file.ThumbnailLink)
|
||||
} else {
|
||||
f.Thumbnail = file.ThumbnailLink
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func Save(driver base.Driver, account, old *model.Account) error {
|
||||
return driver.Save(account, old)
|
||||
}
|
||||
|
||||
func Path(driver base.Driver, account *model.Account, path string) (*model.File, []model.File, error) {
|
||||
return driver.Path(path, account)
|
||||
}
|
||||
|
@ -64,6 +64,12 @@ func (driver Quark) Save(account *model.Account, old *model.Account) error {
|
||||
return nil
|
||||
}
|
||||
_, err := driver.Get("/config", nil, nil, account)
|
||||
if err == nil {
|
||||
account.Status = "work"
|
||||
} else {
|
||||
account.Status = err.Error()
|
||||
}
|
||||
_ = model.SaveAccount(account)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/Xhofe/alist/utils/cookie"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
@ -62,7 +63,12 @@ func (driver Quark) Request(pathname string, method int, headers, query, form ma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("%s response: %s", pathname, res.String())
|
||||
__puus := cookie.GetCookie(res.Cookies(), "__puus")
|
||||
if __puus != nil {
|
||||
account.AccessToken = cookie.SetStr(account.AccessToken, "__puus", __puus.Value)
|
||||
_ = model.SaveAccount(account)
|
||||
}
|
||||
//log.Debugf("%s response: %s", pathname, res.String())
|
||||
if e.Status >= 400 || e.Code != 0 {
|
||||
return nil, errors.New(e.Message)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@ -78,6 +80,7 @@ func (driver S3) Items() []base.Item {
|
||||
Label: "placeholder filename",
|
||||
Type: base.TypeString,
|
||||
Description: "default empty string",
|
||||
Default: defaultPlaceholderName,
|
||||
},
|
||||
{
|
||||
Name: "bool_1",
|
||||
@ -213,8 +216,19 @@ func (driver S3) Preview(path string, account *model.Account) (interface{}, erro
|
||||
}
|
||||
|
||||
func (driver S3) MakeDir(path string, account *model.Account) error {
|
||||
// not support, default as success
|
||||
return nil
|
||||
// not support, generate a placeholder file
|
||||
_, err := driver.File(path, account)
|
||||
// exist
|
||||
if err != base.ErrPathNotFound {
|
||||
return nil
|
||||
}
|
||||
return driver.Upload(&model.FileStream{
|
||||
File: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
Size: 0,
|
||||
ParentPath: path,
|
||||
Name: getPlaceholderName(account.Zone),
|
||||
MIMEType: "application/octet-stream",
|
||||
}, account)
|
||||
}
|
||||
|
||||
func (driver S3) Move(src string, dst string, account *model.Account) error {
|
||||
@ -277,6 +291,7 @@ func (driver S3) Upload(file *model.FileStream, account *model.Account) error {
|
||||
}
|
||||
uploader := s3manager.NewUploader(s)
|
||||
key := driver.GetKey(utils.Join(file.ParentPath, file.GetFileName()), account, false)
|
||||
log.Debugln("key:", key)
|
||||
input := &s3manager.UploadInput{
|
||||
Bucket: &account.Bucket,
|
||||
Key: &key,
|
||||
|
@ -88,7 +88,7 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
|
||||
}
|
||||
for _, object := range listObjectsResult.Contents {
|
||||
name := utils.Base(*object.Key)
|
||||
if name == account.Zone {
|
||||
if name == getPlaceholderName(account.Zone) {
|
||||
continue
|
||||
}
|
||||
file := model.File{
|
||||
@ -152,7 +152,7 @@ func (driver S3) ListV2(prefix string, account *model.Account) ([]model.File, er
|
||||
}
|
||||
for _, object := range listObjectsResult.Contents {
|
||||
name := utils.Base(*object.Key)
|
||||
if name == account.Zone {
|
||||
if name == getPlaceholderName(account.Zone) {
|
||||
continue
|
||||
}
|
||||
file := model.File{
|
||||
|
10
drivers/s3/util.go
Normal file
10
drivers/s3/util.go
Normal file
@ -0,0 +1,10 @@
|
||||
package s3
|
||||
|
||||
var defaultPlaceholderName = ".placeholder"
|
||||
|
||||
func getPlaceholderName(placeholder string) string {
|
||||
if placeholder == "" {
|
||||
return defaultPlaceholderName
|
||||
}
|
||||
return placeholder
|
||||
}
|
@ -4,12 +4,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
@ -95,12 +95,13 @@ func (driver XunLeiCloud) File(path string, account *model.Account) (*model.File
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
cache, err := base.GetCache(path, account)
|
||||
if err == nil {
|
||||
files, _ := cache.([]model.File)
|
||||
return files, nil
|
||||
}
|
||||
file, err := driver.File(utils.ParsePath(path), account)
|
||||
file, err := driver.File(path, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -108,8 +109,16 @@ func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.Fi
|
||||
files := make([]model.File, 0)
|
||||
for {
|
||||
var fileList FileList
|
||||
u := fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files?parent_id=%s&page_token=%s&with_audit=true&filters=%s", file.Id, fileList.NextPageToken, url.QueryEscape(`{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`))
|
||||
if err = GetState(account).Request("GET", u, nil, &fileList, account); err != nil {
|
||||
_, err = GetState(account).Request("GET", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetQueryParams(map[string]string{
|
||||
"parent_id": file.Id,
|
||||
"page_token": fileList.NextPageToken,
|
||||
"with_audit": "true",
|
||||
"filters": `{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`,
|
||||
})
|
||||
r.SetResult(&fileList)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range fileList.Files {
|
||||
@ -153,7 +162,12 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li
|
||||
return nil, base.ErrNotFile
|
||||
}
|
||||
var lFile Files
|
||||
if err = GetState(account).Request("GET", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s?&with_audit=true", file.Id), nil, &lFile, account); err != nil {
|
||||
_, err = GetState(account).Request("GET", FILE_API_URL+"/{id}", func(r *resty.Request) {
|
||||
r.SetPathParam("id", file.Id)
|
||||
r.SetQueryParam("with_audit", "true")
|
||||
r.SetResult(&lFile)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &base.Link{
|
||||
@ -194,7 +208,14 @@ func (driver XunLeiCloud) MakeDir(path string, account *model.Account) error {
|
||||
if !parentFile.IsDir() {
|
||||
return base.ErrNotFolder
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{"kind": FOLDER, "name": name, "parent_id": parentFile.Id}, nil, account)
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"kind": FOLDER,
|
||||
"name": name,
|
||||
"parent_id": parentFile.Id,
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) error {
|
||||
@ -207,7 +228,14 @@ func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchMove", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
|
||||
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL+":batchMove", func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDirFile.Id},
|
||||
"ids": []string{srcFile.Id},
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) error {
|
||||
@ -220,7 +248,13 @@ func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchCopy", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL+":batchCopy", func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDirFile.Id},
|
||||
"ids": []string{srcFile.Id},
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
|
||||
@ -228,7 +262,11 @@ func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s/trash", srcFile.Id), &base.Json{}, nil, account)
|
||||
_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}/trash", func(r *resty.Request) {
|
||||
r.SetPathParam("id", srcFile.Id)
|
||||
r.SetBody(&base.Json{})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) error {
|
||||
@ -246,7 +284,6 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
|
||||
return err
|
||||
}
|
||||
|
||||
defer tempFile.Close()
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
gcid, err := getGcid(io.TeeReader(file, tempFile), int64(file.Size))
|
||||
@ -254,29 +291,37 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
|
||||
return err
|
||||
}
|
||||
|
||||
var rep UploadTaskResponse
|
||||
err = GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{
|
||||
"kind": FILE,
|
||||
"parent_id": parentFile.Id,
|
||||
"name": file.Name,
|
||||
"size": fmt.Sprint(file.Size),
|
||||
"hash": gcid,
|
||||
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||
}, &rep, account)
|
||||
tempFile.Close()
|
||||
|
||||
var resp UploadTaskResponse
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"kind": FILE,
|
||||
"parent_id": parentFile.Id,
|
||||
"name": file.Name,
|
||||
"size": fmt.Sprint(file.Size),
|
||||
"hash": gcid,
|
||||
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||
})
|
||||
r.SetResult(&resp)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
param := rep.Resumable.Params
|
||||
client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true), oss.HTTPClient(xunleiClient.GetClient()))
|
||||
if err != nil {
|
||||
return err
|
||||
param := resp.Resumable.Params
|
||||
if resp.UploadType == UPLOAD_TYPE_RESUMABLE {
|
||||
client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket, err := client.Bucket(param.Bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.UploadFile(param.Key, tempFile.Name(), 1<<22, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
|
||||
}
|
||||
bucket, err := client.Bucket(param.Bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.UploadFile(param.Key, tempFile.Name(), 4*1024*1024, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account) error {
|
||||
@ -285,8 +330,11 @@ func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s", srcFile.Id), &base.Json{"name": dstName}, nil, account)
|
||||
_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}", func(r *resty.Request) {
|
||||
r.SetPathParam("id", srcFile.Id)
|
||||
r.SetBody(&base.Json{"name": dstName})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ base.Driver = (*XunLeiCloud)(nil)
|
||||
|
@ -37,6 +37,12 @@ var Algorithms = []string{
|
||||
"T78dnANexYRbiZy",
|
||||
}
|
||||
|
||||
const (
|
||||
API_URL = "https://api-pan.xunlei.com/drive/v1"
|
||||
FILE_API_URL = API_URL + "/files"
|
||||
XLUSER_API_URL = "https://xluser-ssl.xunlei.com/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
FOLDER = "drive#folder"
|
||||
FILE = "drive#file"
|
||||
|
@ -2,6 +2,7 @@ package xunlei
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -12,7 +13,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var xunleiClient = resty.New().SetTimeout(120 * time.Second)
|
||||
var xunleiClient = resty.New().SetHeaders(map[string]string{"Accept": "application/json;charset=UTF-8"}).SetTimeout(base.DefaultTimeout)
|
||||
|
||||
// 一个账户只允许登陆一次
|
||||
var userStateCache = struct {
|
||||
@ -102,16 +103,16 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
|
||||
var e Erron
|
||||
var resp CaptchaTokenResponse
|
||||
_, err := xunleiClient.R().
|
||||
SetHeader("X-Device-Id", driverID).
|
||||
SetBody(&creq).
|
||||
SetError(&e).
|
||||
SetResult(&resp).
|
||||
Post("https://xluser-ssl.xunlei.com/v1/shield/captcha/init?client_id=" + CLIENT_ID)
|
||||
SetHeader("X-Device-Id", driverID).
|
||||
SetQueryParam("client_id", CLIENT_ID).
|
||||
Post(XLUSER_API_URL + "/shield/captcha/init")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if e.ErrorCode != 0 {
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return "", fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
if resp.Url != "" {
|
||||
@ -120,7 +121,6 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
|
||||
|
||||
s.captchaTokenExpiresTime = (ctime + resp.ExpiresIn*1000) - 30000
|
||||
s.captchaToken = resp.CaptchaToken
|
||||
log.Debugf("%+v\n %+v", s.captchaToken, account)
|
||||
return s.captchaToken, nil
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ func (s *State) refreshToken_(account *model.Account) error {
|
||||
"client_secret": CLIENT_SECRET,
|
||||
}).
|
||||
SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).SetQueryParam("client_id", CLIENT_ID).
|
||||
Post("https://xluser-ssl.xunlei.com/v1/auth/token")
|
||||
Post(XLUSER_API_URL + "/auth/token")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -152,7 +152,6 @@ func (s *State) refreshToken_(account *model.Account) error {
|
||||
s.userID = resp.UserID
|
||||
return nil
|
||||
default:
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
}
|
||||
@ -160,7 +159,7 @@ func (s *State) refreshToken_(account *model.Account) error {
|
||||
func (s *State) login(account *model.Account) error {
|
||||
s.init()
|
||||
ctime := time.Now().UnixMilli()
|
||||
url := "https://xluser-ssl.xunlei.com/v1/auth/signin"
|
||||
url := XLUSER_API_URL + "/auth/signin"
|
||||
captchaToken, err := s.newCaptchaToken(getAction("POST", url), map[string]string{"username": account.Username}, account)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -190,7 +189,6 @@ func (s *State) login(account *model.Account) error {
|
||||
defer model.SaveAccount(account)
|
||||
if e.ErrorCode != 0 {
|
||||
account.Status = e.Error
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
account.Status = "work"
|
||||
@ -199,67 +197,68 @@ func (s *State) login(account *model.Account) error {
|
||||
s.accessToken = resp.AccessToken
|
||||
s.refreshToken = resp.RefreshToken
|
||||
s.userID = resp.UserID
|
||||
log.Debugf("%+v\n %+v", resp, account)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *State) Request(method string, url string, body interface{}, resp interface{}, account *model.Account) error {
|
||||
func (s *State) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
|
||||
s.Lock()
|
||||
token, err := s.getToken(account)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
captchaToken, err := s.getCaptchaToken(getAction(method, url), account)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
s.Unlock()
|
||||
var e Erron
|
||||
|
||||
req := xunleiClient.R().
|
||||
SetError(&e).
|
||||
SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).
|
||||
SetHeader("Authorization", token).
|
||||
SetHeader("X-Captcha-Token", captchaToken).
|
||||
SetHeaders(map[string]string{
|
||||
"X-Device-Id": utils.GetMD5Encode(account.Username),
|
||||
"Authorization": token,
|
||||
"X-Captcha-Token": captchaToken,
|
||||
}).
|
||||
SetQueryParam("client_id", CLIENT_ID)
|
||||
|
||||
if body != nil {
|
||||
req.SetBody(body)
|
||||
}
|
||||
if resp != nil {
|
||||
req.SetResult(resp)
|
||||
}
|
||||
callback(req)
|
||||
s.Unlock()
|
||||
|
||||
var res *resty.Response
|
||||
switch method {
|
||||
case "GET":
|
||||
_, err = req.Get(url)
|
||||
res, err = req.Get(url)
|
||||
case "POST":
|
||||
_, err = req.Post(url)
|
||||
res, err = req.Post(url)
|
||||
case "DELETE":
|
||||
_, err = req.Delete(url)
|
||||
res, err = req.Delete(url)
|
||||
case "PATCH":
|
||||
_, err = req.Patch(url)
|
||||
res, err = req.Patch(url)
|
||||
case "PUT":
|
||||
_, err = req.Put(url)
|
||||
res, err = req.Put(url)
|
||||
default:
|
||||
return base.ErrNotSupport
|
||||
return nil, base.ErrNotSupport
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
log.Debug(res.String())
|
||||
|
||||
var e Erron
|
||||
utils.Json.Unmarshal(res.Body(), &e)
|
||||
switch e.ErrorCode {
|
||||
case 0:
|
||||
return nil
|
||||
case 9:
|
||||
s.newCaptchaToken(getAction(method, url), nil, account)
|
||||
fallthrough
|
||||
case 4122, 4121:
|
||||
return s.Request(method, url, body, resp, account)
|
||||
return s.Request(method, url, callback, account)
|
||||
case 0:
|
||||
if res.StatusCode() == http.StatusOK {
|
||||
return res, nil
|
||||
}
|
||||
return nil, fmt.Errorf(res.String())
|
||||
default:
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
return nil, fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
144
model/account.go
144
model/account.go
@ -2,7 +2,9 @@ package model
|
||||
|
||||
import (
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -97,6 +99,7 @@ func RegisterAccount(account Account) {
|
||||
accountsMap[account.Name] = account
|
||||
}
|
||||
|
||||
// GetAccount 根据名称获取账号(不包含负载均衡账号) 用于定时任务更新账号
|
||||
func GetAccount(name string) (Account, bool) {
|
||||
if len(accountsMap) == 1 {
|
||||
for _, v := range accountsMap {
|
||||
@ -107,25 +110,29 @@ func GetAccount(name string) (Account, bool) {
|
||||
return account, ok
|
||||
}
|
||||
|
||||
func GetAccountsByName(name string) []Account {
|
||||
accounts := make([]Account, 0)
|
||||
if AccountsCount() == 1 {
|
||||
account, _ := GetAccount("")
|
||||
accounts = append(accounts, account)
|
||||
return accounts
|
||||
}
|
||||
for _, v := range accountsMap {
|
||||
if v.Name == name || strings.HasPrefix(v.Name, name+balance) {
|
||||
accounts = append(accounts, v)
|
||||
}
|
||||
}
|
||||
return accounts
|
||||
}
|
||||
// GetAccountsByName 根据名称获取账号(包含负载均衡账号)
|
||||
//func GetAccountsByName(name string) []Account {
|
||||
// accounts := make([]Account, 0)
|
||||
// if AccountsCount() == 1 {
|
||||
// for _, v := range accountsMap {
|
||||
// accounts = append(accounts, v)
|
||||
// }
|
||||
// return accounts
|
||||
// }
|
||||
// for _, v := range accountsMap {
|
||||
// if v.Name == name || strings.HasPrefix(v.Name, name+balance) {
|
||||
// accounts = append(accounts, v)
|
||||
// }
|
||||
// }
|
||||
// return accounts
|
||||
//}
|
||||
|
||||
var balanceMap sync.Map
|
||||
|
||||
// GetBalancedAccount 根据名称获取账号,负载均衡之后的
|
||||
func GetBalancedAccount(name string) (Account, bool) {
|
||||
accounts := GetAccountsByName(name)
|
||||
accounts := GetAccountsByPath(name)
|
||||
log.Debugf("accounts: %+v", accounts)
|
||||
accountNum := len(accounts)
|
||||
switch accountNum {
|
||||
case 0:
|
||||
@ -147,6 +154,7 @@ func GetBalancedAccount(name string) (Account, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountById 根据id获取账号,用于更新账号
|
||||
func GetAccountById(id uint) (*Account, error) {
|
||||
var account Account
|
||||
account.ID = id
|
||||
@ -156,31 +164,111 @@ func GetAccountById(id uint) (*Account, error) {
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func GetAccountFiles() ([]File, error) {
|
||||
files := make([]File, 0)
|
||||
// GetAccountFiles 获取账号虚拟文件(去除负载均衡)
|
||||
//func GetAccountFiles() ([]File, error) {
|
||||
// files := make([]File, 0)
|
||||
// var accounts []Account
|
||||
// if err := conf.DB.Order(columnName("index")).Find(&accounts).Error; err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, v := range accounts {
|
||||
// if strings.Contains(v.Name, balance) {
|
||||
// continue
|
||||
// }
|
||||
// files = append(files, File{
|
||||
// Name: v.Name,
|
||||
// Size: 0,
|
||||
// Driver: v.Type,
|
||||
// Type: conf.FOLDER,
|
||||
// UpdatedAt: v.UpdatedAt,
|
||||
// })
|
||||
// }
|
||||
// return files, nil
|
||||
//}
|
||||
|
||||
// GetAccounts 获取所有账号
|
||||
func GetAccounts() ([]Account, error) {
|
||||
var accounts []Account
|
||||
if err := conf.DB.Order("`index`").Find(&accounts).Error; err != nil {
|
||||
if err := conf.DB.Order(columnName("index")).Find(&accounts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// GetAccountsByPath 根据路径获取账号,最长匹配,未负载均衡
|
||||
// 如有账号: /a/b,/a/c,/a/d/e,/a/d/e.balance
|
||||
// GetAccountsByPath(/a/d/e/f) => /a/d/e,/a/d/e.balance
|
||||
func GetAccountsByPath(path string) []Account {
|
||||
accounts := make([]Account, 0)
|
||||
curSlashCount := 0
|
||||
for _, v := range accountsMap {
|
||||
name := utils.ParsePath(v.Name)
|
||||
bIndex := strings.LastIndex(name, balance)
|
||||
if bIndex != -1 {
|
||||
name = name[:bIndex]
|
||||
}
|
||||
// 不是这个账号
|
||||
if path != name && !strings.HasPrefix(path, name+"/") {
|
||||
continue
|
||||
}
|
||||
slashCount := strings.Count(name, "/")
|
||||
// 不是最长匹配
|
||||
if slashCount < curSlashCount {
|
||||
continue
|
||||
}
|
||||
if slashCount > curSlashCount {
|
||||
accounts = accounts[:0]
|
||||
curSlashCount = slashCount
|
||||
}
|
||||
accounts = append(accounts, v)
|
||||
}
|
||||
sort.Slice(accounts, func(i, j int) bool {
|
||||
return accounts[i].Name < accounts[j].Name
|
||||
})
|
||||
return accounts
|
||||
}
|
||||
|
||||
// GetAccountFilesByPath 根据路径获取账号虚拟文件
|
||||
// 如有账号: /a/b,/a/c,/a/d/e,/a/b.balance1,/av
|
||||
// GetAccountFilesByPath(/a) => b,c,d
|
||||
func GetAccountFilesByPath(prefix string) []File {
|
||||
files := make([]File, 0)
|
||||
accounts := make([]Account, AccountsCount())
|
||||
i := 0
|
||||
for _, v := range accountsMap {
|
||||
accounts[i] = v
|
||||
i += 1
|
||||
}
|
||||
sort.Slice(accounts, func(i, j int) bool {
|
||||
if accounts[i].Index == accounts[j].Index {
|
||||
return accounts[i].Name < accounts[j].Name
|
||||
}
|
||||
return accounts[i].Index < accounts[j].Index
|
||||
})
|
||||
prefix = utils.ParsePath(prefix)
|
||||
set := make(map[string]interface{})
|
||||
for _, v := range accounts {
|
||||
// 负载均衡账号
|
||||
if strings.Contains(v.Name, balance) {
|
||||
continue
|
||||
}
|
||||
full := utils.ParsePath(v.Name)
|
||||
// 不是以prefix为前缀
|
||||
if !strings.HasPrefix(full, prefix+"/") && prefix != "/" {
|
||||
continue
|
||||
}
|
||||
name := strings.Split(strings.TrimPrefix(strings.TrimPrefix(full, prefix), "/"), "/")[0]
|
||||
if _, ok := set[name]; ok {
|
||||
continue
|
||||
}
|
||||
files = append(files, File{
|
||||
Name: v.Name,
|
||||
Name: name,
|
||||
Size: 0,
|
||||
Driver: v.Type,
|
||||
Type: conf.FOLDER,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
})
|
||||
set[name] = nil
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func GetAccounts() ([]Account, error) {
|
||||
var accounts []Account
|
||||
if err := conf.DB.Order("`index`").Find(&accounts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return accounts, nil
|
||||
return files
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func SaveSetting(item SettingItem) error {
|
||||
|
||||
func GetSettingsPublic() ([]SettingItem, error) {
|
||||
var items []SettingItem
|
||||
if err := conf.DB.Where("`access` <> ?", 1).Find(&items).Error; err != nil {
|
||||
if err := conf.DB.Where(fmt.Sprintf("%s <> ?", columnName("access")), 1).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
@ -58,7 +58,7 @@ func GetSettingsPublic() ([]SettingItem, error) {
|
||||
|
||||
func GetSettingsByGroup(group int) ([]SettingItem, error) {
|
||||
var items []SettingItem
|
||||
if err := conf.DB.Where("`group` = ?", group).Find(&items).Error; err != nil {
|
||||
if err := conf.DB.Where(fmt.Sprintf("%s = ?", columnName("group")), group).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append([]SettingItem{Version}, items...)
|
||||
@ -82,7 +82,7 @@ func DeleteSetting(key string) error {
|
||||
|
||||
func GetSettingByKey(key string) (*SettingItem, error) {
|
||||
var items SettingItem
|
||||
if err := conf.DB.Where("`key` = ?", key).First(&items).Error; err != nil {
|
||||
if err := conf.DB.Where(fmt.Sprintf("%s = ?", columnName("key")), key).First(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &items, nil
|
||||
@ -93,6 +93,14 @@ func LoadSettings() {
|
||||
if err == nil {
|
||||
conf.TextTypes = strings.Split(textTypes.Value, ",")
|
||||
}
|
||||
audioTypes, err := GetSettingByKey("audio types")
|
||||
if err == nil {
|
||||
conf.AudioTypes = strings.Split(audioTypes.Value, ",")
|
||||
}
|
||||
videoTypes, err := GetSettingByKey("video types")
|
||||
if err == nil {
|
||||
conf.VideoTypes = strings.Split(videoTypes.Value, ",")
|
||||
}
|
||||
dProxyTypes, err := GetSettingByKey("d_proxy types")
|
||||
if err == nil {
|
||||
conf.DProxyTypes = strings.Split(dProxyTypes.Value, ",")
|
||||
|
13
model/util.go
Normal file
13
model/util.go
Normal file
@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
)
|
||||
|
||||
func columnName(name string) string {
|
||||
if conf.Conf.Database.Type == "postgres" {
|
||||
return fmt.Sprintf(`"%s"`, name)
|
||||
}
|
||||
return fmt.Sprintf("`%s`", name)
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/model"
|
||||
@ -25,30 +24,21 @@ type PathReq struct {
|
||||
}
|
||||
|
||||
func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
|
||||
var path, name string
|
||||
switch model.AccountsCount() {
|
||||
case 0:
|
||||
return nil, "", nil, fmt.Errorf("no accounts,please add one first")
|
||||
case 1:
|
||||
path = rawPath
|
||||
break
|
||||
default:
|
||||
if path == "/" {
|
||||
return nil, "", nil, errors.New("can't operate root of multiple accounts")
|
||||
}
|
||||
paths := strings.Split(rawPath, "/")
|
||||
path = "/" + strings.Join(paths[2:], "/")
|
||||
name = paths[1]
|
||||
}
|
||||
account, ok := model.GetBalancedAccount(name)
|
||||
rawPath = utils.ParsePath(rawPath)
|
||||
account, ok := model.GetBalancedAccount(rawPath)
|
||||
if !ok {
|
||||
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
||||
return nil, "", nil, fmt.Errorf("path not found")
|
||||
}
|
||||
driver, ok := base.GetDriver(account.Type)
|
||||
if !ok {
|
||||
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
|
||||
}
|
||||
return &account, path, driver, nil
|
||||
name := utils.ParsePath(account.Name)
|
||||
bIndex := strings.LastIndex(name, ".balance")
|
||||
if bIndex != -1 {
|
||||
name = name[:bIndex]
|
||||
}
|
||||
return &account, strings.TrimPrefix(rawPath, name), driver, nil
|
||||
}
|
||||
|
||||
func ErrorResp(c *gin.Context, err error, code int) {
|
||||
|
33
server/common/files.go
Normal file
33
server/common/files.go
Normal file
@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/drivers/operate"
|
||||
"github.com/Xhofe/alist/model"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Path(rawPath string) (*model.File, []model.File, *model.Account, base.Driver, string, error) {
|
||||
account, path, driver, err := ParsePath(rawPath)
|
||||
if err != nil {
|
||||
if err.Error() == "path not found" {
|
||||
accountFiles := model.GetAccountFilesByPath(rawPath)
|
||||
if len(accountFiles) != 0 {
|
||||
return nil, accountFiles, nil, nil, path, nil
|
||||
}
|
||||
}
|
||||
return nil, nil, nil, nil, "", err
|
||||
}
|
||||
log.Debugln("use account: ", account.Name)
|
||||
file, files, err := operate.Path(driver, account, path)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, "", err
|
||||
}
|
||||
if file != nil {
|
||||
return file, nil, account, driver, path, nil
|
||||
} else {
|
||||
accountFiles := model.GetAccountFilesByPath(rawPath)
|
||||
files = append(files, accountFiles...)
|
||||
return nil, files, account, driver, path, nil
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/drivers/operate"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -37,7 +38,8 @@ func CreateAccount(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 500)
|
||||
} else {
|
||||
log.Debugf("new account: %+v", req)
|
||||
err = driver.Save(&req, nil)
|
||||
//err = driver.Save(&req, nil)
|
||||
err = operate.Save(driver, &req, nil)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -71,7 +73,8 @@ func SaveAccount(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 500)
|
||||
} else {
|
||||
log.Debugf("save account: %+v", req)
|
||||
err = driver.Save(&req, old)
|
||||
//err = driver.Save(&req, old)
|
||||
err = operate.Save(driver, &req, nil)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -93,7 +96,8 @@ func DeleteAccount(c *gin.Context) {
|
||||
} else {
|
||||
driver, ok := base.GetDriver(account.Type)
|
||||
if ok {
|
||||
_ = driver.Save(nil, account)
|
||||
//_ = driver.Save(nil, account)
|
||||
_ = operate.Save(driver, nil, account)
|
||||
} else {
|
||||
log.Errorf("no driver: %s", account.Type)
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"path"
|
||||
)
|
||||
|
||||
func Down(c *gin.Context) {
|
||||
@ -19,7 +18,7 @@ func Down(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
if driver.Config().OnlyProxy || account.Proxy || utils.IsContain(conf.DProxyTypes, path.Ext(rawPath)) {
|
||||
if driver.Config().OnlyProxy || account.Proxy || utils.IsContain(conf.DProxyTypes, utils.Ext(rawPath)) {
|
||||
Proxy(c)
|
||||
return
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/Xhofe/alist/drivers/operate"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -18,31 +17,18 @@ func Folder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var files = make([]model.File, 0)
|
||||
var err error
|
||||
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
|
||||
files, err = model.GetAccountFiles()
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
account, path, driver, err := common.ParsePath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
file, rawFiles, err := operate.Path(driver, account, path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
if file != nil {
|
||||
common.ErrorStrResp(c, "Not folder", 400)
|
||||
}
|
||||
for _, file := range rawFiles {
|
||||
if file.IsDir() {
|
||||
files = append(files, file)
|
||||
}
|
||||
_, rawFiles, _, _, _, err := common.Path(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
if rawFiles == nil {
|
||||
common.ErrorStrResp(c, "not a folder", 400)
|
||||
return
|
||||
}
|
||||
for _, file := range rawFiles {
|
||||
if file.IsDir() {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
c.JSON(200, common.Resp{
|
||||
|
@ -18,6 +18,10 @@ func RefreshFolder(c *gin.Context) {
|
||||
}
|
||||
account, path_, _, err := common.ParsePath(req.Path)
|
||||
if err != nil {
|
||||
if err.Error() == "path not found" && req.Path == "/" {
|
||||
common.SuccessResp(c)
|
||||
return
|
||||
}
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
"github.com/Xhofe/alist/drivers/operate"
|
||||
"github.com/Xhofe/alist/model"
|
||||
"github.com/Xhofe/alist/server/common"
|
||||
"github.com/Xhofe/alist/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Pagination(files []model.File, req *common.PathReq) (int, []model.File) {
|
||||
@ -78,39 +78,12 @@ func Path(c *gin.Context) {
|
||||
if meta != nil && meta.Upload {
|
||||
upload = true
|
||||
}
|
||||
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
|
||||
files, err := model.GetAccountFiles()
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
files = common.Hide(meta, files)
|
||||
}
|
||||
c.JSON(200, common.Resp{
|
||||
Code: 200,
|
||||
Message: "success",
|
||||
Data: PathResp{
|
||||
Type: "folder",
|
||||
Meta: Meta{
|
||||
Driver: "root",
|
||||
},
|
||||
Files: files,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
err := CheckPagination(&req)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
account, path, driver, err := common.ParsePath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
file, files, err := operate.Path(driver, account, path)
|
||||
file, files, account, driver, path, err := common.Path(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -119,7 +92,7 @@ func Path(c *gin.Context) {
|
||||
// 对于中转文件或只能中转,将链接修改为中转链接
|
||||
if driver.Config().OnlyProxy || account.Proxy {
|
||||
if account.DownProxyUrl != "" {
|
||||
file.Url = fmt.Sprintf("%s%s?sign=%s", account.DownProxyUrl, req.Path, utils.SignWithToken(file.Name, conf.Token))
|
||||
file.Url = fmt.Sprintf("%s%s?sign=%s", strings.Split(account.DownProxyUrl, "\n")[0], req.Path, utils.SignWithToken(file.Name, conf.Token))
|
||||
} else {
|
||||
file.Url = fmt.Sprintf("//%s/p%s?sign=%s", c.Request.Host, req.Path, utils.SignWithToken(file.Name, conf.Token))
|
||||
}
|
||||
@ -146,10 +119,14 @@ func Path(c *gin.Context) {
|
||||
if !ok {
|
||||
files = common.Hide(meta, files)
|
||||
}
|
||||
if driver.Config().LocalSort {
|
||||
model.SortFiles(files, account)
|
||||
driverName := "root"
|
||||
if driver != nil {
|
||||
if driver.Config().LocalSort {
|
||||
model.SortFiles(files, account)
|
||||
}
|
||||
model.ExtractFolder(files, account)
|
||||
driverName = driver.Config().Name
|
||||
}
|
||||
model.ExtractFolder(files, account)
|
||||
total, files := Pagination(files, &req)
|
||||
c.JSON(200, common.Resp{
|
||||
Code: 200,
|
||||
@ -157,7 +134,7 @@ func Path(c *gin.Context) {
|
||||
Data: PathResp{
|
||||
Type: "folder",
|
||||
Meta: Meta{
|
||||
Driver: driver.Config().Name,
|
||||
Driver: driverName,
|
||||
Upload: upload,
|
||||
Total: total,
|
||||
},
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Proxy(c *gin.Context) {
|
||||
@ -29,7 +30,7 @@ func Proxy(c *gin.Context) {
|
||||
// 4. 开启webdav中转(需要验证sign)
|
||||
if !account.Proxy && !driver.Config().OnlyProxy &&
|
||||
utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT &&
|
||||
!utils.IsContain(conf.DProxyTypes, filepath.Ext(rawPath)) {
|
||||
!utils.IsContain(conf.DProxyTypes, utils.Ext(rawPath)) {
|
||||
// 只开启了webdav中转,验证sign
|
||||
ok := false
|
||||
if account.WebdavProxy {
|
||||
@ -43,7 +44,7 @@ func Proxy(c *gin.Context) {
|
||||
// 中转时有中转机器使用中转机器,若携带标志位则表明不能再走中转机器了
|
||||
if account.DownProxyUrl != "" && c.Query("d") != "1" {
|
||||
name := utils.Base(rawPath)
|
||||
link := fmt.Sprintf("%s%s?sign=%s", account.DownProxyUrl, rawPath, utils.SignWithToken(name, conf.Token))
|
||||
link := fmt.Sprintf("%s%s?sign=%s", strings.Split(account.DownProxyUrl, "\n")[0], rawPath, utils.SignWithToken(name, conf.Token))
|
||||
c.Redirect(302, link)
|
||||
return
|
||||
}
|
||||
|
@ -50,17 +50,18 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
||||
|
||||
func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
|
||||
rawPath = utils.ParsePath(rawPath)
|
||||
var files []model.File
|
||||
var err error
|
||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||
files, err = model.GetAccountFiles()
|
||||
} else {
|
||||
account, path_, driver, err := common.ParsePath(rawPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files, err = operate.Files(driver, account, path_)
|
||||
}
|
||||
//var files []model.File
|
||||
//var err error
|
||||
//if model.AccountsCount() > 1 && rawPath == "/" {
|
||||
// files, err = model.GetAccountFilesByPath("/")
|
||||
//} else {
|
||||
// account, path_, driver, err := common.ParsePath(rawPath)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// files, err = operate.Files(driver, account, path_)
|
||||
//}
|
||||
_, files, _, _, _, err := common.Path(rawPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -125,10 +126,10 @@ func (fs *FileSystem) Link(w http.ResponseWriter, r *http.Request, rawPath strin
|
||||
}
|
||||
if driver.Config().OnlyProxy || account.WebdavProxy {
|
||||
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
||||
if conf.GetBool("check down link") {
|
||||
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
|
||||
link += "?sign=" + sign
|
||||
}
|
||||
//if conf.GetBool("check down link") {
|
||||
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
|
||||
link += "?sign=" + sign
|
||||
//}
|
||||
} else {
|
||||
link_, err := driver.Link(base.Args{Path: path_, IP: ClientIP(r)}, account)
|
||||
if err != nil {
|
||||
@ -156,29 +157,27 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error
|
||||
return operate.MakeDir(driver, account, path_, true)
|
||||
}
|
||||
|
||||
func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) error {
|
||||
func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) (FileInfo, error) {
|
||||
rawPath = utils.ParsePath(rawPath)
|
||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||
return ErrNotImplemented
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
account, path_, driver, err := common.ParsePath(rawPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, 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_)
|
||||
now := time.Now()
|
||||
fi := &model.File{
|
||||
Name: fileName,
|
||||
Size: 0,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
if fileSize == 0 {
|
||||
upFileMap[rawPath] = &model.File{
|
||||
Name: fileName,
|
||||
Size: 0,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
return nil
|
||||
// 如果文件大小为0,默认成功
|
||||
upFileMap[rawPath] = fi
|
||||
return fi, nil
|
||||
} else {
|
||||
delete(upFileMap, rawPath)
|
||||
}
|
||||
@ -189,7 +188,7 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
|
||||
Name: fileName,
|
||||
ParentPath: filePath,
|
||||
}
|
||||
return operate.Upload(driver, account, &fileData, true)
|
||||
return fi, operate.Upload(driver, account, &fileData, true)
|
||||
}
|
||||
|
||||
func (fs *FileSystem) Delete(rawPath string) error {
|
||||
|
@ -94,7 +94,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
||||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string, status int, err error) {
|
||||
token, err = h.LockSystem.Create(now, LockDetails{
|
||||
Root: root,
|
||||
@ -110,7 +109,6 @@ func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string
|
||||
return token, 0, nil
|
||||
}
|
||||
|
||||
// ok
|
||||
func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem) (release func(), status int, err error) {
|
||||
hdr := r.Header.Get("If")
|
||||
if hdr == "" {
|
||||
@ -189,7 +187,6 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem)
|
||||
return nil, http.StatusPreconditionFailed, ErrLocked
|
||||
}
|
||||
|
||||
//OK
|
||||
func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
@ -213,7 +210,6 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *File
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
@ -248,7 +244,6 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
@ -267,7 +262,6 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileS
|
||||
return http.StatusNoContent, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
@ -283,12 +277,13 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
err = fs.Upload(ctx, r, reqPath)
|
||||
fi, err := fs.Upload(ctx, r, reqPath)
|
||||
if err != nil {
|
||||
return http.StatusMethodNotAllowed, err
|
||||
}
|
||||
|
||||
_, fi := isPathExist(ctx, fs, reqPath)
|
||||
//_, fi := isPathExist(ctx, fs, reqPath)
|
||||
// 为防止有些网盘上传有延时,这里认为上传没有报错就已经成功了,不再去判断是否存在
|
||||
etag, err := findETag(ctx, fs, h.LockSystem, reqPath, fi)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@ -297,7 +292,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
||||
return http.StatusCreated, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
@ -325,7 +319,6 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSy
|
||||
return http.StatusCreated, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
hdr := r.Header.Get("Destination")
|
||||
@ -410,7 +403,6 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
|
||||
return moveFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") == "T")
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (retStatus int, retErr error) {
|
||||
|
||||
duration, err := parseTimeout(r.Header.Get("Timeout"))
|
||||
@ -506,7 +498,6 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSys
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
|
||||
@ -531,7 +522,6 @@ func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileS
|
||||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
|
28
utils/check.go
Normal file
28
utils/check.go
Normal file
@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func IsLocalIPAddr(ip string) bool {
|
||||
return IsLocalIP(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
func IsLocalIP(ip net.IP) bool {
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
if ip.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return ip4[0] == 10 || // 10.0.0.0/8
|
||||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
|
||||
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
|
||||
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
|
||||
}
|
59
utils/cookie/cookie.go
Normal file
59
utils/cookie/cookie.go
Normal file
@ -0,0 +1,59 @@
|
||||
package cookie
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Parse(str string) []*http.Cookie {
|
||||
header := http.Header{}
|
||||
header.Add("Cookie", str)
|
||||
request := http.Request{Header: header}
|
||||
return request.Cookies()
|
||||
}
|
||||
|
||||
func ToString(cookies []*http.Cookie) string {
|
||||
if cookies == nil {
|
||||
return ""
|
||||
}
|
||||
cookieStrings := make([]string, len(cookies))
|
||||
for i, cookie := range cookies {
|
||||
cookieStrings[i] = cookie.String()
|
||||
}
|
||||
return strings.Join(cookieStrings, ";")
|
||||
}
|
||||
|
||||
func SetCookie(cookies []*http.Cookie, name, value string) []*http.Cookie {
|
||||
for i, cookie := range cookies {
|
||||
if cookie.Name == name {
|
||||
cookies[i].Value = value
|
||||
return cookies
|
||||
}
|
||||
}
|
||||
cookies = append(cookies, &http.Cookie{Name: name, Value: value})
|
||||
return cookies
|
||||
}
|
||||
|
||||
func GetCookie(cookies []*http.Cookie, name string) *http.Cookie {
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == name {
|
||||
return cookie
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetStr(cookiesStr, name, value string) string {
|
||||
cookies := Parse(cookiesStr)
|
||||
cookies = SetCookie(cookies, name, value)
|
||||
return ToString(cookies)
|
||||
}
|
||||
|
||||
func GetStr(cookiesStr, name string) string {
|
||||
cookies := Parse(cookiesStr)
|
||||
cookie := GetCookie(cookies, name)
|
||||
if cookie == nil {
|
||||
return ""
|
||||
}
|
||||
return cookie.Value
|
||||
}
|
@ -35,7 +35,7 @@ func GetFileType(ext string) int {
|
||||
if ext == "" {
|
||||
return conf.UNKNOWN
|
||||
}
|
||||
ext = strings.ToLower(strings.TrimLeft(ext, "."))
|
||||
ext = strings.ToLower(strings.TrimPrefix(ext, "."))
|
||||
if IsContain(conf.OfficeTypes, ext) {
|
||||
return conf.OFFICE
|
||||
}
|
||||
@ -128,10 +128,6 @@ func Split(p string) (string, string) {
|
||||
return path.Split(p)
|
||||
}
|
||||
|
||||
// FormatName TODO
|
||||
func FormatName(name string) string {
|
||||
name = strings.ReplaceAll(name, "/", " ")
|
||||
name = strings.ReplaceAll(name, "#", " ")
|
||||
name = strings.ReplaceAll(name, "?", " ")
|
||||
return name
|
||||
func Ext(name string) string {
|
||||
return strings.TrimPrefix(path.Ext(name), ".")
|
||||
}
|
||||
|
@ -13,14 +13,12 @@ func GetSHA1Encode(data string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// GetMD5Encode
|
||||
func GetMD5Encode(data string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Get16MD5Encode
|
||||
func Get16MD5Encode(data string) string {
|
||||
return GetMD5Encode(data)[8:24]
|
||||
}
|
||||
|
Reference in New Issue
Block a user