mirror of https://github.com/Xhofe/alist
				
				
				
			
		
			
				
	
	
		
			210 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
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 d.RootNamespaceId != "" {
 | 
						|
		apiPathRootJson, err := utils.Json.MarshalToString(map[string]interface{}{
 | 
						|
			".tag": "root",
 | 
						|
			"root": d.RootNamespaceId,
 | 
						|
		})
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		req.SetHeader("Dropbox-API-Path-Root", apiPathRootJson)
 | 
						|
	}
 | 
						|
	if callback != nil {
 | 
						|
		callback(req)
 | 
						|
	}
 | 
						|
	if method == http.MethodPost && req.Body != nil {
 | 
						|
		req.SetHeader("Content-Type", "application/json")
 | 
						|
	}
 | 
						|
	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
 | 
						|
}
 |