diff --git a/assets b/assets index 63ee122..b44dc05 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 63ee122d977284e3ccf20f4c1ed228584e705c4d +Subproject commit b44dc0514520580fc284937400743123bbb2c6d4 diff --git a/middleware/option.go b/middleware/option.go index a4c6fcd..9c95432 100644 --- a/middleware/option.go +++ b/middleware/option.go @@ -1,10 +1,13 @@ package middleware import ( + "github.com/HFO4/cloudreve/bootstrap" model "github.com/HFO4/cloudreve/models" "github.com/HFO4/cloudreve/pkg/hashid" "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/HFO4/cloudreve/pkg/util" "github.com/gin-gonic/gin" + "io/ioutil" ) // HashID 将给定对象的HashID转换为真实ID @@ -38,3 +41,43 @@ func IsFunctionEnabled(key string) gin.HandlerFunc { c.Next() } } + +// InjectSiteInfo 向首页html中插入站点信息 +func InjectSiteInfo() gin.HandlerFunc { + ignoreFunc := func(c *gin.Context) { + c.Next() + } + // 读取index.html + file, err := bootstrap.StaticFS.Open("/index.html") + if err != nil { + util.Log().Warning("静态文件[index.html]不存在,可能会影响首页展示") + return ignoreFunc + } + + fileContentBytes, err := ioutil.ReadAll(file) + if err != nil { + util.Log().Warning("静态文件[index.html]读取失败,可能会影响首页展示") + return ignoreFunc + } + fileContent := string(fileContentBytes) + + return func(c *gin.Context) { + if c.Request.URL.Path == "/" || c.Request.URL.Path == "/index.html" { + // 读取、替换站点设置 + options := model.GetSettingByNames("siteName", "siteKeywords", "siteScript", + "pwa_small_icon") + finalHTML := util.Replace(map[string]string{ + "{siteName}": options["siteName"], + "{siteDes}": options["siteDes"], + "{siteScript}": options["siteScript"], + "{pwa_small_icon}": options["pwa_small_icon"], + }, fileContent) + + c.Header("Content-Type", "text/html") + c.String(200, finalHTML) + c.Abort() + return + } + c.Next() + } +} diff --git a/middleware/option_test.go b/middleware/option_test.go index 97d8d5e..b5a4852 100644 --- a/middleware/option_test.go +++ b/middleware/option_test.go @@ -1,12 +1,17 @@ package middleware import ( + "errors" + "github.com/HFO4/cloudreve/bootstrap" "github.com/HFO4/cloudreve/pkg/cache" "github.com/HFO4/cloudreve/pkg/hashid" + "github.com/HFO4/cloudreve/pkg/util" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + testMock "github.com/stretchr/testify/mock" "net/http" "net/http/httptest" + "os" "testing" ) @@ -75,3 +80,96 @@ func TestIsFunctionEnabled(t *testing.T) { } } + +type StaticMock struct { + testMock.Mock +} + +func (m StaticMock) Open(name string) (http.File, error) { + args := m.Called(name) + return args.Get(0).(http.File), args.Error(1) +} + +func (m StaticMock) Exists(prefix string, filepath string) bool { + args := m.Called(prefix, filepath) + return args.Bool(0) +} + +func TestInjectSiteInfo(t *testing.T) { + asserts := assert.New(t) + rec := httptest.NewRecorder() + + // index.html 不存在 + { + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(&os.File{}, errors.New("error")) + TestFunc := InjectSiteInfo() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/", nil) + TestFunc(c) + asserts.False(c.IsAborted()) + } + + // index.html 读取失败 + { + file, _ := util.CreatNestedFile("tests/index.html") + file.Close() + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(file, nil) + TestFunc := InjectSiteInfo() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/", nil) + TestFunc(c) + asserts.False(c.IsAborted()) + } + + // 成功且命中 + { + file, _ := util.CreatNestedFile("tests/index.html") + defer file.Close() + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(file, nil) + TestFunc := InjectSiteInfo() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/", nil) + + cache.Set("setting_siteName", "cloudreve", 0) + cache.Set("setting_siteKeywords", "cloudreve", 0) + cache.Set("setting_siteScript", "cloudreve", 0) + cache.Set("setting_pwa_small_icon", "cloudreve", 0) + + TestFunc(c) + asserts.True(c.IsAborted()) + } + + // 成功且未命中 + { + file, _ := util.CreatNestedFile("tests/index.html") + defer file.Close() + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(file, nil) + TestFunc := InjectSiteInfo() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/2", nil) + + TestFunc(c) + asserts.False(c.IsAborted()) + } + +} diff --git a/models/migration.go b/models/migration.go index b50041e..85969e4 100644 --- a/models/migration.go +++ b/models/migration.go @@ -82,6 +82,7 @@ func addDefaultSettings() { {Name: "siteKeywords", Value: `网盘,网盘`, Type: "basic"}, {Name: "siteDes", Value: `Cloudreve`, Type: "basic"}, {Name: "siteTitle", Value: `平步云端`, Type: "basic"}, + {Name: "siteScript", Value: ``, Type: "basic"}, {Name: "fromName", Value: `Cloudreve`, Type: "mail"}, {Name: "mail_keepalive", Value: `30`, Type: "mail"}, {Name: "fromAdress", Value: `no-reply@acg.blue`, Type: "mail"}, diff --git a/routers/router.go b/routers/router.go index ad0da4b..083a007 100644 --- a/routers/router.go +++ b/routers/router.go @@ -82,6 +82,7 @@ func InitMasterRouter() *gin.Engine { 静态资源 */ r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/api/"}))) + r.Use(middleware.InjectSiteInfo()) r.Use(static.Serve("/", bootstrap.StaticFS)) r.GET("manifest.json", controllers.Manifest)