mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 16:49:03 +08:00 
			
		
		
		
	Feat: OneDrive OAuth / refresh token
This commit is contained in:
		| @ -43,6 +43,9 @@ type PolicyOption struct { | |||||||
| 	FileType []string `json:"file_type"` | 	FileType []string `json:"file_type"` | ||||||
| 	// MimeType | 	// MimeType | ||||||
| 	MimeType string `json:"mimetype"` | 	MimeType string `json:"mimetype"` | ||||||
|  |  | ||||||
|  | 	// OdRedirect Onedrive重定向地址 | ||||||
|  | 	OdRedirect string `json:"od_redirect,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| @ -190,3 +193,17 @@ func (policy *Policy) GetUploadURL() string { | |||||||
| 	} | 	} | ||||||
| 	return server.ResolveReference(controller).String() | 	return server.ResolveReference(controller).String() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateAccessKey 更新 AccessKey | ||||||
|  | // TODO 测试 | ||||||
|  | func (policy *Policy) UpdateAccessKey(key string) error { | ||||||
|  | 	policy.AccessKey = key | ||||||
|  | 	err := DB.Save(policy).Error | ||||||
|  | 	policy.ClearCache() | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ClearCache 清空policy缓存 | ||||||
|  | func (policy *Policy) ClearCache() { | ||||||
|  | 	cache.Deletes([]string{strconv.FormatUint(uint64(policy.ID), 10)}, "policy_") | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										59
									
								
								pkg/filesystem/driver/onedrive/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								pkg/filesystem/driver/onedrive/client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | package onedrive | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	model "github.com/HFO4/cloudreve/models" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/request" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrAuthEndpoint 无法解析授权端点地址 | ||||||
|  | 	ErrAuthEndpoint        = errors.New("无法解析授权端点地址") | ||||||
|  | 	ErrInvalidRefreshToken = errors.New("上传策略无有效的RefreshToken") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Client OneDrive客户端 | ||||||
|  | type Client struct { | ||||||
|  | 	Endpoints  *Endpoints | ||||||
|  | 	Policy     *model.Policy | ||||||
|  | 	Credential *Credential | ||||||
|  |  | ||||||
|  | 	ClientID     string | ||||||
|  | 	ClientSecret string | ||||||
|  | 	Redirect     string | ||||||
|  |  | ||||||
|  | 	Request request.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Endpoints OneDrive客户端相关设置 | ||||||
|  | type Endpoints struct { | ||||||
|  | 	OAuthURL       string // OAuth认证的基URL | ||||||
|  | 	OAuthEndpoints *oauthEndpoint | ||||||
|  | 	EndpointURL    string // 接口请求的基URL | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewClient 根据存储策略获取新的client | ||||||
|  | func NewClient(policy *model.Policy) (*Client, error) { | ||||||
|  | 	client := &Client{ | ||||||
|  | 		Endpoints: &Endpoints{ | ||||||
|  | 			OAuthURL:    policy.BaseURL, | ||||||
|  | 			EndpointURL: policy.Server, | ||||||
|  | 		}, | ||||||
|  | 		Credential: &Credential{ | ||||||
|  | 			RefreshToken: policy.AccessKey, | ||||||
|  | 		}, | ||||||
|  | 		Policy:       policy, | ||||||
|  | 		ClientID:     policy.BucketName, | ||||||
|  | 		ClientSecret: policy.SecretKey, | ||||||
|  | 		Redirect:     policy.OptionsSerialized.OdRedirect, | ||||||
|  | 		Request:      request.HTTPClient{}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	oauthBase := client.getOAuthEndpoint() | ||||||
|  | 	if oauthBase == nil { | ||||||
|  | 		return nil, ErrAuthEndpoint | ||||||
|  | 	} | ||||||
|  | 	client.Endpoints.OAuthEndpoints = oauthBase | ||||||
|  |  | ||||||
|  | 	return client, nil | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								pkg/filesystem/driver/onedrive/handller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								pkg/filesystem/driver/onedrive/handller.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | package onedrive | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	model "github.com/HFO4/cloudreve/models" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||||
|  | 	"io" | ||||||
|  | 	"net/url" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Driver OneDrive 适配器 | ||||||
|  | type Driver struct { | ||||||
|  | 	Policy *model.Policy | ||||||
|  | 	Client *Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get 获取文件 | ||||||
|  | func (handler Driver) Get(ctx context.Context, path string) (response.RSCloser, error) { | ||||||
|  | 	return nil, errors.New("未实现") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Put 将文件流保存到指定目录 | ||||||
|  | func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | ||||||
|  | 	return errors.New("未实现") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete 删除一个或多个文件, | ||||||
|  | // 返回未删除的文件,及遇到的最后一个错误 | ||||||
|  | func (handler Driver) Delete(ctx context.Context, files []string) ([]string, error) { | ||||||
|  | 	return []string{}, errors.New("未实现") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Thumb 获取文件缩略图 | ||||||
|  | func (handler Driver) Thumb(ctx context.Context, path string) (*response.ContentResponse, error) { | ||||||
|  | 	return nil, errors.New("未实现") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Source 获取外链URL | ||||||
|  | func (handler Driver) Source( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	path string, | ||||||
|  | 	baseURL url.URL, | ||||||
|  | 	ttl int64, | ||||||
|  | 	isDownload bool, | ||||||
|  | 	speed int, | ||||||
|  | ) (string, error) { | ||||||
|  | 	return "", errors.New("未实现") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Token 获取上传策略和认证Token | ||||||
|  | func (handler Driver) Token(ctx context.Context, TTL int64, key string) (serializer.UploadCredential, error) { | ||||||
|  | 	err := handler.Client.UpdateCredential(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return serializer.UploadCredential{}, err | ||||||
|  | 	} | ||||||
|  | 	return serializer.UploadCredential{ | ||||||
|  | 		Policy: handler.Client.Credential.AccessToken, | ||||||
|  | 	}, nil | ||||||
|  | 	//res,err := handler.Client.ObtainToken(ctx,WithCode("M2e92c4a9-de12-cdda-9cf4-e01f67272831")) | ||||||
|  | 	//if err != nil{ | ||||||
|  | 	//	return serializer.UploadCredential{},err | ||||||
|  | 	//} | ||||||
|  | 	//return serializer.UploadCredential{ | ||||||
|  | 	//	Policy:res.RefreshToken, | ||||||
|  | 	//}, nil | ||||||
|  | } | ||||||
							
								
								
									
										192
									
								
								pkg/filesystem/driver/onedrive/oauth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								pkg/filesystem/driver/onedrive/oauth.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | |||||||
|  | package onedrive | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/gob" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/cache" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/request" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // oauthEndpoint OAuth接口地址 | ||||||
|  | type oauthEndpoint struct { | ||||||
|  | 	token     url.URL | ||||||
|  | 	authorize url.URL | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Credential 获取token时返回的凭证 | ||||||
|  | type Credential struct { | ||||||
|  | 	TokenType    string `json:"token_type"` | ||||||
|  | 	ExpiresIn    int64  `json:"expires_in"` | ||||||
|  | 	Scope        string `json:"scope"` | ||||||
|  | 	AccessToken  string `json:"access_token"` | ||||||
|  | 	RefreshToken string `json:"refresh_token"` | ||||||
|  | 	UserID       string `json:"user_id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OAuthError OAuth相关接口的错误响应 | ||||||
|  | type OAuthError struct { | ||||||
|  | 	ErrorType        string `json:"error"` | ||||||
|  | 	ErrorDescription string `json:"error_description"` | ||||||
|  | 	CorrelationID    string `json:"correlation_id"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	gob.Register(Credential{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Error 实现error接口 | ||||||
|  | func (err OAuthError) Error() string { | ||||||
|  | 	return err.ErrorDescription | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OAuthURL 获取OAuth认证页面URL | ||||||
|  | func (client *Client) OAuthURL(ctx context.Context, scope []string) string { | ||||||
|  | 	query := url.Values{ | ||||||
|  | 		"client_id":     {client.ClientID}, | ||||||
|  | 		"scope":         {strings.Join(scope, " ")}, | ||||||
|  | 		"response_type": {"code"}, | ||||||
|  | 		"redirect_uri":  {client.Redirect}, | ||||||
|  | 	} | ||||||
|  | 	client.Endpoints.OAuthEndpoints.authorize.RawQuery = query.Encode() | ||||||
|  | 	return client.Endpoints.OAuthEndpoints.authorize.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getOAuthEndpoint 根据指定的AuthURL获取详细的认证接口地址 | ||||||
|  | func (client *Client) getOAuthEndpoint() *oauthEndpoint { | ||||||
|  | 	base, err := url.Parse(client.Endpoints.OAuthURL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	var ( | ||||||
|  | 		token     *url.URL | ||||||
|  | 		authorize *url.URL | ||||||
|  | 	) | ||||||
|  | 	switch base.Host { | ||||||
|  | 	case "login.live.com": | ||||||
|  | 		token, _ = url.Parse("https://login.live.com/oauth20_token.srf") | ||||||
|  | 		authorize, _ = url.Parse("https://login.live.com/oauth20_authorize.srf") | ||||||
|  | 	default: | ||||||
|  | 		token, _ = url.Parse("https://login.microsoftonline.com/common/oauth2/v2.0/token") | ||||||
|  | 		authorize, _ = url.Parse("https://login.microsoftonline.com/common/oauth2/v2.0/authorize") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &oauthEndpoint{ | ||||||
|  | 		token:     *token, | ||||||
|  | 		authorize: *authorize, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ObtainToken 通过code或refresh_token兑换token | ||||||
|  | func (client *Client) ObtainToken(ctx context.Context, opts ...Option) (*Credential, error) { | ||||||
|  | 	options := newDefaultOption() | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o.apply(options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	body := url.Values{ | ||||||
|  | 		"client_id":     {client.ClientID}, | ||||||
|  | 		"redirect_uri":  {client.Redirect}, | ||||||
|  | 		"client_secret": {client.ClientSecret}, | ||||||
|  | 	} | ||||||
|  | 	if options.code != "" { | ||||||
|  | 		body.Add("grant_type", "authorization_code") | ||||||
|  | 		body.Add("code", options.code) | ||||||
|  | 	} else { | ||||||
|  | 		body.Add("grant_type", "refresh_token") | ||||||
|  | 		body.Add("refresh_token", options.refreshToken) | ||||||
|  | 	} | ||||||
|  | 	strBody := body.Encode() | ||||||
|  |  | ||||||
|  | 	res := client.Request.Request( | ||||||
|  | 		"POST", | ||||||
|  | 		client.Endpoints.OAuthEndpoints.token.String(), | ||||||
|  | 		ioutil.NopCloser(strings.NewReader(strBody)), | ||||||
|  | 		request.WithHeader(http.Header{ | ||||||
|  | 			"Content-Type": {"application/x-www-form-urlencoded"}}, | ||||||
|  | 		), | ||||||
|  | 		request.WithContentLength(int64(len(strBody))), | ||||||
|  | 	) | ||||||
|  | 	if res.Err != nil { | ||||||
|  | 		return nil, res.Err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	respBody, err := res.GetResponse() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var ( | ||||||
|  | 		errResp    OAuthError | ||||||
|  | 		credential Credential | ||||||
|  | 		decodeErr  error | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	if res.Response.StatusCode != 200 { | ||||||
|  | 		decodeErr = json.Unmarshal([]byte(respBody), &errResp) | ||||||
|  | 	} else { | ||||||
|  | 		decodeErr = json.Unmarshal([]byte(respBody), &credential) | ||||||
|  | 	} | ||||||
|  | 	if decodeErr != nil { | ||||||
|  | 		return nil, decodeErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if errResp.ErrorType != "" { | ||||||
|  | 		return nil, errResp | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &credential, nil | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateCredential 更新凭证,并检查有效期 | ||||||
|  | func (client *Client) UpdateCredential(ctx context.Context) error { | ||||||
|  | 	// 如果已存在凭证 | ||||||
|  | 	if client.Credential != nil && client.Credential.AccessToken != "" { | ||||||
|  | 		// 检查已有凭证是否过期 | ||||||
|  | 		if client.Credential.ExpiresIn > time.Now().Unix() { | ||||||
|  | 			// 未过期,不要更新 | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 尝试从缓存中获取凭证 | ||||||
|  | 	if cacheCredential, ok := cache.Get("onedrive_" + client.ClientID); ok { | ||||||
|  | 		credential := cacheCredential.(Credential) | ||||||
|  | 		if credential.ExpiresIn > time.Now().Unix() { | ||||||
|  | 			client.Credential = &credential | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 获取新的凭证 | ||||||
|  | 	if client.Credential == nil || client.Credential.RefreshToken == "" { | ||||||
|  | 		// 无有效的RefreshToken | ||||||
|  | 		util.Log().Error("上传策略[%s]凭证刷新失败,请重新授权OneDrive账号", client.Policy.Name) | ||||||
|  | 		return ErrInvalidRefreshToken | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	credential, err := client.ObtainToken(ctx, WithRefreshToken(client.Credential.RefreshToken)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 更新有效期为绝对时间戳 | ||||||
|  | 	expires := credential.ExpiresIn - 60 | ||||||
|  | 	credential.ExpiresIn = time.Now().Add(time.Duration(expires) * time.Second).Unix() | ||||||
|  | 	client.Credential = credential | ||||||
|  |  | ||||||
|  | 	// 更新存储策略的 RefreshToken | ||||||
|  | 	client.Policy.UpdateAccessKey(credential.RefreshToken) | ||||||
|  |  | ||||||
|  | 	// 更新缓存 | ||||||
|  | 	cache.Set("onedrive_"+client.ClientID, *credential, int(expires)) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								pkg/filesystem/driver/onedrive/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								pkg/filesystem/driver/onedrive/options.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | package onedrive | ||||||
|  |  | ||||||
|  | // Option 发送请求的额外设置 | ||||||
|  | type Option interface { | ||||||
|  | 	apply(*options) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type options struct { | ||||||
|  | 	redirect     string | ||||||
|  | 	code         string | ||||||
|  | 	refreshToken string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type optionFunc func(*options) | ||||||
|  |  | ||||||
|  | // WithCode 设置接口Code | ||||||
|  | func WithCode(t string) Option { | ||||||
|  | 	return optionFunc(func(o *options) { | ||||||
|  | 		o.code = t | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithRefreshToken 设置接口RefreshToken | ||||||
|  | func WithRefreshToken(t string) Option { | ||||||
|  | 	return optionFunc(func(o *options) { | ||||||
|  | 		o.refreshToken = t | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f optionFunc) apply(o *options) { | ||||||
|  | 	f(o) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newDefaultOption() *options { | ||||||
|  | 	return &options{} | ||||||
|  | } | ||||||
| @ -12,9 +12,11 @@ import ( | |||||||
| 	model "github.com/HFO4/cloudreve/models" | 	model "github.com/HFO4/cloudreve/models" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/fsctx" | 	"github.com/HFO4/cloudreve/pkg/filesystem/fsctx" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | 	"github.com/HFO4/cloudreve/pkg/filesystem/response" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/request" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/serializer" | 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||||
| 	"github.com/upyun/go-sdk/upyun" | 	"github.com/upyun/go-sdk/upyun" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| @ -40,12 +42,63 @@ type Driver struct { | |||||||
|  |  | ||||||
| // Get 获取文件 | // Get 获取文件 | ||||||
| func (handler Driver) Get(ctx context.Context, path string) (response.RSCloser, error) { | func (handler Driver) Get(ctx context.Context, path string) (response.RSCloser, error) { | ||||||
| 	return nil, errors.New("未实现") | 	// 给文件名加上随机参数以强制拉取 | ||||||
|  | 	path = fmt.Sprintf("%s?v=%d", path, time.Now().UnixNano()) | ||||||
|  |  | ||||||
|  | 	// 获取文件源地址 | ||||||
|  | 	downloadURL, err := handler.Source( | ||||||
|  | 		ctx, | ||||||
|  | 		path, | ||||||
|  | 		url.URL{}, | ||||||
|  | 		int64(model.GetIntSetting("preview_timeout", 60)), | ||||||
|  | 		false, | ||||||
|  | 		0, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 获取文件数据流 | ||||||
|  | 	client := request.HTTPClient{} | ||||||
|  | 	resp, err := client.Request( | ||||||
|  | 		"GET", | ||||||
|  | 		downloadURL, | ||||||
|  | 		nil, | ||||||
|  | 		request.WithContext(ctx), | ||||||
|  | 		request.WithHeader( | ||||||
|  | 			http.Header{"Cache-Control": {"no-cache", "no-store", "must-revalidate"}}, | ||||||
|  | 		), | ||||||
|  | 	).CheckHTTPResponse(200).GetRSCloser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp.SetFirstFakeChunk() | ||||||
|  |  | ||||||
|  | 	// 尝试自主获取文件大小 | ||||||
|  | 	if file, ok := ctx.Value(fsctx.FileModelCtx).(model.File); ok { | ||||||
|  | 		resp.SetContentLength(int64(file.Size)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return resp, nil | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Put 将文件流保存到指定目录 | // Put 将文件流保存到指定目录 | ||||||
| func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, size uint64) error { | ||||||
| 	return errors.New("未实现") | 	defer file.Close() | ||||||
|  |  | ||||||
|  | 	up := upyun.NewUpYun(&upyun.UpYunConfig{ | ||||||
|  | 		Bucket:   handler.Policy.BucketName, | ||||||
|  | 		Operator: handler.Policy.AccessKey, | ||||||
|  | 		Password: handler.Policy.SecretKey, | ||||||
|  | 	}) | ||||||
|  | 	err := up.Put(&upyun.PutObjectConfig{ | ||||||
|  | 		Path:   dst, | ||||||
|  | 		Reader: file, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| // Delete 删除一个或多个文件, | // Delete 删除一个或多个文件, | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import ( | |||||||
| 	"github.com/HFO4/cloudreve/pkg/auth" | 	"github.com/HFO4/cloudreve/pkg/auth" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/conf" | 	"github.com/HFO4/cloudreve/pkg/conf" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/local" | 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/local" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/onedrive" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/oss" | 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/oss" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/qiniu" | 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/qiniu" | ||||||
| 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/remote" | 	"github.com/HFO4/cloudreve/pkg/filesystem/driver/remote" | ||||||
| @ -181,6 +182,13 @@ func (fs *FileSystem) DispatchHandler() error { | |||||||
| 			Policy: currentPolicy, | 			Policy: currentPolicy, | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
|  | 	case "onedrive": | ||||||
|  | 		client, err := onedrive.NewClient(currentPolicy) | ||||||
|  | 		fs.Handler = onedrive.Driver{ | ||||||
|  | 			Policy: currentPolicy, | ||||||
|  | 			Client: client, | ||||||
|  | 		} | ||||||
|  | 		return err | ||||||
| 	default: | 	default: | ||||||
| 		return ErrUnknownPolicyType | 		return ErrUnknownPolicyType | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4