diff --git a/middleware/frontend.go b/middleware/frontend.go index 7fb7606..551a209 100644 --- a/middleware/frontend.go +++ b/middleware/frontend.go @@ -39,13 +39,13 @@ func FrontendFileHandler() gin.HandlerFunc { path := c.Request.URL.Path // API 跳过 - if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "/custom") || strings.HasPrefix(path, "/custom") || path == "manifest.json" { + if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "/custom") || strings.HasPrefix(path, "/dav") || path == "/manifest.json" { c.Next() return } // 不存在的路径和index.html均返回index.html - if !bootstrap.StaticFS.Exists("/", path) || (path == "/index.html") || (path == "/") { + if (path == "/index.html") || (path == "/") || !bootstrap.StaticFS.Exists("/", path) { // 读取、替换站点设置 options := model.GetSettingByNames("siteName", "siteKeywords", "siteScript", "pwa_small_icon") diff --git a/middleware/frontend_test.go b/middleware/frontend_test.go new file mode 100644 index 0000000..d32529d --- /dev/null +++ b/middleware/frontend_test.go @@ -0,0 +1,144 @@ +package middleware + +import ( + "errors" + "github.com/cloudreve/Cloudreve/v3/bootstrap" + "github.com/cloudreve/Cloudreve/v3/pkg/cache" + "github.com/cloudreve/Cloudreve/v3/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" +) + +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 TestFrontendFileHandler(t *testing.T) { + asserts := assert.New(t) + rec := httptest.NewRecorder() + + // 静态资源未加载 + { + TestFunc := FrontendFileHandler() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/", nil) + TestFunc(c) + asserts.False(c.IsAborted()) + } + + // index.html 不存在 + { + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(&os.File{}, errors.New("error")) + TestFunc := FrontendFileHandler() + + 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 := FrontendFileHandler() + + 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 := FrontendFileHandler() + + 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) + testStatic.On("Exists", "/", "/2"). + Return(true) + testStatic.On("Open", "/2"). + Return(file, nil) + TestFunc := FrontendFileHandler() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", "/2", nil) + + TestFunc(c) + asserts.True(c.IsAborted()) + testStatic.AssertExpectations(t) + } + + // API 相关跳过 + { + for _, reqPath := range []string{"/api/user", "/manifest.json", "/dav/path"} { + file, _ := util.CreatNestedFile("tests/index.html") + defer file.Close() + testStatic := &StaticMock{} + bootstrap.StaticFS = testStatic + testStatic.On("Open", "/index.html"). + Return(file, nil) + TestFunc := FrontendFileHandler() + + c, _ := gin.CreateTestContext(rec) + c.Params = []gin.Param{} + c.Request, _ = http.NewRequest("GET", reqPath, nil) + + TestFunc(c) + asserts.False(c.IsAborted()) + } + } + +} diff --git a/middleware/option.go b/middleware/option.go index 6704bcf..daa3753 100644 --- a/middleware/option.go +++ b/middleware/option.go @@ -1,13 +1,9 @@ package middleware import ( - "io/ioutil" - - "github.com/cloudreve/Cloudreve/v3/bootstrap" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" - "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gin-gonic/gin" ) @@ -42,47 +38,3 @@ func IsFunctionEnabled(key string) gin.HandlerFunc { c.Next() } } - -// InjectSiteInfo 向首页html中插入站点信息 -func InjectSiteInfo() gin.HandlerFunc { - ignoreFunc := func(c *gin.Context) { - c.Next() - } - if bootstrap.StaticFS == nil { - return ignoreFunc - } - - // 读取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 512c2e8..5de824a 100644 --- a/middleware/option_test.go +++ b/middleware/option_test.go @@ -1,19 +1,14 @@ package middleware import ( - "errors" "net/http" "net/http/httptest" - "os" "testing" - "github.com/cloudreve/Cloudreve/v3/bootstrap" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" - "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" - testMock "github.com/stretchr/testify/mock" ) func TestHashID(t *testing.T) { @@ -81,107 +76,3 @@ 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() - - // 静态资源未加载 - { - TestFunc := InjectSiteInfo() - - c, _ := gin.CreateTestContext(rec) - c.Params = []gin.Param{} - c.Request, _ = http.NewRequest("GET", "/", nil) - TestFunc(c) - asserts.False(c.IsAborted()) - } - - // 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()) - } - -}