Compare commits
18 Commits
v3.0.0-bet
...
v3.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
6aedd0f425 | |||
5ff0d850d7 | |||
cd73e34ccc | |||
107462e42e | |||
e6c2d22700 | |||
889ddcef7e | |||
68a6a0c40e | |||
969018db37 | |||
fba1471ec4 | |||
8b72ac7f80 | |||
77a6aa487b | |||
fd99c2197b | |||
9c91f062b9 | |||
537ca030b2 | |||
b00dcdec0d | |||
57bcd376b4 | |||
8d4d8648c6 | |||
35d177b67b |
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -12,9 +12,10 @@ body:
|
|||||||
label: Please make sure of the following things
|
label: Please make sure of the following things
|
||||||
description: You may select more than one, even select all.
|
description: You may select more than one, even select all.
|
||||||
options:
|
options:
|
||||||
- label: I have read the [documentation](https://alist-doc.nn.ci).
|
- label: I have read the [documentation](https://alist.nn.ci).
|
||||||
- label: I'm sure there are no duplicate issues or discussions.
|
- label: I'm sure there are no duplicate issues or discussions.
|
||||||
- label: I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).
|
- label: I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).
|
||||||
|
- label: I'm sure I'm using the latest version
|
||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -7,7 +7,7 @@ body:
|
|||||||
label: Please make sure of the following things
|
label: Please make sure of the following things
|
||||||
description: You may select more than one, even select all.
|
description: You may select more than one, even select all.
|
||||||
options:
|
options:
|
||||||
- label: I have read the [documentation](https://alist-doc.nn.ci).
|
- label: I have read the [documentation](https://alist.nn.ci).
|
||||||
- label: I'm sure there are no duplicate issues or discussions.
|
- label: I'm sure there are no duplicate issues or discussions.
|
||||||
- label: I'm sure this feature is not implemented.
|
- label: I'm sure this feature is not implemented.
|
||||||
- label: I'm sure it's a reasonable and popular requirement.
|
- label: I'm sure it's a reasonable and popular requirement.
|
||||||
|
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
i@nn.ci.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md)
|
English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contri
|
|||||||
- [x] [YandexDisk](https://disk.yandex.com/)
|
- [x] [YandexDisk](https://disk.yandex.com/)
|
||||||
- [x] [BaiduNetdisk](http://pan.baidu.com/)
|
- [x] [BaiduNetdisk](http://pan.baidu.com/)
|
||||||
- [x] [Quark](https://pan.quark.cn)
|
- [x] [Quark](https://pan.quark.cn)
|
||||||
|
- [x] [Thunder](https://pan.xunlei.com)
|
||||||
- [x] Easy to deploy and out-of-the-box
|
- [x] Easy to deploy and out-of-the-box
|
||||||
- [x] File preview (PDF, markdown, code, plain text, ...)
|
- [x] File preview (PDF, markdown, code, plain text, ...)
|
||||||
- [x] Image preview in gallery mode
|
- [x] Image preview in gallery mode
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[English](./README.md) | 中文 | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md)
|
[English](./README.md) | 中文 | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -51,6 +51,7 @@
|
|||||||
- [x] [Yandex.Disk](https://disk.yandex.com/)
|
- [x] [Yandex.Disk](https://disk.yandex.com/)
|
||||||
- [x] [百度网盘](http://pan.baidu.com/)
|
- [x] [百度网盘](http://pan.baidu.com/)
|
||||||
- [x] [夸克网盘](https://pan.quark.cn)
|
- [x] [夸克网盘](https://pan.quark.cn)
|
||||||
|
- [x] [迅雷网盘](https://pan.xunlei.com)
|
||||||
- [x] 部署方便,开箱即用
|
- [x] 部署方便,开箱即用
|
||||||
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
||||||
- [x] 画廊模式下的图像预览
|
- [x] 画廊模式下的图像预览
|
||||||
|
8
build.sh
8
build.sh
@ -25,11 +25,11 @@ ldflags="\
|
|||||||
"
|
"
|
||||||
|
|
||||||
FetchWebDev() {
|
FetchWebDev() {
|
||||||
curl -L https://codeload.github.com/alist-org/web-dist/tar.gz/refs/heads/main -o web-dist-main.tar.gz
|
curl -L https://codeload.github.com/alist-org/web-dist/tar.gz/refs/heads/dev -o web-dist-dev.tar.gz
|
||||||
tar -zxvf web-dist-main.tar.gz
|
tar -zxvf web-dist-dev.tar.gz
|
||||||
rm -rf public/dist
|
rm -rf public/dist
|
||||||
mv -f web-dist-main/dist public
|
mv -f web-dist-dev/dist public
|
||||||
rm -rf web-dist-main web-dist-main.tar.gz
|
rm -rf web-dist-dev web-dist-dev.tar.gz
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchWebRelease() {
|
FetchWebRelease() {
|
||||||
|
@ -257,8 +257,4 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan123) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Pan123)(nil)
|
var _ driver.Driver = (*Pan123)(nil)
|
||||||
|
@ -319,8 +319,4 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Yun139) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Yun139)(nil)
|
var _ driver.Driver = (*Yun139)(nil)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -208,8 +207,4 @@ func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
return d.newUpload(dstDir, stream, up)
|
return d.newUpload(dstDir, stream, up)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Cloud189) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Cloud189)(nil)
|
var _ driver.Driver = (*Cloud189)(nil)
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -278,7 +277,3 @@ func (y *Yun189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
}
|
}
|
||||||
return y.CommonUpload(ctx, dstDir, stream, up)
|
return y.CommonUpload(ctx, dstDir, stream, up)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (y *Yun189PC) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
@ -78,8 +78,7 @@ func timestamp() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MustParseTime(str string) *time.Time {
|
func MustParseTime(str string) *time.Time {
|
||||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05 -07", str+" +08", time.Local)
|
||||||
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", str, loc)
|
|
||||||
return &lastOpTime
|
return &lastOpTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ type Addition struct {
|
|||||||
Username string `json:"username" required:"true"`
|
Username string `json:"username" required:"true"`
|
||||||
Password string `json:"password" required:"true"`
|
Password string `json:"password" required:"true"`
|
||||||
VCode string `json:"validate_code"`
|
VCode string `json:"validate_code"`
|
||||||
RootFolderID string `json:"root_folder_id"`
|
driver.RootID
|
||||||
OrderBy string `json:"order_by" type:"select" options:"filename,filesize,lastOpTime" default:"filename"`
|
OrderBy string `json:"order_by" type:"select" options:"filename,filesize,lastOpTime" default:"filename"`
|
||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||||
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
|
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
|
||||||
@ -17,10 +17,6 @@ type Addition struct {
|
|||||||
RapidUpload bool `json:"rapid_upload"`
|
RapidUpload bool `json:"rapid_upload"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Addition) GetRootId() string {
|
|
||||||
return a.RootFolderID
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
Name: "189CloudPC",
|
Name: "189CloudPC",
|
||||||
DefaultRoot: "-11",
|
DefaultRoot: "-11",
|
||||||
|
@ -140,13 +140,7 @@ func (y *Yun189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, er
|
|||||||
fullUrl += "/listFiles.action"
|
fullUrl += "/listFiles.action"
|
||||||
|
|
||||||
res := make([]model.Obj, 0, 130)
|
res := make([]model.Obj, 0, 130)
|
||||||
for pageNum := 1; pageNum < 100; pageNum++ {
|
for pageNum := 1; ; pageNum++ {
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp Cloud189FilesResp
|
var resp Cloud189FilesResp
|
||||||
_, err := y.get(fullUrl, func(r *resty.Request) {
|
_, err := y.get(fullUrl, func(r *resty.Request) {
|
||||||
r.SetContext(ctx)
|
r.SetContext(ctx)
|
||||||
|
@ -264,8 +264,10 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
if count > 0 {
|
||||||
up(i * 100 / count)
|
up(i * 100 / count)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var resp2 base.Json
|
var resp2 base.Json
|
||||||
_, err, e = d.request("https://api.aliyundrive.com/v2/file/complete", http.MethodPost, func(req *resty.Request) {
|
_, err, e = d.request("https://api.aliyundrive.com/v2/file/complete", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(base.Json{
|
req.SetBody(base.Json{
|
||||||
@ -284,7 +286,29 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
func (d *AliDrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
var resp base.Json
|
||||||
|
var url string
|
||||||
|
data := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": args.Obj.GetID(),
|
||||||
|
}
|
||||||
|
switch args.Method {
|
||||||
|
case "doc_preview":
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
||||||
|
data["access_token"] = d.AccessToken
|
||||||
|
case "video_preview":
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
||||||
|
data["category"] = "live_transcoding"
|
||||||
|
default:
|
||||||
return nil, errs.NotSupport
|
return nil, errs.NotSupport
|
||||||
|
}
|
||||||
|
_, err, _ := d.request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data)
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ driver.Driver = (*AliDrive)(nil)
|
var _ driver.Driver = (*AliDrive)(nil)
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
_ "github.com/alist-org/alist/v3/drivers/s3"
|
_ "github.com/alist-org/alist/v3/drivers/s3"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/sftp"
|
_ "github.com/alist-org/alist/v3/drivers/sftp"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/teambition"
|
_ "github.com/alist-org/alist/v3/drivers/teambition"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/thunder"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/uss"
|
_ "github.com/alist-org/alist/v3/drivers/uss"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/virtual"
|
_ "github.com/alist-org/alist/v3/drivers/virtual"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/webdav"
|
_ "github.com/alist-org/alist/v3/drivers/webdav"
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -202,7 +201,7 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
|||||||
"uploadid": precreateResp.Uploadid,
|
"uploadid": precreateResp.Uploadid,
|
||||||
}
|
}
|
||||||
left = stream.GetSize()
|
left = stream.GetSize()
|
||||||
for _, partseq := range precreateResp.BlockList {
|
for i, partseq := range precreateResp.BlockList {
|
||||||
byteSize := Default
|
byteSize := Default
|
||||||
var byteData []byte
|
var byteData []byte
|
||||||
if left < Default {
|
if left < Default {
|
||||||
@ -223,13 +222,12 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugln(res.String())
|
log.Debugln(res.String())
|
||||||
|
if len(precreateResp.BlockList) > 0 {
|
||||||
|
up(i * 100 / len(precreateResp.BlockList))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_, err = d.create(path, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
|
_, err = d.create(path, stream.GetSize(), 0, precreateResp.Uploadid, block_list_str)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BaiduNetdisk) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*BaiduNetdisk)(nil)
|
var _ driver.Driver = (*BaiduNetdisk)(nil)
|
||||||
|
@ -127,8 +127,4 @@ func (d *FTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream
|
|||||||
return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream)
|
return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FTP) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*FTP)(nil)
|
var _ driver.Driver = (*FTP)(nil)
|
||||||
|
@ -141,8 +141,4 @@ func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *GoogleDrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*GoogleDrive)(nil)
|
var _ driver.Driver = (*GoogleDrive)(nil)
|
||||||
|
@ -217,8 +217,4 @@ func (d *Local) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Local) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Local)(nil)
|
var _ driver.Driver = (*Local)(nil)
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
@ -221,8 +220,4 @@ func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MediaTrack) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*MediaTrack)(nil)
|
var _ driver.Driver = (*MediaTrack)(nil)
|
||||||
|
@ -144,8 +144,4 @@ func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Onedrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Onedrive)(nil)
|
var _ driver.Driver = (*Onedrive)(nil)
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
@ -199,8 +198,4 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *PikPak) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*PikPak)(nil)
|
var _ driver.Driver = (*PikPak)(nil)
|
||||||
|
@ -215,8 +215,4 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
|
|||||||
return d.upFinish(pre)
|
return d.upFinish(pre)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Quark) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Quark)(nil)
|
var _ driver.Driver = (*Quark)(nil)
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
@ -153,8 +152,4 @@ func (d *S3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreame
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *S3) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*S3)(nil)
|
var _ driver.Driver = (*S3)(nil)
|
||||||
|
@ -99,8 +99,4 @@ func (d *SFTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SFTP) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*SFTP)(nil)
|
var _ driver.Driver = (*SFTP)(nil)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -156,8 +155,4 @@ func (d *Teambition) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||||||
return d.finishUpload(newFile, dstDir.GetID())
|
return d.finishUpload(newFile, dstDir.GetID())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Teambition) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Teambition)(nil)
|
var _ driver.Driver = (*Teambition)(nil)
|
||||||
|
@ -42,10 +42,10 @@ func (d *Template) List(ctx context.Context, dir model.Obj, args model.ListArgs)
|
|||||||
return nil, errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Get(ctx context.Context, path string) (model.Obj, error) {
|
//func (d *Template) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
// this is optional
|
// // this is optional
|
||||||
return nil, errs.NotImplement
|
// return nil, errs.NotImplement
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
// TODO return link of file
|
// TODO return link of file
|
||||||
@ -82,8 +82,8 @@ func (d *Template) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
return nil, errs.NotSupport
|
// return nil, errs.NotSupport
|
||||||
}
|
//}
|
||||||
|
|
||||||
var _ driver.Driver = (*Template)(nil)
|
var _ driver.Driver = (*Template)(nil)
|
||||||
|
493
drivers/thunder/driver.go
Normal file
493
drivers/thunder/driver.go
Normal file
@ -0,0 +1,493 @@
|
|||||||
|
package thunder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Thunder struct {
|
||||||
|
*XunLeiCommon
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
|
||||||
|
identity string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Thunder) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Thunder) GetAddition() driver.Additional {
|
||||||
|
return x.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Thunder) Init(ctx context.Context, storage model.Storage) (err error) {
|
||||||
|
x.Storage = storage
|
||||||
|
if err = utils.Json.UnmarshalFromString(x.Storage.Addition, &x.Addition); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化所需参数
|
||||||
|
if x.XunLeiCommon == nil {
|
||||||
|
x.XunLeiCommon = &XunLeiCommon{
|
||||||
|
Common: &Common{
|
||||||
|
client: base.NewRestyClient(),
|
||||||
|
Algorithms: []string{
|
||||||
|
"HPxr4BVygTQVtQkIMwQH33ywbgYG5l4JoR",
|
||||||
|
"GzhNkZ8pOBsCY+7",
|
||||||
|
"v+l0ImTpG7c7/",
|
||||||
|
"e5ztohgVXNP",
|
||||||
|
"t",
|
||||||
|
"EbXUWyVVqQbQX39Mbjn2geok3/0WEkAVxeqhtx857++kjJiRheP8l77gO",
|
||||||
|
"o7dvYgbRMOpHXxCs",
|
||||||
|
"6MW8TD8DphmakaxCqVrfv7NReRRN7ck3KLnXBculD58MvxjFRqT+",
|
||||||
|
"kmo0HxCKVfmxoZswLB4bVA/dwqbVAYghSb",
|
||||||
|
"j",
|
||||||
|
"4scKJNdd7F27Hv7tbt",
|
||||||
|
},
|
||||||
|
DeviceID: "9aa5c268e7bcfc197a9ad88e2fb330e5",
|
||||||
|
ClientID: "Xp6vsxz_7IYVw2BB",
|
||||||
|
ClientSecret: "Xp6vsy4tN9toTVdMSpomVdXpRmES",
|
||||||
|
ClientVersion: "7.51.0.8196",
|
||||||
|
PackageName: "com.xunlei.downloadprovider",
|
||||||
|
UserAgent: "ANDROID-com.xunlei.downloadprovider/7.51.0.8196 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)",
|
||||||
|
DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)",
|
||||||
|
},
|
||||||
|
refreshTokenFunc: func() error {
|
||||||
|
// 通过RefreshToken刷新
|
||||||
|
token, err := x.RefreshToken(x.TokenResp.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
// 重新登录
|
||||||
|
token, err = x.Login(x.Username, x.Password)
|
||||||
|
if err != nil {
|
||||||
|
x.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error()))
|
||||||
|
op.MustSaveDriverStorage(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义验证码token
|
||||||
|
ctoekn := strings.TrimSpace(x.CaptchaToken)
|
||||||
|
if ctoekn != "" {
|
||||||
|
x.SetCaptchaToken(ctoekn)
|
||||||
|
x.CaptchaToken = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防止重复登录
|
||||||
|
identity := x.GetIdentity()
|
||||||
|
if x.identity != identity || !x.IsLogin() {
|
||||||
|
x.identity = identity
|
||||||
|
// 登录
|
||||||
|
token, err := x.Login(x.Username, x.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Thunder) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ThunderExpert struct {
|
||||||
|
*XunLeiCommon
|
||||||
|
model.Storage
|
||||||
|
ExpertAddition
|
||||||
|
|
||||||
|
identity string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ThunderExpert) Config() driver.Config {
|
||||||
|
return configExpert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ThunderExpert) GetAddition() driver.Additional {
|
||||||
|
return x.ExpertAddition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ThunderExpert) Init(ctx context.Context, storage model.Storage) (err error) {
|
||||||
|
x.Storage = storage
|
||||||
|
if err = utils.Json.UnmarshalFromString(x.Storage.Addition, &x.ExpertAddition); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防止重复登录
|
||||||
|
identity := x.GetIdentity()
|
||||||
|
if identity != x.identity || !x.IsLogin() {
|
||||||
|
x.identity = identity
|
||||||
|
x.XunLeiCommon = &XunLeiCommon{
|
||||||
|
Common: &Common{
|
||||||
|
client: base.NewRestyClient(),
|
||||||
|
|
||||||
|
DeviceID: x.DeviceID,
|
||||||
|
ClientID: x.ClientID,
|
||||||
|
ClientSecret: x.ClientSecret,
|
||||||
|
ClientVersion: x.ClientVersion,
|
||||||
|
PackageName: x.PackageName,
|
||||||
|
UserAgent: x.UserAgent,
|
||||||
|
DownloadUserAgent: x.DownloadUserAgent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.CaptchaToken != "" {
|
||||||
|
x.SetCaptchaToken(x.CaptchaToken)
|
||||||
|
x.CaptchaToken = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 签名方法
|
||||||
|
if x.SignType == "captcha_sign" {
|
||||||
|
x.Common.Timestamp = x.Timestamp
|
||||||
|
x.Common.CaptchaSign = x.CaptchaSign
|
||||||
|
} else {
|
||||||
|
x.Common.Algorithms = strings.Split(x.Algorithms, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录方式
|
||||||
|
if x.LoginType == "refresh_token" {
|
||||||
|
// 通过RefreshToken登录
|
||||||
|
token, err := x.XunLeiCommon.RefreshToken(x.ExpertAddition.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
|
||||||
|
// 刷新token方法
|
||||||
|
x.SetRefreshTokenFunc(func() error {
|
||||||
|
token, err := x.XunLeiCommon.RefreshToken(x.TokenResp.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
x.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error()))
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
op.MustSaveDriverStorage(x)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 通过用户密码登录
|
||||||
|
token, err := x.Login(x.Username, x.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
x.SetRefreshTokenFunc(func() error {
|
||||||
|
token, err := x.XunLeiCommon.RefreshToken(x.TokenResp.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
token, err = x.Login(x.Username, x.Password)
|
||||||
|
if err != nil {
|
||||||
|
x.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.SetTokenResp(token)
|
||||||
|
op.MustSaveDriverStorage(x)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 仅修改验证码token
|
||||||
|
if x.CaptchaToken != "" {
|
||||||
|
x.SetCaptchaToken(x.CaptchaToken)
|
||||||
|
x.CaptchaToken = ""
|
||||||
|
}
|
||||||
|
x.XunLeiCommon.UserAgent = x.UserAgent
|
||||||
|
x.XunLeiCommon.DownloadUserAgent = x.DownloadUserAgent
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ThunderExpert) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ThunderExpert) SetTokenResp(token *TokenResp) {
|
||||||
|
x.XunLeiCommon.SetTokenResp(token)
|
||||||
|
if token != nil {
|
||||||
|
x.ExpertAddition.RefreshToken = token.RefreshToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type XunLeiCommon struct {
|
||||||
|
*Common
|
||||||
|
*TokenResp // 登录信息
|
||||||
|
|
||||||
|
refreshTokenFunc func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
return xc.getFiles(ctx, dir.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
var lFile Files
|
||||||
|
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodGet, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetPathParam("fileID", file.GetID())
|
||||||
|
//r.SetQueryParam("space", "")
|
||||||
|
}, &lFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
link := &model.Link{
|
||||||
|
URL: lFile.WebContentLink,
|
||||||
|
Header: http.Header{
|
||||||
|
"User-Agent": {xc.DownloadUserAgent},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
strs := regexp.MustCompile(`e=([0-9]*)`).FindStringSubmatch(lFile.WebContentLink)
|
||||||
|
if len(strs) == 2 {
|
||||||
|
timestamp, err := strconv.ParseInt(strs[1], 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
expired := time.Duration(timestamp-time.Now().Unix()) * time.Second
|
||||||
|
link.Expiration = &expired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
_, err := xc.Request(FILE_API_URL, http.MethodPost, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetBody(&base.Json{
|
||||||
|
"kind": FOLDER,
|
||||||
|
"name": dirName,
|
||||||
|
"parent_id": parentDir.GetID(),
|
||||||
|
})
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
_, err := xc.Request(FILE_API_URL+":batchMove", http.MethodPost, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetBody(&base.Json{
|
||||||
|
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||||
|
"ids": []string{srcObj.GetID()},
|
||||||
|
})
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodPatch, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetPathParam("fileID", srcObj.GetID())
|
||||||
|
r.SetBody(&base.Json{"name": newName})
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
_, err := xc.Request(FILE_API_URL+":batchCopy", http.MethodPost, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetBody(&base.Json{
|
||||||
|
"to": base.Json{"parent_id": dstDir.GetID()},
|
||||||
|
"ids": []string{srcObj.GetID()},
|
||||||
|
})
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
_, err := xc.Request(FILE_API_URL+"/{fileID}/trash", http.MethodPatch, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetPathParam("fileID", obj.GetID())
|
||||||
|
r.SetBody("{}")
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
var resp UploadTaskResponse
|
||||||
|
_, err := xc.Request(FILE_API_URL, http.MethodPost, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetBody(&base.Json{
|
||||||
|
"kind": FILE,
|
||||||
|
"parent_id": dstDir.GetID(),
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"size": stream.GetSize(),
|
||||||
|
"hash": "1CF254FBC456E1B012CD45C546636AA62CF8350E",
|
||||||
|
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||||
|
})
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
param := resp.Resumable.Params
|
||||||
|
if resp.UploadType == UPLOAD_TYPE_RESUMABLE {
|
||||||
|
param.Endpoint = strings.TrimLeft(param.Endpoint, param.Bucket+".")
|
||||||
|
s, err := session.NewSession(&aws.Config{
|
||||||
|
Credentials: credentials.NewStaticCredentials(param.AccessKeyID, param.AccessKeySecret, param.SecurityToken),
|
||||||
|
Region: aws.String("xunlei"),
|
||||||
|
Endpoint: aws.String(param.Endpoint),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = s3manager.NewUploader(s).UploadWithContext(ctx, &s3manager.UploadInput{
|
||||||
|
Bucket: aws.String(param.Bucket),
|
||||||
|
Key: aws.String(param.Key),
|
||||||
|
Expires: aws.Time(param.Expiration),
|
||||||
|
Body: stream,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) getFiles(ctx context.Context, folderId string) ([]model.Obj, error) {
|
||||||
|
files := make([]model.Obj, 0)
|
||||||
|
var pageToken string
|
||||||
|
for {
|
||||||
|
var fileList FileList
|
||||||
|
_, err := xc.Request(FILE_API_URL, http.MethodGet, func(r *resty.Request) {
|
||||||
|
r.SetContext(ctx)
|
||||||
|
r.SetQueryParams(map[string]string{
|
||||||
|
"space": "",
|
||||||
|
"__type": "drive",
|
||||||
|
"refresh": "true",
|
||||||
|
"__sync": "true",
|
||||||
|
"parent_id": folderId,
|
||||||
|
"page_token": pageToken,
|
||||||
|
"with_audit": "true",
|
||||||
|
"limit": "100",
|
||||||
|
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
|
||||||
|
})
|
||||||
|
}, &fileList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(fileList.Files); i++ {
|
||||||
|
files = append(files, &fileList.Files[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileList.NextPageToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pageToken = fileList.NextPageToken
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置刷新Token的方法
|
||||||
|
func (xc *XunLeiCommon) SetRefreshTokenFunc(fn func() error) {
|
||||||
|
xc.refreshTokenFunc = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置Token
|
||||||
|
func (xc *XunLeiCommon) SetTokenResp(tr *TokenResp) {
|
||||||
|
xc.TokenResp = tr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 携带Authorization和CaptchaToken的请求
|
||||||
|
func (xc *XunLeiCommon) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
data, err := xc.Common.Request(url, method, func(req *resty.Request) {
|
||||||
|
req.SetHeaders(map[string]string{
|
||||||
|
"Authorization": xc.Token(),
|
||||||
|
"X-Captcha-Token": xc.GetCaptchaToken(),
|
||||||
|
})
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
}, resp)
|
||||||
|
|
||||||
|
errResp, ok := err.(*ErrResp)
|
||||||
|
if !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch errResp.ErrorCode {
|
||||||
|
case 0:
|
||||||
|
return data, nil
|
||||||
|
case 4122, 4121, 10, 16:
|
||||||
|
if xc.refreshTokenFunc != nil {
|
||||||
|
if err = xc.refreshTokenFunc(); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
case 9: // 验证码token过期
|
||||||
|
if err = xc.RefreshCaptchaTokenAtLogin(GetAction(method, url), xc.UserID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return xc.Request(url, method, callback, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新Token
|
||||||
|
func (xc *XunLeiCommon) RefreshToken(refreshToken string) (*TokenResp, error) {
|
||||||
|
var resp TokenResp
|
||||||
|
_, err := xc.Common.Request(XLUSER_API_URL+"/auth/token", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(&base.Json{
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": refreshToken,
|
||||||
|
"client_id": xc.ClientID,
|
||||||
|
"client_secret": xc.ClientSecret,
|
||||||
|
})
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.RefreshToken == "" {
|
||||||
|
return nil, errs.EmptyToken
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
func (xc *XunLeiCommon) Login(username, password string) (*TokenResp, error) {
|
||||||
|
url := XLUSER_API_URL + "/auth/signin"
|
||||||
|
err := xc.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp TokenResp
|
||||||
|
_, err = xc.Common.Request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(&SignInRequest{
|
||||||
|
CaptchaToken: xc.GetCaptchaToken(),
|
||||||
|
ClientID: xc.ClientID,
|
||||||
|
ClientSecret: xc.ClientSecret,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
})
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xc *XunLeiCommon) IsLogin() bool {
|
||||||
|
if xc.TokenResp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := xc.Request(XLUSER_API_URL+"/user/me", http.MethodGet, nil, nil)
|
||||||
|
return err == nil
|
||||||
|
}
|
99
drivers/thunder/meta.go
Normal file
99
drivers/thunder/meta.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package thunder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 高级设置
|
||||||
|
type ExpertAddition struct {
|
||||||
|
driver.RootID
|
||||||
|
|
||||||
|
LoginType string `json:"login_type" type:"select" options:"user,refresh_token" default:"user"`
|
||||||
|
SignType string `json:"sign_type" type:"select" options:"algorithms,captcha_sign" default:"algorithms"`
|
||||||
|
|
||||||
|
// 登录方式1
|
||||||
|
Username string `json:"username" required:"true" help:"login type is user,this is required"`
|
||||||
|
Password string `json:"password" required:"true" help:"login type is user,this is required"`
|
||||||
|
// 登录方式2
|
||||||
|
RefreshToken string `json:"refresh_token" required:"true" help:"login type is refresh_token,this is required"`
|
||||||
|
|
||||||
|
// 签名方法1
|
||||||
|
Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"HPxr4BVygTQVtQkIMwQH33ywbgYG5l4JoR,GzhNkZ8pOBsCY+7,v+l0ImTpG7c7/,e5ztohgVXNP,t,EbXUWyVVqQbQX39Mbjn2geok3/0WEkAVxeqhtx857++kjJiRheP8l77gO,o7dvYgbRMOpHXxCs,6MW8TD8DphmakaxCqVrfv7NReRRN7ck3KLnXBculD58MvxjFRqT+,kmo0HxCKVfmxoZswLB4bVA/dwqbVAYghSb,j,4scKJNdd7F27Hv7tbt"`
|
||||||
|
// 签名方法2
|
||||||
|
CaptchaSign string `json:"captcha_sign" required:"true" help:"sign type is captcha_sign,this is required"`
|
||||||
|
Timestamp string `json:"timestamp" required:"true" help:"sign type is captcha_sign,this is required"`
|
||||||
|
|
||||||
|
// 验证码
|
||||||
|
CaptchaToken string `json:"captcha_token"`
|
||||||
|
|
||||||
|
// 必要且影响登录,由签名决定
|
||||||
|
DeviceID string `json:"device_id" required:"true" default:"9aa5c268e7bcfc197a9ad88e2fb330e5"`
|
||||||
|
ClientID string `json:"client_id" required:"true" default:"Xp6vsxz_7IYVw2BB"`
|
||||||
|
ClientSecret string `json:"client_secret" required:"true" default:"Xp6vsy4tN9toTVdMSpomVdXpRmES"`
|
||||||
|
ClientVersion string `json:"client_version" required:"true" default:"7.51.0.8196"`
|
||||||
|
PackageName string `json:"package_name" required:"true" default:"com.xunlei.downloadprovider"`
|
||||||
|
|
||||||
|
//不影响登录,影响下载速度
|
||||||
|
UserAgent string `json:"user_agent" required:"true" default:"ANDROID-com.xunlei.downloadprovider/7.51.0.8196 netWorkType/4G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gdcf98eab238b) (JAVA 0)"`
|
||||||
|
DownloadUserAgent string `json:"download_user_agent" required:"true" default:"Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录特征,用于判断是否重新登录
|
||||||
|
func (i *ExpertAddition) GetIdentity() string {
|
||||||
|
hash := md5.New()
|
||||||
|
if i.LoginType == "refresh_token" {
|
||||||
|
hash.Write([]byte(i.RefreshToken))
|
||||||
|
} else {
|
||||||
|
hash.Write([]byte(i.Username + i.Password))
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.SignType == "captcha_sign" {
|
||||||
|
hash.Write([]byte(i.CaptchaSign + i.Timestamp))
|
||||||
|
} else {
|
||||||
|
hash.Write([]byte(i.Algorithms))
|
||||||
|
}
|
||||||
|
|
||||||
|
hash.Write([]byte(i.DeviceID))
|
||||||
|
hash.Write([]byte(i.ClientID))
|
||||||
|
hash.Write([]byte(i.ClientSecret))
|
||||||
|
hash.Write([]byte(i.ClientVersion))
|
||||||
|
hash.Write([]byte(i.PackageName))
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
Username string `json:"username" required:"true"`
|
||||||
|
Password string `json:"password" required:"true"`
|
||||||
|
CaptchaToken string `json:"captcha_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录特征,用于判断是否重新登录
|
||||||
|
func (i *Addition) GetIdentity() string {
|
||||||
|
return utils.GetMD5Encode(i.Username + i.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "Thunder",
|
||||||
|
LocalSort: true,
|
||||||
|
OnlyProxy: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var configExpert = driver.Config{
|
||||||
|
Name: "ThunderExpert",
|
||||||
|
LocalSort: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(config, func() driver.Driver {
|
||||||
|
return &Thunder{}
|
||||||
|
})
|
||||||
|
op.RegisterDriver(configExpert, func() driver.Driver {
|
||||||
|
return &ThunderExpert{}
|
||||||
|
})
|
||||||
|
}
|
188
drivers/thunder/types.go
Normal file
188
drivers/thunder/types.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package thunder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrResp struct {
|
||||||
|
ErrorCode int64 `json:"error_code"`
|
||||||
|
ErrorMsg string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
// ErrorDetails interface{} `json:"error_details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrResp) IsError() bool {
|
||||||
|
return e.ErrorCode != 0 || e.ErrorMsg != "" || e.ErrorDescription != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrResp) Error() string {
|
||||||
|
return fmt.Sprintf("ErrorCode: %d ,Error: %s ,ErrorDescription: %s ", e.ErrorCode, e.ErrorMsg, e.ErrorDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 验证码Token
|
||||||
|
**/
|
||||||
|
type CaptchaTokenRequest struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
CaptchaToken string `json:"captcha_token"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
DeviceID string `json:"device_id"`
|
||||||
|
Meta map[string]string `json:"meta"`
|
||||||
|
RedirectUri string `json:"redirect_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptchaTokenResponse struct {
|
||||||
|
CaptchaToken string `json:"captcha_token"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 登录
|
||||||
|
**/
|
||||||
|
type TokenResp struct {
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
|
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TokenResp) Token() string {
|
||||||
|
return fmt.Sprint(t.TokenType, " ", t.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignInRequest struct {
|
||||||
|
CaptchaToken string `json:"captcha_token"`
|
||||||
|
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 文件
|
||||||
|
**/
|
||||||
|
type FileList struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
NextPageToken string `json:"next_page_token"`
|
||||||
|
Files []Files `json:"files"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
VersionOutdated bool `json:"version_outdated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
ParentID string `json:"parent_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
//UserID string `json:"user_id"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
//Revision string `json:"revision"`
|
||||||
|
//FileExtension string `json:"file_extension"`
|
||||||
|
//MimeType string `json:"mime_type"`
|
||||||
|
//Starred bool `json:"starred"`
|
||||||
|
WebContentLink string `json:"web_content_link"`
|
||||||
|
CreatedTime time.Time `json:"created_time"`
|
||||||
|
ModifiedTime time.Time `json:"modified_time"`
|
||||||
|
IconLink string `json:"icon_link"`
|
||||||
|
ThumbnailLink string `json:"thumbnail_link"`
|
||||||
|
//Md5Checksum string `json:"md5_checksum"`
|
||||||
|
//Hash string `json:"hash"`
|
||||||
|
//Links struct{} `json:"links"`
|
||||||
|
Phase string `json:"phase"`
|
||||||
|
Audit struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
} `json:"audit"`
|
||||||
|
/* Medias []struct {
|
||||||
|
Category string `json:"category"`
|
||||||
|
IconLink string `json:"icon_link"`
|
||||||
|
IsDefault bool `json:"is_default"`
|
||||||
|
IsOrigin bool `json:"is_origin"`
|
||||||
|
IsVisible bool `json:"is_visible"`
|
||||||
|
//Link interface{} `json:"link"`
|
||||||
|
MediaID string `json:"media_id"`
|
||||||
|
MediaName string `json:"media_name"`
|
||||||
|
NeedMoreQuota bool `json:"need_more_quota"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
RedirectLink string `json:"redirect_link"`
|
||||||
|
ResolutionName string `json:"resolution_name"`
|
||||||
|
Video struct {
|
||||||
|
AudioCodec string `json:"audio_codec"`
|
||||||
|
BitRate int `json:"bit_rate"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
FrameRate int `json:"frame_rate"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
VideoCodec string `json:"video_codec"`
|
||||||
|
VideoType string `json:"video_type"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
} `json:"video"`
|
||||||
|
VipTypes []string `json:"vip_types"`
|
||||||
|
} `json:"medias"` */
|
||||||
|
Trashed bool `json:"trashed"`
|
||||||
|
DeleteTime string `json:"delete_time"`
|
||||||
|
OriginalURL string `json:"original_url"`
|
||||||
|
//Params struct{} `json:"params"`
|
||||||
|
//OriginalFileIndex int `json:"original_file_index"`
|
||||||
|
//Space string `json:"space"`
|
||||||
|
//Apps []interface{} `json:"apps"`
|
||||||
|
//Writable bool `json:"writable"`
|
||||||
|
//FolderType string `json:"folder_type"`
|
||||||
|
//Collection interface{} `json:"collection"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Files) GetSize() int64 { size, _ := strconv.ParseInt(c.Size, 10, 64); return size }
|
||||||
|
func (c *Files) GetName() string { return c.Name }
|
||||||
|
func (c *Files) ModTime() time.Time { return c.ModifiedTime }
|
||||||
|
func (c *Files) IsDir() bool { return c.Kind == FOLDER }
|
||||||
|
func (c *Files) GetID() string { return c.ID }
|
||||||
|
func (c *Files) GetPath() string { return "" }
|
||||||
|
func (c *Files) Thumb() string { return c.ThumbnailLink }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 上传
|
||||||
|
**/
|
||||||
|
type UploadTaskResponse struct {
|
||||||
|
UploadType string `json:"upload_type"`
|
||||||
|
|
||||||
|
/*//UPLOAD_TYPE_FORM
|
||||||
|
Form struct {
|
||||||
|
//Headers struct{} `json:"headers"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
MultiParts struct {
|
||||||
|
OSSAccessKeyID string `json:"OSSAccessKeyId"`
|
||||||
|
Signature string `json:"Signature"`
|
||||||
|
Callback string `json:"callback"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Policy string `json:"policy"`
|
||||||
|
XUserData string `json:"x:user_data"`
|
||||||
|
} `json:"multi_parts"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
} `json:"form"`*/
|
||||||
|
|
||||||
|
//UPLOAD_TYPE_RESUMABLE
|
||||||
|
Resumable struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Params struct {
|
||||||
|
AccessKeyID string `json:"access_key_id"`
|
||||||
|
AccessKeySecret string `json:"access_key_secret"`
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
Expiration time.Time `json:"expiration"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
SecurityToken string `json:"security_token"`
|
||||||
|
} `json:"params"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
} `json:"resumable"`
|
||||||
|
|
||||||
|
File Files `json:"file"`
|
||||||
|
}
|
166
drivers/thunder/util.go
Normal file
166
drivers/thunder/util.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package thunder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
RESUMABLE = "drive#resumable"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UPLOAD_TYPE_UNKNOWN = "UPLOAD_TYPE_UNKNOWN"
|
||||||
|
//UPLOAD_TYPE_FORM = "UPLOAD_TYPE_FORM"
|
||||||
|
UPLOAD_TYPE_RESUMABLE = "UPLOAD_TYPE_RESUMABLE"
|
||||||
|
UPLOAD_TYPE_URL = "UPLOAD_TYPE_URL"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAction(method string, url string) string {
|
||||||
|
urlpath := regexp.MustCompile(`://[^/]+((/[^/\s?#]+)*)`).FindStringSubmatch(url)[1]
|
||||||
|
return method + ":" + urlpath
|
||||||
|
}
|
||||||
|
|
||||||
|
type Common struct {
|
||||||
|
client *resty.Client
|
||||||
|
|
||||||
|
captchaToken string
|
||||||
|
|
||||||
|
// 签名相关,二选一
|
||||||
|
Algorithms []string
|
||||||
|
Timestamp, CaptchaSign string
|
||||||
|
|
||||||
|
// 必要值,签名相关
|
||||||
|
DeviceID string
|
||||||
|
ClientID string
|
||||||
|
ClientSecret string
|
||||||
|
ClientVersion string
|
||||||
|
PackageName string
|
||||||
|
UserAgent string
|
||||||
|
DownloadUserAgent string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Common) SetCaptchaToken(captchaToken string) {
|
||||||
|
c.captchaToken = captchaToken
|
||||||
|
}
|
||||||
|
func (c *Common) GetCaptchaToken() string {
|
||||||
|
return c.captchaToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新验证码token(登录后)
|
||||||
|
func (c *Common) RefreshCaptchaTokenAtLogin(action, userID string) error {
|
||||||
|
metas := map[string]string{
|
||||||
|
"client_version": c.ClientVersion,
|
||||||
|
"package_name": c.PackageName,
|
||||||
|
"user_id": userID,
|
||||||
|
}
|
||||||
|
metas["timestamp"], metas["captcha_sign"] = c.GetCaptchaSign()
|
||||||
|
return c.refreshCaptchaToken(action, metas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新验证码token(登录时)
|
||||||
|
func (c *Common) RefreshCaptchaTokenInLogin(action, username string) error {
|
||||||
|
metas := make(map[string]string)
|
||||||
|
if ok, _ := regexp.MatchString(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`, username); ok {
|
||||||
|
metas["email"] = username
|
||||||
|
} else if len(username) >= 11 && len(username) <= 18 {
|
||||||
|
metas["phone_number"] = username
|
||||||
|
} else {
|
||||||
|
metas["username"] = username
|
||||||
|
}
|
||||||
|
return c.refreshCaptchaToken(action, metas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取验证码签名
|
||||||
|
func (c *Common) GetCaptchaSign() (timestamp, sign string) {
|
||||||
|
if len(c.Algorithms) == 0 {
|
||||||
|
return c.Timestamp, c.CaptchaSign
|
||||||
|
}
|
||||||
|
timestamp = fmt.Sprint(time.Now().UnixMilli())
|
||||||
|
str := fmt.Sprint(c.ClientID, c.ClientVersion, c.PackageName, c.DeviceID, timestamp)
|
||||||
|
for _, algorithm := range c.Algorithms {
|
||||||
|
str = utils.GetMD5Encode(str + algorithm)
|
||||||
|
}
|
||||||
|
sign = "1." + str
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新验证码token
|
||||||
|
func (c *Common) refreshCaptchaToken(action string, metas map[string]string) error {
|
||||||
|
param := CaptchaTokenRequest{
|
||||||
|
Action: action,
|
||||||
|
CaptchaToken: c.captchaToken,
|
||||||
|
ClientID: c.ClientID,
|
||||||
|
DeviceID: c.DeviceID,
|
||||||
|
Meta: metas,
|
||||||
|
RedirectUri: "xlaccsdk01://xunlei.com/callback?state=harbor",
|
||||||
|
}
|
||||||
|
var e ErrResp
|
||||||
|
var resp CaptchaTokenResponse
|
||||||
|
_, err := c.Request(XLUSER_API_URL+"/shield/captcha/init", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetError(&e).SetBody(param)
|
||||||
|
}, &resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.IsError() {
|
||||||
|
return &e
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Url != "" {
|
||||||
|
return fmt.Errorf("need verify:%s", resp.Url)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.CaptchaToken == "" {
|
||||||
|
return fmt.Errorf("empty captchaToken")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetCaptchaToken(resp.CaptchaToken)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有基础信息的请求
|
||||||
|
func (c *Common) Request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
req := c.client.R().SetHeaders(map[string]string{
|
||||||
|
"user-agent": c.UserAgent,
|
||||||
|
"accept": "application/json;charset=UTF-8",
|
||||||
|
"x-device-id": c.DeviceID,
|
||||||
|
"x-client-id": c.ClientID,
|
||||||
|
"x-client-version": c.ClientVersion,
|
||||||
|
})
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
res, err := req.Execute(method, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var erron ErrResp
|
||||||
|
utils.Json.Unmarshal(res.Body(), &erron)
|
||||||
|
if erron.IsError() {
|
||||||
|
return nil, &erron
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/upyun/go-sdk/v3/upyun"
|
"github.com/upyun/go-sdk/v3/upyun"
|
||||||
@ -140,8 +139,4 @@ func (d *USS) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *USS) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*USS)(nil)
|
var _ driver.Driver = (*USS)(nil)
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils/random"
|
"github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
@ -89,8 +88,4 @@ func (d *Virtual) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Virtual) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Virtual)(nil)
|
var _ driver.Driver = (*Virtual)(nil)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/cron"
|
"github.com/alist-org/alist/v3/pkg/cron"
|
||||||
"github.com/alist-org/alist/v3/pkg/gowebdav"
|
"github.com/alist-org/alist/v3/pkg/gowebdav"
|
||||||
@ -123,8 +122,4 @@ func (d *WebDav) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *WebDav) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*WebDav)(nil)
|
var _ driver.Driver = (*WebDav)(nil)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
@ -139,8 +138,4 @@ func (d *YandexDisk) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *YandexDisk) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
||||||
return nil, errs.NotSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*YandexDisk)(nil)
|
var _ driver.Driver = (*YandexDisk)(nil)
|
||||||
|
@ -91,7 +91,7 @@ func InitialSettings() []model.SettingItem {
|
|||||||
"Google":"https://docs.google.com/gview?url=$e_url&embedded=true"
|
"Google":"https://docs.google.com/gview?url=$e_url&embedded=true"
|
||||||
},
|
},
|
||||||
"pdf": {
|
"pdf": {
|
||||||
"PDF.js":"https://alist.nn.ci/pdf.js/web/viewer.html?file=$e_url"
|
"PDF.js":"https://alist-org.github.io/pdf.js/web/viewer.html?file=$e_url"
|
||||||
}
|
}
|
||||||
}`, Type: conf.TypeText, Group: model.PREVIEW},
|
}`, Type: conf.TypeText, Group: model.PREVIEW},
|
||||||
// {Key: conf.OfficeViewers, Value: `{
|
// {Key: conf.OfficeViewers, Value: `{
|
||||||
|
@ -10,7 +10,7 @@ type Driver interface {
|
|||||||
Meta
|
Meta
|
||||||
Reader
|
Reader
|
||||||
Writer
|
Writer
|
||||||
Other
|
//Other
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta interface {
|
type Meta interface {
|
||||||
|
@ -28,11 +28,11 @@ type IRootId interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RootPath struct {
|
type RootPath struct {
|
||||||
RootFolderPath string `json:"root_folder_path" required:"true"`
|
RootFolderPath string `json:"root_folder_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RootID struct {
|
type RootID struct {
|
||||||
RootFolderID string `json:"root_folder_id" required:"true"`
|
RootFolderID string `json:"root_folder_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r RootPath) GetRootPath() string {
|
func (r RootPath) GetRootPath() string {
|
||||||
|
@ -145,9 +145,12 @@ func getAdditionalItems(t reflect.Type, defaultRoot string) []driver.Item {
|
|||||||
if tag.Get("type") != "" {
|
if tag.Get("type") != "" {
|
||||||
item.Type = tag.Get("type")
|
item.Type = tag.Get("type")
|
||||||
}
|
}
|
||||||
if (item.Name == "root_folder_id" || item.Name == "root_folder_path") && item.Default == "" {
|
if item.Name == "root_folder_id" || item.Name == "root_folder_path" {
|
||||||
|
if item.Default == "" {
|
||||||
item.Default = defaultRoot
|
item.Default = defaultRoot
|
||||||
}
|
}
|
||||||
|
item.Required = item.Default != ""
|
||||||
|
}
|
||||||
// set default type to string
|
// set default type to string
|
||||||
if item.Type == "" {
|
if item.Type == "" {
|
||||||
item.Type = "string"
|
item.Type = "string"
|
||||||
|
@ -166,11 +166,15 @@ func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithMessagef(err, "failed to get obj")
|
return nil, errors.WithMessagef(err, "failed to get obj")
|
||||||
}
|
}
|
||||||
return storage.Other(ctx, model.OtherArgs{
|
if o, ok := storage.(driver.Other); ok {
|
||||||
|
return o.Other(ctx, model.OtherArgs{
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
Method: args.Method,
|
Method: args.Method,
|
||||||
Data: args.Data,
|
Data: args.Data,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
return nil, errs.NotImplement
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -24,9 +23,10 @@ func ActualPath(storage driver.Additional, rawPath string) string {
|
|||||||
// for path: remove the mount path prefix and join the actual root folder if exists
|
// for path: remove the mount path prefix and join the actual root folder if exists
|
||||||
func GetStorageAndActualPath(rawPath string) (driver.Driver, string, error) {
|
func GetStorageAndActualPath(rawPath string) (driver.Driver, string, error) {
|
||||||
rawPath = utils.StandardizePath(rawPath)
|
rawPath = utils.StandardizePath(rawPath)
|
||||||
if strings.Contains(rawPath, "..") {
|
// why can remove this check? because reqPath has joined the base_path of user, no relative path
|
||||||
return nil, "", errors.WithStack(errs.RelativePath)
|
//if strings.Contains(rawPath, "..") {
|
||||||
}
|
// return nil, "", errors.WithStack(errs.RelativePath)
|
||||||
|
//}
|
||||||
storage := GetBalancedStorage(rawPath)
|
storage := GetBalancedStorage(rawPath)
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
return nil, "", errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
return nil, "", errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/sign"
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Sign(obj model.Obj) string {
|
func Sign(obj model.Obj, encrypt bool) string {
|
||||||
if obj.IsDir() {
|
if obj.IsDir() || !encrypt {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return sign.Sign(obj.GetName())
|
return sign.Sign(obj.GetName())
|
||||||
|
@ -79,7 +79,7 @@ func FsList(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
total, objs := pagination(objs, &req.PageReq)
|
total, objs := pagination(objs, &req.PageReq)
|
||||||
common.SuccessResp(c, FsListResp{
|
common.SuccessResp(c, FsListResp{
|
||||||
Content: toObjResp(objs),
|
Content: toObjResp(objs, isEncrypt(meta, req.Path)),
|
||||||
Total: int64(total),
|
Total: int64(total),
|
||||||
Readme: getReadme(meta, req.Path),
|
Readme: getReadme(meta, req.Path),
|
||||||
Write: user.CanWrite() || canWrite(meta, req.Path),
|
Write: user.CanWrite() || canWrite(meta, req.Path),
|
||||||
@ -157,6 +157,16 @@ func canAccess(user *model.User, meta *model.Meta, path string, password string)
|
|||||||
return meta.Password == password
|
return meta.Password == password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isEncrypt(meta *model.Meta, path string) bool {
|
||||||
|
if meta == nil || meta.Password == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !utils.PathEqual(meta.Path, path) && !meta.PSub {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
||||||
pageIndex, pageSize := req.Page, req.PerPage
|
pageIndex, pageSize := req.Page, req.PerPage
|
||||||
total := len(objs)
|
total := len(objs)
|
||||||
@ -171,7 +181,7 @@ func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
|||||||
return total, objs[start:end]
|
return total, objs[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
func toObjResp(objs []model.Obj) []ObjResp {
|
func toObjResp(objs []model.Obj, encrypt bool) []ObjResp {
|
||||||
var resp []ObjResp
|
var resp []ObjResp
|
||||||
for _, obj := range objs {
|
for _, obj := range objs {
|
||||||
thumb := ""
|
thumb := ""
|
||||||
@ -187,7 +197,7 @@ func toObjResp(objs []model.Obj) []ObjResp {
|
|||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: obj.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: obj.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
Sign: common.Sign(obj),
|
Sign: common.Sign(obj, encrypt),
|
||||||
Thumb: thumb,
|
Thumb: thumb,
|
||||||
Type: tp,
|
Type: tp,
|
||||||
})
|
})
|
||||||
@ -270,23 +280,25 @@ func FsGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var related []model.Obj
|
var related []model.Obj
|
||||||
sameLevelFiles, err := fs.List(c, stdpath.Dir(req.Path))
|
parentPath := stdpath.Dir(req.Path)
|
||||||
|
sameLevelFiles, err := fs.List(c, parentPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
related = filterRelated(sameLevelFiles, obj)
|
related = filterRelated(sameLevelFiles, obj)
|
||||||
}
|
}
|
||||||
|
parentMeta, _ := db.GetNearestMeta(parentPath)
|
||||||
common.SuccessResp(c, FsGetResp{
|
common.SuccessResp(c, FsGetResp{
|
||||||
ObjResp: ObjResp{
|
ObjResp: ObjResp{
|
||||||
Name: obj.GetName(),
|
Name: obj.GetName(),
|
||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: obj.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: obj.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
Sign: common.Sign(obj),
|
Sign: common.Sign(obj, isEncrypt(meta, req.Path)),
|
||||||
Type: utils.GetFileType(obj.GetName()),
|
Type: utils.GetFileType(obj.GetName()),
|
||||||
},
|
},
|
||||||
RawURL: rawURL,
|
RawURL: rawURL,
|
||||||
Readme: getReadme(meta, req.Path),
|
Readme: getReadme(meta, req.Path),
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
Related: toObjResp(related),
|
Related: toObjResp(related, isEncrypt(parentMeta, parentPath)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package middlewares
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
@ -28,7 +29,7 @@ func Down(c *gin.Context) {
|
|||||||
// verify sign
|
// verify sign
|
||||||
if needSign(meta, rawPath) {
|
if needSign(meta, rawPath) {
|
||||||
s := c.Query("sign")
|
s := c.Query("sign")
|
||||||
err = sign.Verify(filename, s)
|
err = sign.Verify(filename, strings.TrimSuffix(s, "/"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 401)
|
common.ErrorResp(c, err, 401)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
|
@ -112,6 +112,7 @@ func admin(g *gin.RouterGroup) {
|
|||||||
func _fs(g *gin.RouterGroup) {
|
func _fs(g *gin.RouterGroup) {
|
||||||
g.Any("/list", handles.FsList)
|
g.Any("/list", handles.FsList)
|
||||||
g.Any("/get", handles.FsGet)
|
g.Any("/get", handles.FsGet)
|
||||||
|
g.Any("/other", handles.FsOther)
|
||||||
g.Any("/dirs", handles.FsDirs)
|
g.Any("/dirs", handles.FsDirs)
|
||||||
g.POST("/mkdir", handles.FsMkdir)
|
g.POST("/mkdir", handles.FsMkdir)
|
||||||
g.POST("/rename", handles.FsRename)
|
g.POST("/rename", handles.FsRename)
|
||||||
|
@ -597,7 +597,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
href := path.Join(h.Prefix, reqPath)
|
href := path.Join(h.Prefix, strings.TrimPrefix(reqPath, user.BasePath))
|
||||||
if href != "/" && info.IsDir() {
|
if href != "/" && info.IsDir() {
|
||||||
href += "/"
|
href += "/"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user