Feat: dynamic writing site title, favicon, description, custom html (#286)
This commit is contained in:
parent
280308bc05
commit
2076d56f0f
5 changed files with 144 additions and 1 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 63ee122d977284e3ccf20f4c1ed228584e705c4d
|
Subproject commit b44dc0514520580fc284937400743123bbb2c6d4
|
|
@ -1,10 +1,13 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/HFO4/cloudreve/bootstrap"
|
||||||
model "github.com/HFO4/cloudreve/models"
|
model "github.com/HFO4/cloudreve/models"
|
||||||
"github.com/HFO4/cloudreve/pkg/hashid"
|
"github.com/HFO4/cloudreve/pkg/hashid"
|
||||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/util"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashID 将给定对象的HashID转换为真实ID
|
// HashID 将给定对象的HashID转换为真实ID
|
||||||
|
@ -38,3 +41,43 @@ func IsFunctionEnabled(key string) gin.HandlerFunc {
|
||||||
c.Next()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/HFO4/cloudreve/bootstrap"
|
||||||
"github.com/HFO4/cloudreve/pkg/cache"
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
"github.com/HFO4/cloudreve/pkg/hashid"
|
"github.com/HFO4/cloudreve/pkg/hashid"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/util"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
testMock "github.com/stretchr/testify/mock"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"testing"
|
"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())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ func addDefaultSettings() {
|
||||||
{Name: "siteKeywords", Value: `网盘,网盘`, Type: "basic"},
|
{Name: "siteKeywords", Value: `网盘,网盘`, Type: "basic"},
|
||||||
{Name: "siteDes", Value: `Cloudreve`, Type: "basic"},
|
{Name: "siteDes", Value: `Cloudreve`, Type: "basic"},
|
||||||
{Name: "siteTitle", Value: `平步云端`, Type: "basic"},
|
{Name: "siteTitle", Value: `平步云端`, Type: "basic"},
|
||||||
|
{Name: "siteScript", Value: ``, Type: "basic"},
|
||||||
{Name: "fromName", Value: `Cloudreve`, Type: "mail"},
|
{Name: "fromName", Value: `Cloudreve`, Type: "mail"},
|
||||||
{Name: "mail_keepalive", Value: `30`, Type: "mail"},
|
{Name: "mail_keepalive", Value: `30`, Type: "mail"},
|
||||||
{Name: "fromAdress", Value: `no-reply@acg.blue`, Type: "mail"},
|
{Name: "fromAdress", Value: `no-reply@acg.blue`, Type: "mail"},
|
||||||
|
|
|
@ -82,6 +82,7 @@ func InitMasterRouter() *gin.Engine {
|
||||||
静态资源
|
静态资源
|
||||||
*/
|
*/
|
||||||
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/api/"})))
|
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/api/"})))
|
||||||
|
r.Use(middleware.InjectSiteInfo())
|
||||||
r.Use(static.Serve("/", bootstrap.StaticFS))
|
r.Use(static.Serve("/", bootstrap.StaticFS))
|
||||||
r.GET("manifest.json", controllers.Manifest)
|
r.GET("manifest.json", controllers.Manifest)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue