Compare commits
80 Commits
Author | SHA1 | Date | |
---|---|---|---|
8bdc67ec3d | |||
4fabc27366 | |||
e4c7b0f17c | |||
5e8bfb017e | |||
7d20a01dba | |||
59dbf4496f | |||
12f40608e6 | |||
89832c296f | |||
f09bb88846 | |||
c518f59528 | |||
e9c74f9959 | |||
21b8e7f6e5 | |||
2ae9cd8634 | |||
cfee536b96 | |||
1c8fe3b24c | |||
84e23c397d | |||
f7baec2e65 | |||
378bab32f1 | |||
6cd8151cad | |||
541449e10f | |||
ca5a53fc24 | |||
f646d2a699 | |||
363e036bf0 | |||
e23f00f349 | |||
9600267bda | |||
a66b0e0151 | |||
3bfa00d5d2 | |||
6cbd2532cc | |||
47976af0d3 | |||
4dca52be85 | |||
62bb09300d | |||
f9e067abec | |||
1e62666406 | |||
0e0cdf15ef | |||
b124fdc092 | |||
5141b3c165 | |||
881d6e271e | |||
bd2418c438 | |||
8421c72c5c | |||
a80e21997c | |||
4369cbbac3 | |||
89f76d7899 | |||
ef68f84787 | |||
2c1f70fbe9 | |||
b2f5757f8d | |||
6b97b4eb20 | |||
645c10c11f | |||
571bcf07b0 | |||
63de65be45 | |||
a3446720a2 | |||
3c4c2ad4e0 | |||
077a525961 | |||
5be79eb26e | |||
ddc19ab699 | |||
ddfca5a29b | |||
c19166be1c | |||
daad61443c | |||
4b0c01158d | |||
f97f1d532e | |||
e15755fef0 | |||
ea88998325 | |||
74d971aa8a | |||
d41d868a8d | |||
555cc26cbf | |||
ab4215080b | |||
9502f5acd7 | |||
b03879403f | |||
ee4ac81677 | |||
b69fc8c306 | |||
ee6c31332d | |||
9fa16bd5fc | |||
c77ed5fcb0 | |||
822be17fb9 | |||
7e3b13ea2d | |||
f8fb48fb32 | |||
4bf46268da | |||
b7ea73b3c2 | |||
9fbc54314d | |||
cf8ab29a17 | |||
51cadd2d49 |
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 44
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 8
|
||||
daysUntilClose: 20
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- accepted
|
||||
@ -16,4 +16,4 @@ markComment: >
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue was closed due to inactive more than 52 days. You can reopen or
|
||||
recreate it if you think it should continue. Thank you for your contributions again.
|
||||
recreate it if you think it should continue. Thank you for your contributions again.
|
||||
|
8
.github/workflows/issue_close_question.yml
vendored
8
.github/workflows/issue_close_question.yml
vendored
@ -2,7 +2,7 @@ name: Close need info
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 */7 * *"
|
||||
- cron: "0 0 */1 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -15,8 +15,8 @@ jobs:
|
||||
actions: 'close-issues'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: 'question'
|
||||
inactive-day: 7
|
||||
inactive-day: 3
|
||||
close-reason: 'not_planned'
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 7 days.
|
||||
你好 @${{ github.event.issue.user.login }},此issue因超过7天未回复被关闭。
|
||||
Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 3 days.
|
||||
你好 @${{ github.event.issue.user.login }},此issue因超过3天未回复被关闭。
|
21
.github/workflows/issue_close_stale.yml
vendored
Normal file
21
.github/workflows/issue_close_stale.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
name: Close inactive
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 */7 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
close-inactive:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: close-issues
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issues'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: 'stale'
|
||||
inactive-day: 8
|
||||
close-reason: 'not_planned'
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}, this issue was closed due to inactive more than 52 days. You can reopen or recreate it if you think it should continue. Thank you for your contributions again.
|
4
.github/workflows/issue_question.yml
vendored
4
.github/workflows/issue_question.yml
vendored
@ -16,5 +16,5 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
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自动关闭。
|
||||
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 3 days.
|
||||
你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 并详细说明问题/日志记录/复现步骤/复现链接/实现思路或提供更多信息等, 3天内未回复issue自动关闭。
|
@ -6,7 +6,7 @@
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [git](https://nodejs.org/zh-cn/)
|
||||
- [git](https://git-scm.com)
|
||||
- [Go 1.19+](https://golang.org/doc/install)
|
||||
- [gcc](https://gcc.gnu.org/)
|
||||
- [nodejs](https://nodejs.org/)
|
||||
|
@ -1,11 +1,11 @@
|
||||
FROM alpine:3.17 as builder
|
||||
FROM alpine:3.18 as builder
|
||||
LABEL stage=go-builder
|
||||
WORKDIR /app/
|
||||
COPY ./ ./
|
||||
RUN apk add --no-cache bash curl gcc git go musl-dev; \
|
||||
bash build.sh release docker
|
||||
|
||||
FROM alpine:3.17
|
||||
FROM alpine:3.18
|
||||
LABEL MAINTAINER="i@nn.ci"
|
||||
VOLUME /opt/alist/data/
|
||||
WORKDIR /opt/alist/
|
||||
@ -14,5 +14,5 @@ COPY entrypoint.sh /entrypoint.sh
|
||||
RUN apk add --no-cache bash ca-certificates su-exec tzdata; \
|
||||
chmod +x /entrypoint.sh
|
||||
ENV PUID=0 PGID=0 UMASK=022
|
||||
EXPOSE 5244
|
||||
EXPOSE 5244 5245
|
||||
CMD [ "/entrypoint.sh" ]
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg"/></a>
|
||||
<p><em>🗂️A file list program that supports multiple storage, powered by Gin and Solidjs.</em></p>
|
||||
<p><em>🗂️A file list program that supports multiple storages, powered by Gin and Solidjs.</em></p>
|
||||
<div>
|
||||
<a href="https://goreportcard.com/report/github.com/alist-org/alist/v3">
|
||||
<img src="https://goreportcard.com/badge/github.com/alist-org/alist/v3" alt="latest version" />
|
||||
@ -62,6 +62,7 @@ English | [中文](./README_cn.md) | [Contributing](./CONTRIBUTING.md) | [CODE_O
|
||||
- [x] [YandexDisk](https://disk.yandex.com/)
|
||||
- [x] [BaiduNetdisk](http://pan.baidu.com/)
|
||||
- [x] [Terabox](https://www.terabox.com/main)
|
||||
- [x] [UC](https://drive.uc.cn)
|
||||
- [x] [Quark](https://pan.quark.cn)
|
||||
- [x] [Thunder](https://pan.xunlei.com)
|
||||
- [x] [Lanzou](https://www.lanzou.com/)
|
||||
@ -72,6 +73,7 @@ English | [中文](./README_cn.md) | [Contributing](./CONTRIBUTING.md) | [CODE_O
|
||||
- [x] SMB
|
||||
- [x] [115](https://115.com/)
|
||||
- [X] Cloudreve
|
||||
- [x] [Dropbox](https://www.dropbox.com/)
|
||||
- [x] Easy to deploy and out-of-the-box
|
||||
- [x] File preview (PDF, markdown, code, plain text, ...)
|
||||
- [x] Image preview in gallery mode
|
||||
@ -109,6 +111,7 @@ https://alist.nn.ci/guide/sponsor.html
|
||||
|
||||
### Special sponsors
|
||||
|
||||
- [亚洲云 - 高防服务器|服务器租用|福州高防|广东电信|香港服务器|美国服务器|海外服务器 - 国内靠谱的企业级云计算服务提供商](https://www.asiayun.com/aff/QQCOOQKZ) (sponsored Chinese API server)
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [KinhDown 百度云盘不限速下载!永久免费!已稳定运行3年!非常可靠!Q群 -> 786799372](https://kinhdown.com)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
@ -61,6 +61,7 @@
|
||||
- [x] [和彩云](https://yun.139.com/) (个人云, 家庭云)
|
||||
- [x] [Yandex.Disk](https://disk.yandex.com/)
|
||||
- [x] [百度网盘](http://pan.baidu.com/)
|
||||
- [x] [UC网盘](https://drive.uc.cn)
|
||||
- [x] [夸克网盘](https://pan.quark.cn)
|
||||
- [x] [迅雷网盘](https://pan.xunlei.com)
|
||||
- [x] [蓝奏云](https://www.lanzou.com/)
|
||||
@ -71,6 +72,7 @@
|
||||
- [x] SMB
|
||||
- [x] [115](https://115.com/)
|
||||
- [X] Cloudreve
|
||||
- [x] [Dropbox](https://www.dropbox.com/)
|
||||
- [x] 部署方便,开箱即用
|
||||
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
||||
- [x] 画廊模式下的图像预览
|
||||
@ -107,6 +109,7 @@ AList 是一个开源软件,如果你碰巧喜欢这个项目,并希望我
|
||||
|
||||
### 特别赞助
|
||||
|
||||
- [亚洲云 - 高防服务器|服务器租用|福州高防|广东电信|香港服务器|美国服务器|海外服务器 - 国内靠谱的企业级云计算服务提供商](https://www.asiayun.com/aff/QQCOOQKZ) (国内API服务器赞助)
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [KinhDown 百度云盘不限速下载!永久免费!已稳定运行3年!非常可靠!Q群 -> 786799372](https://kinhdown.com)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
23
build.sh
23
build.sh
@ -54,11 +54,30 @@ BuildWinArm64() {
|
||||
|
||||
BuildDev() {
|
||||
rm -rf .git/
|
||||
xgo -targets=linux/amd64,windows/amd64,darwin/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
|
||||
mkdir -p "dist"
|
||||
muslflags="--extldflags '-static -fpic' $ldflags"
|
||||
BASE="https://musl.nn.ci/"
|
||||
FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross)
|
||||
for i in "${FILES[@]}"; do
|
||||
url="${BASE}${i}.tgz"
|
||||
curl -L -o "${i}.tgz" "${url}"
|
||||
sudo tar xf "${i}.tgz" --strip-components 1 -C /usr/local
|
||||
done
|
||||
OS_ARCHES=(linux-musl-amd64 linux-musl-arm64)
|
||||
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc)
|
||||
for i in "${!OS_ARCHES[@]}"; do
|
||||
os_arch=${OS_ARCHES[$i]}
|
||||
cgo_cc=${CGO_ARGS[$i]}
|
||||
echo building for ${os_arch}
|
||||
export GOOS=${os_arch%%-*}
|
||||
export GOARCH=${os_arch##*-}
|
||||
export CC=${cgo_cc}
|
||||
export CGO_ENABLED=1
|
||||
go build -o ./dist/$appName-$os_arch -ldflags="$muslflags" -tags=jsoniter .
|
||||
done
|
||||
xgo -targets=windows/amd64,darwin/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
|
||||
mv alist-* dist
|
||||
cd dist
|
||||
upx -9 ./alist-linux*
|
||||
cp ./alist-windows-amd64.exe ./alist-windows-amd64-upx.exe
|
||||
upx -9 ./alist-windows-amd64-upx.exe
|
||||
find . -type f -print0 | xargs -0 md5sum >md5.txt
|
||||
|
@ -24,7 +24,7 @@ func Execute() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "config file")
|
||||
RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "data folder")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
|
||||
RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
|
||||
|
@ -3,9 +3,11 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@ -28,6 +30,10 @@ var ServerCmd = &cobra.Command{
|
||||
the address is defined in config file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Init()
|
||||
if conf.Conf.DelayedStart != 0 {
|
||||
utils.Log.Infof("delayed start for %d seconds", conf.Conf.DelayedStart)
|
||||
time.Sleep(time.Duration(conf.Conf.DelayedStart) * time.Second)
|
||||
}
|
||||
bootstrap.InitAria2()
|
||||
bootstrap.InitQbittorrent()
|
||||
bootstrap.LoadStorages()
|
||||
@ -37,42 +43,85 @@ the address is defined in config file`,
|
||||
r := gin.New()
|
||||
r.Use(gin.LoggerWithWriter(log.StandardLogger().Out), gin.RecoveryWithWriter(log.StandardLogger().Out))
|
||||
server.Init(r)
|
||||
base := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port)
|
||||
utils.Log.Infof("start server @ %s", base)
|
||||
srv := &http.Server{Addr: base, Handler: r}
|
||||
go func() {
|
||||
var err error
|
||||
if conf.Conf.Scheme.Https {
|
||||
//err = r.RunTLS(base, conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile)
|
||||
err = srv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile)
|
||||
} else {
|
||||
err = srv.ListenAndServe()
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
utils.Log.Fatalf("failed to start: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
var httpSrv, httpsSrv, unixSrv *http.Server
|
||||
if conf.Conf.Scheme.HttpPort != -1 {
|
||||
httpBase := fmt.Sprintf("%s:%d", conf.Conf.Scheme.Address, conf.Conf.Scheme.HttpPort)
|
||||
utils.Log.Infof("start HTTP server @ %s", httpBase)
|
||||
httpSrv = &http.Server{Addr: httpBase, Handler: r}
|
||||
go func() {
|
||||
err := httpSrv.ListenAndServe()
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
utils.Log.Fatalf("failed to start http: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
if conf.Conf.Scheme.HttpsPort != -1 {
|
||||
httpsBase := fmt.Sprintf("%s:%d", conf.Conf.Scheme.Address, conf.Conf.Scheme.HttpsPort)
|
||||
utils.Log.Infof("start HTTPS server @ %s", httpsBase)
|
||||
httpsSrv = &http.Server{Addr: httpsBase, Handler: r}
|
||||
go func() {
|
||||
err := httpsSrv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
utils.Log.Fatalf("failed to start https: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
if conf.Conf.Scheme.UnixFile != "" {
|
||||
utils.Log.Infof("start unix server @ %s", conf.Conf.Scheme.UnixFile)
|
||||
unixSrv = &http.Server{Handler: r}
|
||||
go func() {
|
||||
listener, err := net.Listen("unix", conf.Conf.Scheme.UnixFile)
|
||||
if err != nil {
|
||||
utils.Log.Fatalf("failed to listen unix: %+v", err)
|
||||
}
|
||||
err = unixSrv.Serve(listener)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
utils.Log.Fatalf("failed to start unix: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// a timeout of 5 seconds.
|
||||
quit := make(chan os.Signal)
|
||||
// a timeout of 1 second.
|
||||
quit := make(chan os.Signal, 1)
|
||||
// kill (no param) default send syscanll.SIGTERM
|
||||
// kill -2 is syscall.SIGINT
|
||||
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
utils.Log.Println("Shutdown Server ...")
|
||||
utils.Log.Println("Shutdown server...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
utils.Log.Fatal("Server Shutdown:", err)
|
||||
var wg sync.WaitGroup
|
||||
if conf.Conf.Scheme.HttpPort != -1 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := httpSrv.Shutdown(ctx); err != nil {
|
||||
utils.Log.Fatal("HTTP server shutdown err: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
// catching ctx.Done(). timeout of 3 seconds.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
utils.Log.Println("timeout of 1 seconds.")
|
||||
if conf.Conf.Scheme.HttpsPort != -1 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := httpsSrv.Shutdown(ctx); err != nil {
|
||||
utils.Log.Fatal("HTTPS server shutdown err: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
utils.Log.Println("Server exiting")
|
||||
if conf.Conf.Scheme.UnixFile != "" {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := unixSrv.Shutdown(ctx); err != nil {
|
||||
utils.Log.Fatal("Unix server shutdown err: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
utils.Log.Println("Server exit")
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ services:
|
||||
- '/etc/alist:/opt/alist/data'
|
||||
ports:
|
||||
- '5244:5244'
|
||||
- '5245:5245'
|
||||
environment:
|
||||
- PUID=0
|
||||
- PGID=0
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/SheltonZhu/115driver/pkg/driver"
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -15,6 +16,7 @@ func (d *Pan115) login() error {
|
||||
driver.UA(UserAgent),
|
||||
}
|
||||
d.client = driver.New(opts...)
|
||||
d.client.SetHttpClient(base.HttpClient)
|
||||
cr := &driver.Credential{}
|
||||
if d.Addition.QRCodeToken != "" {
|
||||
s := &driver.QRCodeSession{
|
||||
|
@ -98,7 +98,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
||||
}
|
||||
u_ := u.String()
|
||||
log.Debug("download url: ", u_)
|
||||
res, err := base.NoRedirectClient.R().Get(u_)
|
||||
res, err := base.NoRedirectClient.R().SetHeader("Referer", "https://www.123pan.com/").Get(u_)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -112,6 +112,9 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
||||
} else if res.StatusCode() == 200 {
|
||||
link.URL = utils.Json.Get(res.Body(), "data", "redirect_url").ToString()
|
||||
}
|
||||
link.Header = http.Header{
|
||||
"Referer": []string{"https://www.123pan.com/"},
|
||||
}
|
||||
return &link, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("can't convert obj")
|
||||
@ -235,6 +238,7 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
||||
}
|
||||
if resp.Data.AccessKeyId == "" || resp.Data.SecretAccessKey == "" || resp.Data.SessionToken == "" {
|
||||
err = d.newUpload(ctx, &resp, stream, uploadFile, up)
|
||||
return err
|
||||
} else {
|
||||
cfg := &aws.Config{
|
||||
Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken),
|
||||
|
@ -34,14 +34,17 @@ func (d *Pan123) getS3PreSignedUrls(ctx context.Context, upReq *UploadResp, star
|
||||
return &s3PreSignedUrls, nil
|
||||
}
|
||||
|
||||
func (d *Pan123) completeS3(ctx context.Context, upReq *UploadResp) error {
|
||||
func (d *Pan123) completeS3(ctx context.Context, upReq *UploadResp, file model.FileStreamer, isMultipart bool) error {
|
||||
data := base.Json{
|
||||
"StorageNode": upReq.Data.StorageNode,
|
||||
"bucket": upReq.Data.Bucket,
|
||||
"fileId": upReq.Data.FileId,
|
||||
"fileSize": file.GetSize(),
|
||||
"isMultipart": isMultipart,
|
||||
"key": upReq.Data.Key,
|
||||
"uploadId": upReq.Data.UploadId,
|
||||
"StorageNode": upReq.Data.StorageNode,
|
||||
}
|
||||
_, err := d.request(S3Complete, http.MethodPost, func(req *resty.Request) {
|
||||
_, err := d.request(UploadCompleteV2, http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data).SetContext(ctx)
|
||||
}, nil)
|
||||
return err
|
||||
@ -83,7 +86,7 @@ func (d *Pan123) newUpload(ctx context.Context, upReq *UploadResp, file model.Fi
|
||||
}
|
||||
}
|
||||
// complete s3 upload
|
||||
return d.completeS3(ctx, upReq)
|
||||
return d.completeS3(ctx, upReq, file, chunkCount > 1)
|
||||
}
|
||||
|
||||
func (d *Pan123) uploadS3Chunk(ctx context.Context, upReq *UploadResp, s3PreSignedUrls *S3PreSignedURLs, cur, end int, reader io.Reader, curSize int64, retry bool) error {
|
||||
|
@ -15,19 +15,23 @@ import (
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
const (
|
||||
API = "https://www.123pan.com/b/api"
|
||||
SignIn = API + "/user/sign_in"
|
||||
UserInfo = API + "/user/info"
|
||||
FileList = API + "/file/list/new"
|
||||
DownloadInfo = "https://www.123pan.com/a/api/file/download_info"
|
||||
Mkdir = API + "/file/upload_request"
|
||||
Move = API + "/file/mod_pid"
|
||||
Rename = API + "/file/rename"
|
||||
Trash = API + "/file/trash"
|
||||
UploadRequest = API + "/file/upload_request"
|
||||
UploadComplete = API + "/file/upload_complete"
|
||||
S3PreSignedUrls = API + "/file/s3_repare_upload_parts_batch"
|
||||
S3Complete = API + "/file/s3_complete_multipart_upload"
|
||||
AApi = "https://www.123pan.com/a/api"
|
||||
BApi = "https://www.123pan.com/b/api"
|
||||
MainApi = AApi
|
||||
SignIn = MainApi + "/user/sign_in"
|
||||
UserInfo = MainApi + "/user/info"
|
||||
FileList = MainApi + "/file/list/new"
|
||||
DownloadInfo = MainApi + "/file/download_info"
|
||||
Mkdir = MainApi + "/file/upload_request"
|
||||
Move = MainApi + "/file/mod_pid"
|
||||
Rename = MainApi + "/file/rename"
|
||||
Trash = MainApi + "/file/trash"
|
||||
UploadRequest = MainApi + "/file/upload_request"
|
||||
UploadComplete = MainApi + "/file/upload_complete"
|
||||
S3PreSignedUrls = MainApi + "/file/s3_repare_upload_parts_batch"
|
||||
S3Auth = MainApi + "/file/s3_upload_object/auth"
|
||||
UploadCompleteV2 = MainApi + "/file/upload_complete/v2"
|
||||
S3Complete = MainApi + "/file/s3_complete_multipart_upload"
|
||||
)
|
||||
|
||||
func (d *Pan123) login() error {
|
||||
@ -42,6 +46,7 @@ func (d *Pan123) login() error {
|
||||
body = base.Json{
|
||||
"passport": d.Username,
|
||||
"password": d.Password,
|
||||
"remember": true,
|
||||
}
|
||||
}
|
||||
res, err := base.RestyClient.R().
|
||||
@ -61,6 +66,7 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeaders(map[string]string{
|
||||
"origin": "https://www.123pan.com",
|
||||
"referer": "https://www.123pan.com/",
|
||||
"authorization": "Bearer " + d.AccessToken,
|
||||
"platform": "web",
|
||||
"app-version": "1.2",
|
||||
|
@ -2,10 +2,12 @@ package _139
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -18,6 +20,7 @@ import (
|
||||
type Yun139 struct {
|
||||
model.Storage
|
||||
Addition
|
||||
Account string
|
||||
}
|
||||
|
||||
func (d *Yun139) Config() driver.Config {
|
||||
@ -29,7 +32,20 @@ func (d *Yun139) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *Yun139) Init(ctx context.Context) error {
|
||||
_, err := d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
|
||||
if d.Authorization == "" {
|
||||
return fmt.Errorf("authorization is empty")
|
||||
}
|
||||
decode, err := base64.StdEncoding.DecodeString(d.Authorization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decodeStr := string(decode)
|
||||
splits := strings.Split(decodeStr, ":")
|
||||
if len(splits) < 2 {
|
||||
return fmt.Errorf("authorization is invalid, splits < 2")
|
||||
}
|
||||
d.Account = splits[1]
|
||||
_, err = d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
|
||||
"qryUserExternInfoReq": base.Json{
|
||||
"commonAccountInfo": base.Json{
|
||||
"account": d.Account,
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
Account string `json:"account" required:"true"`
|
||||
Cookie string `json:"cookie" type:"text" required:"true"`
|
||||
//Account string `json:"account" required:"true"`
|
||||
Authorization string `json:"authorization" type:"text" required:"true"`
|
||||
driver.RootID
|
||||
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
|
||||
CloudID string `json:"cloud_id"`
|
||||
|
@ -72,7 +72,7 @@ func (d *Yun139) request(pathname string, method string, callback base.ReqCallba
|
||||
req.SetHeaders(map[string]string{
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"CMS-DEVICE": "default",
|
||||
"Cookie": d.Cookie,
|
||||
"Authorization": "Basic " + d.Authorization,
|
||||
"mcloud-channel": "1000101",
|
||||
"mcloud-client": "10701",
|
||||
//"mcloud-route": "001",
|
||||
|
@ -30,12 +30,9 @@ func (d *Cloud189) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *Cloud189) Init(ctx context.Context) error {
|
||||
d.client = resty.New().
|
||||
SetTimeout(base.DefaultTimeout).
|
||||
SetRetryCount(3).
|
||||
SetHeader("Referer", "https://cloud.189.cn/").
|
||||
SetHeader("User-Agent", base.UserAgent)
|
||||
return d.login()
|
||||
d.client = base.NewRestyClient().
|
||||
SetHeader("Referer", "https://cloud.189.cn/")
|
||||
return d.newLogin()
|
||||
}
|
||||
|
||||
func (d *Cloud189) Drop(ctx context.Context) error {
|
||||
|
126
drivers/189/login.go
Normal file
126
drivers/189/login.go
Normal file
@ -0,0 +1,126 @@
|
||||
package _189
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AppConf struct {
|
||||
Data struct {
|
||||
AccountType string `json:"accountType"`
|
||||
AgreementCheck string `json:"agreementCheck"`
|
||||
AppKey string `json:"appKey"`
|
||||
ClientType int `json:"clientType"`
|
||||
IsOauth2 bool `json:"isOauth2"`
|
||||
LoginSort string `json:"loginSort"`
|
||||
MailSuffix string `json:"mailSuffix"`
|
||||
PageKey string `json:"pageKey"`
|
||||
ParamId string `json:"paramId"`
|
||||
RegReturnUrl string `json:"regReturnUrl"`
|
||||
ReqId string `json:"reqId"`
|
||||
ReturnUrl string `json:"returnUrl"`
|
||||
ShowFeedback string `json:"showFeedback"`
|
||||
ShowPwSaveName string `json:"showPwSaveName"`
|
||||
ShowQrSaveName string `json:"showQrSaveName"`
|
||||
ShowSmsSaveName string `json:"showSmsSaveName"`
|
||||
Sso string `json:"sso"`
|
||||
} `json:"data"`
|
||||
Msg string `json:"msg"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type EncryptConf struct {
|
||||
Result int `json:"result"`
|
||||
Data struct {
|
||||
UpSmsOn string `json:"upSmsOn"`
|
||||
Pre string `json:"pre"`
|
||||
PreDomain string `json:"preDomain"`
|
||||
PubKey string `json:"pubKey"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (d *Cloud189) newLogin() error {
|
||||
url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||
res, err := d.client.R().Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Is logged in
|
||||
redirectURL := res.RawResponse.Request.URL
|
||||
if redirectURL.String() == "https://cloud.189.cn/web/main" {
|
||||
return nil
|
||||
}
|
||||
lt := redirectURL.Query().Get("lt")
|
||||
reqId := redirectURL.Query().Get("reqId")
|
||||
appId := redirectURL.Query().Get("appId")
|
||||
headers := map[string]string{
|
||||
"lt": lt,
|
||||
"reqid": reqId,
|
||||
"referer": redirectURL.String(),
|
||||
"origin": "https://open.e.189.cn",
|
||||
}
|
||||
// get app Conf
|
||||
var appConf AppConf
|
||||
res, err = d.client.R().SetHeaders(headers).SetFormData(map[string]string{
|
||||
"version": "2.0",
|
||||
"appKey": appId,
|
||||
}).SetResult(&appConf).Post("https://open.e.189.cn/api/logbox/oauth2/appConf.do")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("189 AppConf resp body: %s", res.String())
|
||||
if appConf.Result != "0" {
|
||||
return errors.New(appConf.Msg)
|
||||
}
|
||||
// get encrypt conf
|
||||
var encryptConf EncryptConf
|
||||
res, err = d.client.R().SetHeaders(headers).SetFormData(map[string]string{
|
||||
"appId": appId,
|
||||
}).Post("https://open.e.189.cn/api/logbox/config/encryptConf.do")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utils.Json.Unmarshal(res.Body(), &encryptConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("189 EncryptConf resp body: %s\n%+v", res.String(), encryptConf)
|
||||
if encryptConf.Result != 0 {
|
||||
return errors.New("get EncryptConf error:" + res.String())
|
||||
}
|
||||
// TODO: getUUID? needcaptcha
|
||||
// login
|
||||
loginData := map[string]string{
|
||||
"version": "v2.0",
|
||||
"apToken": "",
|
||||
"appKey": appId,
|
||||
"accountType": appConf.Data.AccountType,
|
||||
"userName": encryptConf.Data.Pre + RsaEncode([]byte(d.Username), encryptConf.Data.PubKey, true),
|
||||
"epd": encryptConf.Data.Pre + RsaEncode([]byte(d.Password), encryptConf.Data.PubKey, true),
|
||||
"captchaType": "",
|
||||
"validateCode": "",
|
||||
"smsValidateCode": "",
|
||||
"captchaToken": "",
|
||||
"returnUrl": appConf.Data.ReturnUrl,
|
||||
"mailSuffix": appConf.Data.MailSuffix,
|
||||
"dynamicCheck": "FALSE",
|
||||
"clientType": strconv.Itoa(appConf.Data.ClientType),
|
||||
"cb_SaveName": "3",
|
||||
"isOauth2": strconv.FormatBool(appConf.Data.IsOauth2),
|
||||
"state": "",
|
||||
"paramId": appConf.Data.ParamId,
|
||||
}
|
||||
res, err = d.client.R().SetHeaders(headers).SetFormData(loginData).Post("https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("189 login resp body: %s", res.String())
|
||||
loginResult := utils.Json.Get(res.Body(), "result").ToInt()
|
||||
if loginResult != 0 {
|
||||
return errors.New(utils.Json.Get(res.Body(), "msg").ToString())
|
||||
}
|
||||
return nil
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
type Addition struct {
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
Cookie string `json:"cookie" help:"Fill in the cookie if need captcha"`
|
||||
driver.RootID
|
||||
}
|
||||
|
||||
@ -15,6 +16,7 @@ var config = driver.Config{
|
||||
Name: "189Cloud",
|
||||
LocalSort: true,
|
||||
DefaultRoot: "-11",
|
||||
Alert: `info|You can try to use 189PC driver if this driver does not work.`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -11,16 +11,13 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
myrand "github.com/alist-org/alist/v3/pkg/utils/random"
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -30,118 +27,118 @@ import (
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
func (d *Cloud189) login() error {
|
||||
url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||
b := ""
|
||||
lt := ""
|
||||
ltText := regexp.MustCompile(`lt = "(.+?)"`)
|
||||
var res *resty.Response
|
||||
var err error
|
||||
for i := 0; i < 3; i++ {
|
||||
res, err = d.client.R().Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 已经登陆
|
||||
if res.RawResponse.Request.URL.String() == "https://cloud.189.cn/web/main" {
|
||||
return nil
|
||||
}
|
||||
b = res.String()
|
||||
ltTextArr := ltText.FindStringSubmatch(b)
|
||||
if len(ltTextArr) > 0 {
|
||||
lt = ltTextArr[1]
|
||||
break
|
||||
} else {
|
||||
<-time.After(time.Second)
|
||||
}
|
||||
}
|
||||
if lt == "" {
|
||||
return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s\nredirect url: %s",
|
||||
b, res.StatusCode(), res.RawResponse.Request.URL.String(), res.Header().Get("location"))
|
||||
}
|
||||
captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1]
|
||||
returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1]
|
||||
paramId := regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(b)[1]
|
||||
//reqId := regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(b)[1]
|
||||
jRsakey := regexp.MustCompile(`j_rsaKey" value="(\S+)"`).FindStringSubmatch(b)[1]
|
||||
vCodeID := regexp.MustCompile(`picCaptcha\.do\?token\=([A-Za-z0-9\&\=]+)`).FindStringSubmatch(b)[1]
|
||||
vCodeRS := ""
|
||||
if vCodeID != "" {
|
||||
// need ValidateCode
|
||||
log.Debugf("try to identify verification codes")
|
||||
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||
u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
|
||||
imgRes, err := d.client.R().SetHeaders(map[string]string{
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
|
||||
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
|
||||
"Sec-Fetch-Dest": "image",
|
||||
"Sec-Fetch-Mode": "no-cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
}).Get(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Enter the verification code manually
|
||||
//err = message.GetMessenger().WaitSend(message.Message{
|
||||
// Type: "image",
|
||||
// Content: "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgRes.Body()),
|
||||
//}, 10)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//vCodeRS, err = message.GetMessenger().WaitReceive(30)
|
||||
// use ocr api
|
||||
vRes, err := base.RestyClient.R().SetMultipartField(
|
||||
"image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
|
||||
Post(setting.GetStr(conf.OcrApi))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||
}
|
||||
vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||
log.Debugln("code: ", vCodeRS)
|
||||
}
|
||||
userRsa := RsaEncode([]byte(d.Username), jRsakey, true)
|
||||
passwordRsa := RsaEncode([]byte(d.Password), jRsakey, true)
|
||||
url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
|
||||
var loginResp LoginResp
|
||||
res, err = d.client.R().
|
||||
SetHeaders(map[string]string{
|
||||
"lt": lt,
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
|
||||
"Referer": "https://open.e.189.cn/",
|
||||
"accept": "application/json;charset=UTF-8",
|
||||
}).SetFormData(map[string]string{
|
||||
"appKey": "cloud",
|
||||
"accountType": "01",
|
||||
"userName": "{RSA}" + userRsa,
|
||||
"password": "{RSA}" + passwordRsa,
|
||||
"validateCode": vCodeRS,
|
||||
"captchaToken": captchaToken,
|
||||
"returnUrl": returnUrl,
|
||||
"mailSuffix": "@pan.cn",
|
||||
"paramId": paramId,
|
||||
"clientType": "10010",
|
||||
"dynamicCheck": "FALSE",
|
||||
"cb_SaveName": "1",
|
||||
"isOauth2": "false",
|
||||
}).Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utils.Json.Unmarshal(res.Body(), &loginResp)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
if loginResp.Result != 0 {
|
||||
return fmt.Errorf(loginResp.Msg)
|
||||
}
|
||||
_, err = d.client.R().Get(loginResp.ToUrl)
|
||||
return err
|
||||
}
|
||||
//func (d *Cloud189) login() error {
|
||||
// url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||
// b := ""
|
||||
// lt := ""
|
||||
// ltText := regexp.MustCompile(`lt = "(.+?)"`)
|
||||
// var res *resty.Response
|
||||
// var err error
|
||||
// for i := 0; i < 3; i++ {
|
||||
// res, err = d.client.R().Get(url)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // 已经登陆
|
||||
// if res.RawResponse.Request.URL.String() == "https://cloud.189.cn/web/main" {
|
||||
// return nil
|
||||
// }
|
||||
// b = res.String()
|
||||
// ltTextArr := ltText.FindStringSubmatch(b)
|
||||
// if len(ltTextArr) > 0 {
|
||||
// lt = ltTextArr[1]
|
||||
// break
|
||||
// } else {
|
||||
// <-time.After(time.Second)
|
||||
// }
|
||||
// }
|
||||
// if lt == "" {
|
||||
// return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s\nredirect url: %s",
|
||||
// b, res.StatusCode(), res.RawResponse.Request.URL.String(), res.Header().Get("location"))
|
||||
// }
|
||||
// captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1]
|
||||
// returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1]
|
||||
// paramId := regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(b)[1]
|
||||
// //reqId := regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(b)[1]
|
||||
// jRsakey := regexp.MustCompile(`j_rsaKey" value="(\S+)"`).FindStringSubmatch(b)[1]
|
||||
// vCodeID := regexp.MustCompile(`picCaptcha\.do\?token\=([A-Za-z0-9\&\=]+)`).FindStringSubmatch(b)[1]
|
||||
// vCodeRS := ""
|
||||
// if vCodeID != "" {
|
||||
// // need ValidateCode
|
||||
// log.Debugf("try to identify verification codes")
|
||||
// timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||
// u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
|
||||
// imgRes, err := d.client.R().SetHeaders(map[string]string{
|
||||
// "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
|
||||
// "Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
|
||||
// "Sec-Fetch-Dest": "image",
|
||||
// "Sec-Fetch-Mode": "no-cors",
|
||||
// "Sec-Fetch-Site": "same-origin",
|
||||
// }).Get(u)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // Enter the verification code manually
|
||||
// //err = message.GetMessenger().WaitSend(message.Message{
|
||||
// // Type: "image",
|
||||
// // Content: "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgRes.Body()),
|
||||
// //}, 10)
|
||||
// //if err != nil {
|
||||
// // return err
|
||||
// //}
|
||||
// //vCodeRS, err = message.GetMessenger().WaitReceive(30)
|
||||
// // use ocr api
|
||||
// vRes, err := base.RestyClient.R().SetMultipartField(
|
||||
// "image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
|
||||
// Post(setting.GetStr(conf.OcrApi))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
// return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||
// }
|
||||
// vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||
// log.Debugln("code: ", vCodeRS)
|
||||
// }
|
||||
// userRsa := RsaEncode([]byte(d.Username), jRsakey, true)
|
||||
// passwordRsa := RsaEncode([]byte(d.Password), jRsakey, true)
|
||||
// url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
|
||||
// var loginResp LoginResp
|
||||
// res, err = d.client.R().
|
||||
// SetHeaders(map[string]string{
|
||||
// "lt": lt,
|
||||
// "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
|
||||
// "Referer": "https://open.e.189.cn/",
|
||||
// "accept": "application/json;charset=UTF-8",
|
||||
// }).SetFormData(map[string]string{
|
||||
// "appKey": "cloud",
|
||||
// "accountType": "01",
|
||||
// "userName": "{RSA}" + userRsa,
|
||||
// "password": "{RSA}" + passwordRsa,
|
||||
// "validateCode": vCodeRS,
|
||||
// "captchaToken": captchaToken,
|
||||
// "returnUrl": returnUrl,
|
||||
// "mailSuffix": "@pan.cn",
|
||||
// "paramId": paramId,
|
||||
// "clientType": "10010",
|
||||
// "dynamicCheck": "FALSE",
|
||||
// "cb_SaveName": "1",
|
||||
// "isOauth2": "false",
|
||||
// }).Post(url)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// err = utils.Json.Unmarshal(res.Body(), &loginResp)
|
||||
// if err != nil {
|
||||
// log.Error(err.Error())
|
||||
// return err
|
||||
// }
|
||||
// if loginResp.Result != 0 {
|
||||
// return fmt.Errorf(loginResp.Msg)
|
||||
// }
|
||||
// _, err = d.client.R().Get(loginResp.ToUrl)
|
||||
// return err
|
||||
//}
|
||||
|
||||
func (d *Cloud189) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
var e Error
|
||||
@ -163,7 +160,7 @@ func (d *Cloud189) request(url string, method string, callback base.ReqCallback,
|
||||
//log.Debug(res.String())
|
||||
if e.ErrorCode != "" {
|
||||
if e.ErrorCode == "InvalidSessionKey" {
|
||||
err = d.login()
|
||||
err = d.newLogin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -19,8 +18,7 @@ type Cloud189PC struct {
|
||||
|
||||
identity string
|
||||
|
||||
client *resty.Client
|
||||
putClient *resty.Client
|
||||
client *resty.Client
|
||||
|
||||
loginParam *LoginParam
|
||||
tokenInfo *AppSessionResp
|
||||
@ -51,9 +49,6 @@ func (y *Cloud189PC) Init(ctx context.Context) (err error) {
|
||||
"Referer": WEB_URL,
|
||||
})
|
||||
}
|
||||
if y.putClient == nil {
|
||||
y.putClient = base.NewRestyClient().SetTimeout(120 * time.Second)
|
||||
}
|
||||
|
||||
// 避免重复登陆
|
||||
identity := utils.GetMD5Encode(y.Username + y.Password)
|
||||
@ -266,8 +261,14 @@ func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error {
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
if y.RapidUpload {
|
||||
switch y.UploadMethod {
|
||||
case "stream":
|
||||
return y.CommonUpload(ctx, dstDir, stream, up)
|
||||
case "old":
|
||||
return y.OldUpload(ctx, dstDir, stream, up)
|
||||
case "rapid":
|
||||
return y.FastUpload(ctx, dstDir, stream, up)
|
||||
default:
|
||||
return y.CommonUpload(ctx, dstDir, stream, up)
|
||||
}
|
||||
return y.CommonUpload(ctx, dstDir, stream, up)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
@ -131,3 +132,18 @@ func BoolToNumber(b bool) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 计算分片大小
|
||||
// 对分片数量有限制
|
||||
// 10MIB 20 MIB 999片
|
||||
// 50MIB 60MIB 70MIB 80MIB ∞MIB 1999片
|
||||
func partSize(size int64) int64 {
|
||||
const DEFAULT = 1024 * 1024 * 10 // 10MIB
|
||||
if size > DEFAULT*2*999 {
|
||||
return int64(math.Max(math.Ceil((float64(size)/1999) /*=单个切片大小*/ /float64(DEFAULT)) /*=倍率*/, 5) * DEFAULT)
|
||||
}
|
||||
if size > DEFAULT*999 {
|
||||
return DEFAULT * 2 // 20MIB
|
||||
}
|
||||
return DEFAULT
|
||||
}
|
||||
|
@ -14,13 +14,14 @@ type Addition struct {
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||
Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
|
||||
FamilyID string `json:"family_id"`
|
||||
RapidUpload bool `json:"rapid_upload"`
|
||||
UploadMethod string `json:"upload_method" type:"select" options:"stream,rapid,old" default:"stream"`
|
||||
NoUseOcr bool `json:"no_use_ocr"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "189CloudPC",
|
||||
DefaultRoot: "-11",
|
||||
CheckStatus: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -10,20 +10,62 @@ import (
|
||||
|
||||
// 居然有四种返回方式
|
||||
type RespErr struct {
|
||||
ResCode string `json:"res_code"`
|
||||
ResCode any `json:"res_code"` // int or string
|
||||
ResMessage string `json:"res_message"`
|
||||
|
||||
Error_ string `json:"error"`
|
||||
|
||||
XMLName xml.Name `xml:"error"`
|
||||
Code string `json:"code" xml:"code"`
|
||||
Message string `json:"message" xml:"message"`
|
||||
|
||||
// Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Msg string `json:"msg"`
|
||||
|
||||
ErrorCode string `json:"errorCode"`
|
||||
ErrorMsg string `json:"errorMsg"`
|
||||
}
|
||||
|
||||
func (e *RespErr) HasError() bool {
|
||||
switch v := e.ResCode.(type) {
|
||||
case int, int64, int32:
|
||||
return v != 0
|
||||
case string:
|
||||
return e.ResCode != ""
|
||||
}
|
||||
return (e.Code != "" && e.Code != "SUCCESS") || e.ErrorCode != "" || e.Error_ != ""
|
||||
}
|
||||
|
||||
func (e *RespErr) Error() string {
|
||||
switch v := e.ResCode.(type) {
|
||||
case int, int64, int32:
|
||||
if v != 0 {
|
||||
return fmt.Sprintf("res_code: %d ,res_msg: %s", v, e.ResMessage)
|
||||
}
|
||||
case string:
|
||||
if e.ResCode != "" {
|
||||
return fmt.Sprintf("res_code: %s ,res_msg: %s", e.ResCode, e.ResMessage)
|
||||
}
|
||||
}
|
||||
|
||||
if e.Code != "" && e.Code != "SUCCESS" {
|
||||
if e.Msg != "" {
|
||||
return fmt.Sprintf("code: %s ,msg: %s", e.Code, e.Msg)
|
||||
}
|
||||
if e.Message != "" {
|
||||
return fmt.Sprintf("code: %s ,msg: %s", e.Code, e.Message)
|
||||
}
|
||||
return "code: " + e.Code
|
||||
}
|
||||
|
||||
if e.ErrorCode != "" {
|
||||
return fmt.Sprintf("err_code: %s ,err_msg: %s", e.ErrorCode, e.ErrorMsg)
|
||||
}
|
||||
|
||||
if e.Error_ != "" {
|
||||
return fmt.Sprintf("error: %s ,message: %s", e.ErrorCode, e.Message)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 登陆需要的参数
|
||||
type LoginParam struct {
|
||||
// 加密后的用户名和密码
|
||||
@ -218,6 +260,42 @@ type Part struct {
|
||||
RequestHeader string `json:"requestHeader"`
|
||||
}
|
||||
|
||||
/* 第二种上传方式 */
|
||||
type CreateUploadFileResp struct {
|
||||
// 上传文件请求ID
|
||||
UploadFileId int64 `json:"uploadFileId"`
|
||||
// 上传文件数据的URL路径
|
||||
FileUploadUrl string `json:"fileUploadUrl"`
|
||||
// 上传文件完成后确认路径
|
||||
FileCommitUrl string `json:"fileCommitUrl"`
|
||||
// 文件是否已存在云盘中,0-未存在,1-已存在
|
||||
FileDataExists int `json:"fileDataExists"`
|
||||
}
|
||||
|
||||
type GetUploadFileStatusResp struct {
|
||||
CreateUploadFileResp
|
||||
|
||||
// 已上传的大小
|
||||
DataSize int64 `json:"dataSize"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func (r *GetUploadFileStatusResp) GetSize() int64 {
|
||||
return r.DataSize + r.Size
|
||||
}
|
||||
|
||||
type CommitUploadFileResp struct {
|
||||
XMLName xml.Name `xml:"file"`
|
||||
Id string `xml:"id"`
|
||||
Name string `xml:"name"`
|
||||
Size string `xml:"size"`
|
||||
Md5 string `xml:"md5"`
|
||||
CreateDate string `xml:"createDate"`
|
||||
Rev string `xml:"rev"`
|
||||
UserId string `xml:"userId"`
|
||||
}
|
||||
|
||||
/* query 加密参数*/
|
||||
type Params map[string]string
|
||||
|
||||
func (p Params) Set(k, v string) {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
@ -23,9 +25,12 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
|
||||
"github.com/avast/retry-go"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/google/uuid"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -47,7 +52,7 @@ const (
|
||||
CHANNEL_ID = "web_cloud.189.cn"
|
||||
)
|
||||
|
||||
func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
|
||||
func (y *Cloud189PC) SignatureHeader(url, method, params string) map[string]string {
|
||||
dateOfGmt := getHttpDateStr()
|
||||
sessionKey := y.tokenInfo.SessionKey
|
||||
sessionSecret := y.tokenInfo.SessionSecret
|
||||
@ -56,19 +61,40 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
|
||||
sessionSecret = y.tokenInfo.FamilySessionSecret
|
||||
}
|
||||
|
||||
req := y.client.R().SetQueryParams(clientSuffix()).SetHeaders(map[string]string{
|
||||
header := map[string]string{
|
||||
"Date": dateOfGmt,
|
||||
"SessionKey": sessionKey,
|
||||
"X-Request-ID": uuid.NewString(),
|
||||
})
|
||||
"Signature": signatureOfHmac(sessionSecret, sessionKey, method, url, dateOfGmt, params),
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) EncryptParams(params Params) string {
|
||||
sessionSecret := y.tokenInfo.SessionSecret
|
||||
if y.isFamily() {
|
||||
sessionSecret = y.tokenInfo.FamilySessionSecret
|
||||
}
|
||||
if params != nil {
|
||||
return AesECBEncrypt(params.Encode(), sessionSecret[:16])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
|
||||
req := y.client.R().SetQueryParams(clientSuffix())
|
||||
|
||||
// 设置params
|
||||
var paramsData string
|
||||
if params != nil {
|
||||
paramsData = AesECBEncrypt(params.Encode(), sessionSecret[:16])
|
||||
paramsData := y.EncryptParams(params)
|
||||
if paramsData != "" {
|
||||
req.SetQueryParam("params", paramsData)
|
||||
}
|
||||
req.SetHeader("Signature", signatureOfHmac(sessionSecret, sessionKey, method, url, dateOfGmt, paramsData))
|
||||
|
||||
// Signature
|
||||
req.SetHeaders(y.SignatureHeader(url, method, paramsData))
|
||||
|
||||
var erron RespErr
|
||||
req.SetError(&erron)
|
||||
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
@ -80,32 +106,6 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var erron RespErr
|
||||
utils.Json.Unmarshal(res.Body(), &erron)
|
||||
|
||||
if erron.ResCode != "" {
|
||||
return nil, fmt.Errorf("res_code: %s ,res_msg: %s", erron.ResCode, erron.ResMessage)
|
||||
}
|
||||
if erron.Code != "" && erron.Code != "SUCCESS" {
|
||||
if erron.Msg != "" {
|
||||
return nil, fmt.Errorf("code: %s ,msg: %s", erron.Code, erron.Msg)
|
||||
}
|
||||
if erron.Message != "" {
|
||||
return nil, fmt.Errorf("code: %s ,msg: %s", erron.Code, erron.Message)
|
||||
}
|
||||
return nil, fmt.Errorf(res.String())
|
||||
}
|
||||
switch erron.ErrorCode {
|
||||
case "":
|
||||
break
|
||||
case "InvalidSessionKey":
|
||||
if err = y.refreshSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y.request(url, method, callback, params, resp)
|
||||
default:
|
||||
return nil, fmt.Errorf("err_code: %s ,err_msg: %s", erron.ErrorCode, erron.ErrorMsg)
|
||||
}
|
||||
|
||||
if strings.Contains(res.String(), "userSessionBO is null") {
|
||||
if err = y.refreshSession(); err != nil {
|
||||
@ -114,14 +114,17 @@ func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, para
|
||||
return y.request(url, method, callback, params, resp)
|
||||
}
|
||||
|
||||
resCode := utils.Json.Get(res.Body(), "res_code").ToInt64()
|
||||
message := utils.Json.Get(res.Body(), "res_message").ToString()
|
||||
switch resCode {
|
||||
case 0:
|
||||
return res.Body(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("res_code: %d ,res_msg: %s", resCode, message)
|
||||
// 处理错误
|
||||
if erron.HasError() {
|
||||
if erron.ErrorCode == "InvalidSessionKey" {
|
||||
if err = y.refreshSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y.request(url, method, callback, params, resp)
|
||||
}
|
||||
return nil, &erron
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
@ -132,6 +135,50 @@ func (y *Cloud189PC) post(url string, callback base.ReqCallback, resp interface{
|
||||
return y.request(url, http.MethodPost, callback, nil, resp)
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) put(ctx context.Context, url string, headers map[string]string, sign bool, file io.Reader) ([]byte, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := req.URL.Query()
|
||||
for key, value := range clientSuffix() {
|
||||
query.Add(key, value)
|
||||
}
|
||||
req.URL.RawQuery = query.Encode()
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
|
||||
if sign {
|
||||
for key, value := range y.SignatureHeader(url, http.MethodPut, "") {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var erron RespErr
|
||||
jsoniter.Unmarshal(body, &erron)
|
||||
xml.Unmarshal(body, &erron)
|
||||
if erron.HasError() {
|
||||
return nil, &erron
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.Errorf("put fail,err:%s", string(body))
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) {
|
||||
fullUrl := API_URL
|
||||
if y.isFamily() {
|
||||
@ -186,7 +233,7 @@ func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj,
|
||||
|
||||
func (y *Cloud189PC) login() (err error) {
|
||||
// 初始化登陆所需参数
|
||||
if y.loginParam == nil || !y.NoUseOcr {
|
||||
if y.loginParam == nil {
|
||||
if err = y.initLoginParam(); err != nil {
|
||||
// 验证码也通过错误返回
|
||||
return err
|
||||
@ -197,7 +244,7 @@ func (y *Cloud189PC) login() (err error) {
|
||||
y.VCode = ""
|
||||
// 销毁登陆参数
|
||||
y.loginParam = nil
|
||||
// 遇到错误,重新加载登陆参数
|
||||
// 遇到错误,重新加载登陆参数(刷新验证码)
|
||||
if err != nil && y.NoUseOcr {
|
||||
if err1 := y.initLoginParam(); err1 != nil {
|
||||
err = fmt.Errorf("err1: %s \nerr2: %s", err, err1)
|
||||
@ -249,9 +296,8 @@ func (y *Cloud189PC) login() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if erron.ResCode != "" {
|
||||
err = fmt.Errorf(erron.ResMessage)
|
||||
return
|
||||
if erron.HasError() {
|
||||
return &erron
|
||||
}
|
||||
if tokenInfo.ResCode != 0 {
|
||||
err = fmt.Errorf(tokenInfo.ResMessage)
|
||||
@ -304,6 +350,22 @@ func (y *Cloud189PC) initLoginParam() error {
|
||||
param.RsaPassword = encryptConf.Data.Pre + RsaEncrypt(param.jRsaKey, y.Password)
|
||||
y.loginParam = ¶m
|
||||
|
||||
// 判断是否需要验证码
|
||||
resp, err := y.client.R().
|
||||
SetHeader("REQID", param.ReqId).
|
||||
SetFormData(map[string]string{
|
||||
"appKey": APP_ID,
|
||||
"accountType": ACCOUNT_TYPE,
|
||||
"userName": param.RsaUsername,
|
||||
}).Post(AUTH_URL + "/api/logbox/oauth2/needcaptcha.do")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.String() == "0" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 拉取验证码
|
||||
imgRes, err := y.client.R().
|
||||
SetQueryParams(map[string]string{
|
||||
"token": param.CaptchaToken,
|
||||
@ -359,33 +421,23 @@ func (y *Cloud189PC) refreshSession() (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
switch erron.ResCode {
|
||||
case "":
|
||||
break
|
||||
case "UserInvalidOpenToken":
|
||||
if err = y.login(); err != nil {
|
||||
return err
|
||||
if erron.HasError() {
|
||||
if erron.ResCode == "UserInvalidOpenToken" {
|
||||
if err = y.login(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("res_code: %s ,res_msg: %s", erron.ResCode, erron.ResMessage)
|
||||
return
|
||||
}
|
||||
|
||||
switch userSessionResp.ResCode {
|
||||
case 0:
|
||||
y.tokenInfo.UserSessionResp = userSessionResp
|
||||
default:
|
||||
err = fmt.Errorf("code: %d , msg: %s", userSessionResp.ResCode, userSessionResp.ResMessage)
|
||||
return &erron
|
||||
}
|
||||
y.tokenInfo.UserSessionResp = userSessionResp
|
||||
return
|
||||
}
|
||||
|
||||
// 普通上传
|
||||
func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
|
||||
const DEFAULT int64 = 10485760
|
||||
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||
var DEFAULT = partSize(file.GetSize())
|
||||
var count = int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||
|
||||
requestID := uuid.NewString()
|
||||
params := Params{
|
||||
"parentFolderId": dstDir.GetID(),
|
||||
"fileName": url.QueryEscape(file.GetName()),
|
||||
@ -407,7 +459,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
||||
var initMultiUpload InitMultiUploadResp
|
||||
_, err = y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, params, &initMultiUpload)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -417,7 +468,7 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
||||
silceMd5 := md5.New()
|
||||
silceMd5Hexs := make([]string, 0, count)
|
||||
byteData := bytes.NewBuffer(make([]byte, DEFAULT))
|
||||
for i := int64(1); i <= count; i++ {
|
||||
for i := 1; i <= count; i++ {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
@ -440,7 +491,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
||||
_, err = y.request(fullUrl+"/getMultiUploadUrls", http.MethodGet,
|
||||
func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, Params{
|
||||
"partInfo": fmt.Sprintf("%d-%s", i, silceMd5Base64),
|
||||
"uploadFileId": initMultiUpload.Data.UploadFileID,
|
||||
@ -451,18 +501,18 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
||||
|
||||
// 开始上传
|
||||
uploadData := uploadUrl.UploadUrls[fmt.Sprint("partNumber_", i)]
|
||||
res, err := y.putClient.R().
|
||||
SetContext(ctx).
|
||||
SetQueryParams(clientSuffix()).
|
||||
SetHeaders(ParseHttpHeader(uploadData.RequestHeader)).
|
||||
SetBody(byteData).
|
||||
Put(uploadData.RequestURL)
|
||||
|
||||
err = retry.Do(func() error {
|
||||
_, err := y.put(ctx, uploadData.RequestURL, ParseHttpHeader(uploadData.RequestHeader), false, bytes.NewReader(byteData.Bytes()))
|
||||
return err
|
||||
},
|
||||
retry.Context(ctx),
|
||||
retry.Attempts(3),
|
||||
retry.Delay(time.Second),
|
||||
retry.MaxDelay(5*time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("updload fail,msg: %s", res.String())
|
||||
}
|
||||
up(int(i * 100 / count))
|
||||
}
|
||||
|
||||
@ -476,7 +526,6 @@ func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mo
|
||||
_, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet,
|
||||
func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, Params{
|
||||
"uploadFileId": initMultiUpload.Data.UploadFileID,
|
||||
"fileMd5": fileMd5Hex,
|
||||
@ -500,7 +549,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
_ = os.Remove(tempFile.Name())
|
||||
}()
|
||||
|
||||
const DEFAULT int64 = 10485760
|
||||
var DEFAULT = partSize(file.GetSize())
|
||||
count := int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||
|
||||
// 优先计算所需信息
|
||||
@ -531,7 +580,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n")))
|
||||
}
|
||||
|
||||
requestID := uuid.NewString()
|
||||
// 检测是否支持快传
|
||||
params := Params{
|
||||
"parentFolderId": dstDir.GetID(),
|
||||
@ -554,7 +602,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
var uploadInfo InitMultiUploadResp
|
||||
_, err = y.request(fullUrl+"/initMultiUpload", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, params, &uploadInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -566,7 +613,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
_, err = y.request(fullUrl+"/getMultiUploadUrls", http.MethodGet,
|
||||
func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, Params{
|
||||
"uploadFileId": uploadInfo.Data.UploadFileID,
|
||||
"partInfo": strings.Join(silceMd5Base64s, ","),
|
||||
@ -575,26 +621,29 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]byte, DEFAULT)
|
||||
for i := 1; i <= count; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(tempFile, buf)
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
uploadData := uploadUrls.UploadUrls[fmt.Sprint("partNumber_", i)]
|
||||
res, err := y.putClient.R().
|
||||
SetContext(ctx).
|
||||
SetQueryParams(clientSuffix()).
|
||||
SetHeaders(ParseHttpHeader(uploadData.RequestHeader)).
|
||||
SetBody(io.LimitReader(tempFile, DEFAULT)).
|
||||
Put(uploadData.RequestURL)
|
||||
err = retry.Do(func() error {
|
||||
_, err := y.put(ctx, uploadData.RequestURL, ParseHttpHeader(uploadData.RequestHeader), false, bytes.NewReader(buf[:n]))
|
||||
return err
|
||||
},
|
||||
retry.Context(ctx),
|
||||
retry.Attempts(3),
|
||||
retry.Delay(time.Second),
|
||||
retry.MaxDelay(5*time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("updload fail,msg: %s", res.String())
|
||||
}
|
||||
|
||||
up(int(i * 100 / count))
|
||||
}
|
||||
}
|
||||
@ -603,7 +652,6 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
_, err = y.request(fullUrl+"/commitMultiUploadFile", http.MethodGet,
|
||||
func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
req.SetHeader("X-Request-ID", requestID)
|
||||
}, Params{
|
||||
"uploadFileId": uploadInfo.Data.UploadFileID,
|
||||
"isLog": "0",
|
||||
@ -612,6 +660,137 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode
|
||||
return err
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) OldUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
|
||||
// 需要获取完整文件md5,必须支持 io.Seek
|
||||
tempFile, err := utils.CreateTempFile(file.GetReadCloser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
_ = os.Remove(tempFile.Name())
|
||||
}()
|
||||
|
||||
// 计算md5
|
||||
fileMd5 := md5.New()
|
||||
if _, err := io.Copy(fileMd5, tempFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tempFile.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil)))
|
||||
|
||||
// 创建上传会话
|
||||
var uploadInfo CreateUploadFileResp
|
||||
|
||||
fullUrl := API_URL + "/createUploadFile.action"
|
||||
if y.isFamily() {
|
||||
fullUrl = API_URL + "/family/file/createFamilyFile.action"
|
||||
}
|
||||
_, err = y.post(fullUrl, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
if y.isFamily() {
|
||||
req.SetQueryParams(map[string]string{
|
||||
"familyId": y.FamilyID,
|
||||
"fileMd5": fileMd5Hex,
|
||||
"fileName": file.GetName(),
|
||||
"fileSize": fmt.Sprint(file.GetSize()),
|
||||
"parentId": dstDir.GetID(),
|
||||
"resumePolicy": "1",
|
||||
})
|
||||
} else {
|
||||
req.SetFormData(map[string]string{
|
||||
"parentFolderId": dstDir.GetID(),
|
||||
"fileName": file.GetName(),
|
||||
"size": fmt.Sprint(file.GetSize()),
|
||||
"md5": fileMd5Hex,
|
||||
"opertype": "3",
|
||||
"flag": "1",
|
||||
"resumePolicy": "1",
|
||||
"isLog": "0",
|
||||
// "baseFileId": "",
|
||||
// "lastWrite":"",
|
||||
// "localPath": strings.ReplaceAll(param.LocalPath, "\\", "/"),
|
||||
// "fileExt": "",
|
||||
})
|
||||
}
|
||||
}, &uploadInfo)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 网盘中不存在该文件,开始上传
|
||||
status := GetUploadFileStatusResp{CreateUploadFileResp: uploadInfo}
|
||||
for status.Size < file.GetSize() && status.FileDataExists != 1 {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
header := map[string]string{
|
||||
"ResumePolicy": "1",
|
||||
"Expect": "100-continue",
|
||||
}
|
||||
|
||||
if y.isFamily() {
|
||||
header["FamilyId"] = fmt.Sprint(y.FamilyID)
|
||||
header["UploadFileId"] = fmt.Sprint(status.UploadFileId)
|
||||
} else {
|
||||
header["Edrive-UploadFileId"] = fmt.Sprint(status.UploadFileId)
|
||||
}
|
||||
|
||||
_, err := y.put(ctx, status.FileUploadUrl, header, true, io.NopCloser(tempFile))
|
||||
if err, ok := err.(*RespErr); ok && err.Code != "InputStreamReadError" {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取断点状态
|
||||
fullUrl := API_URL + "/getUploadFileStatus.action"
|
||||
if y.isFamily() {
|
||||
fullUrl = API_URL + "/family/file/getFamilyFileStatus.action"
|
||||
}
|
||||
_, err = y.get(fullUrl, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetQueryParams(map[string]string{
|
||||
"uploadFileId": fmt.Sprint(status.UploadFileId),
|
||||
"resumePolicy": "1",
|
||||
})
|
||||
if y.isFamily() {
|
||||
req.SetQueryParam("familyId", fmt.Sprint(y.FamilyID))
|
||||
}
|
||||
}, &status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tempFile.Seek(status.GetSize(), io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
up(int(status.Size / file.GetSize()))
|
||||
}
|
||||
|
||||
// 提交
|
||||
var resp CommitUploadFileResp
|
||||
_, err = y.post(status.FileCommitUrl, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
if y.isFamily() {
|
||||
req.SetHeaders(map[string]string{
|
||||
"ResumePolicy": "1",
|
||||
"UploadFileId": fmt.Sprint(status.UploadFileId),
|
||||
"FamilyId": fmt.Sprint(y.FamilyID),
|
||||
})
|
||||
} else {
|
||||
req.SetFormData(map[string]string{
|
||||
"opertype": "3",
|
||||
"resumePolicy": "1",
|
||||
"uploadFileId": fmt.Sprint(status.UploadFileId),
|
||||
"isLog": "0",
|
||||
})
|
||||
}
|
||||
}, &resp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (y *Cloud189PC) isFamily() bool {
|
||||
return y.Type == "family"
|
||||
}
|
||||
|
@ -72,11 +72,21 @@ func (d *Alias) list(ctx context.Context, dst, sub string) ([]model.Obj, error)
|
||||
return nil, err
|
||||
}
|
||||
return utils.SliceConvert(objs, func(obj model.Obj) (model.Obj, error) {
|
||||
return &model.Object{
|
||||
thumb, ok := model.GetThumb(obj)
|
||||
objRes := model.Object{
|
||||
Name: obj.GetName(),
|
||||
Size: obj.GetSize(),
|
||||
Modified: obj.ModTime(),
|
||||
IsFolder: obj.IsDir(),
|
||||
}
|
||||
if !ok {
|
||||
return &objRes, nil
|
||||
}
|
||||
return &model.ObjThumb{
|
||||
Object: objRes,
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: thumb,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
@ -93,7 +103,8 @@ func (d *Alias) link(ctx context.Context, dst, sub string, args model.LinkArgs)
|
||||
}
|
||||
if common.ShouldProxy(storage, stdpath.Base(sub)) {
|
||||
return &model.Link{
|
||||
URL: fmt.Sprintf("/p%s?sign=%s",
|
||||
URL: fmt.Sprintf("%s/p%s?sign=%s",
|
||||
common.GetApiUrl(args.HttpReq),
|
||||
utils.EncodePath(reqPath, true),
|
||||
sign.Sign(reqPath)),
|
||||
}, nil
|
||||
|
@ -2,13 +2,17 @@ package alist_v3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
@ -32,8 +36,33 @@ func (d *AListV3) Init(ctx context.Context) error {
|
||||
_, err := d.request("/me", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetResult(&resp)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if the username is not empty and the username is not the same as the current username, then login again
|
||||
if d.Username != "" && d.Username != resp.Data.Username {
|
||||
return d.login()
|
||||
err = d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// re-get the user info
|
||||
_, err = d.request("/me", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetResult(&resp)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Data.Role == model.GUEST {
|
||||
url := d.Address + "/api/public/settings"
|
||||
res, err := base.RestyClient.R().Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allowMounted := utils.Json.Get(res.Body(), "data", conf.AllowMounted).ToString() == "true"
|
||||
if !allowMounted {
|
||||
return fmt.Errorf("the site does not allow mounted")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -146,6 +175,7 @@ func (d *AListV3) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt
|
||||
req.SetHeader("File-Path", path.Join(dstDir.GetPath(), stream.GetName())).
|
||||
SetHeader("Password", d.MetaPassword).
|
||||
SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).
|
||||
SetContentLength(true).
|
||||
SetBody(stream.GetReadCloser())
|
||||
})
|
||||
return err
|
||||
|
@ -18,6 +18,7 @@ var config = driver.Config{
|
||||
Name: "AList V3",
|
||||
LocalSort: true,
|
||||
DefaultRoot: "/",
|
||||
CheckStatus: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (d *AListV3) login() error {
|
||||
@ -38,6 +39,7 @@ func (d *AListV3) request(api, method string, callback base.ReqCallback, retry .
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("[alist_v3] response body: %s", res.String())
|
||||
if res.StatusCode() >= 400 {
|
||||
return nil, fmt.Errorf("request failed, status: %s", res.Status())
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package aliyundrive_open
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
@ -21,6 +22,9 @@ type AliyundriveOpen struct {
|
||||
base string
|
||||
|
||||
DriveId string
|
||||
|
||||
limitList func(ctx context.Context, data base.Json) (*Files, error)
|
||||
limitLink func(ctx context.Context, file model.Obj) (*model.Link, error)
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) Config() driver.Config {
|
||||
@ -37,6 +41,8 @@ func (d *AliyundriveOpen) Init(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
d.DriveId = utils.Json.Get(res, "default_drive_id").ToString()
|
||||
d.limitList = utils.LimitRateCtx(d.list, time.Second/4)
|
||||
d.limitLink = utils.LimitRateCtx(d.link, time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -45,7 +51,10 @@ func (d *AliyundriveOpen) Drop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
files, err := d.getFiles(dir.GetID())
|
||||
if d.limitList == nil {
|
||||
return nil, fmt.Errorf("driver not init")
|
||||
}
|
||||
files, err := d.getFiles(ctx, dir.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -54,7 +63,7 @@ func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.Li
|
||||
})
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
func (d *AliyundriveOpen) link(ctx context.Context, file model.Obj) (*model.Link, error) {
|
||||
res, err := d.request("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(base.Json{
|
||||
"drive_id": d.DriveId,
|
||||
@ -66,11 +75,20 @@ func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.L
|
||||
return nil, err
|
||||
}
|
||||
url := utils.Json.Get(res, "url").ToString()
|
||||
exp := time.Hour
|
||||
return &model.Link{
|
||||
URL: url,
|
||||
URL: url,
|
||||
Expiration: &exp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if d.limitLink == nil {
|
||||
return nil, fmt.Errorf("driver not init")
|
||||
}
|
||||
return d.limitLink(ctx, file)
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(base.Json{
|
||||
@ -137,7 +155,9 @@ func (d *AliyundriveOpen) Remove(ctx context.Context, obj model.Obj) error {
|
||||
func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
// rapid_upload is not currently supported
|
||||
// 1. create
|
||||
const DEFAULT int64 = 20971520
|
||||
// Part Size Unit: Bytes, Default: 20MB,
|
||||
// Maximum number of slices 10,000, ≈195.3125GB
|
||||
var partSize int64 = 20 * 1024 * 1024
|
||||
createData := base.Json{
|
||||
"drive_id": d.DriveId,
|
||||
"parent_file_id": dstDir.GetID(),
|
||||
@ -146,8 +166,21 @@ func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream mode
|
||||
"check_name_mode": "ignore",
|
||||
}
|
||||
count := 1
|
||||
if stream.GetSize() > DEFAULT {
|
||||
count = int(math.Ceil(float64(stream.GetSize()) / float64(DEFAULT)))
|
||||
if stream.GetSize() > partSize {
|
||||
if stream.GetSize() > 1*1024*1024*1024*1024 { // file Size over 1TB
|
||||
partSize = 5 * 1024 * 1024 * 1024 // file part size 5GB
|
||||
} else if stream.GetSize() > 768*1024*1024*1024 { // over 768GB
|
||||
partSize = 109951163 // ≈ 104.8576MB, split 1TB into 10,000 part
|
||||
} else if stream.GetSize() > 512*1024*1024*1024 { // over 512GB
|
||||
partSize = 82463373 // ≈ 78.6432MB
|
||||
} else if stream.GetSize() > 384*1024*1024*1024 { // over 384GB
|
||||
partSize = 54975582 // ≈ 52.4288MB
|
||||
} else if stream.GetSize() > 256*1024*1024*1024 { // over 256GB
|
||||
partSize = 41231687 // ≈ 39.3216MB
|
||||
} else if stream.GetSize() > 128*1024*1024*1024 { // over 128GB
|
||||
partSize = 27487791 // ≈ 26.2144MB
|
||||
}
|
||||
count = int(math.Ceil(float64(stream.GetSize()) / float64(partSize)))
|
||||
createData["part_info_list"] = makePartInfos(count)
|
||||
}
|
||||
var createResp CreateResp
|
||||
@ -163,7 +196,7 @@ func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream mode
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
err = d.uploadPart(ctx, i, count, utils.NewMultiReadable(io.LimitReader(stream, DEFAULT)), &createResp, true)
|
||||
err = d.uploadPart(ctx, i, count, utils.NewMultiReadable(io.LimitReader(stream, partSize)), &createResp, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ type Addition struct {
|
||||
RefreshToken string `json:"refresh_token" required:"true"`
|
||||
OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"`
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"`
|
||||
OauthTokenURL string `json:"oauth_token_url" default:"https://api.nn.ci/alist/ali_open/token"`
|
||||
OauthTokenURL string `json:"oauth_token_url" default:"https://api.xhofe.top/alist/ali_open/token"`
|
||||
ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"`
|
||||
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
|
||||
RemoveWay string `json:"remove_way" required:"true" type:"select" options:"trash,delete"`
|
||||
@ -33,7 +33,7 @@ var config = driver.Config{
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &AliyundriveOpen{
|
||||
base: "https://open.aliyundrive.com",
|
||||
base: "https://openapi.aliyundrive.com",
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback,
|
||||
}
|
||||
isRetry := len(retry) > 0 && retry[0]
|
||||
if e.Code != "" {
|
||||
if !isRetry && e.Code == "AccessTokenInvalid" {
|
||||
if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.AccessToken == "") {
|
||||
err = d.refreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -77,14 +77,24 @@ func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback,
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) getFiles(fileId string) ([]File, error) {
|
||||
func (d *AliyundriveOpen) list(ctx context.Context, data base.Json) (*Files, error) {
|
||||
var resp Files
|
||||
_, err := d.request("/adrive/v1.0/openFile/list", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data).SetResult(&resp)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (d *AliyundriveOpen) getFiles(ctx context.Context, fileId string) ([]File, error) {
|
||||
marker := "first"
|
||||
res := make([]File, 0)
|
||||
for marker != "" {
|
||||
if marker == "first" {
|
||||
marker = ""
|
||||
}
|
||||
var resp Files
|
||||
data := base.Json{
|
||||
"drive_id": d.DriveId,
|
||||
"limit": 200,
|
||||
@ -98,9 +108,7 @@ func (d *AliyundriveOpen) getFiles(fileId string) ([]File, error) {
|
||||
//"video_thumbnail_width": 480,
|
||||
//"image_thumbnail_width": 480,
|
||||
}
|
||||
_, err := d.request("/adrive/v1.0/openFile/list", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data).SetResult(&resp)
|
||||
})
|
||||
resp, err := d.limitList(ctx, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package aliyundrive_share
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -22,6 +23,9 @@ type AliyundriveShare struct {
|
||||
ShareToken string
|
||||
DriveId string
|
||||
cron *cron.Cron
|
||||
|
||||
limitList func(ctx context.Context, dir model.Obj) ([]model.Obj, error)
|
||||
limitLink func(ctx context.Context, file model.Obj) (*model.Link, error)
|
||||
}
|
||||
|
||||
func (d *AliyundriveShare) Config() driver.Config {
|
||||
@ -48,6 +52,8 @@ func (d *AliyundriveShare) Init(ctx context.Context) error {
|
||||
log.Errorf("%+v", err)
|
||||
}
|
||||
})
|
||||
d.limitList = utils.LimitRateCtx(d.list, time.Second/4)
|
||||
d.limitLink = utils.LimitRateCtx(d.link, time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -60,6 +66,13 @@ func (d *AliyundriveShare) Drop(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
if d.limitList == nil {
|
||||
return nil, fmt.Errorf("driver not init")
|
||||
}
|
||||
return d.limitList(ctx, dir)
|
||||
}
|
||||
|
||||
func (d *AliyundriveShare) list(ctx context.Context, dir model.Obj) ([]model.Obj, error) {
|
||||
files, err := d.getFiles(dir.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -70,6 +83,13 @@ func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.L
|
||||
}
|
||||
|
||||
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if d.limitLink == nil {
|
||||
return nil, fmt.Errorf("driver not init")
|
||||
}
|
||||
return d.limitLink(ctx, file)
|
||||
}
|
||||
|
||||
func (d *AliyundriveShare) link(ctx context.Context, file model.Obj) (*model.Link, error) {
|
||||
data := base.Json{
|
||||
"drive_id": d.DriveId,
|
||||
"file_id": file.GetID(),
|
||||
@ -79,7 +99,7 @@ func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.
|
||||
}
|
||||
var resp ShareLinkResp
|
||||
_, err := d.request("https://api.aliyundrive.com/v2/file/get_share_link_download_url", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data).SetResult(&resp)
|
||||
req.SetHeader(CanaryHeaderKey, CanaryHeaderValue).SetBody(data).SetResult(&resp)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -9,6 +9,12 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// CanaryHeaderKey CanaryHeaderValue for lifting rate limit restrictions
|
||||
CanaryHeaderKey = "X-Canary"
|
||||
CanaryHeaderValue = "client=web,app=share,version=v2.3.1"
|
||||
)
|
||||
|
||||
func (d *AliyundriveShare) refreshToken() error {
|
||||
url := "https://auth.aliyundrive.com/v2/account/token"
|
||||
var resp base.TokenResp
|
||||
@ -58,6 +64,7 @@ func (d *AliyundriveShare) request(url, method string, callback base.ReqCallback
|
||||
SetError(&e).
|
||||
SetHeader("content-type", "application/json").
|
||||
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
||||
SetHeader(CanaryHeaderKey, CanaryHeaderValue).
|
||||
SetHeader("x-share-token", d.ShareToken)
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
@ -91,7 +98,7 @@ func (d *AliyundriveShare) getFiles(fileId string) ([]File, error) {
|
||||
data := base.Json{
|
||||
"image_thumbnail_process": "image/resize,w_160/format,jpeg",
|
||||
"image_url_process": "image/resize,w_1920/format,jpeg",
|
||||
"limit": 100,
|
||||
"limit": 200,
|
||||
"order_by": d.OrderBy,
|
||||
"order_direction": d.OrderDirection,
|
||||
"parent_file_id": fileId,
|
||||
@ -107,6 +114,7 @@ func (d *AliyundriveShare) getFiles(fileId string) ([]File, error) {
|
||||
var resp ListResp
|
||||
res, err := base.RestyClient.R().
|
||||
SetHeader("x-share-token", d.ShareToken).
|
||||
SetHeader(CanaryHeaderKey, CanaryHeaderValue).
|
||||
SetResult(&resp).SetError(&e).SetBody(data).
|
||||
Post("https://api.aliyundrive.com/adrive/v3/file/list")
|
||||
if err != nil {
|
||||
|
@ -16,18 +16,21 @@ import (
|
||||
_ "github.com/alist-org/alist/v3/drivers/baidu_photo"
|
||||
_ "github.com/alist-org/alist/v3/drivers/baidu_share"
|
||||
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
|
||||
_ "github.com/alist-org/alist/v3/drivers/dropbox"
|
||||
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
||||
_ "github.com/alist-org/alist/v3/drivers/google_drive"
|
||||
_ "github.com/alist-org/alist/v3/drivers/google_photo"
|
||||
_ "github.com/alist-org/alist/v3/drivers/ipfs_api"
|
||||
_ "github.com/alist-org/alist/v3/drivers/lanzou"
|
||||
_ "github.com/alist-org/alist/v3/drivers/local"
|
||||
_ "github.com/alist-org/alist/v3/drivers/mediatrack"
|
||||
_ "github.com/alist-org/alist/v3/drivers/mega"
|
||||
_ "github.com/alist-org/alist/v3/drivers/mopan"
|
||||
_ "github.com/alist-org/alist/v3/drivers/onedrive"
|
||||
_ "github.com/alist-org/alist/v3/drivers/onedrive_app"
|
||||
_ "github.com/alist-org/alist/v3/drivers/pikpak"
|
||||
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
|
||||
_ "github.com/alist-org/alist/v3/drivers/quark"
|
||||
_ "github.com/alist-org/alist/v3/drivers/quark_uc"
|
||||
_ "github.com/alist-org/alist/v3/drivers/s3"
|
||||
_ "github.com/alist-org/alist/v3/drivers/seafile"
|
||||
_ "github.com/alist-org/alist/v3/drivers/sftp"
|
||||
@ -40,6 +43,7 @@ import (
|
||||
_ "github.com/alist-org/alist/v3/drivers/uss"
|
||||
_ "github.com/alist-org/alist/v3/drivers/virtual"
|
||||
_ "github.com/alist-org/alist/v3/drivers/webdav"
|
||||
_ "github.com/alist-org/alist/v3/drivers/wopan"
|
||||
_ "github.com/alist-org/alist/v3/drivers/yandex_disk"
|
||||
)
|
||||
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
type BaiduNetdisk struct {
|
||||
model.Storage
|
||||
Addition
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
func (d *BaiduNetdisk) Config() driver.Config {
|
||||
@ -35,7 +34,11 @@ func (d *BaiduNetdisk) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *BaiduNetdisk) Init(ctx context.Context) error {
|
||||
return d.refreshToken()
|
||||
res, err := d.get("/xpan/nas", map[string]string{
|
||||
"method": "uinfo",
|
||||
}, nil)
|
||||
log.Debugf("[baidu] get uinfo: %s", string(res))
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *BaiduNetdisk) Drop(ctx context.Context) error {
|
||||
|
@ -13,6 +13,8 @@ type Addition struct {
|
||||
DownloadAPI string `json:"download_api" type:"select" options:"official,crack" default:"official"`
|
||||
ClientID string `json:"client_id" required:"true" default:"iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v"`
|
||||
ClientSecret string `json:"client_secret" required:"true" default:"jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG"`
|
||||
CustomCrackUA string `json:"custom_crack_ua" required:"true" default:"netdisk"`
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -154,7 +154,7 @@ func (d *BaiduNetdisk) linkCrack(file model.Obj, args model.LinkArgs) (*model.Li
|
||||
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
|
||||
"dlink": "1",
|
||||
"web": "5",
|
||||
//"origin": "dlna",
|
||||
"origin": "dlna",
|
||||
}
|
||||
_, err := d.request("https://pan.baidu.com/api/filemetas", http.MethodGet, func(req *resty.Request) {
|
||||
req.SetQueryParams(param)
|
||||
@ -165,7 +165,7 @@ func (d *BaiduNetdisk) linkCrack(file model.Obj, args model.LinkArgs) (*model.Li
|
||||
return &model.Link{
|
||||
URL: resp.Info[0].Dlink,
|
||||
Header: http.Header{
|
||||
"User-Agent": []string{"netdisk"},
|
||||
"User-Agent": []string{d.CustomCrackUA},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -16,11 +15,6 @@ func getTid() string {
|
||||
return fmt.Sprintf("3%d%.0f", time.Now().Unix(), math.Floor(9000000*rand.Float64()+1000000))
|
||||
}
|
||||
|
||||
// 检查名称
|
||||
func checkName(name string) bool {
|
||||
return len(name) <= 20 && regexp.MustCompile("[\u4e00-\u9fa5A-Za-z0-9_-]").MatchString(name)
|
||||
}
|
||||
|
||||
func toTime(t int64) *time.Time {
|
||||
tm := time.Unix(t, 0)
|
||||
return &tm
|
||||
|
@ -2,7 +2,6 @@ package baiduphoto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@ -22,10 +21,6 @@ const (
|
||||
FILE_API_URL_V2 = API_URL + "/file/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotSupportName = errors.New("only chinese and english, numbers and underscores are supported, and the length is no more than 20")
|
||||
)
|
||||
|
||||
func (d *BaiduPhoto) Request(furl string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
req := base.RestyClient.R().
|
||||
SetQueryParam("access_token", d.AccessToken)
|
||||
@ -48,6 +43,8 @@ func (d *BaiduPhoto) Request(furl string, method string, callback base.ReqCallba
|
||||
return nil, fmt.Errorf("you have joined album")
|
||||
case 50820:
|
||||
return nil, fmt.Errorf("no shared albums found")
|
||||
case 50100:
|
||||
return nil, fmt.Errorf("illegal title, only supports 50 characters")
|
||||
case -6:
|
||||
if err = d.refreshToken(); err != nil {
|
||||
return nil, err
|
||||
@ -188,9 +185,6 @@ func (d *BaiduPhoto) GetAllAlbumFile(ctx context.Context, album *Album, passwd s
|
||||
|
||||
// 创建相册
|
||||
func (d *BaiduPhoto) CreateAlbum(ctx context.Context, name string) (*Album, error) {
|
||||
if !checkName(name) {
|
||||
return nil, ErrNotSupportName
|
||||
}
|
||||
var resp JoinOrCreateAlbumResp
|
||||
_, err := d.Post(ALBUM_API_URL+"/create", func(r *resty.Request) {
|
||||
r.SetContext(ctx).SetResult(&resp)
|
||||
@ -208,10 +202,6 @@ func (d *BaiduPhoto) CreateAlbum(ctx context.Context, name string) (*Album, erro
|
||||
|
||||
// 相册改名
|
||||
func (d *BaiduPhoto) SetAlbumName(ctx context.Context, album *Album, name string) (*Album, error) {
|
||||
if !checkName(name) {
|
||||
return nil, ErrNotSupportName
|
||||
}
|
||||
|
||||
_, err := d.Post(ALBUM_API_URL+"/settitle", func(r *resty.Request) {
|
||||
r.SetContext(ctx)
|
||||
r.SetFormData(map[string]string{
|
||||
|
@ -1,31 +1,49 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
var NoRedirectClient *resty.Client
|
||||
var RestyClient = NewRestyClient()
|
||||
var HttpClient = &http.Client{}
|
||||
var (
|
||||
NoRedirectClient *resty.Client
|
||||
RestyClient *resty.Client
|
||||
HttpClient *http.Client
|
||||
)
|
||||
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||
var DefaultTimeout = time.Second * 30
|
||||
|
||||
func init() {
|
||||
func InitClient() {
|
||||
NoRedirectClient = resty.New().SetRedirectPolicy(
|
||||
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}),
|
||||
)
|
||||
).SetTLSClientConfig(&tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify})
|
||||
NoRedirectClient.SetHeader("user-agent", UserAgent)
|
||||
|
||||
RestyClient = NewRestyClient()
|
||||
HttpClient = NewHttpClient()
|
||||
}
|
||||
|
||||
func NewRestyClient() *resty.Client {
|
||||
client := resty.New().
|
||||
SetHeader("user-agent", UserAgent).
|
||||
SetRetryCount(3).
|
||||
SetTimeout(DefaultTimeout)
|
||||
SetTimeout(DefaultTimeout).
|
||||
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify})
|
||||
return client
|
||||
}
|
||||
|
||||
func NewHttpClient() *http.Client {
|
||||
return &http.Client{
|
||||
Timeout: time.Hour * 48,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -16,7 +17,6 @@ import (
|
||||
type Cloudreve struct {
|
||||
model.Storage
|
||||
Addition
|
||||
Cookie string
|
||||
}
|
||||
|
||||
func (d *Cloudreve) Config() driver.Config {
|
||||
@ -28,6 +28,11 @@ func (d *Cloudreve) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *Cloudreve) Init(ctx context.Context) error {
|
||||
if d.Cookie != "" {
|
||||
return nil
|
||||
}
|
||||
// removing trailing slash
|
||||
d.Address = strings.TrimSuffix(d.Address, "/")
|
||||
return d.login()
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,9 @@ type Addition struct {
|
||||
driver.RootPath
|
||||
// define other
|
||||
Address string `json:"address" required:"true"`
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Cookie string `json:"cookie"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -49,11 +49,13 @@ func (d *Cloudreve) request(method string, path string, callback base.ReqCallbac
|
||||
|
||||
// 刷新 cookie
|
||||
if r.Code == http.StatusUnauthorized && path != loginPath {
|
||||
err = d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
if d.Username != "" && d.Password != "" {
|
||||
err = d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.request(method, path, callback, out)
|
||||
}
|
||||
return d.request(method, path, callback, out)
|
||||
}
|
||||
|
||||
return errors.New(r.Msg)
|
||||
|
222
drivers/dropbox/driver.go
Normal file
222
drivers/dropbox/driver.go
Normal file
@ -0,0 +1,222 @@
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Dropbox struct {
|
||||
model.Storage
|
||||
Addition
|
||||
base string
|
||||
contentBase string
|
||||
}
|
||||
|
||||
func (d *Dropbox) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *Dropbox) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *Dropbox) Init(ctx context.Context) error {
|
||||
query := "foo"
|
||||
res, err := d.request("/2/check/user", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(base.Json{
|
||||
"query": query,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := utils.Json.Get(res, "result").ToString()
|
||||
if result != query {
|
||||
return fmt.Errorf("failed to check user: %s", string(res))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
files, err := d.getFiles(ctx, dir.GetPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
|
||||
return fileToObj(src), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Dropbox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
res, err := d.request("/2/files/get_temporary_link", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"path": file.GetPath(),
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := utils.Json.Get(res, "link").ToString()
|
||||
exp := time.Hour
|
||||
return &model.Link{
|
||||
URL: url,
|
||||
Expiration: &exp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
_, err := d.request("/2/files/create_folder_v2", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"autorename": false,
|
||||
"path": parentDir.GetPath() + "/" + dirName,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dropbox) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
toPath := dstDir.GetPath() + "/" + srcObj.GetName()
|
||||
|
||||
_, err := d.request("/2/files/move_v2", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"allow_ownership_transfer": false,
|
||||
"allow_shared_folder": false,
|
||||
"autorename": false,
|
||||
"from_path": srcObj.GetID(),
|
||||
"to_path": toPath,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dropbox) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
path := srcObj.GetPath()
|
||||
fileName := srcObj.GetName()
|
||||
toPath := path[:len(path)-len(fileName)] + newName
|
||||
|
||||
_, err := d.request("/2/files/move_v2", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"allow_ownership_transfer": false,
|
||||
"allow_shared_folder": false,
|
||||
"autorename": false,
|
||||
"from_path": srcObj.GetID(),
|
||||
"to_path": toPath,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dropbox) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
toPath := dstDir.GetPath() + "/" + srcObj.GetName()
|
||||
_, err := d.request("/2/files/copy_v2", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"allow_ownership_transfer": false,
|
||||
"allow_shared_folder": false,
|
||||
"autorename": false,
|
||||
"from_path": srcObj.GetID(),
|
||||
"to_path": toPath,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dropbox) Remove(ctx context.Context, obj model.Obj) error {
|
||||
uri := "/2/files/delete_v2"
|
||||
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(base.Json{
|
||||
"path": obj.GetID(),
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dropbox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
// 1. start
|
||||
sessionId, err := d.startUploadSession(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2.append
|
||||
// A single request should not upload more than 150 MB, and each call must be multiple of 4MB (except for last call)
|
||||
const PartSize = 20971520
|
||||
count := 1
|
||||
if stream.GetSize() > PartSize {
|
||||
count = int(math.Ceil(float64(stream.GetSize()) / float64(PartSize)))
|
||||
}
|
||||
offset := int64(0)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
start := i * PartSize
|
||||
byteSize := stream.GetSize() - int64(start)
|
||||
if byteSize > PartSize {
|
||||
byteSize = PartSize
|
||||
}
|
||||
|
||||
url := d.contentBase + "/2/files/upload_session/append_v2"
|
||||
reader := io.LimitReader(stream, PartSize)
|
||||
req, err := http.NewRequest(http.MethodPost, url, reader)
|
||||
if err != nil {
|
||||
log.Errorf("failed to update file when append to upload session, err: %+v", err)
|
||||
return err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Authorization", "Bearer "+d.AccessToken)
|
||||
|
||||
args := UploadAppendArgs{
|
||||
Close: false,
|
||||
Cursor: UploadCursor{
|
||||
Offset: offset,
|
||||
SessionID: sessionId,
|
||||
},
|
||||
}
|
||||
argsJson, err := utils.Json.MarshalToString(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Dropbox-API-Arg", argsJson)
|
||||
|
||||
res, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = res.Body.Close()
|
||||
|
||||
if count > 0 {
|
||||
up((i + 1) * 100 / count)
|
||||
}
|
||||
|
||||
offset += byteSize
|
||||
|
||||
}
|
||||
// 3.finish
|
||||
toPath := dstDir.GetPath() + "/" + stream.GetName()
|
||||
err2 := d.finishUploadSession(ctx, toPath, offset, sessionId)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*Dropbox)(nil)
|
42
drivers/dropbox/meta.go
Normal file
42
drivers/dropbox/meta.go
Normal file
@ -0,0 +1,42 @@
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultClientID = "76lrwrklhdn1icb"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
RefreshToken string `json:"refresh_token" required:"true"`
|
||||
driver.RootPath
|
||||
|
||||
OauthTokenURL string `json:"oauth_token_url" default:"https://api.xhofe.top/alist/dropbox/token"`
|
||||
ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"`
|
||||
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
|
||||
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "Dropbox",
|
||||
LocalSort: false,
|
||||
OnlyLocal: false,
|
||||
OnlyProxy: false,
|
||||
NoCache: false,
|
||||
NoUpload: false,
|
||||
NeedMs: false,
|
||||
DefaultRoot: "",
|
||||
NoOverwriteUpload: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &Dropbox{
|
||||
base: "https://api.dropboxapi.com",
|
||||
contentBase: "https://content.dropboxapi.com",
|
||||
}
|
||||
})
|
||||
}
|
79
drivers/dropbox/types.go
Normal file
79
drivers/dropbox/types.go
Normal file
@ -0,0 +1,79 @@
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenResp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
type ErrorResp struct {
|
||||
Error struct {
|
||||
Tag string `json:".tag"`
|
||||
} `json:"error"`
|
||||
ErrorSummary string `json:"error_summary"`
|
||||
}
|
||||
|
||||
type RefreshTokenErrorResp struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Tag string `json:".tag"`
|
||||
Name string `json:"name"`
|
||||
PathLower string `json:"path_lower"`
|
||||
PathDisplay string `json:"path_display"`
|
||||
ID string `json:"id"`
|
||||
ClientModified time.Time `json:"client_modified"`
|
||||
ServerModified time.Time `json:"server_modified"`
|
||||
Rev string `json:"rev"`
|
||||
Size int `json:"size"`
|
||||
IsDownloadable bool `json:"is_downloadable"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
}
|
||||
|
||||
type ListResp struct {
|
||||
Entries []File `json:"entries"`
|
||||
Cursor string `json:"cursor"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
|
||||
type UploadCursor struct {
|
||||
Offset int64 `json:"offset"`
|
||||
SessionID string `json:"session_id"`
|
||||
}
|
||||
|
||||
type UploadAppendArgs struct {
|
||||
Close bool `json:"close"`
|
||||
Cursor UploadCursor `json:"cursor"`
|
||||
}
|
||||
|
||||
type UploadFinishArgs struct {
|
||||
Commit struct {
|
||||
Autorename bool `json:"autorename"`
|
||||
Mode string `json:"mode"`
|
||||
Mute bool `json:"mute"`
|
||||
Path string `json:"path"`
|
||||
StrictConflict bool `json:"strict_conflict"`
|
||||
} `json:"commit"`
|
||||
Cursor UploadCursor `json:"cursor"`
|
||||
}
|
||||
|
||||
func fileToObj(f File) *model.ObjThumb {
|
||||
return &model.ObjThumb{
|
||||
Object: model.Object{
|
||||
ID: f.ID,
|
||||
Path: f.PathDisplay,
|
||||
Name: f.Name,
|
||||
Size: int64(f.Size),
|
||||
Modified: f.ServerModified,
|
||||
IsFolder: f.Tag == "folder",
|
||||
},
|
||||
Thumbnail: model.Thumbnail{},
|
||||
}
|
||||
}
|
199
drivers/dropbox/util.go
Normal file
199
drivers/dropbox/util.go
Normal file
@ -0,0 +1,199 @@
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (d *Dropbox) refreshToken() error {
|
||||
url := d.base + "/oauth2/token"
|
||||
if utils.SliceContains([]string{"", DefaultClientID}, d.ClientID) {
|
||||
url = d.OauthTokenURL
|
||||
}
|
||||
var tokenResp TokenResp
|
||||
resp, err := base.RestyClient.R().
|
||||
//ForceContentType("application/x-www-form-urlencoded").
|
||||
//SetBasicAuth(d.ClientID, d.ClientSecret).
|
||||
SetFormData(map[string]string{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": d.RefreshToken,
|
||||
"client_id": d.ClientID,
|
||||
"client_secret": d.ClientSecret,
|
||||
}).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("[dropbox] refresh token response: %s", resp.String())
|
||||
if resp.StatusCode() != 200 {
|
||||
return fmt.Errorf("failed to refresh token: %s", resp.String())
|
||||
}
|
||||
_ = utils.Json.UnmarshalFromString(resp.String(), &tokenResp)
|
||||
d.AccessToken = tokenResp.AccessToken
|
||||
op.MustSaveDriverStorage(d)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||
if method == http.MethodPost {
|
||||
req.SetHeader("Content-Type", "application/json")
|
||||
}
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
}
|
||||
var e ErrorResp
|
||||
req.SetError(&e)
|
||||
res, err := req.Execute(method, d.base+uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("[dropbox] request (%s) response: %s", uri, res.String())
|
||||
isRetry := len(retry) > 0 && retry[0]
|
||||
if res.StatusCode() != 200 {
|
||||
body := res.String()
|
||||
if !isRetry && (utils.SliceMeet([]string{"expired_access_token", "invalid_access_token", "authorization"}, body,
|
||||
func(item string, v string) bool {
|
||||
return strings.Contains(v, item)
|
||||
}) || d.AccessToken == "") {
|
||||
err = d.refreshToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.request(uri, method, callback, true)
|
||||
}
|
||||
return nil, fmt.Errorf("%s:%s", e.Error, e.ErrorSummary)
|
||||
}
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) list(ctx context.Context, data base.Json, isContinue bool) (*ListResp, error) {
|
||||
var resp ListResp
|
||||
uri := "/2/files/list_folder"
|
||||
if isContinue {
|
||||
uri += "/continue"
|
||||
}
|
||||
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
|
||||
req.SetContext(ctx).SetBody(data).SetResult(&resp)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) getFiles(ctx context.Context, path string) ([]File, error) {
|
||||
hasMore := true
|
||||
var marker string
|
||||
res := make([]File, 0)
|
||||
|
||||
data := base.Json{
|
||||
"include_deleted": false,
|
||||
"include_has_explicit_shared_members": false,
|
||||
"include_mounted_folders": false,
|
||||
"include_non_downloadable_files": false,
|
||||
"limit": 2000,
|
||||
"path": path,
|
||||
"recursive": false,
|
||||
}
|
||||
resp, err := d.list(ctx, data, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
marker = resp.Cursor
|
||||
hasMore = resp.HasMore
|
||||
res = append(res, resp.Entries...)
|
||||
|
||||
for hasMore {
|
||||
data := base.Json{
|
||||
"cursor": marker,
|
||||
}
|
||||
resp, err := d.list(ctx, data, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
marker = resp.Cursor
|
||||
hasMore = resp.HasMore
|
||||
res = append(res, resp.Entries...)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) finishUploadSession(ctx context.Context, toPath string, offset int64, sessionId string) error {
|
||||
url := d.contentBase + "/2/files/upload_session/finish"
|
||||
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Authorization", "Bearer "+d.AccessToken)
|
||||
|
||||
uploadFinishArgs := UploadFinishArgs{
|
||||
Commit: struct {
|
||||
Autorename bool `json:"autorename"`
|
||||
Mode string `json:"mode"`
|
||||
Mute bool `json:"mute"`
|
||||
Path string `json:"path"`
|
||||
StrictConflict bool `json:"strict_conflict"`
|
||||
}{
|
||||
Autorename: true,
|
||||
Mode: "add",
|
||||
Mute: false,
|
||||
Path: toPath,
|
||||
StrictConflict: false,
|
||||
},
|
||||
Cursor: UploadCursor{
|
||||
Offset: offset,
|
||||
SessionID: sessionId,
|
||||
},
|
||||
}
|
||||
|
||||
argsJson, err := utils.Json.MarshalToString(uploadFinishArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Dropbox-API-Arg", argsJson)
|
||||
|
||||
res, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Errorf("failed to update file when finish session, err: %+v", err)
|
||||
return err
|
||||
}
|
||||
_ = res.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dropbox) startUploadSession(ctx context.Context) (string, error) {
|
||||
url := d.contentBase + "/2/files/upload_session/start"
|
||||
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Authorization", "Bearer "+d.AccessToken)
|
||||
req.Header.Set("Dropbox-API-Arg", "{\"close\":false}")
|
||||
|
||||
res, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Errorf("failed to update file when start session, err: %+v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
sessionId := utils.Json.Get(body, "session_id").ToString()
|
||||
|
||||
_ = res.Body.Close()
|
||||
return sessionId, nil
|
||||
}
|
@ -17,7 +17,9 @@ import (
|
||||
type GoogleDrive struct {
|
||||
model.Storage
|
||||
Addition
|
||||
AccessToken string
|
||||
AccessToken string
|
||||
ServiceAccountFile int
|
||||
ServiceAccountFileList []string
|
||||
}
|
||||
|
||||
func (d *GoogleDrive) Config() driver.Config {
|
||||
|
@ -2,21 +2,134 @@ package google_drive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
type googleDriveServiceAccount struct {
|
||||
//Type string `json:"type"`
|
||||
//ProjectID string `json:"project_id"`
|
||||
//PrivateKeyID string `json:"private_key_id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
ClientEMail string `json:"client_email"`
|
||||
//ClientID string `json:"client_id"`
|
||||
//AuthURI string `json:"auth_uri"`
|
||||
TokenURI string `json:"token_uri"`
|
||||
//AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
|
||||
//ClientX509CertURL string `json:"client_x509_cert_url"`
|
||||
}
|
||||
|
||||
func (d *GoogleDrive) refreshToken() error {
|
||||
// googleDriveServiceAccountFile gdsaFile
|
||||
gdsaFile, gdsaFileErr := os.Stat(d.RefreshToken)
|
||||
if gdsaFileErr == nil {
|
||||
gdsaFileThis := d.RefreshToken
|
||||
if gdsaFile.IsDir() {
|
||||
if len(d.ServiceAccountFileList) <= 0 {
|
||||
gdsaReadDir, gdsaDirErr := ioutil.ReadDir(d.RefreshToken)
|
||||
if gdsaDirErr != nil {
|
||||
log.Error("read dir fail")
|
||||
return gdsaDirErr
|
||||
}
|
||||
var gdsaFileList []string
|
||||
for _, fi := range gdsaReadDir {
|
||||
if !fi.IsDir() {
|
||||
match, _ := regexp.MatchString("^.*\\.json$", fi.Name())
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
gdsaDirText := d.RefreshToken
|
||||
if d.RefreshToken[len(d.RefreshToken)-1:] != "/" {
|
||||
gdsaDirText = d.RefreshToken + "/"
|
||||
}
|
||||
gdsaFileList = append(gdsaFileList, gdsaDirText+fi.Name())
|
||||
}
|
||||
}
|
||||
d.ServiceAccountFileList = gdsaFileList
|
||||
gdsaFileThis = d.ServiceAccountFileList[d.ServiceAccountFile]
|
||||
d.ServiceAccountFile++
|
||||
} else {
|
||||
if d.ServiceAccountFile < len(d.ServiceAccountFileList) {
|
||||
d.ServiceAccountFile++
|
||||
} else {
|
||||
d.ServiceAccountFile = 0
|
||||
}
|
||||
gdsaFileThis = d.ServiceAccountFileList[d.ServiceAccountFile]
|
||||
}
|
||||
}
|
||||
|
||||
gdsaFileThisContent, err := ioutil.ReadFile(gdsaFileThis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now let's unmarshal the data into `payload`
|
||||
var jsonData googleDriveServiceAccount
|
||||
err = utils.Json.Unmarshal(gdsaFileThisContent, &jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gdsaScope := "https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.metadata https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.scripts"
|
||||
|
||||
timeNow := time.Now()
|
||||
var timeStart int64 = timeNow.Unix()
|
||||
var timeEnd int64 = timeNow.Add(time.Minute * 60).Unix()
|
||||
|
||||
// load private key from string
|
||||
privateKeyPem, _ := pem.Decode([]byte(jsonData.PrivateKey))
|
||||
privateKey, _ := x509.ParsePKCS8PrivateKey(privateKeyPem.Bytes)
|
||||
|
||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256,
|
||||
jwt.MapClaims{
|
||||
"iss": jsonData.ClientEMail,
|
||||
"scope": gdsaScope,
|
||||
"aud": jsonData.TokenURI,
|
||||
"exp": timeEnd,
|
||||
"iat": timeStart,
|
||||
})
|
||||
assertion, err := jwtToken.SignedString(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp base.TokenResp
|
||||
var e TokenError
|
||||
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
|
||||
SetFormData(map[string]string{
|
||||
"assertion": assertion,
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
}).Post(jsonData.TokenURI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug(res.String())
|
||||
if e.Error != "" {
|
||||
return fmt.Errorf(e.Error)
|
||||
}
|
||||
d.AccessToken = resp.AccessToken
|
||||
return nil
|
||||
}
|
||||
if gdsaFileErr != nil && os.IsExist(gdsaFileErr) {
|
||||
return gdsaFileErr
|
||||
}
|
||||
url := "https://www.googleapis.com/oauth2/v4/token"
|
||||
var resp base.TokenResp
|
||||
var e TokenError
|
||||
|
128
drivers/ipfs_api/driver.go
Normal file
128
drivers/ipfs_api/driver.go
Normal file
@ -0,0 +1,128 @@
|
||||
package ipfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
stdpath "path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
shell "github.com/ipfs/go-ipfs-api"
|
||||
)
|
||||
|
||||
type IPFS struct {
|
||||
model.Storage
|
||||
Addition
|
||||
sh *shell.Shell
|
||||
gateURL *url.URL
|
||||
}
|
||||
|
||||
func (d *IPFS) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *IPFS) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *IPFS) Init(ctx context.Context) error {
|
||||
d.sh = shell.NewShell(d.Endpoint)
|
||||
gateURL, err := url.Parse(d.Gateway)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.gateURL = gateURL
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *IPFS) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *IPFS) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
path := dir.GetPath()
|
||||
if path[len(path):] != "/" {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
path_cid, err := d.sh.FilesStat(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dirs, err := d.sh.List(path_cid.Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objlist := []model.Obj{}
|
||||
for _, file := range dirs {
|
||||
gateurl := *d.gateURL
|
||||
gateurl.Path = "ipfs/" + file.Hash
|
||||
gateurl.RawQuery = "filename=" + file.Name
|
||||
objlist = append(objlist, &model.ObjectURL{
|
||||
Object: model.Object{ID: file.Hash, Name: file.Name, Size: int64(file.Size), IsFolder: file.Type == 1},
|
||||
Url: model.Url{Url: gateurl.String()},
|
||||
})
|
||||
}
|
||||
|
||||
return objlist, nil
|
||||
}
|
||||
|
||||
func (d *IPFS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
link := d.Gateway + "/ipfs/" + file.GetID() + "/?filename=" + file.GetName()
|
||||
return &model.Link{URL: link}, nil
|
||||
}
|
||||
|
||||
func (d *IPFS) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
path := parentDir.GetPath()
|
||||
if path[len(path):] != "/" {
|
||||
path += "/"
|
||||
}
|
||||
return d.sh.FilesMkdir(ctx, path+dirName)
|
||||
}
|
||||
|
||||
func (d *IPFS) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return d.sh.FilesMv(ctx, srcObj.GetPath(), dstDir.GetPath())
|
||||
}
|
||||
|
||||
func (d *IPFS) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
newFileName := filepath.Dir(srcObj.GetPath()) + "/" + newName
|
||||
return d.sh.FilesMv(ctx, srcObj.GetPath(), strings.ReplaceAll(newFileName, "\\", "/"))
|
||||
}
|
||||
|
||||
func (d *IPFS) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
// TODO copy obj, optional
|
||||
fmt.Println(srcObj.GetPath())
|
||||
fmt.Println(dstDir.GetPath())
|
||||
newFileName := dstDir.GetPath() + "/" + filepath.Base(srcObj.GetPath())
|
||||
fmt.Println(newFileName)
|
||||
return d.sh.FilesCp(ctx, srcObj.GetPath(), strings.ReplaceAll(newFileName, "\\", "/"))
|
||||
}
|
||||
|
||||
func (d *IPFS) Remove(ctx context.Context, obj model.Obj) error {
|
||||
// TODO remove obj, optional
|
||||
return d.sh.FilesRm(ctx, obj.GetPath(), true)
|
||||
}
|
||||
|
||||
func (d *IPFS) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
// TODO upload file, optional
|
||||
_, err := d.sh.Add(stream, ToFiles(stdpath.Join(dstDir.GetPath(), stream.GetName())))
|
||||
return err
|
||||
}
|
||||
|
||||
func ToFiles(dstDir string) shell.AddOpts {
|
||||
return func(rb *shell.RequestBuilder) error {
|
||||
rb.Option("to-files", dstDir)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||
// return nil, errs.NotSupport
|
||||
//}
|
||||
|
||||
var _ driver.Driver = (*IPFS)(nil)
|
25
drivers/ipfs_api/meta.go
Normal file
25
drivers/ipfs_api/meta.go
Normal file
@ -0,0 +1,25 @@
|
||||
package ipfs
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
// Usually one of two
|
||||
driver.RootPath
|
||||
Endpoint string `json:"endpoint" default:"http://127.0.0.1:5001"`
|
||||
Gateway string `json:"gateway" default:"https://ipfs.io"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "IPFS API",
|
||||
DefaultRoot: "/",
|
||||
LocalSort: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &IPFS{}
|
||||
})
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -15,12 +14,11 @@ import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
var upClient = base.NewRestyClient().SetTimeout(120 * time.Second)
|
||||
|
||||
type LanZou struct {
|
||||
Addition
|
||||
model.Storage
|
||||
uid string
|
||||
vei string
|
||||
}
|
||||
|
||||
func (d *LanZou) Config() driver.Config {
|
||||
@ -31,7 +29,7 @@ func (d *LanZou) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *LanZou) Init(ctx context.Context) error {
|
||||
func (d *LanZou) Init(ctx context.Context) (err error) {
|
||||
if d.IsCookie() {
|
||||
if d.RootFolderID == "" {
|
||||
d.RootFolderID = "-1"
|
||||
@ -41,8 +39,9 @@ func (d *LanZou) Init(ctx context.Context) error {
|
||||
return fmt.Errorf("cookie does not contain ylogin")
|
||||
}
|
||||
d.uid = ylogin[1]
|
||||
d.vei, err = d.getVei()
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (d *LanZou) Drop(ctx context.Context) error {
|
||||
@ -209,11 +208,11 @@ func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
||||
var resp RespText[[]FileOrFolder]
|
||||
_, err := d._post(d.BaseUrl+"/fileup.php", func(req *resty.Request) {
|
||||
req.SetFormData(map[string]string{
|
||||
"task": "1",
|
||||
"vie": "2",
|
||||
"ve": "2",
|
||||
"id": "WU_FILE_0",
|
||||
"name": stream.GetName(),
|
||||
"task": "1",
|
||||
"vie": "2",
|
||||
"ve": "2",
|
||||
"id": "WU_FILE_0",
|
||||
"name": stream.GetName(),
|
||||
"folder_id_bb_n": dstDir.GetID(),
|
||||
}).SetFileReader("upload_file", stream.GetName(), stream).SetContext(ctx)
|
||||
}, &resp, true)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
@ -16,14 +17,22 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var upClient *resty.Client
|
||||
var once sync.Once
|
||||
|
||||
func (d *LanZou) doupload(callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
return d.post(d.BaseUrl+"/doupload.php", func(req *resty.Request) {
|
||||
req.SetQueryParam("uid", d.uid)
|
||||
callback(req)
|
||||
req.SetQueryParams(map[string]string{
|
||||
"uid": d.uid,
|
||||
"vei": d.vei,
|
||||
})
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
}
|
||||
}, resp)
|
||||
}
|
||||
|
||||
func (d *LanZou) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
func (d *LanZou) get(url string, callback base.ReqCallback) ([]byte, error) {
|
||||
return d.request(url, http.MethodGet, callback, false)
|
||||
}
|
||||
|
||||
@ -64,6 +73,9 @@ func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{},
|
||||
func (d *LanZou) request(url string, method string, callback base.ReqCallback, up bool) ([]byte, error) {
|
||||
var req *resty.Request
|
||||
if up {
|
||||
once.Do(func() {
|
||||
upClient = base.NewRestyClient().SetTimeout(120 * time.Second)
|
||||
})
|
||||
req = upClient.R()
|
||||
} else {
|
||||
req = base.RestyClient.R()
|
||||
@ -217,7 +229,7 @@ func (d *LanZou) getShareUrlHtml(shareID string) (string, error) {
|
||||
Value: vs,
|
||||
})
|
||||
}
|
||||
}, nil)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -308,7 +320,7 @@ func (d *LanZou) getFilesByShareUrl(shareID, pwd string, sharePageData string) (
|
||||
log.Errorf("lanzou: err => not find file page param ,data => %s\n", sharePageData)
|
||||
return nil, fmt.Errorf("not find file page param")
|
||||
}
|
||||
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil, nil)
|
||||
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -438,3 +450,22 @@ func (d *LanZou) getFileRealInfo(downURL string) (*int64, *time.Time) {
|
||||
size, _ := strconv.ParseInt(res.Header().Get("Content-Length"), 10, 64)
|
||||
return &size, &time
|
||||
}
|
||||
|
||||
func (d *LanZou) getVei() (string, error) {
|
||||
resp, err := d.get("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
|
||||
req.SetQueryParams(map[string]string{
|
||||
"item": "files",
|
||||
"action": "index",
|
||||
"u": d.uid,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
html := RemoveNotes(string(resp))
|
||||
data, err := htmlJsonToMap(html)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return data["vei"], nil
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -20,7 +19,6 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/sign"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/disintegration/imaging"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
@ -54,6 +52,12 @@ func (d *Local) Init(ctx context.Context) error {
|
||||
}
|
||||
d.Addition.RootFolderPath = abs
|
||||
}
|
||||
if d.ThumbCacheFolder != "" && !utils.Exists(d.ThumbCacheFolder) {
|
||||
err := os.MkdirAll(d.ThumbCacheFolder, os.FileMode(d.mkdirPerm))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -135,36 +139,18 @@ func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
||||
fullPath := file.GetPath()
|
||||
var link model.Link
|
||||
if args.Type == "thumb" && utils.Ext(file.GetName()) != "svg" {
|
||||
var srcBuf *bytes.Buffer
|
||||
if utils.GetFileType(file.GetName()) == conf.VIDEO {
|
||||
videoBuf, err := GetSnapshot(fullPath, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcBuf = videoBuf
|
||||
} else {
|
||||
imgData, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgBuf := bytes.NewBuffer(imgData)
|
||||
srcBuf = imgBuf
|
||||
}
|
||||
|
||||
image, err := imaging.Decode(srcBuf)
|
||||
buf, thumbPath, err := d.getThumb(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thumbImg := imaging.Resize(image, 144, 0, imaging.Lanczos)
|
||||
var buf bytes.Buffer
|
||||
err = imaging.Encode(&buf, thumbImg, imaging.PNG)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := buf.Len()
|
||||
link.Data = io.NopCloser(&buf)
|
||||
link.Header = http.Header{
|
||||
"Content-Length": []string{strconv.Itoa(size)},
|
||||
"Content-Type": []string{"image/png"},
|
||||
}
|
||||
if thumbPath != nil {
|
||||
link.FilePath = thumbPath
|
||||
} else {
|
||||
link.Data = io.NopCloser(buf)
|
||||
link.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||
}
|
||||
} else {
|
||||
link.FilePath = &fullPath
|
||||
|
@ -7,9 +7,10 @@ import (
|
||||
|
||||
type Addition struct {
|
||||
driver.RootPath
|
||||
Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"`
|
||||
ShowHidden bool `json:"show_hidden" default:"true" required:"false" help:"show hidden directories and files"`
|
||||
MkdirPerm string `json:"mkdir_perm" default:"777"`
|
||||
Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"`
|
||||
ThumbCacheFolder string `json:"thumb_cache_folder"`
|
||||
ShowHidden bool `json:"show_hidden" default:"true" required:"false" help:"show hidden directories and files"`
|
||||
MkdirPerm string `json:"mkdir_perm" default:"777"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -7,7 +7,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/disintegration/imaging"
|
||||
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||
)
|
||||
|
||||
@ -55,3 +60,52 @@ func readDir(dirname string) ([]fs.FileInfo, error) {
|
||||
sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
|
||||
fullPath := file.GetPath()
|
||||
thumbPrefix := "alist_thumb_"
|
||||
thumbName := thumbPrefix + utils.GetMD5Encode(fullPath) + ".png"
|
||||
if d.ThumbCacheFolder != "" {
|
||||
// skip if the file is a thumbnail
|
||||
if strings.HasPrefix(file.GetName(), thumbPrefix) {
|
||||
return nil, &fullPath, nil
|
||||
}
|
||||
thumbPath := filepath.Join(d.ThumbCacheFolder, thumbName)
|
||||
if utils.Exists(thumbPath) {
|
||||
return nil, &thumbPath, nil
|
||||
}
|
||||
}
|
||||
var srcBuf *bytes.Buffer
|
||||
if utils.GetFileType(file.GetName()) == conf.VIDEO {
|
||||
videoBuf, err := GetSnapshot(fullPath, 10)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
srcBuf = videoBuf
|
||||
} else {
|
||||
imgData, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
imgBuf := bytes.NewBuffer(imgData)
|
||||
srcBuf = imgBuf
|
||||
}
|
||||
|
||||
image, err := imaging.Decode(srcBuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
thumbImg := imaging.Resize(image, 144, 0, imaging.Lanczos)
|
||||
var buf bytes.Buffer
|
||||
err = imaging.Encode(&buf, thumbImg, imaging.PNG)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if d.ThumbCacheFolder != "" {
|
||||
err = os.WriteFile(filepath.Join(d.ThumbCacheFolder, thumbName), buf.Bytes(), 0666)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return &buf, nil, nil
|
||||
}
|
||||
|
295
drivers/mopan/driver.go
Normal file
295
drivers/mopan/driver.go
Normal file
@ -0,0 +1,295 @@
|
||||
package mopan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"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/avast/retry-go"
|
||||
"github.com/foxxorcat/mopan-sdk-go"
|
||||
)
|
||||
|
||||
type MoPan struct {
|
||||
model.Storage
|
||||
Addition
|
||||
client *mopan.MoClient
|
||||
|
||||
userID string
|
||||
}
|
||||
|
||||
func (d *MoPan) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *MoPan) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *MoPan) Init(ctx context.Context) error {
|
||||
login := func() error {
|
||||
data, err := d.client.Login(d.Phone, d.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.client.SetAuthorization(data.Token)
|
||||
|
||||
info, err := d.client.GetUserInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.userID = info.UserID
|
||||
return nil
|
||||
}
|
||||
d.client = mopan.NewMoClient().
|
||||
SetRestyClient(base.RestyClient).
|
||||
SetOnAuthorizationExpired(func(_ error) error {
|
||||
err := login()
|
||||
if err != nil {
|
||||
d.Status = err.Error()
|
||||
op.MustSaveDriverStorage(d)
|
||||
}
|
||||
return err
|
||||
}).SetDeviceInfo(d.DeviceInfo)
|
||||
d.DeviceInfo = d.client.GetDeviceInfo()
|
||||
return login()
|
||||
}
|
||||
|
||||
func (d *MoPan) Drop(ctx context.Context) error {
|
||||
d.client = nil
|
||||
d.userID = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *MoPan) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
var files []model.Obj
|
||||
for page := 1; ; page++ {
|
||||
data, err := d.client.QueryFiles(dir.GetID(), page, mopan.WarpParamOption(
|
||||
func(j mopan.Json) {
|
||||
j["orderBy"] = d.OrderBy
|
||||
j["descending"] = d.OrderDirection == "desc"
|
||||
},
|
||||
mopan.ParamOptionShareFile(d.CloudID),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data.FileListAO.FileList)+len(data.FileListAO.FolderList) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, utils.MustSliceConvert(data.FileListAO.FolderList, folderToObj)...)
|
||||
files = append(files, utils.MustSliceConvert(data.FileListAO.FileList, fileToObj)...)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (d *MoPan) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
data, err := d.client.GetFileDownloadUrl(file.GetID(), mopan.WarpParamOption(mopan.ParamOptionShareFile(d.CloudID)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Link{
|
||||
URL: data.DownloadUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *MoPan) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
|
||||
f, err := d.client.CreateFolder(dirName, parentDir.GetID(), mopan.WarpParamOption(
|
||||
mopan.ParamOptionShareFile(d.CloudID),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return folderToObj(*f), nil
|
||||
}
|
||||
|
||||
func (d *MoPan) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
||||
return d.newTask(srcObj, dstDir, mopan.TASK_MOVE)
|
||||
}
|
||||
|
||||
func (d *MoPan) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
|
||||
if srcObj.IsDir() {
|
||||
_, err := d.client.RenameFolder(srcObj.GetID(), newName, mopan.WarpParamOption(
|
||||
mopan.ParamOptionShareFile(d.CloudID),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, err := d.client.RenameFile(srcObj.GetID(), newName, mopan.WarpParamOption(
|
||||
mopan.ParamOptionShareFile(d.CloudID),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return CloneObj(srcObj, srcObj.GetID(), newName), nil
|
||||
}
|
||||
|
||||
func (d *MoPan) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
||||
return d.newTask(srcObj, dstDir, mopan.TASK_COPY)
|
||||
}
|
||||
|
||||
func (d *MoPan) newTask(srcObj, dstDir model.Obj, taskType mopan.TaskType) (model.Obj, error) {
|
||||
param := mopan.TaskParam{
|
||||
UserOrCloudID: d.userID,
|
||||
Source: 1,
|
||||
TaskType: taskType,
|
||||
TargetSource: 1,
|
||||
TargetUserOrCloudID: d.userID,
|
||||
TargetType: 1,
|
||||
TargetFolderID: dstDir.GetID(),
|
||||
TaskStatusDetailDTOList: []mopan.TaskFileParam{
|
||||
{
|
||||
FileID: srcObj.GetID(),
|
||||
IsFolder: srcObj.IsDir(),
|
||||
FileName: srcObj.GetName(),
|
||||
},
|
||||
},
|
||||
}
|
||||
if d.CloudID != "" {
|
||||
param.UserOrCloudID = d.CloudID
|
||||
param.Source = 2
|
||||
param.TargetSource = 2
|
||||
param.TargetUserOrCloudID = d.CloudID
|
||||
}
|
||||
|
||||
task, err := d.client.AddBatchTask(param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for count := 0; count < 5; count++ {
|
||||
stat, err := d.client.CheckBatchTask(mopan.TaskCheckParam{
|
||||
TaskId: task.TaskIDList[0],
|
||||
TaskType: task.TaskType,
|
||||
TargetType: 1,
|
||||
TargetFolderID: task.TargetFolderID,
|
||||
TargetSource: param.TargetSource,
|
||||
TargetUserOrCloudID: param.TargetUserOrCloudID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch stat.TaskStatus {
|
||||
case 2:
|
||||
if err := d.client.CancelBatchTask(stat.TaskID, task.TaskType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.New("file name conflict")
|
||||
case 4:
|
||||
if task.TaskType == mopan.TASK_MOVE {
|
||||
return CloneObj(srcObj, srcObj.GetID(), srcObj.GetName()), nil
|
||||
}
|
||||
return CloneObj(srcObj, stat.SuccessedFileIDList[0], srcObj.GetName()), nil
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *MoPan) Remove(ctx context.Context, obj model.Obj) error {
|
||||
_, err := d.client.DeleteToRecycle([]mopan.TaskFileParam{
|
||||
{
|
||||
FileID: obj.GetID(),
|
||||
IsFolder: obj.IsDir(),
|
||||
FileName: obj.GetName(),
|
||||
},
|
||||
}, mopan.WarpParamOption(mopan.ParamOptionShareFile(d.CloudID)))
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *MoPan) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
|
||||
file, err := utils.CreateTempFile(stream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
_ = os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
initUpdload, err := d.client.InitMultiUpload(ctx, mopan.UpdloadFileParam{
|
||||
ParentFolderId: dstDir.GetID(),
|
||||
FileName: stream.GetName(),
|
||||
FileSize: stream.GetSize(),
|
||||
File: file,
|
||||
}, mopan.WarpParamOption(
|
||||
mopan.ParamOptionShareFile(d.CloudID),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !initUpdload.FileDataExists {
|
||||
parts, err := d.client.GetAllMultiUploadUrls(initUpdload.UploadFileID, initUpdload.PartInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.client.CloudDiskStartBusiness()
|
||||
for i, part := range parts {
|
||||
if utils.IsCanceled(ctx) {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
err := retry.Do(func() error {
|
||||
if _, err := file.Seek(int64(part.PartNumber-1)*int64(initUpdload.PartSize), io.SeekStart); err != nil {
|
||||
return retry.Unrecoverable(err)
|
||||
}
|
||||
|
||||
req, err := part.NewRequest(ctx, io.LimitReader(file, int64(initUpdload.PartSize)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("upload err,code=%d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
retry.Context(ctx),
|
||||
retry.Attempts(3),
|
||||
retry.Delay(time.Second),
|
||||
retry.MaxDelay(5*time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
up(100 * (i + 1) / len(parts))
|
||||
}
|
||||
}
|
||||
uFile, err := d.client.CommitMultiUploadFile(initUpdload.UploadFileID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.Object{
|
||||
ID: uFile.UserFileID,
|
||||
Name: uFile.FileName,
|
||||
Size: int64(uFile.FileSize),
|
||||
Modified: time.Time(uFile.CreateDate),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*MoPan)(nil)
|
||||
var _ driver.MkdirResult = (*MoPan)(nil)
|
||||
var _ driver.MoveResult = (*MoPan)(nil)
|
||||
var _ driver.RenameResult = (*MoPan)(nil)
|
||||
var _ driver.Remove = (*MoPan)(nil)
|
||||
var _ driver.CopyResult = (*MoPan)(nil)
|
||||
var _ driver.PutResult = (*MoPan)(nil)
|
37
drivers/mopan/meta.go
Normal file
37
drivers/mopan/meta.go
Normal file
@ -0,0 +1,37 @@
|
||||
package mopan
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
Phone string `json:"phone" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
|
||||
RootFolderID string `json:"root_folder_id" default:"-11" required:"true" help:"be careful when using the -11 value, some operations may cause system errors"`
|
||||
|
||||
CloudID string `json:"cloud_id"`
|
||||
|
||||
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"`
|
||||
|
||||
DeviceInfo string `json:"device_info"`
|
||||
}
|
||||
|
||||
func (a *Addition) GetRootId() string {
|
||||
return a.RootFolderID
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "MoPan",
|
||||
// DefaultRoot: "root, / or other",
|
||||
CheckStatus: true,
|
||||
Alert: "warning|This network disk may store your password in clear text. Please set your password carefully",
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &MoPan{}
|
||||
})
|
||||
}
|
1
drivers/mopan/types.go
Normal file
1
drivers/mopan/types.go
Normal file
@ -0,0 +1 @@
|
||||
package mopan
|
58
drivers/mopan/util.go
Normal file
58
drivers/mopan/util.go
Normal file
@ -0,0 +1,58 @@
|
||||
package mopan
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/foxxorcat/mopan-sdk-go"
|
||||
)
|
||||
|
||||
func fileToObj(f mopan.File) model.Obj {
|
||||
return &model.ObjThumb{
|
||||
Object: model.Object{
|
||||
ID: string(f.ID),
|
||||
Name: f.Name,
|
||||
Size: f.Size,
|
||||
Modified: time.Time(f.LastOpTime),
|
||||
},
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: f.Icon.SmallURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func folderToObj(f mopan.Folder) model.Obj {
|
||||
return &model.Object{
|
||||
ID: string(f.ID),
|
||||
Name: f.Name,
|
||||
Modified: time.Time(f.LastOpTime),
|
||||
IsFolder: true,
|
||||
}
|
||||
}
|
||||
|
||||
func CloneObj(o model.Obj, newID, newName string) model.Obj {
|
||||
if o.IsDir() {
|
||||
return &model.Object{
|
||||
ID: newID,
|
||||
Name: newName,
|
||||
IsFolder: true,
|
||||
Modified: o.ModTime(),
|
||||
}
|
||||
}
|
||||
|
||||
thumb := ""
|
||||
if o, ok := o.(model.Thumb); ok {
|
||||
thumb = o.Thumb()
|
||||
}
|
||||
return &model.ObjThumb{
|
||||
Object: model.Object{
|
||||
ID: newID,
|
||||
Name: newName,
|
||||
Size: o.GetSize(),
|
||||
Modified: o.ModTime(),
|
||||
},
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: thumb,
|
||||
},
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package quark
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
Cookie string `json:"cookie" required:"true"`
|
||||
driver.RootID
|
||||
OrderBy string `json:"order_by" type:"select" options:"none,file_type,file_name,updated_at" default:"none"`
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "Quark",
|
||||
OnlyLocal: true,
|
||||
DefaultRoot: "0",
|
||||
NoOverwriteUpload: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &Quark{}
|
||||
})
|
||||
}
|
@ -22,29 +22,31 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Quark struct {
|
||||
type QuarkOrUC struct {
|
||||
model.Storage
|
||||
Addition
|
||||
config driver.Config
|
||||
conf Conf
|
||||
}
|
||||
|
||||
func (d *Quark) Config() driver.Config {
|
||||
return config
|
||||
func (d *QuarkOrUC) Config() driver.Config {
|
||||
return d.config
|
||||
}
|
||||
|
||||
func (d *Quark) GetAddition() driver.Additional {
|
||||
func (d *QuarkOrUC) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *Quark) Init(ctx context.Context) error {
|
||||
func (d *QuarkOrUC) Init(ctx context.Context) error {
|
||||
_, err := d.request("/config", http.MethodGet, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Quark) Drop(ctx context.Context) error {
|
||||
func (d *QuarkOrUC) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Quark) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
func (d *QuarkOrUC) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
files, err := d.GetFiles(dir.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -54,12 +56,12 @@ func (d *Quark) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
func (d *QuarkOrUC) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
data := base.Json{
|
||||
"fids": []string{file.GetID()},
|
||||
}
|
||||
var resp DownResp
|
||||
ua := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch"
|
||||
ua := d.conf.ua
|
||||
_, err := d.request("/file/download", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetHeader("User-Agent", ua).
|
||||
SetBody(data)
|
||||
@ -69,61 +71,63 @@ func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
||||
}
|
||||
u := resp.Data[0].DownloadUrl
|
||||
start, end := int64(0), file.GetSize()
|
||||
return &model.Link{
|
||||
Handle: func(w http.ResponseWriter, r *http.Request) error {
|
||||
if rg := r.Header.Get("Range"); rg != "" {
|
||||
parseRange, err := http_range.ParseRange(rg, file.GetSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
start, end = parseRange[0].Start, parseRange[0].Start+parseRange[0].Length
|
||||
w.Header().Set("Content-Range", parseRange[0].ContentRange(file.GetSize()))
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(parseRange[0].Length, 10))
|
||||
w.WriteHeader(http.StatusPartialContent)
|
||||
} else {
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
link := model.Link{
|
||||
Header: http.Header{},
|
||||
}
|
||||
if rg := args.Header.Get("Range"); rg != "" {
|
||||
parseRange, err := http_range.ParseRange(rg, file.GetSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start, end = parseRange[0].Start, parseRange[0].Start+parseRange[0].Length
|
||||
link.Header.Set("Content-Range", parseRange[0].ContentRange(file.GetSize()))
|
||||
link.Header.Set("Content-Length", strconv.FormatInt(parseRange[0].Length, 10))
|
||||
link.Status = http.StatusPartialContent
|
||||
} else {
|
||||
link.Header.Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
|
||||
link.Status = http.StatusOK
|
||||
}
|
||||
link.Writer = func(w io.Writer) error {
|
||||
// request 10 MB at a time
|
||||
chunkSize := int64(10 * 1024 * 1024)
|
||||
for start < end {
|
||||
_end := start + chunkSize
|
||||
if _end > end {
|
||||
_end = end
|
||||
}
|
||||
// request 10 MB at a time
|
||||
chunkSize := int64(10 * 1024 * 1024)
|
||||
for start < end {
|
||||
_end := start + chunkSize
|
||||
if _end > end {
|
||||
_end = end
|
||||
}
|
||||
_range := "bytes=" + strconv.FormatInt(start, 10) + "-" + strconv.FormatInt(_end-1, 10)
|
||||
start = _end
|
||||
err = func() error {
|
||||
req, err := http.NewRequest(r.Method, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Range", _range)
|
||||
req.Header.Set("User-Agent", ua)
|
||||
req.Header.Set("Cookie", d.Cookie)
|
||||
req.Header.Set("Referer", "https://pan.quark.cn")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusPartialContent {
|
||||
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
_, err = io.Copy(w, resp.Body)
|
||||
return err
|
||||
}()
|
||||
_range := "bytes=" + strconv.FormatInt(start, 10) + "-" + strconv.FormatInt(_end-1, 10)
|
||||
start = _end
|
||||
err = func() error {
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Range", _range)
|
||||
req.Header.Set("User-Agent", ua)
|
||||
req.Header.Set("Cookie", d.Cookie)
|
||||
req.Header.Set("Referer", d.conf.referer)
|
||||
resp, err := base.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusPartialContent {
|
||||
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
_, err = io.Copy(w, resp.Body)
|
||||
return err
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return &link, nil
|
||||
}
|
||||
|
||||
func (d *Quark) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
func (d *QuarkOrUC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
data := base.Json{
|
||||
"dir_init_lock": false,
|
||||
"dir_path": "",
|
||||
@ -139,7 +143,7 @@ func (d *Quark) MakeDir(ctx context.Context, parentDir model.Obj, dirName string
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Quark) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
func (d *QuarkOrUC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
data := base.Json{
|
||||
"action_type": 1,
|
||||
"exclude_fids": []string{},
|
||||
@ -152,7 +156,7 @@ func (d *Quark) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Quark) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
func (d *QuarkOrUC) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
data := base.Json{
|
||||
"fid": srcObj.GetID(),
|
||||
"file_name": newName,
|
||||
@ -163,11 +167,11 @@ func (d *Quark) Rename(ctx context.Context, srcObj model.Obj, newName string) er
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Quark) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
func (d *QuarkOrUC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (d *Quark) Remove(ctx context.Context, obj model.Obj) error {
|
||||
func (d *QuarkOrUC) Remove(ctx context.Context, obj model.Obj) error {
|
||||
data := base.Json{
|
||||
"action_type": 1,
|
||||
"exclude_fids": []string{},
|
||||
@ -179,7 +183,7 @@ func (d *Quark) Remove(ctx context.Context, obj model.Obj) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
func (d *QuarkOrUC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -264,4 +268,4 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
|
||||
return d.upFinish(pre)
|
||||
}
|
||||
|
||||
var _ driver.Driver = (*Quark)(nil)
|
||||
var _ driver.Driver = (*QuarkOrUC)(nil)
|
55
drivers/quark_uc/meta.go
Normal file
55
drivers/quark_uc/meta.go
Normal file
@ -0,0 +1,55 @@
|
||||
package quark
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
Cookie string `json:"cookie" required:"true"`
|
||||
driver.RootID
|
||||
OrderBy string `json:"order_by" type:"select" options:"none,file_type,file_name,updated_at" default:"none"`
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||
}
|
||||
|
||||
type Conf struct {
|
||||
ua string
|
||||
referer string
|
||||
api string
|
||||
pr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &QuarkOrUC{
|
||||
config: driver.Config{
|
||||
Name: "Quark",
|
||||
OnlyLocal: true,
|
||||
DefaultRoot: "0",
|
||||
NoOverwriteUpload: true,
|
||||
},
|
||||
conf: Conf{
|
||||
ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch",
|
||||
referer: "https://pan.quark.cn",
|
||||
api: "https://drive.quark.cn/1/clouddrive",
|
||||
pr: "ucpro",
|
||||
},
|
||||
}
|
||||
})
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &QuarkOrUC{
|
||||
config: driver.Config{
|
||||
Name: "UC",
|
||||
OnlyLocal: true,
|
||||
DefaultRoot: "0",
|
||||
NoOverwriteUpload: true,
|
||||
},
|
||||
conf: Conf{
|
||||
ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch",
|
||||
referer: "https://drive.uc.cn",
|
||||
api: "https://pc-api.uc.cn/1/clouddrive",
|
||||
pr: "UCBrowser",
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
@ -22,15 +22,15 @@ import (
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
func (d *Quark) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
u := "https://drive.quark.cn/1/clouddrive" + pathname
|
||||
func (d *QuarkOrUC) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
u := d.conf.api + pathname
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeaders(map[string]string{
|
||||
"Cookie": d.Cookie,
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Referer": "https://pan.quark.cn/",
|
||||
"Referer": d.conf.referer,
|
||||
})
|
||||
req.SetQueryParam("pr", "ucpro")
|
||||
req.SetQueryParam("pr", d.conf.pr)
|
||||
req.SetQueryParam("fr", "pc")
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
@ -55,7 +55,7 @@ func (d *Quark) request(pathname string, method string, callback base.ReqCallbac
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
func (d *Quark) GetFiles(parent string) ([]File, error) {
|
||||
func (d *QuarkOrUC) GetFiles(parent string) ([]File, error) {
|
||||
files := make([]File, 0)
|
||||
page := 1
|
||||
size := 100
|
||||
@ -85,7 +85,7 @@ func (d *Quark) GetFiles(parent string) ([]File, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (d *Quark) upPre(file model.FileStreamer, parentId string) (UpPreResp, error) {
|
||||
func (d *QuarkOrUC) upPre(file model.FileStreamer, parentId string) (UpPreResp, error) {
|
||||
now := time.Now()
|
||||
data := base.Json{
|
||||
"ccp_hash_update": true,
|
||||
@ -105,7 +105,7 @@ func (d *Quark) upPre(file model.FileStreamer, parentId string) (UpPreResp, erro
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (d *Quark) upHash(md5, sha1, taskId string) (bool, error) {
|
||||
func (d *QuarkOrUC) upHash(md5, sha1, taskId string) (bool, error) {
|
||||
data := base.Json{
|
||||
"md5": md5,
|
||||
"sha1": sha1,
|
||||
@ -119,8 +119,8 @@ func (d *Quark) upHash(md5, sha1, taskId string) (bool, error) {
|
||||
return resp.Data.Finish, err
|
||||
}
|
||||
|
||||
func (d *Quark) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
|
||||
//func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
|
||||
func (d *QuarkOrUC) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
|
||||
//func (driver QuarkOrUC) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
|
||||
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
||||
data := base.Json{
|
||||
"auth_info": pre.Data.AuthInfo,
|
||||
@ -169,7 +169,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
||||
return res.Header().Get("ETag"), nil
|
||||
}
|
||||
|
||||
func (d *Quark) upCommit(pre UpPreResp, md5s []string) error {
|
||||
func (d *QuarkOrUC) upCommit(pre UpPreResp, md5s []string) error {
|
||||
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
||||
log.Debugf("md5s: %+v", md5s)
|
||||
bodyBuilder := strings.Builder{}
|
||||
@ -236,7 +236,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Quark) upFinish(pre UpPreResp) error {
|
||||
func (d *QuarkOrUC) upFinish(pre UpPreResp) error {
|
||||
data := base.Json{
|
||||
"obj_key": pre.Data.ObjKey,
|
||||
"task_id": pre.Data.TaskId,
|
@ -61,7 +61,10 @@ func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]mo
|
||||
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
path := getKey(file.GetPath(), false)
|
||||
filename := stdpath.Base(path)
|
||||
disposition := fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, filename, url.PathEscape(filename))
|
||||
disposition := fmt.Sprintf(`attachment; filename*=UTF-8''%s`, url.PathEscape(filename))
|
||||
if d.AddFilenameToDisposition {
|
||||
disposition = fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, filename, url.PathEscape(filename))
|
||||
}
|
||||
input := &s3.GetObjectInput{
|
||||
Bucket: &d.Bucket,
|
||||
Key: &path,
|
||||
@ -136,11 +139,13 @@ func (d *S3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreame
|
||||
uploader.PartSize = stream.GetSize() / (s3manager.MaxUploadParts - 1)
|
||||
}
|
||||
key := getKey(stdpath.Join(dstDir.GetPath(), stream.GetName()), false)
|
||||
contentType := stream.GetMimetype()
|
||||
log.Debugln("key:", key)
|
||||
input := &s3manager.UploadInput{
|
||||
Bucket: &d.Bucket,
|
||||
Key: &key,
|
||||
Body: stream,
|
||||
Bucket: &d.Bucket,
|
||||
Key: &key,
|
||||
Body: stream,
|
||||
ContentType: &contentType,
|
||||
}
|
||||
_, err := uploader.UploadWithContext(ctx, input)
|
||||
return err
|
||||
|
@ -7,17 +7,18 @@ import (
|
||||
|
||||
type Addition struct {
|
||||
driver.RootPath
|
||||
Bucket string `json:"bucket" required:"true"`
|
||||
Endpoint string `json:"endpoint" required:"true"`
|
||||
Region string `json:"region"`
|
||||
AccessKeyID string `json:"access_key_id" required:"true"`
|
||||
SecretAccessKey string `json:"secret_access_key" required:"true"`
|
||||
CustomHost string `json:"custom_host"`
|
||||
SignURLExpire int `json:"sign_url_expire" type:"number" default:"4"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
ForcePathStyle bool `json:"force_path_style"`
|
||||
ListObjectVersion string `json:"list_object_version" type:"select" options:"v1,v2" default:"v1"`
|
||||
RemoveBucket bool `json:"remove_bucket" help:"Remove bucket name from path when using custom host."`
|
||||
Bucket string `json:"bucket" required:"true"`
|
||||
Endpoint string `json:"endpoint" required:"true"`
|
||||
Region string `json:"region"`
|
||||
AccessKeyID string `json:"access_key_id" required:"true"`
|
||||
SecretAccessKey string `json:"secret_access_key" required:"true"`
|
||||
CustomHost string `json:"custom_host"`
|
||||
SignURLExpire int `json:"sign_url_expire" type:"number" default:"4"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
ForcePathStyle bool `json:"force_path_style"`
|
||||
ListObjectVersion string `json:"list_object_version" type:"select" options:"v1,v2" default:"v1"`
|
||||
RemoveBucket bool `json:"remove_bucket" help:"Remove bucket name from path when using custom host."`
|
||||
AddFilenameToDisposition bool `json:"add_filename_to_disposition" help:"Add filename to Content-Disposition header."`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -36,11 +36,14 @@ func (d *Seafile) request(method string, pathname string, callback base.ReqCallb
|
||||
if len(noRedirect) > 0 && noRedirect[0] {
|
||||
req = base.NoRedirectClient.R()
|
||||
}
|
||||
var res resty.Response
|
||||
req.SetHeader("Authorization", d.authorization)
|
||||
callback(req)
|
||||
var (
|
||||
res *resty.Response
|
||||
err error
|
||||
)
|
||||
for i := 0; i < 2; i++ {
|
||||
req.SetHeader("Authorization", d.authorization)
|
||||
callback(req)
|
||||
res, err := req.Execute(method, full)
|
||||
res, err = req.Execute(method, full)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -125,6 +125,9 @@ func (d *Teambition) Remove(ctx context.Context, obj model.Obj) error {
|
||||
}
|
||||
|
||||
func (d *Teambition) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
if d.UseS3UploadMethod {
|
||||
return d.newUpload(ctx, dstDir, stream, up)
|
||||
}
|
||||
res, err := d.request("/api/v2/users/me", http.MethodGet, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -10,8 +10,9 @@ type Addition struct {
|
||||
Cookie string `json:"cookie" required:"true"`
|
||||
ProjectID string `json:"project_id" required:"true"`
|
||||
driver.RootID
|
||||
OrderBy string `json:"order_by" type:"select" options:"fileName,fileSize,updated,created" default:"fileName"`
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"Asc,Desc" default:"Asc"`
|
||||
OrderBy string `json:"order_by" type:"select" options:"fileName,fileSize,updated,created" default:"fileName"`
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"Asc,Desc" default:"Asc"`
|
||||
UseS3UploadMethod bool `json:"use_s3_upload_method" default:"true"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -66,3 +66,24 @@ type ChunkUpload struct {
|
||||
PreviewExt string `json:"previewExt"`
|
||||
LastUploadTime interface{} `json:"lastUploadTime"`
|
||||
}
|
||||
|
||||
type UploadToken struct {
|
||||
Sdk struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
Region string `json:"region"`
|
||||
S3ForcePathStyle bool `json:"s3ForcePathStyle"`
|
||||
Credentials struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
SessionToken string `json:"sessionToken"`
|
||||
} `json:"credentials"`
|
||||
} `json:"sdk"`
|
||||
Upload struct {
|
||||
Bucket string `json:"Bucket"`
|
||||
Key string `json:"Key"`
|
||||
ContentDisposition string `json:"ContentDisposition"`
|
||||
ContentType string `json:"ContentType"`
|
||||
} `json:"upload"`
|
||||
Token string `json:"token"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
}
|
||||
|
@ -7,13 +7,16 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"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/model"
|
||||
"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"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -210,17 +213,56 @@ func (d *Teambition) finishUpload(file *FileUpload, parentId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func getBetweenStr(str, start, end string) string {
|
||||
n := strings.Index(str, start)
|
||||
if n == -1 {
|
||||
return ""
|
||||
func (d *Teambition) newUpload(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
var uploadToken UploadToken
|
||||
_, err := d.request("/api/awos/upload-token", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(base.Json{
|
||||
"category": "work",
|
||||
"fileName": stream.GetName(),
|
||||
"fileSize": stream.GetSize(),
|
||||
"fileType": stream.GetMimetype(),
|
||||
"payload": base.Json{
|
||||
"involveMembers": []struct{}{},
|
||||
"visible": "members",
|
||||
},
|
||||
"scope": "project:" + d.ProjectID,
|
||||
})
|
||||
}, &uploadToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n = n + len(start)
|
||||
str = string([]byte(str)[n:])
|
||||
m := strings.Index(str, end)
|
||||
if m == -1 {
|
||||
return ""
|
||||
cfg := &aws.Config{
|
||||
Credentials: credentials.NewStaticCredentials(
|
||||
uploadToken.Sdk.Credentials.AccessKeyId, uploadToken.Sdk.Credentials.SecretAccessKey, uploadToken.Sdk.Credentials.SessionToken),
|
||||
Region: &uploadToken.Sdk.Region,
|
||||
Endpoint: &uploadToken.Sdk.Endpoint,
|
||||
S3ForcePathStyle: &uploadToken.Sdk.S3ForcePathStyle,
|
||||
}
|
||||
str = string([]byte(str)[:m])
|
||||
return str
|
||||
ss, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uploader := s3manager.NewUploader(ss)
|
||||
input := &s3manager.UploadInput{
|
||||
Bucket: &uploadToken.Upload.Bucket,
|
||||
Key: &uploadToken.Upload.Key,
|
||||
ContentDisposition: &uploadToken.Upload.ContentDisposition,
|
||||
ContentType: &uploadToken.Upload.ContentType,
|
||||
Body: stream,
|
||||
}
|
||||
_, err = uploader.UploadWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// finish upload
|
||||
_, err = d.request("/api/works", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(base.Json{
|
||||
"fileTokens": []string{uploadToken.Token},
|
||||
"involveMembers": []struct{}{},
|
||||
"visible": "members",
|
||||
"works": []struct{}{},
|
||||
"_parentId": dstDir.GetID(),
|
||||
})
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package thunder
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
@ -331,15 +333,32 @@ func (xc *XunLeiCommon) Remove(ctx context.Context, obj model.Obj) error {
|
||||
}
|
||||
|
||||
func (xc *XunLeiCommon) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
_ = os.Remove(tempFile.Name())
|
||||
}()
|
||||
|
||||
gcid, err := getGcid(tempFile, stream.GetSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tempFile.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp UploadTaskResponse
|
||||
_, err := xc.Request(FILE_API_URL, http.MethodPost, func(r *resty.Request) {
|
||||
_, 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",
|
||||
"hash": gcid,
|
||||
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||
})
|
||||
}, &resp)
|
||||
@ -362,7 +381,7 @@ func (xc *XunLeiCommon) Put(ctx context.Context, dstDir model.Obj, stream model.
|
||||
Bucket: aws.String(param.Bucket),
|
||||
Key: aws.String(param.Key),
|
||||
Expires: aws.Time(param.Expiration),
|
||||
Body: stream,
|
||||
Body: tempFile,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package thunder
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
@ -171,3 +174,29 @@ func (c *Common) Request(url, method string, callback base.ReqCallback, resp int
|
||||
|
||||
return res.Body(), nil
|
||||
}
|
||||
|
||||
// 计算文件Gcid
|
||||
func getGcid(r io.Reader, size int64) (string, error) {
|
||||
calcBlockSize := func(j int64) int64 {
|
||||
var psize int64 = 0x40000
|
||||
for float64(j)/float64(psize) > 0x200 && psize < 0x200000 {
|
||||
psize = psize << 1
|
||||
}
|
||||
return psize
|
||||
}
|
||||
|
||||
hash1 := sha1.New()
|
||||
hash2 := sha1.New()
|
||||
readSize := calcBlockSize(size)
|
||||
for {
|
||||
hash2.Reset()
|
||||
if n, err := io.CopyN(hash2, r, readSize); err != nil && n == 0 {
|
||||
if err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
break
|
||||
}
|
||||
hash1.Write(hash2.Sum(nil))
|
||||
}
|
||||
return hex.EncodeToString(hash1.Sum(nil)), nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
@ -31,7 +32,7 @@ func (d *Trainbit) GetAddition() driver.Additional {
|
||||
}
|
||||
|
||||
func (d *Trainbit) Init(ctx context.Context) error {
|
||||
http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
base.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
var err error
|
||||
@ -119,7 +120,7 @@ func (d *Trainbit) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
||||
query := &url.Values{}
|
||||
query.Add("q", strings.Split(dstDir.GetID(), "_")[1])
|
||||
query.Add("guid", guid)
|
||||
query.Add("name", url.QueryEscape(local2provider(stream.GetName(), false) + "."))
|
||||
query.Add("name", url.QueryEscape(local2provider(stream.GetName(), false)+"."))
|
||||
endpoint.RawQuery = query.Encode()
|
||||
var total int64
|
||||
total = 0
|
||||
@ -135,7 +136,7 @@ func (d *Trainbit) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "text/json; charset=UTF-8")
|
||||
_, err = http.DefaultClient.Do(req)
|
||||
_, err = base.HttpClient.Do(req)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
@ -38,7 +39,7 @@ func get(url string, apiKey string, AUSHELLPORTAL string) (*http.Response, error
|
||||
Value: apiKey,
|
||||
MaxAge: 2 * 60,
|
||||
})
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
res, err := base.HttpClient.Do(req)
|
||||
return res, err
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ func postForm(endpoint string, data url.Values, apiExpiredate string, apiKey str
|
||||
Value: apiKey,
|
||||
MaxAge: 2 * 60,
|
||||
})
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
res, err := base.HttpClient.Do(req)
|
||||
return res, err
|
||||
}
|
||||
|
||||
|
@ -78,19 +78,19 @@ func (d *WebDav) MakeDir(ctx context.Context, parentDir model.Obj, dirName strin
|
||||
}
|
||||
|
||||
func (d *WebDav) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return d.client.Rename(srcObj.GetPath(), path.Join(dstDir.GetPath(), srcObj.GetName()), true)
|
||||
return d.client.Rename(getPath(srcObj), path.Join(dstDir.GetPath(), srcObj.GetName()), true)
|
||||
}
|
||||
|
||||
func (d *WebDav) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
return d.client.Rename(srcObj.GetPath(), path.Join(path.Dir(srcObj.GetPath()), newName), true)
|
||||
return d.client.Rename(getPath(srcObj), path.Join(path.Dir(srcObj.GetPath()), newName), true)
|
||||
}
|
||||
|
||||
func (d *WebDav) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
return d.client.Copy(srcObj.GetPath(), path.Join(dstDir.GetPath(), srcObj.GetName()), true)
|
||||
return d.client.Copy(getPath(srcObj), path.Join(dstDir.GetPath(), srcObj.GetName()), true)
|
||||
}
|
||||
|
||||
func (d *WebDav) Remove(ctx context.Context, obj model.Obj) error {
|
||||
return d.client.RemoveAll(obj.GetPath())
|
||||
return d.client.RemoveAll(getPath(obj))
|
||||
}
|
||||
|
||||
func (d *WebDav) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/base"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
@ -185,7 +186,7 @@ func (ca *CookieAuth) getSPToken() (*SuccessResponse, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
client := base.HttpClient
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/webdav/odrvcookie"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/gowebdav"
|
||||
)
|
||||
|
||||
@ -29,3 +30,10 @@ func (d *WebDav) setClient() error {
|
||||
d.client = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPath(obj model.Obj) string {
|
||||
if obj.IsDir() {
|
||||
return obj.GetPath() + "/"
|
||||
}
|
||||
return obj.GetPath()
|
||||
}
|
||||
|
161
drivers/wopan/driver.go
Normal file
161
drivers/wopan/driver.go
Normal file
@ -0,0 +1,161 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Xhofe/wopan-sdk-go"
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"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/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type Wopan struct {
|
||||
model.Storage
|
||||
Addition
|
||||
client *wopan.WoClient
|
||||
}
|
||||
|
||||
func (d *Wopan) Config() driver.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (d *Wopan) GetAddition() driver.Additional {
|
||||
return &d.Addition
|
||||
}
|
||||
|
||||
func (d *Wopan) Init(ctx context.Context) error {
|
||||
d.client = wopan.DefaultWithRefreshToken(d.RefreshToken)
|
||||
d.client.SetAccessToken(d.AccessToken)
|
||||
d.client.OnRefreshToken(func(accessToken, refreshToken string) {
|
||||
d.AccessToken = accessToken
|
||||
d.RefreshToken = refreshToken
|
||||
op.MustSaveDriverStorage(d)
|
||||
})
|
||||
return d.client.InitData()
|
||||
}
|
||||
|
||||
func (d *Wopan) Drop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Wopan) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
var res []model.Obj
|
||||
pageNum := 0
|
||||
pageSize := 100
|
||||
for {
|
||||
data, err := d.client.QueryAllFiles(d.getSpaceType(), dir.GetID(), pageNum, pageSize, 0, d.FamilyID, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs, err := utils.SliceConvert(data.Files, fileToObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, objs...)
|
||||
if len(data.Files) < pageSize {
|
||||
break
|
||||
}
|
||||
pageNum++
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *Wopan) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if f, ok := file.(*Object); ok {
|
||||
res, err := d.client.GetDownloadUrlV2([]string{f.FID}, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.Link{
|
||||
URL: res.List[0].DownloadUrl,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to convert file to Object")
|
||||
}
|
||||
|
||||
func (d *Wopan) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
_, err := d.client.CreateDirectory(d.getSpaceType(), parentDir.GetID(), dirName, d.FamilyID, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Wopan) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
dirList := make([]string, 0)
|
||||
fileList := make([]string, 0)
|
||||
if srcObj.IsDir() {
|
||||
dirList = append(dirList, srcObj.GetID())
|
||||
} else {
|
||||
fileList = append(fileList, srcObj.GetID())
|
||||
}
|
||||
return d.client.MoveFile(dirList, fileList, dstDir.GetID(),
|
||||
d.getSpaceType(), d.getSpaceType(),
|
||||
d.FamilyID, d.FamilyID, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Wopan) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||
_type := 1
|
||||
if srcObj.IsDir() {
|
||||
_type = 0
|
||||
}
|
||||
return d.client.RenameFileOrDirectory(d.getSpaceType(), _type, srcObj.GetID(), newName, d.FamilyID, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Wopan) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||
dirList := make([]string, 0)
|
||||
fileList := make([]string, 0)
|
||||
if srcObj.IsDir() {
|
||||
dirList = append(dirList, srcObj.GetID())
|
||||
} else {
|
||||
fileList = append(fileList, srcObj.GetID())
|
||||
}
|
||||
return d.client.CopyFile(dirList, fileList, dstDir.GetID(),
|
||||
d.getSpaceType(), d.getSpaceType(),
|
||||
d.FamilyID, d.FamilyID, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Wopan) Remove(ctx context.Context, obj model.Obj) error {
|
||||
dirList := make([]string, 0)
|
||||
fileList := make([]string, 0)
|
||||
if obj.IsDir() {
|
||||
dirList = append(dirList, obj.GetID())
|
||||
} else {
|
||||
fileList = append(fileList, obj.GetID())
|
||||
}
|
||||
return d.client.DeleteFile(d.getSpaceType(), dirList, fileList, func(req *resty.Request) {
|
||||
req.SetContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Wopan) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||
_, err := d.client.Upload2C(d.getSpaceType(), wopan.Upload2CFile{
|
||||
Name: stream.GetName(),
|
||||
Size: stream.GetSize(),
|
||||
Content: stream,
|
||||
ContentType: stream.GetMimetype(),
|
||||
}, dstDir.GetID(), d.FamilyID, wopan.Upload2COption{
|
||||
OnProgress: func(current, total int64) {
|
||||
up(int(100 * current / total))
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//func (d *Wopan) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||
// return nil, errs.NotSupport
|
||||
//}
|
||||
|
||||
var _ driver.Driver = (*Wopan)(nil)
|
37
drivers/wopan/meta.go
Normal file
37
drivers/wopan/meta.go
Normal file
@ -0,0 +1,37 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Addition struct {
|
||||
// Usually one of two
|
||||
driver.RootID
|
||||
// define other
|
||||
RefreshToken string `json:"refresh_token" required:"true"`
|
||||
FamilyID string `json:"family_id" help:"Keep it empty if you want to use your personal drive"`
|
||||
SortRule string `json:"sort_rule" type:"select" options:"name_asc,name_desc,time_asc,time_desc,size_asc,size_desc" default:"name_asc"`
|
||||
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
Name: "WoPan",
|
||||
LocalSort: false,
|
||||
OnlyLocal: false,
|
||||
OnlyProxy: false,
|
||||
NoCache: false,
|
||||
NoUpload: false,
|
||||
NeedMs: false,
|
||||
DefaultRoot: "0",
|
||||
CheckStatus: false,
|
||||
Alert: "",
|
||||
NoOverwriteUpload: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterDriver(func() driver.Driver {
|
||||
return &Wopan{}
|
||||
})
|
||||
}
|
34
drivers/wopan/types.go
Normal file
34
drivers/wopan/types.go
Normal file
@ -0,0 +1,34 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/Xhofe/wopan-sdk-go"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
type Object struct {
|
||||
model.ObjThumb
|
||||
FID string
|
||||
}
|
||||
|
||||
func fileToObj(file wopan.File) (model.Obj, error) {
|
||||
t, err := getTime(file.CreateTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Object{
|
||||
ObjThumb: model.ObjThumb{
|
||||
Object: model.Object{
|
||||
ID: file.Id,
|
||||
//Path: "",
|
||||
Name: file.Name,
|
||||
Size: file.Size,
|
||||
Modified: t,
|
||||
IsFolder: file.Type == 0,
|
||||
},
|
||||
Thumbnail: model.Thumbnail{
|
||||
Thumbnail: file.ThumbUrl,
|
||||
},
|
||||
},
|
||||
FID: file.Fid,
|
||||
}, nil
|
||||
}
|
40
drivers/wopan/util.go
Normal file
40
drivers/wopan/util.go
Normal file
@ -0,0 +1,40 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Xhofe/wopan-sdk-go"
|
||||
)
|
||||
|
||||
// do others that not defined in Driver interface
|
||||
|
||||
func (d *Wopan) getSortRule() int {
|
||||
switch d.SortRule {
|
||||
case "name_asc":
|
||||
return wopan.SortNameAsc
|
||||
case "name_desc":
|
||||
return wopan.SortNameDesc
|
||||
case "time_asc":
|
||||
return wopan.SortTimeAsc
|
||||
case "time_desc":
|
||||
return wopan.SortTimeDesc
|
||||
case "size_asc":
|
||||
return wopan.SortSizeAsc
|
||||
case "size_desc":
|
||||
return wopan.SortSizeDesc
|
||||
default:
|
||||
return wopan.SortNameAsc
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Wopan) getSpaceType() string {
|
||||
if d.FamilyID == "" {
|
||||
return wopan.SpaceTypePersonal
|
||||
}
|
||||
return wopan.SpaceTypeFamily
|
||||
}
|
||||
|
||||
// 20230607214351
|
||||
func getTime(str string) (time.Time, error) {
|
||||
return time.Parse("20060102150405", str)
|
||||
}
|
75
go.mod
75
go.mod
@ -5,19 +5,24 @@ go 1.20
|
||||
require (
|
||||
github.com/SheltonZhu/115driver v1.0.14
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
||||
github.com/aws/aws-sdk-go v1.44.194
|
||||
github.com/blevesearch/bleve/v2 v2.3.7
|
||||
github.com/Xhofe/wopan-sdk-go v0.1.1
|
||||
github.com/avast/retry-go v3.0.0+incompatible
|
||||
github.com/aws/aws-sdk-go v1.44.262
|
||||
github.com/blevesearch/bleve/v2 v2.3.8
|
||||
github.com/caarlos0/env/v7 v7.1.0
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/deckarep/golang-set/v2 v2.3.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
||||
github.com/foxxorcat/mopan-sdk-go v0.1.0
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hirochachacha/go-smb2 v1.1.0
|
||||
github.com/ipfs/go-ipfs-api v0.6.0
|
||||
github.com/jlaffaye/ftp v0.1.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/maruel/natural v1.1.0
|
||||
@ -25,15 +30,16 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.5
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/sirupsen/logrus v1.9.2
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca
|
||||
github.com/u2takey/ffmpeg-go v0.4.1
|
||||
github.com/upyun/go-sdk/v3 v3.0.4
|
||||
github.com/winfsp/cgofuse v1.5.0
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/image v0.7.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/oauth2 v0.4.0
|
||||
gorm.io/driver/mysql v1.4.7
|
||||
gorm.io/driver/postgres v1.4.8
|
||||
gorm.io/driver/sqlite v1.4.4
|
||||
@ -46,6 +52,7 @@ require (
|
||||
github.com/aead/ecdh v0.2.0 // indirect
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
|
||||
github.com/andreburgaud/crypt2go v1.1.0 // indirect
|
||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 // indirect
|
||||
github.com/blevesearch/geo v0.1.17 // indirect
|
||||
@ -61,54 +68,78 @@ require (
|
||||
github.com/blevesearch/zapx/v12 v12.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.9 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.10 // indirect
|
||||
github.com/bluele/gcache v0.0.2 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8 // indirect
|
||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
github.com/golang/protobuf v1.5.0 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ipfs/boxo v0.8.0 // indirect
|
||||
github.com/ipfs/go-cid v0.4.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.3.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
|
||||
github.com/libp2p/go-libp2p v0.26.3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.8.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.1.1 // indirect
|
||||
github.com/multiformats/go-multicodec v0.8.1 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
||||
github.com/multiformats/go-multistream v0.4.1 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/u2takey/go-utils v0.3.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
|
163
go.sum
163
go.sum
@ -6,19 +6,25 @@ github.com/SheltonZhu/115driver v1.0.14 h1:uW3dl8J9KDMw+3gPxQdhTysoGhw0/uI1484GT
|
||||
github.com/SheltonZhu/115driver v1.0.14/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
||||
github.com/Xhofe/wopan-sdk-go v0.1.1 h1:dSrTxNYclqNuo9libjtC+R6C4RCen/inh/dUXd12vpM=
|
||||
github.com/Xhofe/wopan-sdk-go v0.1.1/go.mod h1:xWcUS7PoFLDD9gy2BK2VQfilEsZngLMz2Vkx3oF2zJY=
|
||||
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
||||
github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1eXdZyMw3F7uGA9qPn2J4+R8=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andreburgaud/crypt2go v1.1.0 h1:eitZxTPY1krUsxinsng3Qvt/Ud7q/aQmmYRh8p4hyPw=
|
||||
github.com/andreburgaud/crypt2go v1.1.0/go.mod h1:4qhZPzarj1dCIRmCkpdgCklwp+hBq9yEt0zPe9Ayuhc=
|
||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.44.194 h1:1ZDK+QDcc5oRbZGgRZSz561eR8XVizXCeGpoZKo33NU=
|
||||
github.com/aws/aws-sdk-go v1.44.194/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I=
|
||||
github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/blevesearch/bleve/v2 v2.3.7 h1:nIfIrhv28tvgBpbVF8Dq7/U1zW/YiwSqg/PBgE3x8bo=
|
||||
github.com/blevesearch/bleve/v2 v2.3.7/go.mod h1:2tToYD6mDeseIA13jcZiEEqYrVLg6xdk0v6+F7dWquU=
|
||||
github.com/blevesearch/bleve/v2 v2.3.8 h1:IqFyMJ73n4gY8AmVqM8Sa6EtAZ5beE8yramVqCvs2kQ=
|
||||
github.com/blevesearch/bleve/v2 v2.3.8/go.mod h1:Lh9aZEHrLKxwPnW4z4lsBEGnflZQ1V/aWP/t+htsiDw=
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 h1:Lc986kpC4Z0/n1g3gg8ul7H+lxgOQPcXb9SxvQGu+tw=
|
||||
github.com/blevesearch/bleve_index_api v1.0.5/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms=
|
||||
github.com/blevesearch/geo v0.1.17 h1:AguzI6/5mHXapzB0gE9IKWo+wWPHZmXZoscHcjFgAFA=
|
||||
@ -47,32 +53,44 @@ github.com/blevesearch/zapx/v13 v13.3.7 h1:igIQg5eKmjw168I7av0Vtwedf7kHnQro/M+ub
|
||||
github.com/blevesearch/zapx/v13 v13.3.7/go.mod h1:yyrB4kJ0OT75UPZwT/zS+Ru0/jYKorCOOSY5dBzAy+s=
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 h1:gfe+fbWslDWP/evHLtp/GOvmNM3sw1BbqD7LhycBX20=
|
||||
github.com/blevesearch/zapx/v14 v14.3.7/go.mod h1:9J/RbOkqZ1KSjmkOes03AkETX7hrXT0sFMpWH4ewC4w=
|
||||
github.com/blevesearch/zapx/v15 v15.3.9 h1:/s9zqKxFaZKQTTcMO2b/Tup0ch5MSztlvw+frVDfIBk=
|
||||
github.com/blevesearch/zapx/v15 v15.3.9/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY=
|
||||
github.com/blevesearch/zapx/v15 v15.3.10 h1:bQ9ZxJCj6rKp873EuVJu2JPxQ+EWQZI1cjJGeroovaQ=
|
||||
github.com/blevesearch/zapx/v15 v15.3.10/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
|
||||
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg=
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
|
||||
github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
||||
github.com/foxxorcat/mopan-sdk-go v0.1.0 h1:U/E/uNK4N7xNbcHXdw+DG56LWw2W6Xpjj+yoH8EmTj0=
|
||||
github.com/foxxorcat/mopan-sdk-go v0.1.0/go.mod h1:LpBPmwezjQNyhaNo3HGzgFtQbhvxmF5ZybSVuKi7OVA=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8 h1:EJt4PSmcbvBY4KUh2zSo5p6fN9LZFNkIzuKejipubVw=
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8/go.mod h1:BKbeY6j8SKs3+rzBFFALznGxbPmefEm3vA+dGhqgOGU=
|
||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||
@ -82,8 +100,8 @@ github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURU
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@ -95,26 +113,28 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
@ -131,6 +151,12 @@ github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRK
|
||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/ipfs/boxo v0.8.0 h1:UdjAJmHzQHo/j3g3b1bAcAXCj/GM6iTwvSlBDvPBNBs=
|
||||
github.com/ipfs/boxo v0.8.0/go.mod h1:RIsi4CnTyQ7AUsNn5gXljJYZlQrHBMnJp94p73liFiA=
|
||||
github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA=
|
||||
github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
||||
github.com/ipfs/go-ipfs-api v0.6.0 h1:JARgG0VTbjyVhO5ZfesnbXv9wTcMvoKRBLF1SzJqzmg=
|
||||
github.com/ipfs/go-ipfs-api v0.6.0/go.mod h1:iDC2VMwN9LUpQV/GzEeZ2zNqd8NUdRmWcFM+K/6odf0=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
@ -154,8 +180,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@ -166,15 +194,27 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
||||
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
||||
github.com/libp2p/go-libp2p v0.26.3 h1:6g/psubqwdaBqNNoidbRKSTBEYgaOuKBhHl8Q5tO+PM=
|
||||
github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8=
|
||||
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
||||
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -182,16 +222,34 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
|
||||
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
|
||||
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
|
||||
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
|
||||
github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8=
|
||||
github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
|
||||
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
|
||||
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
|
||||
github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo=
|
||||
github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q=
|
||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 h1:dg/EaaJLPIg4xn2kaZil7Ax3wfoxcFXaBwyOTlcz5AI=
|
||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77/go.mod h1:FD9a09Vw07CSMTdT0Y7ttStOa1WZsnPBslliMw2DkeM=
|
||||
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@ -202,6 +260,8 @@ github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
||||
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
|
||||
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@ -209,10 +269,12 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
|
||||
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
@ -230,8 +292,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca h1:I9rVnNXdIkij4UvMT7OmKhH9sOIvS8iXkxfPdnn9wQA=
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca/go.mod h1:suDIky6yrK07NnaBadCB4sS0CqFOvUK91lH7CR+JlDA=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
@ -242,19 +306,22 @@ github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4y
|
||||
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/upyun/go-sdk/v3 v3.0.4 h1:2DCJa/Yi7/3ZybT9UCPATSzvU3wpPPxhXinNlb1Hi8Q=
|
||||
github.com/upyun/go-sdk/v3 v3.0.4/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E=
|
||||
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c h1:GGsyl0dZ2jJgVT+VvWBf/cNijrHRhkrTjkmp5wg7li0=
|
||||
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs=
|
||||
github.com/winfsp/cgofuse v1.5.0 h1:MsBP7Mi/LiJf/7/F3O/7HjjR009ds6KCdqXzKpZSWxI=
|
||||
github.com/winfsp/cgofuse v1.5.0/go.mod h1:h3awhoUOcn2VYVKCwDaYxSLlZwnyK+A8KaDoLUp2lbU=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@ -265,14 +332,15 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@ -281,8 +349,10 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
|
||||
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -300,18 +370,19 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -319,8 +390,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||
@ -332,12 +404,13 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@ -345,6 +418,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -365,5 +440,7 @@ gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
|
||||
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
@ -1,7 +1,6 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -78,9 +77,7 @@ func InitConfig() {
|
||||
log.Fatalf("create temp dir error: %+v", err)
|
||||
}
|
||||
log.Debugf("config: %+v", conf.Conf)
|
||||
if conf.Conf.TlsInsecureSkipVerify {
|
||||
base.RestyClient = base.RestyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
base.InitClient()
|
||||
initURL()
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,8 @@ func InitialSettings() []model.SettingItem {
|
||||
{Key: "pagination_type", Value: "all", Type: conf.TypeSelect, Options: "all,pagination,load_more,auto_load_more", Group: model.SITE},
|
||||
{Key: "default_page_size", Value: "30", Type: conf.TypeNumber, Group: model.SITE},
|
||||
{Key: conf.AllowIndexed, Value: "false", Type: conf.TypeBool, Group: model.SITE},
|
||||
{Key: conf.AllowMounted, Value: "true", Type: conf.TypeBool, Group: model.SITE},
|
||||
{Key: conf.RobotsTxt, Value: "User-agent: *\nAllow: /", Type: conf.TypeText, Group: model.SITE},
|
||||
// style settings
|
||||
{Key: conf.Logo, Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", Type: conf.TypeText, Group: model.STYLE},
|
||||
{Key: conf.Favicon, Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", Type: conf.TypeString, Group: model.STYLE},
|
||||
@ -152,7 +154,7 @@ func InitialSettings() []model.SettingItem {
|
||||
|
||||
// SSO settings
|
||||
{Key: conf.SSOLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.SSO, Flag: model.PUBLIC},
|
||||
{Key: conf.SSOLoginplatform, Type: conf.TypeSelect, Options: "Casdoor,Github,Microsoft,Google,Dingtalk", Group: model.SSO, Flag: model.PUBLIC},
|
||||
{Key: conf.SSOLoginplatform, Type: conf.TypeSelect, Options: "Casdoor,Github,Microsoft,Google,Dingtalk,OIDC", Group: model.SSO, Flag: model.PUBLIC},
|
||||
{Key: conf.SSOClientId, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||
{Key: conf.SSOClientSecret, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||
{Key: conf.SSOOrganizationName, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||
|
@ -20,9 +20,13 @@ type Database struct {
|
||||
}
|
||||
|
||||
type Scheme struct {
|
||||
Https bool `json:"https" env:"HTTPS"`
|
||||
CertFile string `json:"cert_file" env:"CERT_FILE"`
|
||||
KeyFile string `json:"key_file" env:"KEY_FILE"`
|
||||
Address string `json:"address" env:"ADDR"`
|
||||
HttpPort int `json:"http_port" env:"HTTP_PORT"`
|
||||
HttpsPort int `json:"https_port" env:"HTTPS_PORT"`
|
||||
ForceHttps bool `json:"force_https" env:"FORCE_HTTPS"`
|
||||
CertFile string `json:"cert_file" env:"CERT_FILE"`
|
||||
KeyFile string `json:"key_file" env:"KEY_FILE"`
|
||||
UnixFile string `json:"unix_file" env:"UNIX_FILE"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
@ -36,8 +40,6 @@ type LogConfig struct {
|
||||
|
||||
type Config struct {
|
||||
Force bool `json:"force" env:"FORCE"`
|
||||
Address string `json:"address" env:"ADDR"`
|
||||
Port int `json:"port" env:"PORT"`
|
||||
SiteURL string `json:"site_url" env:"SITE_URL"`
|
||||
Cdn string `json:"cdn" env:"CDN"`
|
||||
JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"`
|
||||
@ -47,6 +49,7 @@ type Config struct {
|
||||
TempDir string `json:"temp_dir" env:"TEMP_DIR"`
|
||||
BleveDir string `json:"bleve_dir" env:"BLEVE_DIR"`
|
||||
Log LogConfig `json:"log"`
|
||||
DelayedStart int `json:"delayed_start" env:"DELAYED_START"`
|
||||
MaxConnections int `json:"max_connections" env:"MAX_CONNECTIONS"`
|
||||
TlsInsecureSkipVerify bool `json:"tls_insecure_skip_verify" env:"TLS_INSECURE_SKIP_VERIFY"`
|
||||
}
|
||||
@ -57,8 +60,15 @@ func DefaultConfig() *Config {
|
||||
logPath := filepath.Join(flags.DataDir, "log/log.log")
|
||||
dbPath := filepath.Join(flags.DataDir, "data.db")
|
||||
return &Config{
|
||||
Address: "0.0.0.0",
|
||||
Port: 5244,
|
||||
Scheme: Scheme{
|
||||
Address: "0.0.0.0",
|
||||
UnixFile: "",
|
||||
HttpPort: 5244,
|
||||
HttpsPort: -1,
|
||||
ForceHttps: false,
|
||||
CertFile: "",
|
||||
KeyFile: "",
|
||||
},
|
||||
JwtSecret: random.String(16),
|
||||
TokenExpiresIn: 48,
|
||||
TempDir: tempDir,
|
||||
|
@ -14,6 +14,8 @@ const (
|
||||
SiteTitle = "site_title"
|
||||
Announcement = "announcement"
|
||||
AllowIndexed = "allow_indexed"
|
||||
AllowMounted = "allow_mounted"
|
||||
RobotsTxt = "robots_txt"
|
||||
|
||||
Logo = "logo"
|
||||
Favicon = "favicon"
|
||||
|
@ -74,7 +74,7 @@ func SearchNode(req model.SearchReq, useFullText bool) ([]model.SearchNode, int6
|
||||
}
|
||||
var count int64
|
||||
if err := searchDB.Count(&count).Error; err != nil {
|
||||
return nil, 0, errors.Wrapf(err, "failed get users count")
|
||||
return nil, 0, errors.Wrapf(err, "failed get search items count")
|
||||
}
|
||||
var files []model.SearchNode
|
||||
if err := searchDB.Offset((req.Page - 1) * req.PerPage).Limit(req.PerPage).Find(&files).Error; err != nil {
|
||||
|
@ -3,6 +3,7 @@ package fs
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
stdpath "path"
|
||||
"sync/atomic"
|
||||
|
||||
@ -87,7 +88,9 @@ func copyFileBetween2Storages(tsk *task.Task[uint64], srcStorage, dstStorage dri
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed get src [%s] file", srcFilePath)
|
||||
}
|
||||
link, _, err := op.Link(tsk.Ctx, srcStorage, srcFilePath, model.LinkArgs{})
|
||||
link, _, err := op.Link(tsk.Ctx, srcStorage, srcFilePath, model.LinkArgs{
|
||||
Header: http.Header{},
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed get [%s] link", srcFilePath)
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
|
||||
// List files
|
||||
func list(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error) {
|
||||
meta := ctx.Value("meta").(*model.Meta)
|
||||
user := ctx.Value("user").(*model.User)
|
||||
meta, _ := ctx.Value("meta").(*model.Meta)
|
||||
user, _ := ctx.Value("user").(*model.User)
|
||||
virtualFiles := op.GetStorageVirtualFilesByPath(path)
|
||||
storage, actualPath, err := op.GetStorageAndActualPath(path)
|
||||
if err != nil && len(virtualFiles) == 0 {
|
||||
@ -45,7 +45,7 @@ func list(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error)
|
||||
|
||||
func whetherHide(user *model.User, meta *model.Meta, path string) bool {
|
||||
// if is admin, don't hide
|
||||
if user.CanSeeHides() {
|
||||
if user == nil || user.CanSeeHides() {
|
||||
return false
|
||||
}
|
||||
// if meta is nil, don't hide
|
||||
|
@ -10,31 +10,13 @@ import (
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"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/alist-org/alist/v3/server/common"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func ClearCache(path string) {
|
||||
storage, actualPath, err := op.GetStorageAndActualPath(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
op.ClearCache(storage, actualPath)
|
||||
}
|
||||
|
||||
func containsByName(files []model.Obj, file model.Obj) bool {
|
||||
for _, f := range files {
|
||||
if f.GetName() == file.GetName() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var httpClient = &http.Client{}
|
||||
|
||||
func getFileStreamFromLink(file model.Obj, link *model.Link) (*model.FileStream, error) {
|
||||
var rc io.ReadCloser
|
||||
mimetype := utils.GetMimeType(file.GetName())
|
||||
@ -52,6 +34,16 @@ func getFileStreamFromLink(file model.Obj, link *model.Link) (*model.FileStream,
|
||||
return nil, errors.Wrapf(err, "failed to open file %s", *link.FilePath)
|
||||
}
|
||||
rc = f
|
||||
} else if link.Writer != nil {
|
||||
r, w := io.Pipe()
|
||||
go func() {
|
||||
err := link.Writer(w)
|
||||
err = w.CloseWithError(err)
|
||||
if err != nil {
|
||||
log.Errorf("[getFileStreamFromLink] failed to write: %v", err)
|
||||
}
|
||||
}()
|
||||
rc = r
|
||||
} else {
|
||||
req, err := http.NewRequest(http.MethodGet, link.URL, nil)
|
||||
if err != nil {
|
||||
@ -60,7 +52,7 @@ func getFileStreamFromLink(file model.Obj, link *model.Link) (*model.FileStream,
|
||||
for h, val := range link.Header {
|
||||
req.Header[h] = val
|
||||
}
|
||||
res, err := httpClient.Do(req)
|
||||
res, err := common.HttpClient().Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get response for %s", link.URL)
|
||||
}
|
||||
|
@ -11,19 +11,21 @@ type ListArgs struct {
|
||||
}
|
||||
|
||||
type LinkArgs struct {
|
||||
IP string
|
||||
Header http.Header
|
||||
Type string
|
||||
IP string
|
||||
Header http.Header
|
||||
Type string
|
||||
HttpReq *http.Request
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
URL string `json:"url"`
|
||||
Header http.Header `json:"header"` // needed header
|
||||
Data io.ReadCloser // return file reader directly
|
||||
Status int // status maybe 200 or 206, etc
|
||||
FilePath *string // local file, return the filepath
|
||||
Expiration *time.Duration // url expiration time
|
||||
Handle func(w http.ResponseWriter, r *http.Request) error // custom handler
|
||||
URL string `json:"url"`
|
||||
Header http.Header `json:"header"` // needed header (for url) or response header(for data or writer)
|
||||
Data io.ReadCloser // return file reader directly
|
||||
Status int // status maybe 200 or 206, etc
|
||||
FilePath *string // local file, return the filepath
|
||||
Expiration *time.Duration // url expiration time
|
||||
//Handle func(w http.ResponseWriter, r *http.Request) error `json:"-"` // custom handler
|
||||
Writer WriterFunc `json:"-"` // custom writer
|
||||
}
|
||||
|
||||
type OtherArgs struct {
|
||||
@ -37,3 +39,5 @@ type FsOtherArgs struct {
|
||||
Method string `json:"method" form:"method"`
|
||||
Data interface{} `json:"data" form:"data"`
|
||||
}
|
||||
|
||||
type WriterFunc func(w io.Writer) error
|
||||
|
@ -63,7 +63,7 @@ func getMainItems(config driver.Config) []driver.Item {
|
||||
Name: "mount_path",
|
||||
Type: conf.TypeString,
|
||||
Required: true,
|
||||
Help: "",
|
||||
Help: "The path you want to mount to, it is unique and cannot be repeated",
|
||||
}, {
|
||||
Name: "order",
|
||||
Type: conf.TypeNumber,
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
|
||||
var r *http.Request
|
||||
var retryBuf io.Reader
|
||||
|
||||
canRetry := true
|
||||
if body != nil {
|
||||
// If the authorization fails, we will need to restart reading
|
||||
// from the passed body stream.
|
||||
@ -25,6 +25,8 @@ func (c *Client) req(method, path string, body io.Reader, intercept func(*http.R
|
||||
return
|
||||
}
|
||||
retryBuf = body
|
||||
} else if method == http.MethodPut {
|
||||
canRetry = false
|
||||
} else {
|
||||
buff := &bytes.Buffer{}
|
||||
retryBuf = buff
|
||||
@ -83,7 +85,9 @@ func (c *Client) req(method, path string, body io.Reader, intercept func(*http.R
|
||||
|
||||
// retryBuf will be nil if body was nil initially so no check
|
||||
// for body == nil is required here.
|
||||
return c.req(method, path, retryBuf, intercept)
|
||||
if canRetry {
|
||||
return c.req(method, path, retryBuf, intercept)
|
||||
}
|
||||
} else if rs.StatusCode == 401 {
|
||||
return rs, newPathError("Authorize", c.root, rs.StatusCode)
|
||||
}
|
||||
@ -193,7 +197,7 @@ func (c *Client) copymove(method string, oldpath string, newpath string, overwri
|
||||
}
|
||||
|
||||
func (c *Client) put(path string, stream io.Reader, callback func(r *http.Request)) (status int, err error) {
|
||||
rs, err := c.req("PUT", path, stream, callback)
|
||||
rs, err := c.req(http.MethodPut, path, stream, callback)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user