diff --git a/app/route/route.go b/app/route/route.go index 575dac8..5aa5358 100644 --- a/app/route/route.go +++ b/app/route/route.go @@ -8,6 +8,8 @@ import ( "github.com/fthvgb1/wp-go/app/theme" "github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/cache/reload" + "github.com/fthvgb1/wp-go/helper/slice" + "github.com/fthvgb1/wp-go/helper/slice/mockmap" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/gin-contrib/gzip" "github.com/gin-contrib/pprof" @@ -16,11 +18,27 @@ import ( "github.com/gin-gonic/gin" ) -var hooker []func(r *gin.Engine) +type GinHook func(*gin.Engine) -// Hook 方便插件在init时使用 -func Hook(fn ...func(r *gin.Engine)) { - hooker = append(hooker, fn...) +var hookers mockmap.Map[string, GinHook] + +// SetGinAction 方便插件在init时使用 +func SetGinAction(name string, hook GinHook, orders ...float64) { + hookers.Set(name, hook, orders...) +} + +func HookGinAction(name string, fn func(item mockmap.Item[string, GinHook]) mockmap.Item[string, GinHook]) { + item := hookers.Get(name) + if item.Name == "" { + return + } + t := fn(item) + SetGinAction(name, t.Value, t.Order) +} + +// DelGinAction 方便插件在init时使用 +func DelGinAction(name string) { + hookers.Del(name) } func SetupRouter() *gin.Engine { @@ -28,72 +46,99 @@ func SetupRouter() *gin.Engine { // gin.DisableConsoleColor() r := gin.New() c := config.GetConfig() - if len(c.TrustIps) > 0 { - err := r.SetTrustedProxies(c.TrustIps) - if err != nil { - panic(err) + SetGinAction("initTrustIp", func(r *gin.Engine) { + if len(c.TrustIps) > 0 { + err := r.SetTrustedProxies(c.TrustIps) + if err != nil { + panic(err) + } } - } + }, 99.5) + + SetGinAction("setTemplate", func(r *gin.Engine) { + r.HTMLRender = theme.BuildTemplate() + wpconfig.SetTemplateFs(theme.TemplateFs) + }, 90.5) - r.HTMLRender = theme.BuildTemplate() - wpconfig.SetTemplateFs(theme.TemplateFs) siteFlowLimitMiddleware, siteFlow := middleware.FlowLimit(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime) - r.Use( - gin.Logger(), - middleware.ValidateServerNames(), - middleware.RecoverAndSendMail(gin.DefaultErrorWriter), - siteFlowLimitMiddleware, - middleware.SetStaticFileCache, - ) - //gzip 因为一般会用nginx做反代时自动使用gzip,所以go这边本身可以不用 - if c.Gzip { - r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{ - "/wp-includes/", "/wp-content/", - }))) - } - - if c.WpDir == "" { - panic("wordpress path can't be empty") - } - r.Static("/wp-content/uploads", str.Join(c.WpDir, "/wp-content/uploads")) - r.Static("/wp-content/themes", str.Join(c.WpDir, "/wp-content/themes")) - r.Static("/wp-content/plugins", str.Join(c.WpDir, "/wp-content/plugins")) - r.Static("/wp-includes/css", str.Join(c.WpDir, "/wp-includes/css")) - r.Static("/wp-includes/fonts", str.Join(c.WpDir, "/wp-includes/fonts")) - r.Static("/wp-includes/js", str.Join(c.WpDir, "/wp-includes/js")) - - store := cookie.NewStore([]byte("secret")) - r.Use(sessions.Sessions("go-wp", store)) - r.GET("/", actions.Feed, middleware.SearchLimit(c.SingleIpSearchNum), - actions.ThemeHook(constraints.Home)) - r.GET("/page/:page", actions.ThemeHook(constraints.Home)) - r.GET("/p/category/:category", actions.ThemeHook(constraints.Category)) - r.GET("/p/category/:category/page/:page", actions.ThemeHook(constraints.Category)) - r.GET("/p/tag/:tag", actions.ThemeHook(constraints.Tag)) - r.GET("/p/tag/:tag/page/:page", actions.ThemeHook(constraints.Tag)) - r.GET("/p/date/:year/:month", actions.ThemeHook(constraints.Archive)) - r.GET("/p/date/:year/:month/page/:page", actions.ThemeHook(constraints.Archive)) - r.GET("/p/author/:author", actions.ThemeHook(constraints.Author)) - r.GET("/p/author/:author/page/:page", actions.ThemeHook(constraints.Author)) - r.POST("/login", actions.Login) - r.GET("/p/:id", actions.ThemeHook(constraints.Detail)) - r.GET("/p/:id/comment-page-:page", actions.ThemeHook(constraints.Detail)) - r.GET("/p/:id/feed", actions.PostFeed) - r.GET("/feed", actions.SiteFeed) - r.GET("/comments/feed", actions.CommentsFeed) - //r.NoRoute(actions.ThemeHook(constraints.NoRoute)) - commentMiddleWare, _ := middleware.FlowLimit(c.MaxRequestSleepNum, 5, c.CacheTime.SleepTime) - r.POST("/comment", commentMiddleWare, actions.PostComment) - if c.Pprof != "" { - pprof.Register(r, c.Pprof) - } - for _, fn := range hooker { - fn(r) - } - reload.Append(func() { - c := config.GetConfig() + c = config.GetConfig() siteFlow(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime) }, "site-flowLimit-config") + + SetGinAction("setGlobalMiddleware", func(r *gin.Engine) { + r.Use( + gin.Logger(), + middleware.ValidateServerNames(), + middleware.RecoverAndSendMail(gin.DefaultErrorWriter), + siteFlowLimitMiddleware, + middleware.SetStaticFileCache, + ) + }, 88.5) + + SetGinAction("setGzip", func(r *gin.Engine) { + //gzip 因为一般会用nginx做反代时自动使用gzip,所以go这边本身可以不用 + if c.Gzip { + r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{ + "/wp-includes/", "/wp-content/", + }))) + } + }, 87.6) + + SetGinAction("setWpDir", func(r *gin.Engine) { + if c.WpDir == "" { + panic("wordpress path can't be empty") + } + r.Static("/wp-content/uploads", str.Join(c.WpDir, "/wp-content/uploads")) + r.Static("/wp-content/themes", str.Join(c.WpDir, "/wp-content/themes")) + r.Static("/wp-content/plugins", str.Join(c.WpDir, "/wp-content/plugins")) + r.Static("/wp-includes/css", str.Join(c.WpDir, "/wp-includes/css")) + r.Static("/wp-includes/fonts", str.Join(c.WpDir, "/wp-includes/fonts")) + r.Static("/wp-includes/js", str.Join(c.WpDir, "/wp-includes/js")) + }, 86.1) + + SetGinAction("setSession", func(r *gin.Engine) { + store := cookie.NewStore([]byte("secret")) + r.Use(sessions.Sessions("go-wp", store)) + }, 85.1) + + SetGinAction("setRoute", func(r *gin.Engine) { + r.GET("/", actions.Feed, middleware.SearchLimit(c.SingleIpSearchNum), + actions.ThemeHook(constraints.Home)) + r.GET("/page/:page", actions.ThemeHook(constraints.Home)) + r.GET("/p/category/:category", actions.ThemeHook(constraints.Category)) + r.GET("/p/category/:category/page/:page", actions.ThemeHook(constraints.Category)) + r.GET("/p/tag/:tag", actions.ThemeHook(constraints.Tag)) + r.GET("/p/tag/:tag/page/:page", actions.ThemeHook(constraints.Tag)) + r.GET("/p/date/:year/:month", actions.ThemeHook(constraints.Archive)) + r.GET("/p/date/:year/:month/page/:page", actions.ThemeHook(constraints.Archive)) + r.GET("/p/author/:author", actions.ThemeHook(constraints.Author)) + r.GET("/p/author/:author/page/:page", actions.ThemeHook(constraints.Author)) + r.POST("/login", actions.Login) + r.GET("/p/:id", actions.ThemeHook(constraints.Detail)) + r.GET("/p/:id/comment-page-:page", actions.ThemeHook(constraints.Detail)) + r.GET("/p/:id/feed", actions.PostFeed) + r.GET("/feed", actions.SiteFeed) + r.GET("/comments/feed", actions.CommentsFeed) + commentMiddleWare, _ := middleware.FlowLimit(c.MaxRequestSleepNum, 5, c.CacheTime.SleepTime) + r.POST("/comment", commentMiddleWare, actions.PostComment) + + r.NoRoute(actions.ThemeHook(constraints.NoRoute)) + }, 84.6) + + SetGinAction("setpprof", func(r *gin.Engine) { + if c.Pprof != "" { + pprof.Register(r, c.Pprof) + } + }, 80.8) + + slice.SimpleSort(hookers, slice.DESC, func(t mockmap.Item[string, GinHook]) float64 { + return t.Order + }) + + for _, fn := range hookers { + fn.Value(r) + } + return r } diff --git a/app/theme/wp/middleware/middleware.go b/app/theme/wp/middleware/middleware.go index 57d328d..8f8fefd 100644 --- a/app/theme/wp/middleware/middleware.go +++ b/app/theme/wp/middleware/middleware.go @@ -5,6 +5,7 @@ import ( "github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp/components/widget" + "github.com/fthvgb1/wp-go/app/theme/wp/route" "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/number" @@ -160,4 +161,7 @@ func CommonMiddleware(h *wp.Handle) { h.PushHandler(constraints.PipeMiddleware, constraints.Detail, wp.NewHandleFn(ShowPreComment, 100, "middleware.ShowPreComment"), ) + h.PushHandler(constraints.PipeMiddleware, constraints.NoRoute, + wp.NewHandleFn(route.ResolveRoute, 100, "route.ResolveRoute"), + ) } diff --git a/app/theme/wp/route/route.go b/app/theme/wp/route/route.go index 078c2bc..9889c7a 100644 --- a/app/theme/wp/route/route.go +++ b/app/theme/wp/route/route.go @@ -5,9 +5,12 @@ import ( "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/safety" + "net/http" "regexp" ) +// Route +// Type value equal const or reg type Route struct { Path string Scene string @@ -131,4 +134,6 @@ func ResolveRoute(h *wp.Handle) { return } } + h.C.Status(http.StatusNotFound) + h.Abort() } diff --git a/cache/cachemanager/manger.go b/cache/cachemanager/manger.go index 68b5d2a..d7cc5ec 100644 --- a/cache/cachemanager/manger.go +++ b/cache/cachemanager/manger.go @@ -5,7 +5,7 @@ import ( "github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper" - "github.com/fthvgb1/wp-go/helper/slice" + "github.com/fthvgb1/wp-go/helper/slice/mockmap" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/safety" "runtime" @@ -15,52 +15,20 @@ import ( var mutex sync.Mutex -type Queue struct { - Name string - Fn func(context.Context) -} - -type Queues []Queue - -func (q *Queues) Get(name string) Queue { - _, v := slice.SearchFirst(*q, func(t Queue) bool { - return name == t.Name - }) - return v -} - -func (q *Queues) Set(name string, fn func(context.Context)) { - i := slice.IndexOfBy(*q, func(t Queue) bool { - return name == t.Name - }) - if i > -1 { - (*q)[i].Fn = fn - return - } - *q = append(*q, Queue{name, fn}) -} - -func (q *Queues) Del(name string) { - i := slice.IndexOfBy(*q, func(t Queue) bool { - return name == t.Name - }) - if i > -1 { - slice.Delete((*[]Queue)(q), i) - } -} +type Fn func(context.Context) type clearExpired interface { ClearExpired(ctx context.Context) } -var clears = safety.NewVar(Queues{}) +var clears = safety.NewVar(mockmap.Map[string, Fn]{}) -var flushes = safety.NewVar(Queues{}) +var flushes = safety.NewVar(mockmap.Map[string, Fn]{}) func Flush() { ctx := context.WithValue(context.Background(), "execFlushBy", "mangerFlushFn") for _, f := range flushes.Load() { - f.Fn(ctx) + f.Value(ctx) } } @@ -68,12 +36,12 @@ func Flushes(ctx context.Context, names ...string) { execute(ctx, flushes, names...) } -func execute(ctx context.Context, q *safety.Var[Queues], names ...string) { +func execute(ctx context.Context, q *safety.Var[mockmap.Map[string, Fn]], names ...string) { queues := q.Load() for _, name := range names { queue := queues.Get(name) - if queue.Fn != nil { - queue.Fn(ctx) + if queue.Value != nil { + queue.Value(ctx) } } } @@ -118,9 +86,9 @@ func buildLockFn[K comparable](args ...any) cache.LockFn[K] { } else { lo := cache.NewLocks[K](loFn) lockFn = lo.GetLock - PushOrSetFlush(Queue{ - Name: name, - Fn: lo.Flush, + PushOrSetFlush(mockmap.Item[string, Fn]{ + Name: name, + Value: lo.Flush, }) } @@ -141,14 +109,14 @@ func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) { reload.SetFnVal(s, t, coverConf) } } -func pushOrSet(q *safety.Var[Queues], queues ...Queue) { +func pushOrSet(q *safety.Var[mockmap.Map[string, Fn]], queues ...mockmap.Item[string, Fn]) { mutex.Lock() defer mutex.Unlock() qu := q.Load() for _, queue := range queues { v := qu.Get(queue.Name) - if v.Fn != nil { - qu.Set(queue.Name, queue.Fn) + if v.Value != nil { + qu.Set(queue.Name, queue.Value) } else { qu = append(qu, queue) } @@ -157,16 +125,16 @@ func pushOrSet(q *safety.Var[Queues], queues ...Queue) { } // PushOrSetFlush will execute flush func when call Flush or Flushes -func PushOrSetFlush(queues ...Queue) { +func PushOrSetFlush(queues ...mockmap.Item[string, Fn]) { pushOrSet(flushes, queues...) } // PushOrSetClearExpired will execute clearExpired func when call ClearExpired or ClearExpireds -func PushOrSetClearExpired(queues ...Queue) { +func PushOrSetClearExpired(queues ...mockmap.Item[string, Fn]) { pushOrSet(clears, queues...) } -func del(q *safety.Var[Queues], names ...string) { +func del(q *safety.Var[mockmap.Map[string, Fn]], names ...string) { mutex.Lock() defer mutex.Unlock() queues := q.Load() @@ -190,6 +158,6 @@ func ClearExpireds(ctx context.Context, names ...string) { func ClearExpired() { ctx := context.WithValue(context.Background(), "execClearExpired", "mangerClearExpiredFn") for _, queue := range clears.Load() { - queue.Fn(ctx) + queue.Value(ctx) } } diff --git a/cache/cachemanager/mapcache.go b/cache/cachemanager/mapcache.go index 466a38c..821af77 100644 --- a/cache/cachemanager/mapcache.go +++ b/cache/cachemanager/mapcache.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/helper" + "github.com/fthvgb1/wp-go/helper/slice/mockmap" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/safety" "time" @@ -110,13 +111,13 @@ func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapB if name != "" { PushMangerMap(name, m) } - PushOrSetFlush(Queue{ - Name: name, - Fn: m.Flush, + PushOrSetFlush(mockmap.Item[string, Fn]{ + Name: name, + Value: m.Flush, }) - PushOrSetClearExpired(Queue{ - Name: name, - Fn: m.ClearExpired, + PushOrSetClearExpired(mockmap.Item[string, Fn]{ + Name: name, + Value: m.ClearExpired, }) if f != nil && name != "" { SetExpireTime(any(data).(cache.SetTime), name, 0, f) diff --git a/cache/cachemanager/varcache.go b/cache/cachemanager/varcache.go index f5f91d4..faf9732 100644 --- a/cache/cachemanager/varcache.go +++ b/cache/cachemanager/varcache.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/helper" + "github.com/fthvgb1/wp-go/helper/slice/mockmap" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/safety" "time" @@ -35,15 +36,15 @@ func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T if name != "" { varCache.Store(name, v) } - PushOrSetFlush(Queue{ - Name: name, - Fn: v.Flush, + PushOrSetFlush(mockmap.Item[string, Fn]{ + Name: name, + Value: v.Flush, }) cc, ok := any(c).(clearExpired) if ok { - PushOrSetClearExpired(Queue{ - Name: name, - Fn: cc.ClearExpired, + PushOrSetClearExpired(mockmap.Item[string, Fn]{ + Name: name, + Value: cc.ClearExpired, }) } return v diff --git a/helper/slice/mockmap/map.go b/helper/slice/mockmap/map.go new file mode 100644 index 0000000..d6fde61 --- /dev/null +++ b/helper/slice/mockmap/map.go @@ -0,0 +1,44 @@ +package mockmap + +import ( + "github.com/fthvgb1/wp-go/helper/slice" +) + +type Item[K comparable, T any] struct { + Name K + Value T + Order float64 +} + +type Map[K comparable, T any] []Item[K, T] + +func (q *Map[K, T]) Get(name K) Item[K, T] { + _, v := slice.SearchFirst(*q, func(t Item[K, T]) bool { + return name == t.Name + }) + return v +} + +func (q *Map[K, T]) Set(name K, value T, orders ...float64) { + i := slice.IndexOfBy(*q, func(t Item[K, T]) bool { + return name == t.Name + }) + if i > -1 { + (*q)[i].Value = value + return + } + order := float64(0) + if len(orders) > 0 { + order = orders[0] + } + *q = append(*q, Item[K, T]{name, value, order}) +} + +func (q *Map[K, T]) Del(name K) { + i := slice.IndexOfBy(*q, func(t Item[K, T]) bool { + return name == t.Name + }) + if i > -1 { + slice.Delete((*[]Item[K, T])(q), i) + } +}