mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-11-26 03:45:06 +08:00
feat(onedrive): support frontend direct upload (#1532)
* OneDrive添加直连上传 * refactor * fix: duplicate root path join --------- Co-authored-by: KirCute <951206789@qq.com>
This commit is contained in:
@@ -236,4 +236,19 @@ func (d *Onedrive) GetDetails(ctx context.Context) (*model.StorageDetails, error
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Onedrive) GetDirectUploadTools() []string {
|
||||||
|
if !d.EnableDirectUpload {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{"HttpDirect"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDirectUploadInfo returns the direct upload info for OneDrive
|
||||||
|
func (d *Onedrive) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj, fileName string, _ int64) (any, error) {
|
||||||
|
if !d.EnableDirectUpload {
|
||||||
|
return nil, errs.NotImplement
|
||||||
|
}
|
||||||
|
return d.getDirectUploadInfo(ctx, path.Join(dstDir.GetPath(), fileName))
|
||||||
|
}
|
||||||
|
|
||||||
var _ driver.Driver = (*Onedrive)(nil)
|
var _ driver.Driver = (*Onedrive)(nil)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Addition struct {
|
|||||||
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
||||||
CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"`
|
CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"`
|
||||||
DisableDiskUsage bool `json:"disable_disk_usage" default:"false"`
|
DisableDiskUsage bool `json:"disable_disk_usage" default:"false"`
|
||||||
|
EnableDirectUpload bool `json:"enable_direct_upload" default:"false" help:"Enable direct upload from client to OneDrive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ func (d *Onedrive) _refreshToken() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, resp interface{}, noRetry ...bool) ([]byte, error) {
|
||||||
if d.ref != nil {
|
if d.ref != nil {
|
||||||
return d.ref.Request(url, method, callback, resp)
|
return d.ref.Request(url, method, callback, resp)
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ func (d *Onedrive) Request(url string, method string, callback base.ReqCallback,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.Error.Code != "" {
|
if e.Error.Code != "" {
|
||||||
if e.Error.Code == "InvalidAuthenticationToken" {
|
if e.Error.Code == "InvalidAuthenticationToken" && !utils.IsBool(noRetry...) {
|
||||||
err = d.refreshToken()
|
err = d.refreshToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -310,9 +310,36 @@ func (d *Onedrive) getDrive(ctx context.Context) (*DriveResp, error) {
|
|||||||
var resp DriveResp
|
var resp DriveResp
|
||||||
_, err := d.Request(api, http.MethodGet, func(req *resty.Request) {
|
_, err := d.Request(api, http.MethodGet, func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
req.SetContext(ctx)
|
||||||
}, &resp)
|
}, &resp, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Onedrive) getDirectUploadInfo(ctx context.Context, path string) (*model.HttpDirectUploadInfo, error) {
|
||||||
|
// Create upload session
|
||||||
|
url := d.GetMetaUrl(false, path) + "/createUploadSession"
|
||||||
|
metadata := map[string]any{
|
||||||
|
"item": map[string]any{
|
||||||
|
"@microsoft.graph.conflictBehavior": "rename",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := d.Request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(metadata).SetContext(ctx)
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadUrl := jsoniter.Get(res, "uploadUrl").ToString()
|
||||||
|
if uploadUrl == "" {
|
||||||
|
return nil, fmt.Errorf("failed to get upload URL from response")
|
||||||
|
}
|
||||||
|
return &model.HttpDirectUploadInfo{
|
||||||
|
UploadURL: uploadUrl,
|
||||||
|
ChunkSize: d.ChunkSize * 1024 * 1024, // Convert MB to bytes
|
||||||
|
Method: "PUT",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -222,4 +222,18 @@ func (d *OnedriveAPP) GetDetails(ctx context.Context) (*model.StorageDetails, er
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *OnedriveAPP) GetDirectUploadTools() []string {
|
||||||
|
if !d.EnableDirectUpload {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{"HttpDirect"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OnedriveAPP) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj, fileName string, _ int64) (any, error) {
|
||||||
|
if !d.EnableDirectUpload {
|
||||||
|
return nil, errs.NotImplement
|
||||||
|
}
|
||||||
|
return d.getDirectUploadInfo(ctx, path.Join(dstDir.GetPath(), fileName))
|
||||||
|
}
|
||||||
|
|
||||||
var _ driver.Driver = (*OnedriveAPP)(nil)
|
var _ driver.Driver = (*OnedriveAPP)(nil)
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ import (
|
|||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
driver.RootPath
|
driver.RootPath
|
||||||
Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"`
|
Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"`
|
||||||
ClientID string `json:"client_id" required:"true"`
|
ClientID string `json:"client_id" required:"true"`
|
||||||
ClientSecret string `json:"client_secret" required:"true"`
|
ClientSecret string `json:"client_secret" required:"true"`
|
||||||
TenantID string `json:"tenant_id"`
|
TenantID string `json:"tenant_id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
||||||
CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"`
|
CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"`
|
||||||
DisableDiskUsage bool `json:"disable_disk_usage" default:"false"`
|
DisableDiskUsage bool `json:"disable_disk_usage" default:"false"`
|
||||||
|
EnableDirectUpload bool `json:"enable_direct_upload" default:"false" help:"Enable direct upload from client to OneDrive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (d *OnedriveAPP) _accessToken() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *OnedriveAPP) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
func (d *OnedriveAPP) Request(url string, method string, callback base.ReqCallback, resp interface{}, noRetry ...bool) ([]byte, error) {
|
||||||
req := base.RestyClient.R()
|
req := base.RestyClient.R()
|
||||||
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
@@ -104,7 +104,7 @@ func (d *OnedriveAPP) Request(url string, method string, callback base.ReqCallba
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.Error.Code != "" {
|
if e.Error.Code != "" {
|
||||||
if e.Error.Code == "InvalidAuthenticationToken" {
|
if e.Error.Code == "InvalidAuthenticationToken" && !utils.IsBool(noRetry...) {
|
||||||
err = d.accessToken()
|
err = d.accessToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -216,9 +216,36 @@ func (d *OnedriveAPP) getDrive(ctx context.Context) (*DriveResp, error) {
|
|||||||
var resp DriveResp
|
var resp DriveResp
|
||||||
_, err := d.Request(api, http.MethodGet, func(req *resty.Request) {
|
_, err := d.Request(api, http.MethodGet, func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
req.SetContext(ctx)
|
||||||
}, &resp)
|
}, &resp, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *OnedriveAPP) getDirectUploadInfo(ctx context.Context, path string) (*model.HttpDirectUploadInfo, error) {
|
||||||
|
// Create upload session
|
||||||
|
url := d.GetMetaUrl(false, path) + "/createUploadSession"
|
||||||
|
metadata := map[string]any{
|
||||||
|
"item": map[string]any{
|
||||||
|
"@microsoft.graph.conflictBehavior": "rename",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := d.Request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(metadata).SetContext(ctx)
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadUrl := jsoniter.Get(res, "uploadUrl").ToString()
|
||||||
|
if uploadUrl == "" {
|
||||||
|
return nil, fmt.Errorf("failed to get upload URL from response")
|
||||||
|
}
|
||||||
|
return &model.HttpDirectUploadInfo{
|
||||||
|
UploadURL: uploadUrl,
|
||||||
|
ChunkSize: d.ChunkSize * 1024 * 1024, // Convert MB to bytes
|
||||||
|
Method: "PUT",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -218,3 +218,12 @@ type LinkCacheModeResolver interface {
|
|||||||
// ResolveLinkCacheMode returns the LinkCacheMode for the given path.
|
// ResolveLinkCacheMode returns the LinkCacheMode for the given path.
|
||||||
ResolveLinkCacheMode(path string) LinkCacheMode
|
ResolveLinkCacheMode(path string) LinkCacheMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DirectUploader interface {
|
||||||
|
// GetDirectUploadTools returns available frontend-direct upload tools
|
||||||
|
GetDirectUploadTools() []string
|
||||||
|
// GetDirectUploadInfo returns the information needed for direct upload from client to storage
|
||||||
|
// actualPath is the path relative to the storage root (after removing mount path prefix)
|
||||||
|
// return errs.NotImplement if the driver does not support the given direct upload tool
|
||||||
|
GetDirectUploadInfo(ctx context.Context, tool string, dstDir model.Obj, fileName string, fileSize int64) (any, error)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ObjectNotFound = errors.New("object not found")
|
ObjectNotFound = errors.New("object not found")
|
||||||
NotFolder = errors.New("not a folder")
|
ObjectAlreadyExists = errors.New("object already exists")
|
||||||
NotFile = errors.New("not a file")
|
NotFolder = errors.New("not a folder")
|
||||||
|
NotFile = errors.New("not a file")
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsObjectNotFound(err error) bool {
|
func IsObjectNotFound(err error) bool {
|
||||||
|
|||||||
@@ -167,6 +167,14 @@ func GetStorage(path string, args *GetStoragesArgs) (driver.Driver, error) {
|
|||||||
return storageDriver, nil
|
return storageDriver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetStorageAndActualPath(path string) (driver.Driver, string, error) {
|
||||||
|
return op.GetStorageAndActualPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetByActualPath(ctx context.Context, storage driver.Driver, actualPath string) (model.Obj, error) {
|
||||||
|
return op.Get(ctx, storage, actualPath)
|
||||||
|
}
|
||||||
|
|
||||||
func Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) {
|
func Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) {
|
||||||
res, err := other(ctx, args)
|
res, err := other(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -190,3 +198,11 @@ func PutURL(ctx context.Context, path, dstName, urlStr string) error {
|
|||||||
}
|
}
|
||||||
return op.PutURL(ctx, storage, dstDirActualPath, dstName, urlStr)
|
return op.PutURL(ctx, storage, dstDirActualPath, dstName, urlStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDirectUploadInfo(ctx context.Context, tool, path, dstName string, fileSize int64) (any, error) {
|
||||||
|
info, err := getDirectUploadInfo(ctx, tool, path, dstName, fileSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed get %s direct upload info for %s(%d bytes): %+v", path, dstName, fileSize, err)
|
||||||
|
}
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,3 +105,11 @@ func putDirectly(ctx context.Context, dstDirPath string, file model.FileStreamer
|
|||||||
}
|
}
|
||||||
return op.Put(ctx, storage, dstDirActualPath, file, nil, lazyCache...)
|
return op.Put(ctx, storage, dstDirActualPath, file, nil, lazyCache...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDirectUploadInfo(ctx context.Context, tool, dstDirPath, dstName string, fileSize int64) (any, error) {
|
||||||
|
storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, "failed get storage")
|
||||||
|
}
|
||||||
|
return op.GetDirectUploadInfo(ctx, tool, storage, dstDirActualPath, dstName, fileSize)
|
||||||
|
}
|
||||||
|
|||||||
8
internal/model/direct_upload.go
Normal file
8
internal/model/direct_upload.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type HttpDirectUploadInfo struct {
|
||||||
|
UploadURL string `json:"upload_url"` // The URL to upload the file
|
||||||
|
ChunkSize int64 `json:"chunk_size"` // The chunk size for uploading, 0 means no chunking required
|
||||||
|
Headers map[string]string `json:"headers,omitempty"` // Optional headers to include in the upload request
|
||||||
|
Method string `json:"method,omitempty"` // HTTP method, default is PUT
|
||||||
|
}
|
||||||
@@ -568,15 +568,15 @@ func PutURL(ctx context.Context, storage driver.Driver, dstDirPath, dstName, url
|
|||||||
dstPath := stdpath.Join(dstDirPath, dstName)
|
dstPath := stdpath.Join(dstDirPath, dstName)
|
||||||
_, err := GetUnwrap(ctx, storage, dstPath)
|
_, err := GetUnwrap(ctx, storage, dstPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return errors.New("obj already exists")
|
return errors.WithStack(errs.ObjectAlreadyExists)
|
||||||
}
|
}
|
||||||
err = MakeDir(ctx, storage, dstDirPath)
|
err = MakeDir(ctx, storage, dstDirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed to put url")
|
return errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath)
|
||||||
}
|
}
|
||||||
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed to put url")
|
return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)
|
||||||
}
|
}
|
||||||
switch s := storage.(type) {
|
switch s := storage.(type) {
|
||||||
case driver.PutURLResult:
|
case driver.PutURLResult:
|
||||||
@@ -599,8 +599,48 @@ func PutURL(ctx context.Context, storage driver.Driver, dstDirPath, dstName, url
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return errs.NotImplement
|
return errors.WithStack(errs.NotImplement)
|
||||||
}
|
}
|
||||||
log.Debugf("put url [%s](%s) done", dstName, url)
|
log.Debugf("put url [%s](%s) done", dstName, url)
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDirectUploadTools(storage driver.Driver) []string {
|
||||||
|
du, ok := storage.(driver.DirectUploader)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return du.GetDirectUploadTools()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDirectUploadInfo(ctx context.Context, tool string, storage driver.Driver, dstDirPath, dstName string, fileSize int64) (any, error) {
|
||||||
|
du, ok := storage.(driver.DirectUploader)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.WithStack(errs.NotImplement)
|
||||||
|
}
|
||||||
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
|
return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status)
|
||||||
|
}
|
||||||
|
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
||||||
|
dstPath := stdpath.Join(dstDirPath, dstName)
|
||||||
|
_, err := GetUnwrap(ctx, storage, dstPath)
|
||||||
|
if err == nil {
|
||||||
|
return nil, errors.WithStack(errs.ObjectAlreadyExists)
|
||||||
|
}
|
||||||
|
err = MakeDir(ctx, storage, dstDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath)
|
||||||
|
}
|
||||||
|
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)
|
||||||
|
}
|
||||||
|
info, err := du.GetDirectUploadInfo(ctx, tool, dstDir, dstName, fileSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (a *AferoAdapter) GetHandle(name string, flags int, offset int64) (ftpserve
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if (flags & os.O_EXCL) != 0 {
|
if (flags & os.O_EXCL) != 0 {
|
||||||
return nil, errors.New("file already exists")
|
return nil, errs.ObjectAlreadyExists
|
||||||
}
|
}
|
||||||
if (flags & os.O_WRONLY) != 0 {
|
if (flags & os.O_WRONLY) != 0 {
|
||||||
return nil, errors.New("cannot write to uploading file")
|
return nil, errors.New("cannot write to uploading file")
|
||||||
@@ -122,7 +122,7 @@ func (a *AferoAdapter) GetHandle(name string, flags int, offset int64) (ftpserve
|
|||||||
return nil, errs.ObjectNotFound
|
return nil, errs.ObjectNotFound
|
||||||
}
|
}
|
||||||
if (flags&os.O_EXCL) != 0 && exists {
|
if (flags&os.O_EXCL) != 0 && exists {
|
||||||
return nil, errors.New("file already exists")
|
return nil, errs.ObjectAlreadyExists
|
||||||
}
|
}
|
||||||
if (flags & os.O_WRONLY) != 0 {
|
if (flags & os.O_WRONLY) != 0 {
|
||||||
if offset != 0 {
|
if offset != 0 {
|
||||||
|
|||||||
54
server/handles/direct_upload.go
Normal file
54
server/handles/direct_upload.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package handles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FsGetDirectUploadInfoReq struct {
|
||||||
|
Path string `json:"path" form:"path"`
|
||||||
|
FileName string `json:"file_name" form:"file_name"`
|
||||||
|
FileSize int64 `json:"file_size" form:"file_size"`
|
||||||
|
Tool string `json:"tool" form:"tool"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FsGetDirectUploadInfo returns the direct upload info if supported by the driver
|
||||||
|
// If the driver does not support direct upload, returns null for upload_info
|
||||||
|
func FsGetDirectUploadInfo(c *gin.Context) {
|
||||||
|
var req FsGetDirectUploadInfoReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Decode path
|
||||||
|
path, err := url.PathUnescape(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get user and join path
|
||||||
|
user := c.Request.Context().Value(conf.UserKey).(*model.User)
|
||||||
|
path, err = user.JoinPath(path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
overwrite := c.GetHeader("Overwrite") != "false"
|
||||||
|
if !overwrite {
|
||||||
|
if res, _ := fs.Get(c.Request.Context(), path, &fs.GetArgs{NoLog: true}); res != nil {
|
||||||
|
common.ErrorStrResp(c, "file exists", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
directUploadInfo, err := fs.GetDirectUploadInfo(c, req.Tool, path, req.FileName, req.FileSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c, directUploadInfo)
|
||||||
|
}
|
||||||
@@ -49,12 +49,13 @@ type ObjResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FsListResp struct {
|
type FsListResp struct {
|
||||||
Content []ObjResp `json:"content"`
|
Content []ObjResp `json:"content"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
Readme string `json:"readme"`
|
Readme string `json:"readme"`
|
||||||
Header string `json:"header"`
|
Header string `json:"header"`
|
||||||
Write bool `json:"write"`
|
Write bool `json:"write"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
|
DirectUploadTools []string `json:"direct_upload_tools,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FsListSplit(c *gin.Context) {
|
func FsListSplit(c *gin.Context) {
|
||||||
@@ -109,17 +110,20 @@ func FsList(c *gin.Context, req *ListReq, user *model.User) {
|
|||||||
}
|
}
|
||||||
total, objs := pagination(objs, &req.PageReq)
|
total, objs := pagination(objs, &req.PageReq)
|
||||||
provider := "unknown"
|
provider := "unknown"
|
||||||
storage, err := fs.GetStorage(reqPath, &fs.GetStoragesArgs{})
|
var directUploadTools []string
|
||||||
if err == nil {
|
if user.CanWrite() {
|
||||||
provider = storage.GetStorage().Driver
|
if storage, err := fs.GetStorage(reqPath, &fs.GetStoragesArgs{}); err == nil {
|
||||||
|
directUploadTools = op.GetDirectUploadTools(storage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
common.SuccessResp(c, FsListResp{
|
common.SuccessResp(c, FsListResp{
|
||||||
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath)),
|
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath)),
|
||||||
Total: int64(total),
|
Total: int64(total),
|
||||||
Readme: getReadme(meta, reqPath),
|
Readme: getReadme(meta, reqPath),
|
||||||
Header: getHeader(meta, reqPath),
|
Header: getHeader(meta, reqPath),
|
||||||
Write: user.CanWrite() || common.CanWrite(meta, reqPath),
|
Write: user.CanWrite() || common.CanWrite(meta, reqPath),
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
|
DirectUploadTools: directUploadTools,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,8 @@ func _fs(g *gin.RouterGroup) {
|
|||||||
// g.POST("/add_transmission", handles.SetTransmission)
|
// g.POST("/add_transmission", handles.SetTransmission)
|
||||||
g.POST("/add_offline_download", handles.AddOfflineDownload)
|
g.POST("/add_offline_download", handles.AddOfflineDownload)
|
||||||
g.POST("/archive/decompress", handles.FsArchiveDecompress)
|
g.POST("/archive/decompress", handles.FsArchiveDecompress)
|
||||||
|
// Direct upload (client-side upload to storage)
|
||||||
|
g.POST("/get_direct_upload_info", middlewares.FsUp, handles.FsGetDirectUploadInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _task(g *gin.RouterGroup) {
|
func _task(g *gin.RouterGroup) {
|
||||||
|
|||||||
Reference in New Issue
Block a user