diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index a22cba6..62f871c 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -22,7 +22,7 @@ type system struct { Listen string `validate:"required"` Debug bool SessionSecret string - HashIDSalt string `validate:"required"` + HashIDSalt string } // slave 作为slave存储端配置 @@ -72,8 +72,7 @@ type cors struct { var cfg *ini.File -const defaultConf = ` -[System] +const defaultConf = `[System] Mode = master Listen = :5212 SessionSecret = {SessionSecret} diff --git a/pkg/filesystem/driver/remote/handler.go b/pkg/filesystem/driver/remote/handler.go index 90af421..232e581 100644 --- a/pkg/filesystem/driver/remote/handler.go +++ b/pkg/filesystem/driver/remote/handler.go @@ -215,6 +215,15 @@ func (handler Driver) Source( return "", errors.New("无法解析远程服务端地址") } + // 是否启用了CDN + if handler.Policy.BaseURL != "" { + cdnURL, err := url.Parse(handler.Policy.BaseURL) + if err != nil { + return "", err + } + serverURL = cdnURL + } + var ( signedURI *url.URL controller = "/api/v3/slave/download" diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index 6b28d47..54dd809 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -67,8 +67,10 @@ func HookSlaveUploadValidate(ctx context.Context, fs *FileSystem) error { policy := ctx.Value(fsctx.UploadPolicyCtx).(serializer.UploadPolicy) // 验证单文件尺寸 - if file.GetSize() > policy.MaxSize { - return ErrFileSizeTooBig + if policy.MaxSize > 0 { + if file.GetSize() > policy.MaxSize { + return ErrFileSizeTooBig + } } // 验证文件名 diff --git a/pkg/filesystem/image.go b/pkg/filesystem/image.go index 49cd77d..6024e10 100644 --- a/pkg/filesystem/image.go +++ b/pkg/filesystem/image.go @@ -34,7 +34,7 @@ func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentR ctx = context.WithValue(ctx, fsctx.ThumbSizeCtx, [2]uint{w, h}) ctx = context.WithValue(ctx, fsctx.FileModelCtx, fs.FileTarget[0]) res, err := fs.Handler.Thumb(ctx, fs.FileTarget[0].SourceName) - if err == nil { + if err == nil && conf.SystemConfig.Mode == "master" { res.MaxAge = model.GetIntSetting("preview_timeout", 60) } diff --git a/routers/controllers/admin.go b/routers/controllers/admin.go index a0ecd5a..964d385 100644 --- a/routers/controllers/admin.go +++ b/routers/controllers/admin.go @@ -151,6 +151,17 @@ func AdminTestPath(c *gin.Context) { } } +// AdminTestSlave 测试从机可用性 +func AdminTestSlave(c *gin.Context) { + var service admin.SlaveTestService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Test() + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} + // AdminAddPolicy 新建存储策略 func AdminAddPolicy(c *gin.Context) { var service admin.AddPolicyService diff --git a/routers/controllers/site.go b/routers/controllers/site.go index 78f28ea..52d889f 100644 --- a/routers/controllers/site.go +++ b/routers/controllers/site.go @@ -2,6 +2,7 @@ package controllers import ( model "github.com/HFO4/cloudreve/models" + "github.com/HFO4/cloudreve/pkg/conf" "github.com/HFO4/cloudreve/pkg/serializer" "github.com/HFO4/cloudreve/pkg/util" "github.com/gin-gonic/gin" @@ -41,7 +42,7 @@ func SiteConfig(c *gin.Context) { func Ping(c *gin.Context) { c.JSON(200, serializer.Response{ Code: 0, - Msg: "Pong", + Data: conf.BackendVersion, }) } diff --git a/routers/controllers/slave.go b/routers/controllers/slave.go index 70d5b73..c5535ec 100644 --- a/routers/controllers/slave.go +++ b/routers/controllers/slave.go @@ -6,6 +6,7 @@ import ( "github.com/HFO4/cloudreve/pkg/filesystem/driver/local" "github.com/HFO4/cloudreve/pkg/filesystem/fsctx" "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/HFO4/cloudreve/service/admin" "github.com/HFO4/cloudreve/service/explorer" "github.com/gin-gonic/gin" "net/url" @@ -146,3 +147,14 @@ func SlaveDelete(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// SlavePing 从机测试 +func SlavePing(c *gin.Context) { + var service admin.SlavePingService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Test() + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/router.go b/routers/router.go index e149e56..3a29f09 100644 --- a/routers/router.go +++ b/routers/router.go @@ -35,6 +35,8 @@ func InitSlaveRouter() *gin.Engine { 路由 */ { + // Ping + v3.POST("ping", controllers.SlavePing) // 上传 v3.POST("upload", controllers.SlaveUpload) // 下载 @@ -328,6 +330,8 @@ func InitMasterRouter() *gin.Engine { policy.POST("list", controllers.AdminListPolicy) // 测试本地路径可用性 policy.POST("test/path", controllers.AdminTestPath) + // 测试从机通信 + policy.POST("test/slave", controllers.AdminTestSlave) // 创建存储策略 policy.POST("", controllers.AdminAddPolicy) } diff --git a/service/admin/policy.go b/service/admin/policy.go index c8e9ae2..cde8896 100644 --- a/service/admin/policy.go +++ b/service/admin/policy.go @@ -1,12 +1,19 @@ package admin import ( + "bytes" + "encoding/json" "fmt" model "github.com/HFO4/cloudreve/models" + "github.com/HFO4/cloudreve/pkg/auth" + "github.com/HFO4/cloudreve/pkg/conf" + "github.com/HFO4/cloudreve/pkg/request" "github.com/HFO4/cloudreve/pkg/serializer" "github.com/HFO4/cloudreve/pkg/util" + "net/url" "os" "path/filepath" + "time" ) // PathTestService 本地路径测试服务 @@ -14,11 +21,87 @@ type PathTestService struct { Path string `json:"path" binding:"required"` } +// SlaveTestService 从机测试服务 +type SlaveTestService struct { + Secret string `json:"secret" binding:"required"` + Server string `json:"server" binding:"required"` +} + +// SlavePingService 从机相应ping +type SlavePingService struct { + Callback string `json:"callback" binding:"required"` +} + // AddPolicyService 存储策略添加服务 type AddPolicyService struct { Policy model.Policy `json:"policy" binding:"required"` } +// Test 从机响应ping +func (service *SlavePingService) Test() serializer.Response { + master, err := url.Parse(service.Callback) + if err != nil { + return serializer.ParamErr("无法解析主机站点地址,请检查主机 参数设置 - 站点信息 - 站点URL设置,"+err.Error(), nil) + } + + controller, _ := url.Parse("/api/v3/site/ping") + + r := request.HTTPClient{} + res, err := r.Request( + "GET", + master.ResolveReference(controller).String(), + nil, + request.WithTimeout(time.Duration(10)*time.Second), + ).DecodeResponse() + + if err != nil { + return serializer.ParamErr("从机无法向主机发送回调请求,请检查主机端 参数设置 - 站点信息 - 站点URL设置,并确保从机可以连接到此地址,"+err.Error(), nil) + } + + if res.Data.(string) != conf.BackendVersion { + return serializer.ParamErr("Cloudreve版本不一致,主机:"+res.Data.(string)+",从机:"+conf.BackendVersion, nil) + } + + return serializer.Response{} +} + +// Test 测试从机通信 +func (service *SlaveTestService) Test() serializer.Response { + slave, err := url.Parse(service.Server) + if err != nil { + return serializer.ParamErr("无法解析从机端地址,"+err.Error(), nil) + } + + controller, _ := url.Parse("/api/v3/slave/ping") + + // 请求正文 + body := map[string]string{ + "callback": model.GetSiteURL().String(), + } + bodyByte, _ := json.Marshal(body) + + r := request.HTTPClient{} + res, err := r.Request( + "POST", + slave.ResolveReference(controller).String(), + bytes.NewReader(bodyByte), + request.WithTimeout(time.Duration(10)*time.Second), + request.WithCredential( + auth.HMACAuth{SecretKey: []byte(service.Secret)}, + int64(model.GetIntSetting("slave_api_timeout", 60)), + ), + ).DecodeResponse() + if err != nil { + return serializer.ParamErr("无连接到从机,"+err.Error(), nil) + } + + if res.Code != 0 { + return serializer.ParamErr("成功接到从机,但是"+res.Msg, nil) + } + + return serializer.Response{} +} + // Add 添加存储策略 func (service *AddPolicyService) Add() serializer.Response { if err := model.DB.Create(&service.Policy).Error; err != nil { @@ -54,7 +137,7 @@ func (service *AdminListService) Policies() serializer.Response { } for k, v := range service.Conditions { - tx = tx.Where("? = ?", k, v) + tx = tx.Where(k+" = ?", v) } // 计算总数用于分页