diff --git a/middleware/auth.go b/middleware/auth.go index 67c898e..3a7d763 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -116,6 +116,11 @@ func WebDAVAuth() gin.HandlerFunc { return } + // 用户组已启用WebDAV代理? + if !expectedUser.Group.OptionsSerialized.WebDAVProxy { + webdav.UseProxy = false + } + c.Set("user", &expectedUser) c.Set("webdav", webdav) c.Next() diff --git a/models/group.go b/models/group.go index 7b8930c..0abf21d 100644 --- a/models/group.go +++ b/models/group.go @@ -35,6 +35,7 @@ type GroupOption struct { RedirectedSource bool `json:"redirected_source,omitempty"` Aria2BatchSize int `json:"aria2_batch,omitempty"` AdvanceDelete bool `json:"advance_delete,omitempty"` + WebDAVProxy bool `json:"webdav_proxy,omitempty"` } // GetGroupByID 用ID获取用户组 diff --git a/models/webdav.go b/models/webdav.go index fef4495..0799aee 100644 --- a/models/webdav.go +++ b/models/webdav.go @@ -12,6 +12,7 @@ type Webdav struct { UserID uint `gorm:"unique_index:password_only_on"` // 用户ID Root string `gorm:"type:text"` // 根目录 Readonly bool `gorm:"type:bool"` // 是否只读 + UseProxy bool `gorm:"type:bool"` // 是否进行反代 } // Create 创建账户 @@ -41,7 +42,7 @@ func DeleteWebDAVAccountByID(id, uid uint) { DB.Where("user_id = ? and id = ?", uid, id).Delete(&Webdav{}) } -// UpdateWebDAVAccountReadonlyByID 根据账户ID和UID更新账户的只读性 -func UpdateWebDAVAccountReadonlyByID(id, uid uint, readonly bool) { - DB.Model(&Webdav{Model: gorm.Model{ID: id}, UserID: uid}).UpdateColumn("readonly", readonly) +// UpdateWebDAVAccountByID 根据账户ID和UID更新账户 +func UpdateWebDAVAccountByID(id, uid uint, updates map[string]interface{}) { + DB.Model(&Webdav{Model: gorm.Model{ID: id}, UserID: uid}).Updates(updates) } diff --git a/pkg/filesystem/fsctx/context.go b/pkg/filesystem/fsctx/context.go index 3c6445d..1b7b3be 100644 --- a/pkg/filesystem/fsctx/context.go +++ b/pkg/filesystem/fsctx/context.go @@ -37,4 +37,8 @@ const ( SlaveSrcPath // Webdav目标名称 WebdavDstName + // WebDAVCtx WebDAV + WebDAVCtx + // WebDAV反代Url + WebDAVProxyUrlCtx ) diff --git a/pkg/serializer/user.go b/pkg/serializer/user.go index 45b703e..142f424 100644 --- a/pkg/serializer/user.go +++ b/pkg/serializer/user.go @@ -42,6 +42,7 @@ type group struct { WebDAVEnabled bool `json:"webdav"` SourceBatchSize int `json:"sourceBatch"` AdvanceDelete bool `json:"advanceDelete"` + AllowWebDAVProxy bool `json:"allowWebDAVProxy"` } type tag struct { @@ -100,6 +101,7 @@ func BuildUser(user model.User) User { ShareDownload: user.Group.OptionsSerialized.ShareDownload, CompressEnabled: user.Group.OptionsSerialized.ArchiveTask, WebDAVEnabled: user.Group.WebDAVEnabled, + AllowWebDAVProxy: user.Group.OptionsSerialized.WebDAVProxy, SourceBatchSize: user.Group.OptionsSerialized.SourceBatchSize, AdvanceDelete: user.Group.OptionsSerialized.AdvanceDelete, }, diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 3c72f11..5ab9906 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "net/http" + "net/http/httputil" "net/url" "path" "strconv" @@ -241,6 +242,23 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *file return 0, nil } +var proxy = &httputil.ReverseProxy{ + Director: func(request *http.Request) { + if target, ok := request.Context().Value(fsctx.WebDAVProxyUrlCtx).(*url.URL); ok { + request.URL.Scheme = target.Scheme + request.URL.Host = target.Host + request.URL.Path = target.Path + request.URL.RawPath = target.RawPath + request.URL.RawQuery = target.RawQuery + request.Host = target.Host + request.Header.Del("Authorization") + } + }, + ErrorHandler: func(writer http.ResponseWriter, request *http.Request, err error) { + writer.WriteHeader(http.StatusInternalServerError) + }, +} + // OK func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *filesystem.FileSystem) (status int, err error) { defer fs.Recycle() @@ -279,7 +297,23 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs * return 0, nil } - http.Redirect(w, r, rs.URL, 301) + if application, ok := r.Context().Value(fsctx.WebDAVCtx).(*model.Webdav); ok && application.UseProxy { + target, err := url.Parse(rs.URL) + if err != nil { + return http.StatusInternalServerError, err + } + + r = r.Clone(context.WithValue(r.Context(), fsctx.WebDAVProxyUrlCtx, target)) + // 忽略反向代理在传输错误时报错 + defer func() { + if err := recover(); err != nil && err != http.ErrAbortHandler { + panic(err) + } + }() + proxy.ServeHTTP(w, r) + } else { + http.Redirect(w, r, rs.URL, 301) + } return 0, nil } diff --git a/routers/controllers/webdav.go b/routers/controllers/webdav.go index 5f3f084..0453ada 100644 --- a/routers/controllers/webdav.go +++ b/routers/controllers/webdav.go @@ -1,8 +1,10 @@ package controllers import ( + "context" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" + "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/cloudreve/Cloudreve/v3/pkg/webdav" "github.com/cloudreve/Cloudreve/v3/service/setting" @@ -49,6 +51,9 @@ func ServeWebDAV(c *gin.Context) { return } } + + // 更新Context + c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), fsctx.WebDAVCtx, application)) } handler.ServeHTTP(c.Writer, c.Request, fs) @@ -76,9 +81,9 @@ func DeleteWebDAVAccounts(c *gin.Context) { } } -// UpdateWebDAVAccountsReadonly 更改WebDAV账户只读性 -func UpdateWebDAVAccountsReadonly(c *gin.Context) { - var service setting.WebDAVAccountUpdateReadonlyService +// UpdateWebDAVAccounts 更改WebDAV账户只读性和是否使用代理服务 +func UpdateWebDAVAccounts(c *gin.Context) { + var service setting.WebDAVAccountUpdateService if err := c.ShouldBindJSON(&service); err == nil { res := service.Update(c, CurrentUser(c)) c.JSON(200, res) diff --git a/routers/router.go b/routers/router.go index 4e16b77..aa8e903 100644 --- a/routers/router.go +++ b/routers/router.go @@ -721,8 +721,8 @@ func InitMasterRouter() *gin.Engine { webdav.POST("accounts", controllers.CreateWebDAVAccounts) // 删除账号 webdav.DELETE("accounts/:id", controllers.DeleteWebDAVAccounts) - // 更新账号可读性 - webdav.PATCH("accounts", controllers.UpdateWebDAVAccountsReadonly) + // 更新账号可读性和是否使用代理服务 + webdav.PATCH("accounts", controllers.UpdateWebDAVAccounts) } } diff --git a/service/setting/webdav.go b/service/setting/webdav.go index 4f252ad..1f81751 100644 --- a/service/setting/webdav.go +++ b/service/setting/webdav.go @@ -22,10 +22,11 @@ type WebDAVAccountCreateService struct { Name string `json:"name" binding:"required,min=1,max=255"` } -// WebDAVAccountUpdateReadonlyService WebDAV 修改只读性服务 -type WebDAVAccountUpdateReadonlyService struct { - ID uint `json:"id" binding:"required,min=1"` - Readonly bool `json:"readonly"` +// WebDAVAccountUpdateService WebDAV 修改只读性和是否使用代理服务 +type WebDAVAccountUpdateService struct { + ID uint `json:"id" binding:"required,min=1"` + Readonly *bool `json:"readonly" binding:"required_without=UseProxy"` + UseProxy *bool `json:"use_proxy" binding:"required_without=Readonly"` } // WebDAVMountCreateService WebDAV 挂载创建服务 @@ -62,12 +63,17 @@ func (service *WebDAVAccountService) Delete(c *gin.Context, user *model.User) se return serializer.Response{} } -// Update 修改WebDAV账户的只读性 -func (service *WebDAVAccountUpdateReadonlyService) Update(c *gin.Context, user *model.User) serializer.Response { - model.UpdateWebDAVAccountReadonlyByID(service.ID, user.ID, service.Readonly) - return serializer.Response{Data: map[string]bool{ - "readonly": service.Readonly, - }} +// Update 修改WebDAV账户只读性和是否使用代理服务 +func (service *WebDAVAccountUpdateService) Update(c *gin.Context, user *model.User) serializer.Response { + var updates = make(map[string]interface{}) + if service.Readonly != nil { + updates["readonly"] = *service.Readonly + } + if service.UseProxy != nil { + updates["use_proxy"] = *service.UseProxy + } + model.UpdateWebDAVAccountByID(service.ID, user.ID, updates) + return serializer.Response{Data: updates} } // Accounts 列出WebDAV账号