feat(webdav): support setting download proxy

pull/1798/head
Aaron Liu 1 year ago
parent ad6c6bcd93
commit a1747073df

@ -116,6 +116,11 @@ func WebDAVAuth() gin.HandlerFunc {
return return
} }
// 用户组已启用WebDAV代理
if !expectedUser.Group.OptionsSerialized.WebDAVProxy {
webdav.UseProxy = false
}
c.Set("user", &expectedUser) c.Set("user", &expectedUser)
c.Set("webdav", webdav) c.Set("webdav", webdav)
c.Next() c.Next()

@ -35,6 +35,7 @@ type GroupOption struct {
RedirectedSource bool `json:"redirected_source,omitempty"` RedirectedSource bool `json:"redirected_source,omitempty"`
Aria2BatchSize int `json:"aria2_batch,omitempty"` Aria2BatchSize int `json:"aria2_batch,omitempty"`
AdvanceDelete bool `json:"advance_delete,omitempty"` AdvanceDelete bool `json:"advance_delete,omitempty"`
WebDAVProxy bool `json:"webdav_proxy,omitempty"`
} }
// GetGroupByID 用ID获取用户组 // GetGroupByID 用ID获取用户组

@ -12,6 +12,7 @@ type Webdav struct {
UserID uint `gorm:"unique_index:password_only_on"` // 用户ID UserID uint `gorm:"unique_index:password_only_on"` // 用户ID
Root string `gorm:"type:text"` // 根目录 Root string `gorm:"type:text"` // 根目录
Readonly bool `gorm:"type:bool"` // 是否只读 Readonly bool `gorm:"type:bool"` // 是否只读
UseProxy bool `gorm:"type:bool"` // 是否进行反代
} }
// Create 创建账户 // Create 创建账户
@ -41,7 +42,7 @@ func DeleteWebDAVAccountByID(id, uid uint) {
DB.Where("user_id = ? and id = ?", uid, id).Delete(&Webdav{}) DB.Where("user_id = ? and id = ?", uid, id).Delete(&Webdav{})
} }
// UpdateWebDAVAccountReadonlyByID 根据账户ID和UID更新账户的只读性 // UpdateWebDAVAccountByID 根据账户ID和UID更新账户
func UpdateWebDAVAccountReadonlyByID(id, uid uint, readonly bool) { func UpdateWebDAVAccountByID(id, uid uint, updates map[string]interface{}) {
DB.Model(&Webdav{Model: gorm.Model{ID: id}, UserID: uid}).UpdateColumn("readonly", readonly) DB.Model(&Webdav{Model: gorm.Model{ID: id}, UserID: uid}).Updates(updates)
} }

@ -37,4 +37,8 @@ const (
SlaveSrcPath SlaveSrcPath
// Webdav目标名称 // Webdav目标名称
WebdavDstName WebdavDstName
// WebDAVCtx WebDAV
WebDAVCtx
// WebDAV反代Url
WebDAVProxyUrlCtx
) )

@ -42,6 +42,7 @@ type group struct {
WebDAVEnabled bool `json:"webdav"` WebDAVEnabled bool `json:"webdav"`
SourceBatchSize int `json:"sourceBatch"` SourceBatchSize int `json:"sourceBatch"`
AdvanceDelete bool `json:"advanceDelete"` AdvanceDelete bool `json:"advanceDelete"`
AllowWebDAVProxy bool `json:"allowWebDAVProxy"`
} }
type tag struct { type tag struct {
@ -100,6 +101,7 @@ func BuildUser(user model.User) User {
ShareDownload: user.Group.OptionsSerialized.ShareDownload, ShareDownload: user.Group.OptionsSerialized.ShareDownload,
CompressEnabled: user.Group.OptionsSerialized.ArchiveTask, CompressEnabled: user.Group.OptionsSerialized.ArchiveTask,
WebDAVEnabled: user.Group.WebDAVEnabled, WebDAVEnabled: user.Group.WebDAVEnabled,
AllowWebDAVProxy: user.Group.OptionsSerialized.WebDAVProxy,
SourceBatchSize: user.Group.OptionsSerialized.SourceBatchSize, SourceBatchSize: user.Group.OptionsSerialized.SourceBatchSize,
AdvanceDelete: user.Group.OptionsSerialized.AdvanceDelete, AdvanceDelete: user.Group.OptionsSerialized.AdvanceDelete,
}, },

@ -10,6 +10,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"path" "path"
"strconv" "strconv"
@ -241,6 +242,23 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *file
return 0, nil 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 // OK
func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *filesystem.FileSystem) (status int, err error) { func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *filesystem.FileSystem) (status int, err error) {
defer fs.Recycle() defer fs.Recycle()
@ -279,7 +297,23 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
return 0, nil return 0, nil
} }
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) http.Redirect(w, r, rs.URL, 301)
}
return 0, nil return 0, nil
} }

@ -1,8 +1,10 @@
package controllers package controllers
import ( import (
"context"
model "github.com/cloudreve/Cloudreve/v3/models" model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "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/util"
"github.com/cloudreve/Cloudreve/v3/pkg/webdav" "github.com/cloudreve/Cloudreve/v3/pkg/webdav"
"github.com/cloudreve/Cloudreve/v3/service/setting" "github.com/cloudreve/Cloudreve/v3/service/setting"
@ -49,6 +51,9 @@ func ServeWebDAV(c *gin.Context) {
return return
} }
} }
// 更新Context
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), fsctx.WebDAVCtx, application))
} }
handler.ServeHTTP(c.Writer, c.Request, fs) handler.ServeHTTP(c.Writer, c.Request, fs)
@ -76,9 +81,9 @@ func DeleteWebDAVAccounts(c *gin.Context) {
} }
} }
// UpdateWebDAVAccountsReadonly 更改WebDAV账户只读性 // UpdateWebDAVAccounts 更改WebDAV账户只读性和是否使用代理服务
func UpdateWebDAVAccountsReadonly(c *gin.Context) { func UpdateWebDAVAccounts(c *gin.Context) {
var service setting.WebDAVAccountUpdateReadonlyService var service setting.WebDAVAccountUpdateService
if err := c.ShouldBindJSON(&service); err == nil { if err := c.ShouldBindJSON(&service); err == nil {
res := service.Update(c, CurrentUser(c)) res := service.Update(c, CurrentUser(c))
c.JSON(200, res) c.JSON(200, res)

@ -721,8 +721,8 @@ func InitMasterRouter() *gin.Engine {
webdav.POST("accounts", controllers.CreateWebDAVAccounts) webdav.POST("accounts", controllers.CreateWebDAVAccounts)
// 删除账号 // 删除账号
webdav.DELETE("accounts/:id", controllers.DeleteWebDAVAccounts) webdav.DELETE("accounts/:id", controllers.DeleteWebDAVAccounts)
// 更新账号可读性 // 更新账号可读性和是否使用代理服务
webdav.PATCH("accounts", controllers.UpdateWebDAVAccountsReadonly) webdav.PATCH("accounts", controllers.UpdateWebDAVAccounts)
} }
} }

@ -22,10 +22,11 @@ type WebDAVAccountCreateService struct {
Name string `json:"name" binding:"required,min=1,max=255"` Name string `json:"name" binding:"required,min=1,max=255"`
} }
// WebDAVAccountUpdateReadonlyService WebDAV 修改只读性服务 // WebDAVAccountUpdateService WebDAV 修改只读性和是否使用代理服务
type WebDAVAccountUpdateReadonlyService struct { type WebDAVAccountUpdateService struct {
ID uint `json:"id" binding:"required,min=1"` ID uint `json:"id" binding:"required,min=1"`
Readonly bool `json:"readonly"` Readonly *bool `json:"readonly" binding:"required_without=UseProxy"`
UseProxy *bool `json:"use_proxy" binding:"required_without=Readonly"`
} }
// WebDAVMountCreateService WebDAV 挂载创建服务 // WebDAVMountCreateService WebDAV 挂载创建服务
@ -62,12 +63,17 @@ func (service *WebDAVAccountService) Delete(c *gin.Context, user *model.User) se
return serializer.Response{} return serializer.Response{}
} }
// Update 修改WebDAV账户的只读性 // Update 修改WebDAV账户只读性和是否使用代理服务
func (service *WebDAVAccountUpdateReadonlyService) Update(c *gin.Context, user *model.User) serializer.Response { func (service *WebDAVAccountUpdateService) Update(c *gin.Context, user *model.User) serializer.Response {
model.UpdateWebDAVAccountReadonlyByID(service.ID, user.ID, service.Readonly) var updates = make(map[string]interface{})
return serializer.Response{Data: map[string]bool{ if service.Readonly != nil {
"readonly": service.Readonly, 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账号 // Accounts 列出WebDAV账号

Loading…
Cancel
Save