Compare commits

..

37 Commits

Author SHA1 Message Date
Andy Hsu 8bdc67ec3d fix(webdav): return 404 if error happened on handlePropfind 2023-07-05 13:52:21 +08:00
Andy Hsu 4fabc27366 fix(aliyundrive_open): panic if driver not init 2023-07-05 13:51:46 +08:00
Andy Hsu e4c7b0f17c fix: https port is not effective 2023-07-05 13:02:52 +08:00
Andy Hsu 5e8bfb017e fix(123): add Referer to request (close #4631) 2023-07-04 18:36:46 +08:00
Andy Hsu 7d20a01dba feat!: support listen to the unix (close #4671)
Starting from this commit, the HTTP server related config all move to the scheme
2023-07-04 17:56:02 +08:00
Andy Hsu 59dbf4496f feat(offline_download): try to init client if not ready (close #4674) 2023-07-03 22:57:42 +08:00
vickunwu 12f40608e6 fix(oidc): use TOTP as state verification to replace the static 'state' parameter (#4665) 2023-07-03 22:41:08 +08:00
Andy Hsu 89832c296f fix: judge can proxy with ext (close #4688) 2023-07-03 20:41:37 +08:00
foxxorcat f09bb88846 fix(thunder): upload issues (close #4663 in #4667) 2023-06-29 13:21:30 +08:00
foxxorcat c518f59528 feat: add MoPan driver (close #4325 in #4659)
Co-authored-by: Andy Hsu <i@nn.ci>
2023-06-28 14:53:43 +08:00
walloo e9c74f9959 fix: regexp rename error (close #4644 in #4653)
Co-authored-by: Andy Hsu <i@nn.ci>
2023-06-26 15:15:57 +08:00
wenmig 21b8e7f6e5 fix(aliyundrive_share): add limit rate and lift rate limit restrictions (#4587) 2023-06-26 14:49:21 +08:00
Andy Hsu 2ae9cd8634 fix(dropbox): failed get link in #4639
close https://github.com/alist-org/alist/commit/cfee536b96f38e5ba3f3575fab4e89f6c0e1bc5b#commitcomment-119404554
2023-06-25 17:07:31 +08:00
wenmig cfee536b96 feat: add Dropbox driver (#4639 close #4590)
Co-authored-by: Andy Hsu <i@nn.ci>
2023-06-23 17:36:40 +08:00
william 1c8fe3b24c fix(aliyundrive_open): adaptive part size adjustment (#4609)
Co-authored-by: Andy Hsu <i@nn.ci>
2023-06-23 14:25:30 +08:00
Andy Hsu 84e23c397d fix(baidu_netdisk): rollback #3652 (close #4628) 2023-06-21 18:37:25 +08:00
Andy Hsu f7baec2e65 feat: add WoPan driver (close #4541) 2023-06-17 20:20:00 +08:00
wenmig 378bab32f1 chore(aliyundrive_share): increase the limit of the list api (#4588) 2023-06-17 20:10:34 +08:00
Andy Hsu 6cd8151cad fix(aliyundrive_open): change default oauth_token_url 2023-06-16 15:03:27 +08:00
Andy Hsu 541449e10f docs: add special sponsor [skip ci] 2023-06-14 05:42:21 +08:00
Andy Hsu ca5a53fc24 fix(aliyundrive_open): openFile/list rate limit 2023-06-11 18:18:09 +08:00
BoYanZh f646d2a699 feat!: listen to both http & https (#4536)
Co-authored-by: Andy Hsu <i@nn.ci>
2023-06-11 18:17:37 +08:00
Andy Hsu 363e036bf0 chore: fix typo [skip ci] 2023-06-10 22:25:35 +08:00
Andy Hsu e23f00f349 fix(139): avoid panic due to Authorization for emptiness 2023-06-10 00:12:04 +08:00
Andy Hsu 9600267bda ci: add linux-musl-amd64/arm64 to dev build 2023-06-09 23:43:52 +08:00
Andy Hsu a66b0e0151 feat(139): auto extract account from Authorization 2023-06-09 23:41:41 +08:00
Andy Hsu 3bfa00d5d2 fix(189pc): add REQID header 2023-06-09 23:33:12 +08:00
Andy Hsu 6cbd2532cc fix(139): modify the authentication mode 2023-06-09 23:02:02 +08:00
hunshcn 47976af0d3 feat: set ProxyFromEnvironment for default http client (#4546) 2023-06-09 22:08:54 +08:00
Andy Hsu 4dca52be85 fix(s3): optional add filename to disposition (close #4538) 2023-06-06 22:47:27 +08:00
Andy Hsu 62bb09300d chore: fix typo [skip ci] 2023-06-06 19:34:10 +08:00
Andy Hsu f9e067abec feat: support delayed start (#4532) 2023-06-05 16:00:31 +08:00
Andy Hsu 1e62666406 feat(baidu_netdisk): allow custom crack ua 2023-06-04 15:57:41 +08:00
Andy Hsu 0e0cdf15ef chore: change daysUntilClose [skip ci] 2023-06-03 21:15:52 +08:00
Andy Hsu b124fdc092 perf(baidu): avoid refreshing the token on every startup 2023-06-02 18:31:42 +08:00
renovate[bot] 5141b3c165 fix(deps): update module github.com/gin-gonic/gin to v1.9.1 [security] [skip ci] (#4521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-02 18:31:14 +08:00
Mg Pig 881d6e271e feat: add OIDC single sign-on (#4496)
close #3914
close #4315
2023-06-02 18:22:07 +08:00
57 changed files with 2028 additions and 303 deletions
+1 -1
View File
@@ -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: 10
daysUntilClose: 20
# Issues with these labels will never be considered stale
exemptLabels:
- accepted
+1 -1
View File
@@ -18,4 +18,4 @@ jobs:
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.
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.
+1 -1
View File
@@ -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" ]
+2
View File
@@ -73,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
@@ -110,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/)
+2
View File
@@ -72,6 +72,7 @@
- [x] SMB
- [x] [115](https://115.com/)
- [X] Cloudreve
- [x] [Dropbox](https://www.dropbox.com/)
- [x] 部署方便,开箱即用
- [x] 文件预览(PDF、markdown、代码、纯文本……)
- [x] 画廊模式下的图像预览
@@ -108,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/)
+21 -2
View File
@@ -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
+1 -1
View File
@@ -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")
+74 -25
View File
@@ -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")
},
}
+1
View File
@@ -6,6 +6,7 @@ services:
- '/etc/alist:/opt/alist/data'
ports:
- '5244:5244'
- '5245:5245'
environment:
- PUID=0
- PGID=0
+5 -1
View File
@@ -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),
+7 -4
View File
@@ -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 {
+19 -13
View File
@@ -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",
+17 -1
View File
@@ -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,
+2 -2
View File
@@ -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"`
+1 -1
View File
@@ -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",
+1
View File
@@ -352,6 +352,7 @@ func (y *Cloud189PC) initLoginParam() error {
// 判断是否需要验证码
resp, err := y.client.R().
SetHeader("REQID", param.ReqId).
SetFormData(map[string]string{
"appKey": APP_ID,
"accountType": ACCOUNT_TYPE,
+29 -11
View File
@@ -2,6 +2,7 @@ package aliyundrive_open
import (
"context"
"fmt"
"io"
"math"
"net/http"
@@ -22,7 +23,7 @@ type AliyundriveOpen struct {
DriveId string
limitList func(ctx context.Context, dir model.Obj) ([]model.Obj, error)
limitList func(ctx context.Context, data base.Json) (*Files, error)
limitLink func(ctx context.Context, file model.Obj) (*model.Link, error)
}
@@ -49,8 +50,11 @@ func (d *AliyundriveOpen) Drop(ctx context.Context) error {
return nil
}
func (d *AliyundriveOpen) list(ctx context.Context, dir model.Obj) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetID())
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
if d.limitList == nil {
return nil, fmt.Errorf("driver not init")
}
files, err := d.getFiles(ctx, dir.GetID())
if err != nil {
return nil, err
}
@@ -59,10 +63,6 @@ func (d *AliyundriveOpen) list(ctx context.Context, dir model.Obj) ([]model.Obj,
})
}
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
return d.limitList(ctx, dir)
}
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{
@@ -83,6 +83,9 @@ func (d *AliyundriveOpen) link(ctx context.Context, file model.Obj) (*model.Link
}
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)
}
@@ -152,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(),
@@ -161,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
@@ -178,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
}
+1 -1
View File
@@ -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"`
+13 -5
View File
@@ -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
}
+21 -1
View File
@@ -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 -1
View File
@@ -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 {
+3
View File
@@ -16,6 +16,7 @@ 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"
@@ -24,6 +25,7 @@ import (
_ "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"
@@ -41,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"
)
+5 -2
View File
@@ -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 {
+2
View File
@@ -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{
+2 -2
View File
@@ -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
}
+1
View File
@@ -42,6 +42,7 @@ func NewHttpClient() *http.Client {
return &http.Client{
Timeout: time.Hour * 48,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify},
},
}
+222
View 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
View 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
View 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
View 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
}
+295
View 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
View 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
View File
@@ -0,0 +1 @@
package mopan
+58
View 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,
},
}
}
+4 -1
View File
@@ -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,
+12 -11
View File
@@ -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{
+22 -3
View File
@@ -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
}
+29
View File
@@ -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
}
+161
View 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
View 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
View 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
View 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)
}
+23 -15
View File
@@ -5,15 +5,18 @@ go 1.20
require (
github.com/SheltonZhu/115driver v1.0.14
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
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
@@ -33,9 +36,10 @@ require (
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.9.0
golang.org/x/crypto v0.10.0
golang.org/x/image v0.7.0
golang.org/x/net v0.10.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
@@ -67,18 +71,19 @@ require (
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.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -93,13 +98,13 @@ require (
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.2.3 // 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/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.17 // 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
@@ -116,22 +121,25 @@ require (
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.8.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
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
)
+55 -32
View File
@@ -6,6 +6,8 @@ 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=
@@ -58,14 +60,16 @@ github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0
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=
@@ -82,7 +86,11 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
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=
@@ -92,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=
@@ -105,27 +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.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
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=
@@ -173,8 +182,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
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=
@@ -185,8 +194,9 @@ 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=
@@ -196,10 +206,11 @@ github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInk
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=
@@ -237,8 +248,8 @@ github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 h1:dg/Eaa
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=
@@ -249,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=
@@ -280,7 +293,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
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=
@@ -291,8 +306,8 @@ 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=
@@ -304,8 +319,9 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
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=
@@ -316,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.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
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=
@@ -332,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.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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=
@@ -354,16 +373,16 @@ golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBc
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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.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.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
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=
@@ -371,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=
@@ -384,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-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
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=
@@ -397,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=
+1 -1
View File
@@ -154,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},
+17 -7
View File
@@ -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,
+1 -1
View File
@@ -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 {
+2 -2
View File
@@ -46,9 +46,9 @@ func IsSubPath(path string, subPath string) bool {
func Ext(path string) string {
ext := stdpath.Ext(path)
if strings.HasPrefix(ext, ".") {
return ext[1:]
ext = ext[1:]
}
return ext
return strings.ToLower(ext)
}
func EncodePath(path string, all ...bool) string {
+9
View File
@@ -60,3 +60,12 @@ func MergeErrors(errs ...error) error {
}
return nil
}
func SliceMeet[T1, T2 any](arr []T1, v T2, meet func(item T1, v T2) bool) bool {
for _, item := range arr {
if meet(item, v) {
return true
}
}
return false
}
+10 -2
View File
@@ -48,8 +48,16 @@ func AddAria2(c *gin.Context) {
return
}
if !aria2.IsAria2Ready() {
common.ErrorStrResp(c, "aria2 not ready", 500)
return
// try to init client
_, err := aria2.InitClient(2)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if !aria2.IsAria2Ready() {
common.ErrorStrResp(c, "aria2 still not ready after init", 500)
return
}
}
var req AddAria2Req
if err := c.ShouldBind(&req); err != nil {
+211
View File
@@ -0,0 +1,211 @@
package handles
import (
"fmt"
"regexp"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/fs"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/generic"
"github.com/alist-org/alist/v3/server/common"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
type BatchRenameReq struct {
SrcDir string `json:"src_dir"`
RenameObjects []struct {
SrcName string `json:"src_name"`
NewName string `json:"new_name"`
} `json:"rename_objects"`
}
func FsBatchRename(c *gin.Context) {
var req BatchRenameReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
if !user.CanRename() {
common.ErrorResp(c, errs.PermissionDenied, 403)
return
}
reqPath, err := user.JoinPath(req.SrcDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
meta, err := op.GetNearestMeta(reqPath)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
for _, renameObject := range req.RenameObjects {
if renameObject.SrcName == "" || renameObject.NewName == "" {
continue
}
filePath := fmt.Sprintf("%s/%s", reqPath, renameObject.SrcName)
if err := fs.Rename(c, filePath, renameObject.NewName); err != nil {
common.ErrorResp(c, err, 500)
return
}
}
common.SuccessResp(c)
}
type RecursiveMoveReq struct {
SrcDir string `json:"src_dir"`
DstDir string `json:"dst_dir"`
}
func FsRecursiveMove(c *gin.Context) {
var req RecursiveMoveReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
if !user.CanMove() {
common.ErrorResp(c, errs.PermissionDenied, 403)
return
}
srcDir, err := user.JoinPath(req.SrcDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
dstDir, err := user.JoinPath(req.DstDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
meta, err := op.GetNearestMeta(srcDir)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
rootFiles, err := fs.List(c, srcDir, &fs.ListArgs{})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// record the file path
filePathMap := make(map[model.Obj]string)
movingFiles := generic.NewQueue[model.Obj]()
for _, file := range rootFiles {
movingFiles.Push(file)
filePathMap[file] = srcDir
}
for !movingFiles.IsEmpty() {
movingFile := movingFiles.Pop()
movingFilePath := filePathMap[movingFile]
movingFileName := fmt.Sprintf("%s/%s", movingFilePath, movingFile.GetName())
if movingFile.IsDir() {
// directory, recursive move
subFilePath := movingFileName
subFiles, err := fs.List(c, movingFileName, &fs.ListArgs{Refresh: true})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for _, subFile := range subFiles {
movingFiles.Push(subFile)
filePathMap[subFile] = subFilePath
}
} else {
if movingFilePath == dstDir {
// same directory, don't move
continue
}
// move
err := fs.Move(c, movingFileName, dstDir, movingFiles.IsEmpty())
if err != nil {
common.ErrorResp(c, err, 500)
return
}
}
}
common.SuccessResp(c)
}
type RegexRenameReq struct {
SrcDir string `json:"src_dir"`
SrcNameRegex string `json:"src_name_regex"`
NewNameRegex string `json:"new_name_regex"`
}
func FsRegexRename(c *gin.Context) {
var req RegexRenameReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
if !user.CanRename() {
common.ErrorResp(c, errs.PermissionDenied, 403)
return
}
reqPath, err := user.JoinPath(req.SrcDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
meta, err := op.GetNearestMeta(reqPath)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
srcRegexp, err := regexp.Compile(req.SrcNameRegex)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
files, err := fs.List(c, reqPath, &fs.ListArgs{})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for _, file := range files {
if srcRegexp.MatchString(file.GetName()) {
filePath := fmt.Sprintf("%s/%s", reqPath, file.GetName())
newFileName := srcRegexp.ReplaceAllString(file.GetName(), req.NewNameRegex)
if err := fs.Rename(c, filePath, newFileName); err != nil {
common.ErrorResp(c, err, 500)
return
}
}
}
common.SuccessResp(c)
}
-150
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"io"
stdpath "path"
"regexp"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/fs"
@@ -96,94 +95,6 @@ func FsMove(c *gin.Context) {
common.SuccessResp(c)
}
type RecursiveMoveReq struct {
SrcDir string `json:"src_dir"`
DstDir string `json:"dst_dir"`
}
func FsRecursiveMove(c *gin.Context) {
var req RecursiveMoveReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
if !user.CanMove() {
common.ErrorResp(c, errs.PermissionDenied, 403)
return
}
srcDir, err := user.JoinPath(req.SrcDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
dstDir, err := user.JoinPath(req.DstDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
meta, err := op.GetNearestMeta(srcDir)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
rootFiles, err := fs.List(c, srcDir, &fs.ListArgs{})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// record the file path
filePathMap := make(map[model.Obj]string)
movingFiles := generic.NewQueue[model.Obj]()
for _, file := range rootFiles {
movingFiles.Push(file)
filePathMap[file] = srcDir
}
for !movingFiles.IsEmpty() {
movingFile := movingFiles.Pop()
movingFilePath := filePathMap[movingFile]
movingFileName := fmt.Sprintf("%s/%s", movingFilePath, movingFile.GetName())
if movingFile.IsDir() {
// directory, recursive move
subFilePath := movingFileName
subFiles, err := fs.List(c, movingFileName, &fs.ListArgs{Refresh: true})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for _, subFile := range subFiles {
movingFiles.Push(subFile)
filePathMap[subFile] = subFilePath
}
} else {
if movingFilePath == dstDir {
// same directory, don't move
continue
}
// move
err := fs.Move(c, movingFileName, dstDir, movingFiles.IsEmpty())
if err != nil {
common.ErrorResp(c, err, 500)
return
}
}
}
common.SuccessResp(c)
}
func FsCopy(c *gin.Context) {
var req MoveCopyReq
if err := c.ShouldBind(&req); err != nil {
@@ -255,67 +166,6 @@ func FsRename(c *gin.Context) {
common.SuccessResp(c)
}
type RegexRenameReq struct {
SrcDir string `json:"src_dir"`
SrcNameRegex string `json:"src_name_regex"`
NewNameRegex string `json:"new_name_regex"`
}
func FsRegexRename(c *gin.Context) {
var req RegexRenameReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
if !user.CanRename() {
common.ErrorResp(c, errs.PermissionDenied, 403)
return
}
reqPath, err := user.JoinPath(req.SrcDir)
if err != nil {
common.ErrorResp(c, err, 403)
return
}
meta, err := op.GetNearestMeta(reqPath)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
srcRegexp, err := regexp.Compile(req.SrcNameRegex)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
files, err := fs.List(c, reqPath, &fs.ListArgs{})
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for _, file := range files {
if srcRegexp.MatchString(file.GetName()) {
filePath := fmt.Sprintf("%s/%s", reqPath, file.GetName())
newFileName := srcRegexp.ReplaceAllString(file.GetName(), req.NewNameRegex)
if err := fs.Rename(c, filePath, newFileName); err != nil {
common.ErrorResp(c, err, 500)
return
}
}
}
common.SuccessResp(c)
}
type RemoveReq struct {
Dir string `json:"dir"`
Names []string `json:"names"`
+10 -2
View File
@@ -47,8 +47,16 @@ func AddQbittorrent(c *gin.Context) {
return
}
if !qbittorrent.IsQbittorrentReady() {
common.ErrorStrResp(c, "qbittorrent not ready", 500)
return
// try to init client
err := qbittorrent.InitClient()
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if !qbittorrent.IsQbittorrentReady() {
common.ErrorStrResp(c, "qbittorrent still not ready after init", 500)
return
}
}
var req AddQbittorrentReq
if err := c.ShouldBind(&req); err != nil {
+146
View File
@@ -1,20 +1,36 @@
package handles
import (
"encoding/base32"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/db"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/alist-org/alist/v3/server/common"
"github.com/coreos/go-oidc"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"golang.org/x/oauth2"
)
var opts = totp.ValidateOpts{
// state verify won't expire in 30 secs, which is quite enough for the callback
Period: 30,
Skew: 1,
// in some OIDC providers(such as Authelia), state parameter must be at least 8 characters
Digits: otp.DigitsEight,
Algorithm: otp.AlgorithmSHA1,
}
func SSOLoginRedirect(c *gin.Context) {
method := c.Query("method")
enabled := setting.GetBool(conf.SSOLoginEnabled)
@@ -53,6 +69,20 @@ func SSOLoginRedirect(c *gin.Context) {
r_url = endpoint + "/login/oauth/authorize?"
urlValues.Add("scope", "profile")
urlValues.Add("state", endpoint)
case "OIDC":
oauth2Config, err := GetOIDCClient(c)
if err != nil {
common.ErrorStrResp(c, err.Error(), 400)
return
}
// generate state parameter
state,err := totp.GenerateCodeCustom(base32.StdEncoding.EncodeToString([]byte(oauth2Config.ClientSecret)), time.Now(), opts)
if err != nil {
common.ErrorStrResp(c, err.Error(), 400)
return
}
c.Redirect(http.StatusFound, oauth2Config.AuthCodeURL(state))
return
default:
common.ErrorStrResp(c, "invalid platform", 400)
return
@@ -65,6 +95,119 @@ func SSOLoginRedirect(c *gin.Context) {
var ssoClient = resty.New().SetRetryCount(3)
func GetOIDCClient(c *gin.Context) (*oauth2.Config, error) {
argument := c.Query("method")
redirect_uri := common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + argument
endpoint := setting.GetStr(conf.SSOEndpointName)
provider, err := oidc.NewProvider(c, endpoint)
if err != nil {
return nil, err
}
clientId := setting.GetStr(conf.SSOClientId)
clientSecret := setting.GetStr(conf.SSOClientSecret)
return &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirect_uri,
// Discovery returns the OAuth2 endpoints.
Endpoint: provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile"},
}, nil
}
func OIDCLoginCallback(c *gin.Context) {
argument := c.Query("method")
enabled := setting.GetBool(conf.SSOLoginEnabled)
clientId := setting.GetStr(conf.SSOClientId)
if !enabled {
common.ErrorResp(c, errors.New("invalid request"), 500)
}
endpoint := setting.GetStr(conf.SSOEndpointName)
provider, err := oidc.NewProvider(c, endpoint)
if err != nil {
common.ErrorResp(c, err, 400)
return
}
oauth2Config, err := GetOIDCClient(c)
if err != nil {
common.ErrorResp(c, err, 400)
return
}
// add state verify process
stateVerification, err := totp.ValidateCustom(c.Query("state"), base32.StdEncoding.EncodeToString([]byte(oauth2Config.ClientSecret)), time.Now(), opts)
if err != nil {
common.ErrorResp(c, err, 400)
return
}
if !stateVerification {
common.ErrorStrResp(c, "incorrect or expired state parameter", 400)
return
}
oauth2Token, err := oauth2Config.Exchange(c, c.Query("code"))
if err != nil {
common.ErrorResp(c, err, 400)
return
}
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
common.ErrorStrResp(c, "no id_token found in oauth2 token", 400)
return
}
verifier := provider.Verifier(&oidc.Config{
ClientID: clientId,
})
idToken, err := verifier.Verify(c, rawIDToken)
if err != nil {
common.ErrorResp(c, err, 400)
return
}
type UserInfo struct {
Name string `json:"name"`
}
claims := UserInfo{}
if err := idToken.Claims(&claims); err != nil {
c.Error(err)
return
}
UserID := claims.Name
if argument == "get_sso_id" {
html := fmt.Sprintf(`<!DOCTYPE html>
<head></head>
<body>
<script>
window.opener.postMessage({"sso_id": "%s"}, "*")
window.close()
</script>
</body>`, UserID)
c.Data(200, "text/html; charset=utf-8", []byte(html))
return
}
if argument == "sso_get_token" {
user, err := db.GetUserBySSOID(UserID)
if err != nil {
common.ErrorResp(c, err, 400)
}
token, err := common.GenerateToken(user.Username)
if err != nil {
common.ErrorResp(c, err, 400)
}
html := fmt.Sprintf(`<!DOCTYPE html>
<head></head>
<body>
<script>
window.opener.postMessage({"token":"%s"}, "*")
window.close()
</script>
</body>`, token)
c.Data(200, "text/html; charset=utf-8", []byte(html))
return
}
}
func SSOLoginCallback(c *gin.Context) {
argument := c.Query("method")
if argument == "get_sso_id" || argument == "sso_get_token" {
@@ -108,6 +251,9 @@ func SSOLoginCallback(c *gin.Context) {
scope = "profile"
authstring = "code"
idstring = "preferred_username"
case "OIDC":
OIDCLoginCallback(c)
return
default:
common.ErrorStrResp(c, "invalid platform", 400)
return
+21
View File
@@ -0,0 +1,21 @@
package middlewares
import (
"fmt"
"strings"
"github.com/alist-org/alist/v3/internal/conf"
"github.com/gin-gonic/gin"
)
func ForceHttps(c *gin.Context) {
if c.Request.TLS == nil {
host := c.Request.Host
// change port to https port
host = strings.Replace(host, fmt.Sprintf(":%d", conf.Conf.Scheme.HttpPort), fmt.Sprintf(":%d", conf.Conf.Scheme.HttpsPort), 1)
c.Redirect(302, "https://"+host+c.Request.RequestURI)
c.Abort()
return
}
c.Next()
}
+4
View File
@@ -21,6 +21,9 @@ func Init(e *gin.Engine) {
}
Cors(e)
g := e.Group(conf.URL.Path)
if conf.Conf.Scheme.HttpPort != -1 && conf.Conf.Scheme.HttpsPort != -1 && conf.Conf.Scheme.ForceHttps {
g.Use(middlewares.ForceHttps)
}
g.Any("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
@@ -127,6 +130,7 @@ func _fs(g *gin.RouterGroup) {
g.Any("/dirs", handles.FsDirs)
g.POST("/mkdir", handles.FsMkdir)
g.POST("/rename", handles.FsRename)
g.POST("/batch_rename", handles.FsBatchRename)
g.POST("/regex_rename", handles.FsRegexRename)
g.POST("/move", handles.FsMove)
g.POST("/recursive_move", handles.FsRecursiveMove)
+4
View File
@@ -71,6 +71,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
status, err = h.handleUnlock(brw, r)
case "PROPFIND":
status, err = h.handlePropfind(brw, r)
// if there is a error for PROPFIND, we should be as an empty folder to the client
if err != nil {
status = http.StatusNotFound
}
case "PROPPATCH":
status, err = h.handleProppatch(brw, r)
}