Compare commits
114 Commits
scriptload
...
master
Author | SHA1 | Date | |
---|---|---|---|
8aeec60181 | |||
58d3e4f645 | |||
e592a753bf | |||
d598fb5ce5 | |||
63f769796f | |||
7cbfdf8601 | |||
9c60d10568 | |||
dcbe760f09 | |||
d220bb2a6c | |||
eb7ccd4f2d | |||
39c9dc1b09 | |||
68beb0fcbb | |||
1f5f7f465d | |||
b7c09cb5a2 | |||
5942f76972 | |||
1f40810b78 | |||
da858d55e0 | |||
166bb08ed0 | |||
5eaf798a6c | |||
e2e6bcc8ce | |||
daa7101493 | |||
6f51f1c295 | |||
a8d1dcdd5b | |||
9af2257650 | |||
63591899bb | |||
ee9ba3fcf0 | |||
a78815f3d3 | |||
6209f2b5e5 | |||
08d3bca39c | |||
9b8e597066 | |||
3215b0c8ea | |||
c7c97c469f | |||
b45ad0e54e | |||
a0c6e48e83 | |||
3c3ed73971 | |||
be58a35245 | |||
67bdf1e070 | |||
981e0ad85b | |||
74159940b5 | |||
206e5902d1 | |||
2d38b36525 | |||
af712f06a5 | |||
73575965d3 | |||
f49ad731e3 | |||
802c7c7dcc | |||
20ea36c727 | |||
fd3199a83c | |||
e25f679ea6 | |||
4842d53316 | |||
65bfccdd48 | |||
c60146d614 | |||
9285698ee4 | |||
5e18c9babd | |||
8bdc53d175 | |||
ad9bdc7574 | |||
0352dd0fd0 | |||
6044b8eaec | |||
8d3197ee98 | |||
8fcf3ecca2 | |||
4d9d011213 | |||
12b75b9d82 | |||
568ab15a34 | |||
c38b62c82a | |||
0774b122ee | |||
f7d2377101 | |||
088fc306de | |||
57d345e445 | |||
f257a966e6 | |||
ceffeccf8d | |||
eed45f51ba | |||
d72bed0c8c | |||
0cdb3ba040 | |||
227de8bdc8 | |||
74304b5b12 | |||
ac29cf2448 | |||
137b2a9738 | |||
9f49a274cd | |||
7c3f8baaa2 | |||
547d8e59e6 | |||
7bbc961f72 | |||
66c02f7fc9 | |||
a5294e2e20 | |||
7978e9ee74 | |||
f6a5cf4255 | |||
928a608878 | |||
42d2795a05 | |||
041d06104b | |||
d1fb560578 | |||
42484e3f96 | |||
64a2c2e33b | |||
86d1616732 | |||
2eb58f732b | |||
936d033547 | |||
cd4787991d | |||
71ddf299e4 | |||
0f467bbc98 | |||
171b3bc59c | |||
f369cf4f22 | |||
ddf6e62e5a | |||
e502f581e1 | |||
acb064b762 | |||
f0c1744998 | |||
876a81c062 | |||
78f5157efe | |||
acbcbf455f | |||
179545f2bb | |||
48a3fe6588 | |||
3fd4f412b1 | |||
5273f4ecf9 | |||
b9c2527f4c | |||
1286338af0 | |||
21a4ff3b3e | |||
2302aa7ff8 | |||
bc71be7238 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
/config.yaml
|
/config.yaml
|
||||||
err.log
|
err.log
|
||||||
/plugins/
|
/plugins/
|
||||||
/config.json
|
/config.json
|
||||||
|
go.sum
|
@ -1,8 +1,7 @@
|
|||||||
FROM golang:latest as gobulidIso
|
FROM golang:1.22.2-alpine as gobulidIso
|
||||||
COPY ./ /go/src/wp-go
|
COPY ./ /go/src/wp-go
|
||||||
WORKDIR /go/src/wp-go
|
WORKDIR /go/src/wp-go
|
||||||
ENV GOPROXY="https://goproxy.cn"
|
RUN go build -ldflags "-w" -tags netgo -o wp-go app/cmd/main.go
|
||||||
RUN go build -ldflags "-w" -tags netgo -o wp-go internal/cmd/main.go
|
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
WORKDIR /opt/wp-go
|
WORKDIR /opt/wp-go
|
||||||
|
18
README.md
18
README.md
@ -20,6 +20,11 @@
|
|||||||
- kill -SIGUSR1 PID 更新配置和清空缓存
|
- kill -SIGUSR1 PID 更新配置和清空缓存
|
||||||
- kill -SIGUSR2 PID 清空缓存
|
- kill -SIGUSR2 PID 清空缓存
|
||||||
|
|
||||||
|
#### 运行
|
||||||
|
```
|
||||||
|
go run app/cmd/main.go [-c configpath] [-p port]
|
||||||
|
```
|
||||||
|
|
||||||
#### 数据显示支持程度
|
#### 数据显示支持程度
|
||||||
|
|
||||||
| 页表 | 支持程度 |
|
| 页表 | 支持程度 |
|
||||||
@ -48,8 +53,10 @@
|
|||||||
- 博客页面至多显示数量
|
- 博客页面至多显示数量
|
||||||
- Feed中显示最近数量
|
- Feed中显示最近数量
|
||||||
- 讨论
|
- 讨论
|
||||||
- 其他评论设置-启用评论嵌套,最多嵌套层数
|
- 其他评论设置
|
||||||
- 在每个页面顶部显示 `新旧`评论
|
- `启用|禁止`评论嵌套,最多嵌套层数
|
||||||
|
- 分页显示评论,每页显示评论条数,默认显示`最前/后`页
|
||||||
|
- 在每个页面顶部显示 `新旧`评论
|
||||||
|
|
||||||
#### 主题支持程度
|
#### 主题支持程度
|
||||||
|
|
||||||
@ -72,4 +79,9 @@
|
|||||||
|
|
||||||
#### 其它
|
#### 其它
|
||||||
|
|
||||||
用的gin框架和sqlx,在外面封装了层查询的方法。
|
用的gin框架和sqlx,在外面封装了层查询的方法。
|
||||||
|
|
||||||
|
#### 鸣谢
|
||||||
|
|
||||||
|
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." width="30%"></a>
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"io"
|
"io"
|
||||||
@ -77,7 +78,7 @@ func PostComment(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
req.Host = home.Host
|
req.Host = home.Host
|
||||||
res, err := cli.Do(req)
|
res, err := cli.Do(req)
|
||||||
if err != nil && err != http.ErrUseLastResponse {
|
if err != nil && !errors.Is(err, http.ErrUseLastResponse) {
|
||||||
logs.Error(err, "请求评论接口错误")
|
logs.Error(err, "请求评论接口错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -110,6 +111,9 @@ func PostComment(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
cc := c.Copy()
|
cc := c.Copy()
|
||||||
go func() {
|
go func() {
|
||||||
|
if gin.Mode() != gin.ReleaseMode {
|
||||||
|
return
|
||||||
|
}
|
||||||
id := comment.CommentPostId
|
id := comment.CommentPostId
|
||||||
if id <= 0 {
|
if id <= 0 {
|
||||||
logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId)
|
logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId)
|
||||||
@ -130,8 +134,15 @@ func PostComment(c *gin.Context) {
|
|||||||
err = er
|
err = er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cache.NewCommentCache().Set(c, up.RawQuery, string(s))
|
uuu := ""
|
||||||
c.Redirect(http.StatusFound, res.Header.Get("Location"))
|
uu, _ := url.Parse(res.Header.Get("Location"))
|
||||||
|
if up.RawQuery != "" {
|
||||||
|
cache.NewCommentCache().Set(c, up.RawQuery, string(s))
|
||||||
|
uuu = str.Join(uu.Path, "?", uu.RawQuery)
|
||||||
|
} else {
|
||||||
|
uuu = str.Join(uu.Path)
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusFound, uuu)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var r io.Reader
|
var r io.Reader
|
||||||
|
@ -10,6 +10,30 @@ import (
|
|||||||
|
|
||||||
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||||
|
|
||||||
|
func Feed(c *gin.Context) {
|
||||||
|
v, ok := c.GetQuery("feed")
|
||||||
|
if !ok || v == "" {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch v {
|
||||||
|
case "rss2":
|
||||||
|
p, ok := c.GetQuery("p")
|
||||||
|
if ok && p != "" {
|
||||||
|
c.AddParam("id", p)
|
||||||
|
PostFeed(c)
|
||||||
|
} else {
|
||||||
|
SiteFeed(c)
|
||||||
|
}
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
case "comments-rss2":
|
||||||
|
CommentsFeed(c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
||||||
eTag := str.Md5(lastTime.Format(tmp))
|
eTag := str.Md5(lastTime.Format(tmp))
|
||||||
since := c.Request.Header.Get("If-Modified-Since")
|
since := c.Request.Header.Get("If-Modified-Since")
|
||||||
@ -24,20 +48,21 @@ func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Feed(c *gin.Context) {
|
func SiteFeed(c *gin.Context) {
|
||||||
if !isCacheExpired(c, cache.FeedCache().GetLastSetTime()) {
|
feed := cache.FeedCache()
|
||||||
|
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := cache.FeedCache().GetCache(c, time.Second, c)
|
r, err := feed.GetCache(c, time.Second, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(r[0], c, cache.FeedCache().GetLastSetTime())
|
setFeed(r[0], c, feed.GetLastSetTime(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFeed(s string, c *gin.Context, t time.Time) {
|
func setFeed(s string, c *gin.Context, t time.Time) {
|
||||||
@ -51,31 +76,33 @@ func setFeed(s string, c *gin.Context, t time.Time) {
|
|||||||
|
|
||||||
func PostFeed(c *gin.Context) {
|
func PostFeed(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if !isCacheExpired(c, cache.PostFeedCache().GetLastSetTime(c, id)) {
|
postFeed := cache.PostFeedCache()
|
||||||
|
if !isCacheExpired(c, postFeed.GetLastSetTime(c, id)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s, err := cache.PostFeedCache().GetCache(c, id, time.Second, c, id)
|
s, err := postFeed.GetCache(c, id, time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(s, c, cache.PostFeedCache().GetLastSetTime(c, id))
|
setFeed(s, c, postFeed.GetLastSetTime(c, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommentsFeed(c *gin.Context) {
|
func CommentsFeed(c *gin.Context) {
|
||||||
if !isCacheExpired(c, cache.CommentsFeedCache().GetLastSetTime()) {
|
feed := cache.CommentsFeedCache()
|
||||||
|
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r, err := cache.CommentsFeedCache().GetCache(c, time.Second, c)
|
r, err := feed.GetCache(c, time.Second, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(r[0], c, cache.CommentsFeedCache().GetLastSetTime())
|
setFeed(r[0], c, feed.GetLastSetTime(c))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package actions
|
package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
|
||||||
"github.com/fthvgb1/wp-go/app/theme"
|
"github.com/fthvgb1/wp-go/app/theme"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -9,18 +8,8 @@ import (
|
|||||||
|
|
||||||
func ThemeHook(scene string) func(*gin.Context) {
|
func ThemeHook(scene string) func(*gin.Context) {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
s := scene
|
t := theme.GetCurrentTheme()
|
||||||
if scene == constraints.Home {
|
h := wp.NewHandle(c, scene, t)
|
||||||
if _, ok := c.GetQuery("s"); ok {
|
|
||||||
s = constraints.Search
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t := theme.GetCurrentTemplateName()
|
|
||||||
h := wp.NewHandle(c, s, t)
|
|
||||||
h.Index = wp.NewIndexHandle(h)
|
|
||||||
h.Detail = wp.NewDetailHandle(h)
|
|
||||||
templ, _ := theme.GetTemplate(t)
|
|
||||||
h.SetTemplate(templ)
|
|
||||||
theme.Hook(t, h)
|
theme.Hook(t, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package cachemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/fthvgb1/wp-go/cache"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ctx = context.Background()
|
|
||||||
|
|
||||||
type flush interface {
|
|
||||||
Flush(ctx context.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
type clear interface {
|
|
||||||
ClearExpired(ctx context.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
var clears []clear
|
|
||||||
|
|
||||||
var flushes []flush
|
|
||||||
|
|
||||||
func Flush() {
|
|
||||||
for _, f := range flushes {
|
|
||||||
f.Flush(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MapCacheBy[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *cache.MapCache[K, V] {
|
|
||||||
m := cache.NewMemoryMapCacheByFn[K, V](fn, expireTime)
|
|
||||||
FlushPush(m)
|
|
||||||
ClearPush(m)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
func MapBatchCacheBy[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *cache.MapCache[K, V] {
|
|
||||||
m := cache.NewMemoryMapCacheByBatchFn[K, V](fn, expireTime)
|
|
||||||
FlushPush(m)
|
|
||||||
ClearPush(m)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlushPush(f ...flush) {
|
|
||||||
flushes = append(flushes, f...)
|
|
||||||
}
|
|
||||||
func ClearPush(c ...clear) {
|
|
||||||
clears = append(clears, c...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClearExpired() {
|
|
||||||
for _, c := range clears {
|
|
||||||
c.ClearExpired(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,25 +3,21 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
|
"github.com/fthvgb1/wp-go/app/ossigns"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/route"
|
|
||||||
"github.com/fthvgb1/wp-go/app/mail"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/db"
|
"github.com/fthvgb1/wp-go/app/pkg/db"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle"
|
"github.com/fthvgb1/wp-go/app/plugins/wphandle"
|
||||||
|
"github.com/fthvgb1/wp-go/app/route"
|
||||||
"github.com/fthvgb1/wp-go/app/theme"
|
"github.com/fthvgb1/wp-go/app/theme"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,7 +25,7 @@ var confPath string
|
|||||||
var address string
|
var address string
|
||||||
var intReg = regexp.MustCompile(`^\d`)
|
var intReg = regexp.MustCompile(`^\d`)
|
||||||
|
|
||||||
func init() {
|
func inits() {
|
||||||
flag.StringVar(&confPath, "c", "config.yaml", "config file support json,yaml or url")
|
flag.StringVar(&confPath, "c", "config.yaml", "config file support json,yaml or url")
|
||||||
flag.StringVar(&address, "p", "", "listen address and port")
|
flag.StringVar(&address, "p", "", "listen address and port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -89,54 +85,16 @@ func cronClearCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func flushCache() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err := mail.SendMail([]string{config.GetConfig().Mail.User}, "清空缓存失败", fmt.Sprintf("err:[%s]", r))
|
|
||||||
logs.IfError(err, "发邮件失败")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
cachemanager.Flush()
|
|
||||||
log.Println("all cache flushed")
|
|
||||||
}
|
|
||||||
|
|
||||||
func reloads() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
log.Println(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err := config.InitConfig(confPath)
|
|
||||||
logs.IfError(err, "获取配置文件失败", confPath)
|
|
||||||
err = logs.InitLogger()
|
|
||||||
logs.IfError(err, "日志配置错误")
|
|
||||||
_, err = db.InitDb()
|
|
||||||
logs.IfError(err, "重新读取db失败", config.GetConfig().Mysql)
|
|
||||||
err = wpconfig.InitOptions()
|
|
||||||
logs.IfError(err, "获取网站设置WpOption失败")
|
|
||||||
err = wpconfig.InitTerms()
|
|
||||||
logs.IfError(err, "获取WpTerms表失败")
|
|
||||||
wphandle.LoadPlugins()
|
|
||||||
reload.Reload()
|
|
||||||
flushCache()
|
|
||||||
log.Println("reload complete")
|
|
||||||
}
|
|
||||||
|
|
||||||
func signalNotify() {
|
|
||||||
c := make(chan os.Signal)
|
|
||||||
signal.Notify(c, syscall.SIGUSR1, syscall.SIGUSR2)
|
|
||||||
for {
|
|
||||||
switch <-c {
|
|
||||||
case syscall.SIGUSR1:
|
|
||||||
go reloads()
|
|
||||||
case syscall.SIGUSR2:
|
|
||||||
go flushCache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go signalNotify()
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println(r)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
inits()
|
||||||
|
ossigns.SetConfPath(confPath)
|
||||||
|
go ossigns.SignalNotify()
|
||||||
Gin := route.SetupRouter()
|
Gin := route.SetupRouter()
|
||||||
c := config.GetConfig()
|
c := config.GetConfig()
|
||||||
if c.Ssl.Key != "" && c.Ssl.Cert != "" {
|
if c.Ssl.Key != "" && c.Ssl.Cert != "" {
|
||||||
|
@ -1,183 +0,0 @@
|
|||||||
package reload
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var calls []func()
|
|
||||||
|
|
||||||
var anyMap = safety.NewMap[string, any]()
|
|
||||||
|
|
||||||
type safetyVar[T, A any] struct {
|
|
||||||
Val *safety.Var[val[T]]
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
type val[T any] struct {
|
|
||||||
v T
|
|
||||||
ok bool
|
|
||||||
counter number.Counter[int]
|
|
||||||
}
|
|
||||||
type safetyMap[K comparable, V, A any] struct {
|
|
||||||
val *safety.Map[K, V]
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
var safetyMaps = safety.NewMap[string, any]()
|
|
||||||
var safetyMapLock = sync.Mutex{}
|
|
||||||
|
|
||||||
func GetAnyMapFnBys[K comparable, V, A any](namespace string, fn func(A) V) func(key K, args A) V {
|
|
||||||
m := safetyMapFn[K, V, A](namespace)
|
|
||||||
return func(key K, a A) V {
|
|
||||||
v, ok := m.val.Load(key)
|
|
||||||
if ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
m.mutex.Lock()
|
|
||||||
v, ok = m.val.Load(key)
|
|
||||||
if ok {
|
|
||||||
m.mutex.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
v = fn(a)
|
|
||||||
m.val.Store(key, v)
|
|
||||||
m.mutex.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func safetyMapFn[K comparable, V, A any](namespace string) *safetyMap[K, V, A] {
|
|
||||||
vv, ok := safetyMaps.Load(namespace)
|
|
||||||
var m *safetyMap[K, V, A]
|
|
||||||
if ok {
|
|
||||||
m = vv.(*safetyMap[K, V, A])
|
|
||||||
} else {
|
|
||||||
safetyMapLock.Lock()
|
|
||||||
vv, ok = safetyMaps.Load(namespace)
|
|
||||||
if ok {
|
|
||||||
m = vv.(*safetyMap[K, V, A])
|
|
||||||
} else {
|
|
||||||
m = &safetyMap[K, V, A]{safety.NewMap[K, V](), sync.Mutex{}}
|
|
||||||
Push(func() {
|
|
||||||
m.val.Flush()
|
|
||||||
})
|
|
||||||
safetyMaps.Store(namespace, m)
|
|
||||||
}
|
|
||||||
safetyMapLock.Unlock()
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAnyValMapBy[K comparable, V, A any](namespace string, key K, a A, fn func(A) V) V {
|
|
||||||
m := safetyMapFn[K, V, A](namespace)
|
|
||||||
v, ok := m.val.Load(key)
|
|
||||||
if ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
m.mutex.Lock()
|
|
||||||
v, ok = m.val.Load(key)
|
|
||||||
if ok {
|
|
||||||
m.mutex.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
v = fn(a)
|
|
||||||
m.val.Store(key, v)
|
|
||||||
m.mutex.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyVal[T, A any](namespace string, counter bool) *safetyVar[T, A] {
|
|
||||||
var vv *safetyVar[T, A]
|
|
||||||
vvv, ok := safetyMaps.Load(namespace)
|
|
||||||
if ok {
|
|
||||||
vv = vvv.(*safetyVar[T, A])
|
|
||||||
} else {
|
|
||||||
safetyMapLock.Lock()
|
|
||||||
vvv, ok = safetyMaps.Load(namespace)
|
|
||||||
if ok {
|
|
||||||
vv = vvv.(*safetyVar[T, A])
|
|
||||||
} else {
|
|
||||||
v := val[T]{}
|
|
||||||
if counter {
|
|
||||||
v.counter = number.Counters[int]()
|
|
||||||
}
|
|
||||||
vv = &safetyVar[T, A]{safety.NewVar(v), sync.Mutex{}}
|
|
||||||
Push(func() {
|
|
||||||
vv.Val.Flush()
|
|
||||||
})
|
|
||||||
safetyMaps.Store(namespace, vv)
|
|
||||||
}
|
|
||||||
safetyMapLock.Unlock()
|
|
||||||
}
|
|
||||||
return vv
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAnyValBy[T, A any](namespace string, tryTimes int, a A, fn func(A) (T, bool)) T {
|
|
||||||
var vv = anyVal[T, A](namespace, true)
|
|
||||||
var ok bool
|
|
||||||
v := vv.Val.Load()
|
|
||||||
if v.ok {
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
vv.mutex.Lock()
|
|
||||||
v = vv.Val.Load()
|
|
||||||
if v.ok {
|
|
||||||
vv.mutex.Unlock()
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
v.v, ok = fn(a)
|
|
||||||
times := v.counter()
|
|
||||||
if ok || times >= tryTimes {
|
|
||||||
v.ok = true
|
|
||||||
vv.Val.Store(v)
|
|
||||||
}
|
|
||||||
vv.mutex.Unlock()
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAnyValBys[T, A any](namespace string, a A, fn func(A) T) T {
|
|
||||||
var vv = anyVal[T, A](namespace, false)
|
|
||||||
v := vv.Val.Load()
|
|
||||||
if v.ok {
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
vv.mutex.Lock()
|
|
||||||
v = vv.Val.Load()
|
|
||||||
if v.ok {
|
|
||||||
vv.mutex.Unlock()
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
v.v = fn(a)
|
|
||||||
v.ok = true
|
|
||||||
vv.Val.Store(v)
|
|
||||||
vv.mutex.Unlock()
|
|
||||||
return v.v
|
|
||||||
}
|
|
||||||
|
|
||||||
func Vars[T any](defaults T) *safety.Var[T] {
|
|
||||||
ss := safety.NewVar(defaults)
|
|
||||||
calls = append(calls, func() {
|
|
||||||
ss.Store(defaults)
|
|
||||||
})
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
func VarsBy[T any](fn func() T) *safety.Var[T] {
|
|
||||||
ss := safety.NewVar(fn())
|
|
||||||
calls = append(calls, func() {
|
|
||||||
ss.Store(fn())
|
|
||||||
})
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
func Push(fn ...func()) {
|
|
||||||
calls = append(calls, fn...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reload() {
|
|
||||||
for _, call := range calls {
|
|
||||||
call()
|
|
||||||
}
|
|
||||||
anyMap.Flush()
|
|
||||||
safetyMaps.Flush()
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package route
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fthvgb1/wp-go/app/actions"
|
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/middleware"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
|
||||||
"github.com/fthvgb1/wp-go/app/theme"
|
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
|
||||||
"github.com/gin-contrib/gzip"
|
|
||||||
"github.com/gin-contrib/pprof"
|
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
var hooker []func(r *gin.Engine)
|
|
||||||
|
|
||||||
// Hook 方便插件在init时使用
|
|
||||||
func Hook(fn ...func(r *gin.Engine)) {
|
|
||||||
hooker = append(hooker, fn...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupRouter() *gin.Engine {
|
|
||||||
// Disable Console Color
|
|
||||||
// gin.DisableConsoleColor()
|
|
||||||
r := gin.New()
|
|
||||||
c := config.GetConfig()
|
|
||||||
if len(c.TrustIps) > 0 {
|
|
||||||
err := r.SetTrustedProxies(c.TrustIps)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.HTMLRender = theme.Template()
|
|
||||||
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("/", 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/feed", actions.PostFeed)
|
|
||||||
r.GET("/feed", actions.Feed)
|
|
||||||
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.Push(func() {
|
|
||||||
c := config.GetConfig()
|
|
||||||
siteFlow(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
|
|
||||||
})
|
|
||||||
return r
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/soxfmr/gomail"
|
"gopkg.in/gomail.v2"
|
||||||
"mime"
|
"mime"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
@ -19,9 +19,9 @@ func FlowLimit(maxRequestSleepNum, maxRequestNum int64, sleepTime []time.Duratio
|
|||||||
}
|
}
|
||||||
s := safety.Var[[]time.Duration]{}
|
s := safety.Var[[]time.Duration]{}
|
||||||
s.Store(sleepTime)
|
s.Store(sleepTime)
|
||||||
fn := func(msn, mn int64, st []time.Duration) {
|
fn := func(sleepNum, maxNum int64, st []time.Duration) {
|
||||||
atomic.StoreInt64(&maxRequestSleepNum, msn)
|
atomic.StoreInt64(&maxRequestSleepNum, sleepNum)
|
||||||
atomic.StoreInt64(&maxRequestNum, mn)
|
atomic.StoreInt64(&maxRequestNum, maxNum)
|
||||||
s.Store(st)
|
s.Store(st)
|
||||||
}
|
}
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
@ -25,14 +25,25 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
|
|||||||
fn(num)
|
fn(num)
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
if atomic.LoadInt64(m.limitNum) <= 0 {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
ip := c.ClientIP()
|
ip := c.ClientIP()
|
||||||
s := false
|
|
||||||
m.mux.RLock()
|
m.mux.RLock()
|
||||||
i, ok := m.m[ip]
|
i, ok := m.m[ip]
|
||||||
m.mux.RUnlock()
|
m.mux.RUnlock()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
m.mux.Lock()
|
||||||
|
i = new(int64)
|
||||||
|
m.m[ip] = i
|
||||||
|
m.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
ii := atomic.LoadInt64(i)
|
ii := atomic.LoadInt64(i)
|
||||||
if s && ii > 0 {
|
if ii > 0 {
|
||||||
atomic.AddInt64(i, -1)
|
atomic.AddInt64(i, -1)
|
||||||
if atomic.LoadInt64(i) == 0 {
|
if atomic.LoadInt64(i) == 0 {
|
||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
@ -42,20 +53,12 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !ok {
|
if atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) {
|
||||||
m.mux.Lock()
|
|
||||||
i = new(int64)
|
|
||||||
m.m[ip] = i
|
|
||||||
m.mux.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt64(m.limitNum) > 0 && atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) {
|
|
||||||
c.Status(http.StatusForbidden)
|
c.Status(http.StatusForbidden)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
atomic.AddInt64(i, 1)
|
atomic.AddInt64(i, 1)
|
||||||
s = true
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}, fn
|
}, fn
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SearchLimit(num int64) func(ctx *gin.Context) {
|
func SearchLimit(num int64) func(ctx *gin.Context) {
|
||||||
fn, reFn := IpLimit(num)
|
fn, reFn := IpLimit(num)
|
||||||
reload.Push(func() {
|
reload.Append(func() {
|
||||||
reFn(config.GetConfig().SingleIpSearchNum)
|
reFn(config.GetConfig().SingleIpSearchNum)
|
||||||
})
|
}, "search-ip-limit-number")
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
if c.Query("s") != "" {
|
if c.Query("s") != "" {
|
||||||
fn(c)
|
fn(c)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -19,7 +19,7 @@ func ValidateServerNames() func(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
})
|
}, "site-names")
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
m := sites.Load()
|
m := sites.Load()
|
||||||
|
69
app/ossigns/signs.go
Normal file
69
app/ossigns/signs.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package ossigns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/mail"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/db"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
|
"github.com/fthvgb1/wp-go/app/plugins/wphandle"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
|
"github.com/fthvgb1/wp-go/signs"
|
||||||
|
"log"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var confPath string
|
||||||
|
|
||||||
|
func SetConfPath(path string) {
|
||||||
|
confPath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlushCache() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err := mail.SendMail([]string{config.GetConfig().Mail.User}, "清空缓存失败", fmt.Sprintf("err:[%s]", r))
|
||||||
|
logs.IfError(err, "发邮件失败")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
cachemanager.Flush()
|
||||||
|
log.Println("all cache flushed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reloads() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Println(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err := config.InitConfig(confPath)
|
||||||
|
logs.IfError(err, "获取配置文件失败", confPath)
|
||||||
|
err = logs.InitLogger()
|
||||||
|
logs.IfError(err, "日志配置错误")
|
||||||
|
_, err = db.InitDb()
|
||||||
|
logs.IfError(err, "重新读取db失败", config.GetConfig().Mysql)
|
||||||
|
err = wpconfig.InitOptions()
|
||||||
|
logs.IfError(err, "获取网站设置WpOption失败")
|
||||||
|
err = wpconfig.InitTerms()
|
||||||
|
logs.IfError(err, "获取WpTerms表失败")
|
||||||
|
wphandle.LoadPlugins()
|
||||||
|
reload.Reloads("themeArgAndConfig")
|
||||||
|
FlushCache()
|
||||||
|
log.Println("reload complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignalNotify() {
|
||||||
|
rel := func() bool {
|
||||||
|
go Reloads()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
flu := func() bool {
|
||||||
|
go FlushCache()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
signs.Install(syscall.SIGUSR1, rel, "reload")
|
||||||
|
signs.Install(syscall.SIGUSR2, flu, "flush")
|
||||||
|
signs.Wait()
|
||||||
|
}
|
148
app/pkg/cache/cache.go
vendored
148
app/pkg/cache/cache.go
vendored
@ -2,85 +2,96 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/cache"
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var postContextCache *cache.MapCache[uint64, dao.PostContext]
|
|
||||||
var categoryAndTagsCaches *cache.MapCache[string, []models.TermsMy]
|
|
||||||
var recentPostsCaches *cache.VarCache[[]models.Posts]
|
|
||||||
var recentCommentsCaches *cache.VarCache[[]models.Comments]
|
|
||||||
var postCommentCaches *cache.MapCache[uint64, []uint64]
|
|
||||||
var postsCache *cache.MapCache[uint64, models.Posts]
|
|
||||||
|
|
||||||
var postMetaCache *cache.MapCache[uint64, map[string]any]
|
|
||||||
|
|
||||||
var monthPostsCache *cache.MapCache[string, []uint64]
|
|
||||||
var postListIdsCache *cache.MapCache[string, dao.PostIds]
|
|
||||||
var searchPostIdsCache *cache.MapCache[string, dao.PostIds]
|
|
||||||
var maxPostIdCache *cache.VarCache[uint64]
|
|
||||||
|
|
||||||
var usersCache *cache.MapCache[uint64, models.Users]
|
|
||||||
var usersNameCache *cache.MapCache[string, models.Users]
|
|
||||||
var commentsCache *cache.MapCache[uint64, models.Comments]
|
|
||||||
|
|
||||||
var feedCache *cache.VarCache[[]string]
|
|
||||||
|
|
||||||
var postFeedCache *cache.MapCache[string, string]
|
|
||||||
|
|
||||||
var commentsFeedCache *cache.VarCache[[]string]
|
|
||||||
|
|
||||||
var newCommentCache *cache.MapCache[string, string]
|
|
||||||
|
|
||||||
var allUsernameCache *cache.VarCache[map[string]struct{}]
|
|
||||||
|
|
||||||
func InitActionsCommonCache() {
|
func InitActionsCommonCache() {
|
||||||
c := config.GetConfig()
|
c := config.GetConfig()
|
||||||
|
|
||||||
searchPostIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.SearchPostCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.SearchPostCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postListIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.PostListCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.PostListCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
monthPostsCache = cachemanager.MapCacheBy[string](dao.MonthPost, c.CacheTime.MonthPostCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.MonthPostCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postContextCache = cachemanager.MapCacheBy[uint64](dao.GetPostContext, c.CacheTime.ContextPostCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.ContextPostCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postsCache = cachemanager.MapBatchCacheBy(dao.GetPostsByIds, c.CacheTime.PostDataCacheTime)
|
cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.PostDataCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postMetaCache = cachemanager.MapBatchCacheBy(dao.GetPostMetaByPostIds, c.CacheTime.PostDataCacheTime)
|
cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.PostDataCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
categoryAndTagsCaches = cachemanager.MapCacheBy[string](dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CategoryCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime)
|
cachemanager.NewVarMemoryCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime, "recentPosts", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.RecentPostCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime)
|
cachemanager.NewVarMemoryCache(RecentComment, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.RecentCommentsCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postCommentCaches = cachemanager.MapCacheBy[uint64](dao.PostComments, c.CacheTime.PostCommentsCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||||
|
})
|
||||||
|
|
||||||
maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime)
|
cachemanager.NewMemoryMapCache(nil, PostTopComments, 30*time.Second, "PostCommentsIds", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||||
|
})
|
||||||
|
cachemanager.NewMemoryMapCache(nil, dao.PostTopCommentNum, 30*time.Second, "postTopCommentsNum", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||||
|
})
|
||||||
|
|
||||||
usersCache = cachemanager.MapCacheBy[uint64](dao.GetUserById, c.CacheTime.UserInfoCacheTime)
|
cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, time.Hour, "postCommentData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CommentsCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
usersNameCache = cachemanager.MapCacheBy[string](dao.GetUserByName, c.CacheTime.UserInfoCacheTime)
|
cachemanager.NewMemoryMapCache(dao.CommentChildren, nil, time.Minute, "commentChildren", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||||
|
})
|
||||||
|
|
||||||
commentsCache = cachemanager.MapBatchCacheBy(dao.GetCommentByIds, c.CacheTime.CommentsCacheTime)
|
cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.MaxPostIdCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
allUsernameCache = cache.NewVarCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime)
|
cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
feedCache = cache.NewVarCache(feed, time.Hour)
|
cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameToUserData", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
postFeedCache = cachemanager.MapCacheBy[string](postFeed, time.Hour)
|
cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
commentsFeedCache = cache.NewVarCache(commentsFeed, time.Hour)
|
cachemanager.NewVarMemoryCache(SiteFeed, time.Hour, "siteFeed")
|
||||||
|
|
||||||
newCommentCache = cachemanager.MapCacheBy[string, string](nil, 15*time.Minute)
|
cachemanager.NewMemoryMapCache(nil, PostFeed, time.Hour, "postFeed")
|
||||||
|
|
||||||
|
cachemanager.NewVarMemoryCache(CommentsFeed, time.Hour, "commentsFeed")
|
||||||
|
|
||||||
|
cachemanager.NewMemoryMapCache[string, string](nil, nil, 15*time.Minute, "NewComment")
|
||||||
|
|
||||||
InitFeed()
|
InitFeed()
|
||||||
}
|
}
|
||||||
@ -91,19 +102,19 @@ type Arch struct {
|
|||||||
month time.Month
|
month time.Month
|
||||||
}
|
}
|
||||||
|
|
||||||
var arch = safety.NewVar(Arch{
|
var arch = reload.Vars(Arch{
|
||||||
fn: dao.Archives,
|
fn: dao.Archives,
|
||||||
})
|
}, "archives-year-month-data")
|
||||||
|
|
||||||
func Archives(ctx context.Context) []models.PostArchive {
|
func Archives(ctx context.Context) []models.PostArchive {
|
||||||
a := arch.Load()
|
a := arch.Load()
|
||||||
data := a.data
|
data := a.data
|
||||||
l := len(data)
|
l := len(data)
|
||||||
m := time.Now().Month()
|
m := time.Now().Month()
|
||||||
if l > 0 && a.month != m || l < 1 {
|
if l < 1 || a.month != m {
|
||||||
r, err := a.fn(ctx)
|
r, err := a.fn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error(err, "set cache fail")
|
logs.Error(err, "set cache Archives fail")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a.month = m
|
a.month = m
|
||||||
@ -113,30 +124,3 @@ func Archives(ctx context.Context) []models.PostArchive {
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// CategoriesTags categories or tags
|
|
||||||
//
|
|
||||||
// t is constraints.Tag or constraints.Category
|
|
||||||
func CategoriesTags(ctx context.Context, t ...string) []models.TermsMy {
|
|
||||||
tt := ""
|
|
||||||
if len(t) > 0 {
|
|
||||||
tt = t[0]
|
|
||||||
}
|
|
||||||
r, err := categoryAndTagsCaches.GetCache(ctx, tt, time.Second, ctx, tt)
|
|
||||||
logs.IfError(err, "get category fail")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
func AllCategoryTagsNames(ctx context.Context, t ...string) map[string]struct{} {
|
|
||||||
tt := ""
|
|
||||||
if len(t) > 0 {
|
|
||||||
tt = t[0]
|
|
||||||
}
|
|
||||||
r, err := categoryAndTagsCaches.GetCache(ctx, tt, time.Second, ctx, tt)
|
|
||||||
if err != nil {
|
|
||||||
logs.Error(err, "get category fail")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return slice.ToMap(r, func(t models.TermsMy) (string, struct{}) {
|
|
||||||
return t.Name, struct{}{}
|
|
||||||
}, true)
|
|
||||||
}
|
|
||||||
|
39
app/pkg/cache/categoryandtag.go
vendored
Normal file
39
app/pkg/cache/categoryandtag.go
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CategoriesTags get all categories or tags
|
||||||
|
//
|
||||||
|
// query func see dao.CategoriesAndTags
|
||||||
|
//
|
||||||
|
// t is constraints.Tag or constraints.Category
|
||||||
|
func CategoriesTags(ctx context.Context, t ...string) []models.TermsMy {
|
||||||
|
tt := ""
|
||||||
|
if len(t) > 0 {
|
||||||
|
tt = t[0]
|
||||||
|
}
|
||||||
|
r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second)
|
||||||
|
logs.IfError(err, "get category fail")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
func AllCategoryTagsNames(ctx context.Context, t ...string) map[string]struct{} {
|
||||||
|
tt := ""
|
||||||
|
if len(t) > 0 {
|
||||||
|
tt = t[0]
|
||||||
|
}
|
||||||
|
r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err, "get category fail")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return slice.ToMap(r, func(t models.TermsMy) (string, struct{}) {
|
||||||
|
return t.Name, struct{}{}
|
||||||
|
}, true)
|
||||||
|
}
|
104
app/pkg/cache/comments.go
vendored
104
app/pkg/cache/comments.go
vendored
@ -2,16 +2,23 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/cache"
|
"github.com/fthvgb1/wp-go/cache"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RecentComments query func see RecentComment
|
||||||
func RecentComments(ctx context.Context, n int) (r []models.Comments) {
|
func RecentComments(ctx context.Context, n int) (r []models.Comments) {
|
||||||
nn := number.Max(n, 10)
|
nn := number.Max(n, 10)
|
||||||
r, err := recentCommentsCaches.GetCache(ctx, time.Second, ctx, nn)
|
r, err := cachemanager.GetVarVal[[]models.Comments]("recentComments", ctx, time.Second, ctx, nn)
|
||||||
if len(r) > n {
|
if len(r) > n {
|
||||||
r = r[0:n]
|
r = r[0:n]
|
||||||
}
|
}
|
||||||
@ -19,22 +26,99 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) {
|
// PostTopLevelCommentIds query func see PostTopComments
|
||||||
ids, err := postCommentCaches.GetCache(ctx, Id, time.Second, ctx, Id)
|
func PostTopLevelCommentIds(ctx context.Context, postId uint64, page, limit, total int, order string, a ...any) ([]uint64, error) {
|
||||||
if err != nil {
|
var key string
|
||||||
return nil, err
|
if len(a) > 0 {
|
||||||
|
key = helper.ParseArgs("", a...)
|
||||||
}
|
}
|
||||||
return GetCommentByIds(ctx, ids)
|
if key == "" {
|
||||||
|
key = fmt.Sprintf("%d-%d-%d-%d-%s", postId, page, limit, total, order)
|
||||||
|
}
|
||||||
|
return cachemanager.GetBy[[]uint64]("PostCommentsIds", ctx,
|
||||||
|
key, time.Second, postId, page, limit, 0, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCommentById query func see dao.GetCommentByIds
|
||||||
func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
|
func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
|
||||||
return commentsCache.GetCache(ctx, id, time.Second, ctx, id)
|
return cachemanager.GetBy[models.Comments]("postCommentData", ctx, id, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommentByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
|
// GetCommentDataByIds query func see dao.GetCommentByIds
|
||||||
return commentsCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
|
func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
|
||||||
|
return cachemanager.GetBatchBy[models.Comments]("postCommentData", ctx, ids, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommentCache() *cache.MapCache[string, string] {
|
func NewCommentCache() *cache.MapCache[string, string] {
|
||||||
return newCommentCache
|
r, _ := cachemanager.GetMapCache[string, string]("NewComment")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostTopComments(ctx context.Context, _ string, a ...any) ([]uint64, error) {
|
||||||
|
postId := a[0].(uint64)
|
||||||
|
page := a[1].(int)
|
||||||
|
limit := a[2].(int)
|
||||||
|
total := a[3].(int)
|
||||||
|
order := helper.ParseArgs("", a...)
|
||||||
|
if order == "" {
|
||||||
|
order = wpconfig.GetOption("comment_order")
|
||||||
|
}
|
||||||
|
v, _, err := dao.PostCommentsIds(ctx, postId, page, limit, total, order)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecentComment(ctx context.Context, a ...any) (r []models.Comments, err error) {
|
||||||
|
r, err = dao.RecentComments(ctx, a...)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
for i, comment := range r {
|
||||||
|
r[i].CommentAuthorUrl, err = GetCommentUrl(ctx, comment.CommentId, comment.CommentPostId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommentUrl(ctx context.Context, commentId, postId uint64) (string, error) {
|
||||||
|
if wpconfig.GetOption("page_comments") != "1" {
|
||||||
|
return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil
|
||||||
|
}
|
||||||
|
commentsPerPage := str.ToInteger(wpconfig.GetOption("comments_per_page"), 5)
|
||||||
|
topCommentId, err := AncestorCommentId(ctx, commentId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
totalNum, err := cachemanager.GetBy[int]("postTopCommentsNum", ctx, postId, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if totalNum <= commentsPerPage {
|
||||||
|
return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil
|
||||||
|
}
|
||||||
|
num, err := dao.PreviousCommentNum(ctx, topCommentId, postId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
order := wpconfig.GetOption("comment_order")
|
||||||
|
page := number.DivideCeil(num+1, commentsPerPage)
|
||||||
|
if order == "desc" {
|
||||||
|
page = number.DivideCeil(totalNum-num, commentsPerPage)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/p/%d/comment-page-%d/#comment-%d", postId, page, commentId), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AncestorCommentId(ctx context.Context, commentId uint64) (uint64, error) {
|
||||||
|
comment, err := GetCommentById(ctx, commentId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if comment.CommentParent == 0 {
|
||||||
|
return comment.CommentId, nil
|
||||||
|
}
|
||||||
|
return AncestorCommentId(ctx, comment.CommentParent)
|
||||||
}
|
}
|
||||||
|
55
app/pkg/cache/feed.go
vendored
55
app/pkg/cache/feed.go
vendored
@ -1,6 +1,8 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
@ -8,11 +10,11 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/cache"
|
"github.com/fthvgb1/wp-go/cache"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/plugin/digest"
|
"github.com/fthvgb1/wp-go/plugin/digest"
|
||||||
"github.com/fthvgb1/wp-go/rss2"
|
"github.com/fthvgb1/wp-go/rss2"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -33,20 +35,25 @@ func InitFeed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommentsFeedCache query func see CommentsFeed
|
||||||
func CommentsFeedCache() *cache.VarCache[[]string] {
|
func CommentsFeedCache() *cache.VarCache[[]string] {
|
||||||
return commentsFeedCache
|
r, _ := cachemanager.GetVarCache[[]string]("commentsFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FeedCache query func see SiteFeed
|
||||||
func FeedCache() *cache.VarCache[[]string] {
|
func FeedCache() *cache.VarCache[[]string] {
|
||||||
return feedCache
|
r, _ := cachemanager.GetVarCache[[]string]("siteFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostFeedCache query func see PostFeed
|
||||||
func PostFeedCache() *cache.MapCache[string, string] {
|
func PostFeedCache() *cache.MapCache[string, string] {
|
||||||
return postFeedCache
|
r, _ := cachemanager.GetMapCache[string, string]("postFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func feed(arg ...any) (xml []string, err error) {
|
func SiteFeed(c context.Context, _ ...any) (xml []string, err error) {
|
||||||
c := arg[0].(*gin.Context)
|
|
||||||
r := RecentPosts(c, 10)
|
r := RecentPosts(c, 10)
|
||||||
ids := slice.Map(r, func(t models.Posts) uint64 {
|
ids := slice.Map(r, func(t models.Posts) uint64 {
|
||||||
return t.Id
|
return t.Id
|
||||||
@ -92,9 +99,7 @@ func feed(arg ...any) (xml []string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func postFeed(arg ...any) (x string, err error) {
|
func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
|
||||||
c := arg[0].(*gin.Context)
|
|
||||||
id := arg[1].(string)
|
|
||||||
ID := str.ToInteger[uint64](id, 0)
|
ID := str.ToInteger[uint64](id, 0)
|
||||||
maxId, err := GetMaxPostId(c)
|
maxId, err := GetMaxPostId(c)
|
||||||
logs.IfError(err, "get max post id")
|
logs.IfError(err, "get max post id")
|
||||||
@ -105,7 +110,12 @@ func postFeed(arg ...any) (x string, err error) {
|
|||||||
if post.Id == 0 || err != nil {
|
if post.Id == 0 || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
comments, err := PostComments(c, post.Id)
|
limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10)
|
||||||
|
ids, err := PostTopLevelCommentIds(c, ID, 1, limit, 0, "desc", "latest-comment")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
comments, err := GetCommentDataByIds(c, ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -121,10 +131,14 @@ func postFeed(arg ...any) (x string, err error) {
|
|||||||
wpposts.PasswdProjectContent(&post)
|
wpposts.PasswdProjectContent(&post)
|
||||||
if len(comments) > 0 {
|
if len(comments) > 0 {
|
||||||
t := comments[len(comments)-1]
|
t := comments[len(comments)-1]
|
||||||
|
u, err := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
rs.Items = []rss2.Item{
|
rs.Items = []rss2.Item{
|
||||||
{
|
{
|
||||||
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: fmt.Sprintf("%s%s", site, u),
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
||||||
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
||||||
@ -135,9 +149,14 @@ func postFeed(arg ...any) (x string, err error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
|
rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
|
||||||
|
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if er != nil {
|
||||||
|
err = errors.Join(err, er)
|
||||||
|
return rss2.Item{}
|
||||||
|
}
|
||||||
return rss2.Item{
|
return rss2.Item{
|
||||||
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: fmt.Sprintf("%s%s", site, u),
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
||||||
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
||||||
@ -150,15 +169,14 @@ func postFeed(arg ...any) (x string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func commentsFeed(args ...any) (r []string, err error) {
|
func CommentsFeed(c context.Context, _ ...any) (r []string, err error) {
|
||||||
c := args[0].(*gin.Context)
|
|
||||||
commens := RecentComments(c, 10)
|
commens := RecentComments(c, 10)
|
||||||
rs := templateRss
|
rs := templateRss
|
||||||
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
|
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
|
||||||
rs.LastBuildDate = time.Now().Format(timeFormat)
|
rs.LastBuildDate = time.Now().Format(timeFormat)
|
||||||
site := wpconfig.GetOption("siteurl")
|
site := wpconfig.GetOption("siteurl")
|
||||||
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
|
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
|
||||||
com, err := GetCommentByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
|
com, err := GetCommentDataByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
|
||||||
return t.CommentId
|
return t.CommentId
|
||||||
}))
|
}))
|
||||||
if nil != err {
|
if nil != err {
|
||||||
@ -175,9 +193,14 @@ func commentsFeed(args ...any) (r []string, err error) {
|
|||||||
} else {
|
} else {
|
||||||
content = digest.StripTags(t.CommentContent, "")
|
content = digest.StripTags(t.CommentContent, "")
|
||||||
}
|
}
|
||||||
|
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if er != nil {
|
||||||
|
errors.Join(err, er)
|
||||||
|
}
|
||||||
|
u = str.Join(site, u)
|
||||||
return rss2.Item{
|
return rss2.Item{
|
||||||
Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle),
|
Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: u,
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
Description: desc,
|
Description: desc,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
||||||
|
14
app/pkg/cache/postmeta.go
vendored
14
app/pkg/cache/postmeta.go
vendored
@ -2,14 +2,16 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostMetaByPostIds(ctx context.Context, ids []uint64) (r []map[string]any, err error) {
|
// GetPostMetaByPostIds query func see dao.GetPostMetaByPostIds
|
||||||
r, err = postMetaCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
|
func GetPostMetaByPostIds(ctx context.Context, ids []uint64) ([]map[string]any, error) {
|
||||||
return
|
return cachemanager.GetBatchBy[map[string]any]("postMetaData", ctx, ids, time.Second)
|
||||||
}
|
}
|
||||||
func GetPostMetaByPostId(ctx context.Context, id uint64) (r map[string]any, err error) {
|
|
||||||
r, err = postMetaCache.GetCache(ctx, id, time.Second, ctx, id)
|
// GetPostMetaByPostId query func see dao.GetPostMetaByPostIds
|
||||||
return
|
func GetPostMetaByPostId(ctx context.Context, id uint64) (map[string]any, error) {
|
||||||
|
return cachemanager.GetBy[map[string]any]("postMetaData", ctx, id, time.Second)
|
||||||
}
|
}
|
||||||
|
29
app/pkg/cache/posts.go
vendored
29
app/pkg/cache/posts.go
vendored
@ -3,26 +3,30 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetPostById query func see dao.GetPostsByIds
|
||||||
func GetPostById(ctx context.Context, id uint64) (models.Posts, error) {
|
func GetPostById(ctx context.Context, id uint64) (models.Posts, error) {
|
||||||
return postsCache.GetCache(ctx, id, time.Second, ctx, id)
|
return cachemanager.GetBy[models.Posts]("postData", ctx, id, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPostsByIds query func see dao.GetPostsByIds
|
||||||
func GetPostsByIds(ctx context.Context, ids []uint64) ([]models.Posts, error) {
|
func GetPostsByIds(ctx context.Context, ids []uint64) ([]models.Posts, error) {
|
||||||
return postsCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
|
return cachemanager.GetBatchBy[models.Posts]("postData", ctx, ids, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchPost query func see dao.SearchPostIds
|
||||||
func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
|
func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
|
||||||
ids, err := searchPostIdsCache.GetCache(ctx, key, time.Second, args...)
|
ids, err := cachemanager.GetBy[dao.PostIds]("searchPostIds", ctx, key, time.Second, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -31,8 +35,9 @@ func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostLists query func see dao.SearchPostIds
|
||||||
func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
|
func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
|
||||||
ids, err := postListIdsCache.GetCache(ctx, key, time.Second, args...)
|
ids, err := cachemanager.GetBy[dao.PostIds]("listPostIds", ctx, key, time.Second, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -41,15 +46,17 @@ func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxPostId(ctx *gin.Context) (uint64, error) {
|
// GetMaxPostId query func see dao.GetMaxPostId
|
||||||
return maxPostIdCache.GetCache(ctx, time.Second, ctx)
|
func GetMaxPostId(ctx context.Context) (uint64, error) {
|
||||||
|
return cachemanager.GetVarVal[uint64]("maxPostId", ctx, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentPosts query func see dao.RecentPosts
|
||||||
func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
|
func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
|
||||||
nn := n
|
nn := n
|
||||||
feedNum := str.ToInteger(wpconfig.GetOption("posts_per_rss"), 10)
|
feedNum := str.ToInteger(wpconfig.GetOption("posts_per_rss"), 10)
|
||||||
nn = number.Max(n, feedNum)
|
nn = number.Max(n, feedNum)
|
||||||
r, err := recentPostsCaches.GetCache(ctx, time.Second, ctx, nn)
|
r, err := cachemanager.GetVarVal[[]models.Posts]("recentPosts", ctx, time.Second, nn)
|
||||||
if n < len(r) {
|
if n < len(r) {
|
||||||
r = r[:n]
|
r = r[:n]
|
||||||
}
|
}
|
||||||
@ -57,8 +64,9 @@ func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContextPost query func see dao.GetPostContext
|
||||||
func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.Posts, err error) {
|
func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.Posts, err error) {
|
||||||
postCtx, err := postContextCache.GetCache(ctx, id, time.Second, ctx, date)
|
postCtx, err := cachemanager.GetBy[dao.PostContext]("postContext", ctx, id, time.Second, date)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Posts{}, models.Posts{}, err
|
return models.Posts{}, models.Posts{}, err
|
||||||
}
|
}
|
||||||
@ -67,8 +75,9 @@ func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMonthPostIds query func see dao.MonthPost
|
||||||
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.Posts, total int, err error) {
|
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.Posts, total int, err error) {
|
||||||
res, err := monthPostsCache.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, ctx, year, month)
|
res, err := cachemanager.GetBy[[]uint64]("monthPostIds", ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
20
app/pkg/cache/users.go
vendored
20
app/pkg/cache/users.go
vendored
@ -4,27 +4,23 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getUserById(a ...any) (r models.Users, err error) {
|
// GetUserByName query func see dao.GetUserByName
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
uid := a[1].(uint64)
|
|
||||||
r, err = model.FindOneById[models.Users](ctx, uid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUserByName(ctx context.Context, username string) (models.Users, error) {
|
func GetUserByName(ctx context.Context, username string) (models.Users, error) {
|
||||||
return usersNameCache.GetCache(ctx, username, time.Second, ctx, username)
|
return cachemanager.GetBy[models.Users]("usernameToUserData", ctx, username, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllUsername(ctx context.Context) (map[string]struct{}, error) {
|
// GetAllUsername query func see dao.AllUsername
|
||||||
return allUsernameCache.GetCache(ctx, time.Second, ctx)
|
func GetAllUsername(ctx context.Context) (map[string]uint64, error) {
|
||||||
|
return cachemanager.GetVarVal[map[string]uint64]("allUsername", ctx, time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserById query func see dao.GetUserById
|
||||||
func GetUserById(ctx context.Context, uid uint64) models.Users {
|
func GetUserById(ctx context.Context, uid uint64) models.Users {
|
||||||
r, err := usersCache.GetCache(ctx, uid, time.Second, ctx, uid)
|
r, err := cachemanager.GetBy[models.Users]("userData", ctx, uid, time.Second)
|
||||||
logs.IfError(err, "get user", uid)
|
logs.IfError(err, "get user", uid)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -46,23 +46,24 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CacheTime struct {
|
type CacheTime struct {
|
||||||
CacheControl time.Duration `yaml:"cacheControl" json:"cacheControl,omitempty"`
|
CacheControl time.Duration `yaml:"cacheControl" json:"cacheControl,omitempty"`
|
||||||
RecentPostCacheTime time.Duration `yaml:"recentPostCacheTime" json:"recentPostCacheTime,omitempty"`
|
RecentPostCacheTime time.Duration `yaml:"recentPostCacheTime" json:"recentPostCacheTime,omitempty"`
|
||||||
CategoryCacheTime time.Duration `yaml:"categoryCacheTime" json:"categoryCacheTime,omitempty"`
|
CategoryCacheTime time.Duration `yaml:"categoryCacheTime" json:"categoryCacheTime,omitempty"`
|
||||||
ArchiveCacheTime time.Duration `yaml:"archiveCacheTime" json:"archiveCacheTime,omitempty"`
|
ArchiveCacheTime time.Duration `yaml:"archiveCacheTime" json:"archiveCacheTime,omitempty"`
|
||||||
ContextPostCacheTime time.Duration `yaml:"contextPostCacheTime" json:"contextPostCacheTime,omitempty"`
|
ContextPostCacheTime time.Duration `yaml:"contextPostCacheTime" json:"contextPostCacheTime,omitempty"`
|
||||||
RecentCommentsCacheTime time.Duration `yaml:"recentCommentsCacheTime" json:"recentCommentsCacheTime,omitempty"`
|
RecentCommentsCacheTime time.Duration `yaml:"recentCommentsCacheTime" json:"recentCommentsCacheTime,omitempty"`
|
||||||
DigestCacheTime time.Duration `yaml:"digestCacheTime" json:"digestCacheTime,omitempty"`
|
DigestCacheTime time.Duration `yaml:"digestCacheTime" json:"digestCacheTime,omitempty"`
|
||||||
PostListCacheTime time.Duration `yaml:"postListCacheTime" json:"postListCacheTime,omitempty"`
|
PostListCacheTime time.Duration `yaml:"postListCacheTime" json:"postListCacheTime,omitempty"`
|
||||||
SearchPostCacheTime time.Duration `yaml:"searchPostCacheTime" json:"searchPostCacheTime,omitempty"`
|
SearchPostCacheTime time.Duration `yaml:"searchPostCacheTime" json:"searchPostCacheTime,omitempty"`
|
||||||
MonthPostCacheTime time.Duration `yaml:"monthPostCacheTime" json:"monthPostCacheTime,omitempty"`
|
MonthPostCacheTime time.Duration `yaml:"monthPostCacheTime" json:"monthPostCacheTime,omitempty"`
|
||||||
PostDataCacheTime time.Duration `yaml:"postDataCacheTime" json:"postDataCacheTime,omitempty"`
|
PostDataCacheTime time.Duration `yaml:"postDataCacheTime" json:"postDataCacheTime,omitempty"`
|
||||||
PostCommentsCacheTime time.Duration `yaml:"postCommentsCacheTime" json:"postCommentsCacheTime,omitempty"`
|
PostCommentsCacheTime time.Duration `yaml:"postCommentsCacheTime" json:"postCommentsCacheTime,omitempty"`
|
||||||
CrontabClearCacheTime time.Duration `yaml:"crontabClearCacheTime" json:"crontabClearCacheTime,omitempty"`
|
CrontabClearCacheTime time.Duration `yaml:"crontabClearCacheTime" json:"crontabClearCacheTime,omitempty"`
|
||||||
MaxPostIdCacheTime time.Duration `yaml:"maxPostIdCacheTime" json:"maxPostIdCacheTime,omitempty"`
|
MaxPostIdCacheTime time.Duration `yaml:"maxPostIdCacheTime" json:"maxPostIdCacheTime,omitempty"`
|
||||||
UserInfoCacheTime time.Duration `yaml:"userInfoCacheTime" json:"userInfoCacheTime,omitempty"`
|
UserInfoCacheTime time.Duration `yaml:"userInfoCacheTime" json:"userInfoCacheTime,omitempty"`
|
||||||
CommentsCacheTime time.Duration `yaml:"commentsCacheTime" json:"commentsCacheTime,omitempty"`
|
CommentsCacheTime time.Duration `yaml:"commentsCacheTime" json:"commentsCacheTime,omitempty"`
|
||||||
SleepTime []time.Duration `yaml:"sleepTime" json:"sleepTime,omitempty"`
|
SleepTime []time.Duration `yaml:"sleepTime" json:"sleepTime,omitempty"`
|
||||||
|
CommentsIncreaseUpdateTime time.Duration `yaml:"commentsIncreaseUpdateTime" json:"commentsIncreaseUpdateTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ssl struct {
|
type Ssl struct {
|
||||||
@ -84,6 +85,14 @@ type Mysql struct {
|
|||||||
Pool Pool `yaml:"pool" json:"pool"`
|
Pool Pool `yaml:"pool" json:"pool"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCustomizedConfig[T any]() (T, error) {
|
||||||
|
var r T
|
||||||
|
err := yaml.Unmarshal(fileData.Load(), &r)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileData = safety.NewVar([]byte{})
|
||||||
|
|
||||||
func InitConfig(conf string) error {
|
func InitConfig(conf string) error {
|
||||||
if conf == "" {
|
if conf == "" {
|
||||||
conf = "config.yaml"
|
conf = "config.yaml"
|
||||||
@ -95,6 +104,7 @@ func InitConfig(conf string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer get.Body.Close()
|
||||||
file, err = io.ReadAll(get.Body)
|
file, err = io.ReadAll(get.Body)
|
||||||
} else {
|
} else {
|
||||||
file, err = os.ReadFile(conf)
|
file, err = os.ReadFile(conf)
|
||||||
@ -102,6 +112,7 @@ func InitConfig(conf string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fileData.Store(file)
|
||||||
var c Config
|
var c Config
|
||||||
err = yaml.Unmarshal(file, &c)
|
err = yaml.Unmarshal(file, &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,17 +2,21 @@ package dao
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecentComments
|
// RecentComments
|
||||||
// param context.Context
|
// param context.Context
|
||||||
func RecentComments(a ...any) (r []models.Comments, err error) {
|
func RecentComments(ctx context.Context, a ...any) (r []models.Comments, err error) {
|
||||||
ctx := a[0].(context.Context)
|
n := helper.ParseArgs(10, a...)
|
||||||
n := a[1].(int)
|
|
||||||
return model.Finds[models.Comments](ctx, model.Conditions(
|
return model.Finds[models.Comments](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{
|
model.Where(model.SqlBuilder{
|
||||||
{"comment_approved", "1"},
|
{"comment_approved", "1"},
|
||||||
@ -28,10 +32,8 @@ func RecentComments(a ...any) (r []models.Comments, err error) {
|
|||||||
// PostComments
|
// PostComments
|
||||||
// param1 context.Context
|
// param1 context.Context
|
||||||
// param2 postId
|
// param2 postId
|
||||||
func PostComments(args ...any) ([]uint64, error) {
|
func PostComments(ctx context.Context, postId uint64, _ ...any) ([]uint64, error) {
|
||||||
ctx := args[0].(context.Context)
|
r, err := model.ChunkFind[models.Comments](ctx, 300, model.Conditions(
|
||||||
postId := args[1].(uint64)
|
|
||||||
r, err := model.Finds[models.Comments](ctx, model.Conditions(
|
|
||||||
model.Where(model.SqlBuilder{
|
model.Where(model.SqlBuilder{
|
||||||
{"comment_approved", "1"},
|
{"comment_approved", "1"},
|
||||||
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
||||||
@ -50,17 +52,132 @@ func PostComments(args ...any) ([]uint64, error) {
|
|||||||
}), err
|
}), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommentByIds(args ...any) (map[uint64]models.Comments, error) {
|
func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]models.Comments, error) {
|
||||||
ctx := args[0].(context.Context)
|
if len(ids) < 1 {
|
||||||
ids := args[1].([]uint64)
|
return nil, nil
|
||||||
m := make(map[uint64]models.Comments)
|
|
||||||
r, err := model.SimpleFind[models.Comments](ctx, model.SqlBuilder{
|
|
||||||
{"comment_ID", "in", ""}, {"comment_approved", "1"},
|
|
||||||
}, "*", slice.ToAnySlice(ids))
|
|
||||||
if err != nil {
|
|
||||||
return m, err
|
|
||||||
}
|
}
|
||||||
return slice.SimpleToMap(r, func(t models.Comments) uint64 {
|
m := make(map[uint64]models.Comments)
|
||||||
return t.CommentId
|
off := 0
|
||||||
}), err
|
for {
|
||||||
|
id := slice.Slice(ids, off, 500)
|
||||||
|
if len(id) < 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r, err := model.Finds[models.Comments](ctx, model.Conditions(
|
||||||
|
model.Where(model.SqlBuilder{
|
||||||
|
{"comment_ID", "in", ""}, {"comment_approved", "1"},
|
||||||
|
}),
|
||||||
|
model.Fields("*"),
|
||||||
|
model.In(slice.ToAnySlice(id)),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
for _, comments := range r {
|
||||||
|
m[comments.CommentId] = comments
|
||||||
|
}
|
||||||
|
off += 500
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) {
|
||||||
|
n, err := model.GetField[models.Posts](ctx, "comment_count", model.Conditions(
|
||||||
|
model.Where(model.SqlBuilder{{"ID", "=", number.IntToString(postId), "int"}})))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return str.ToInteger(n, 0), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostTopCommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) {
|
||||||
|
v, err := model.GetField[models.Comments](ctx, "count(*) num", model.Conditions(
|
||||||
|
model.Where(postTopCommentNumWhere(postId)),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return str.ToInteger(v, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func postTopCommentNumWhere(postId uint64) model.SqlBuilder {
|
||||||
|
threadComments := wpconfig.GetOption("thread_comments")
|
||||||
|
pageComments := wpconfig.GetOption("page_comments")
|
||||||
|
where := model.SqlBuilder{
|
||||||
|
{"comment_approved", "1"},
|
||||||
|
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
||||||
|
}
|
||||||
|
if pageComments != "1" || threadComments == "1" || "1" == wpconfig.GetOption("thread_comments_depth") {
|
||||||
|
where = append(where, []string{"comment_parent", "0"})
|
||||||
|
}
|
||||||
|
return where
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostCommentsIds(ctx context.Context, postId uint64, page, limit, totalRaw int, order string) ([]uint64, int, error) {
|
||||||
|
condition := model.Conditions(
|
||||||
|
model.Where(postTopCommentNumWhere(postId)),
|
||||||
|
model.TotalRaw(totalRaw),
|
||||||
|
model.Fields("comment_ID"),
|
||||||
|
model.Order(model.SqlBuilder{
|
||||||
|
{"comment_date_gmt", order},
|
||||||
|
{"comment_ID", "asc"},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
var r []models.Comments
|
||||||
|
var total int
|
||||||
|
var err error
|
||||||
|
if limit < 1 {
|
||||||
|
r, err = model.ChunkFind[models.Comments](ctx, 300, condition)
|
||||||
|
total = len(r)
|
||||||
|
} else {
|
||||||
|
r, total, err = model.Pagination[models.Comments](ctx, condition, page, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && errors.Is(err, sql.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return slice.Map(r, func(t models.Comments) uint64 {
|
||||||
|
return t.CommentId
|
||||||
|
}), total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommentChildren(ctx context.Context, commentIds []uint64, _ ...any) (r map[uint64][]uint64, err error) {
|
||||||
|
rr, err := model.Finds[models.Comments](ctx, model.Conditions(
|
||||||
|
model.Where(model.SqlBuilder{
|
||||||
|
{"comment_parent", "in", ""},
|
||||||
|
{"comment_approved", "1"},
|
||||||
|
}),
|
||||||
|
model.In(slice.ToAnySlice(commentIds)),
|
||||||
|
model.Fields("comment_ID,comment_parent"),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rrr := slice.GroupBy(rr, func(v models.Comments) (uint64, uint64) {
|
||||||
|
return v.CommentParent, v.CommentId
|
||||||
|
})
|
||||||
|
r = make(map[uint64][]uint64)
|
||||||
|
for _, id := range commentIds {
|
||||||
|
r[id] = rrr[id]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func PreviousCommentNum(ctx context.Context, commentId, postId uint64) (int, error) {
|
||||||
|
v, err := model.GetField[models.Comments](ctx, "count(*)", model.Conditions(
|
||||||
|
model.Where(model.SqlBuilder{
|
||||||
|
{"comment_approved", "1"},
|
||||||
|
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
||||||
|
{"comment_ID", "<", number.IntToString(commentId), "int"},
|
||||||
|
{"comment_parent", "=", "0", "int"},
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return str.ToInteger(v, 0), nil
|
||||||
}
|
}
|
||||||
|
@ -21,17 +21,13 @@ type PostContext struct {
|
|||||||
Next models.Posts
|
Next models.Posts
|
||||||
}
|
}
|
||||||
|
|
||||||
func CategoriesAndTags(a ...any) (terms []models.TermsMy, err error) {
|
func CategoriesAndTags(ctx context.Context, t string, _ ...any) (terms []models.TermsMy, err error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
t, ok := a[1].(string)
|
|
||||||
var in = []any{"category", "post_tag"}
|
var in = []any{"category", "post_tag"}
|
||||||
if ok {
|
switch t {
|
||||||
switch t {
|
case constraints.Category:
|
||||||
case constraints.Category:
|
in = []any{"category"}
|
||||||
in = []any{"category"}
|
case constraints.Tag:
|
||||||
case constraints.Tag:
|
in = []any{"post_tag"}
|
||||||
in = []any{"post_tag"}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w := model.SqlBuilder{
|
w := model.SqlBuilder{
|
||||||
{"tt.taxonomy", "in", ""},
|
{"tt.taxonomy", "in", ""},
|
||||||
|
@ -11,10 +11,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) {
|
func GetPostMetaByPostIds(ctx context.Context, ids []uint64, _ ...any) (r map[uint64]map[string]any, err error) {
|
||||||
r = make(map[uint64]map[string]any)
|
r = make(map[uint64]map[string]any)
|
||||||
ctx := args[0].(context.Context)
|
|
||||||
ids := args[1].([]uint64)
|
|
||||||
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
|
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
|
model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
|
||||||
model.In(slice.ToAnySlice(ids)),
|
model.In(slice.ToAnySlice(ids)),
|
||||||
|
@ -3,6 +3,7 @@ package dao
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models/relation"
|
"github.com/fthvgb1/wp-go/app/pkg/models/relation"
|
||||||
@ -15,10 +16,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
func GetPostsByIds(ctx context.Context, ids []uint64, _ ...any) (m map[uint64]models.Posts, err error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
m = make(map[uint64]models.Posts)
|
m = make(map[uint64]models.Posts)
|
||||||
ids := a[1].([]uint64)
|
|
||||||
q := model.Conditions(
|
q := model.Conditions(
|
||||||
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
||||||
model.Join(model.SqlBuilder{
|
model.Join(model.SqlBuilder{
|
||||||
@ -99,11 +98,10 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchPostIds(args ...any) (ids PostIds, err error) {
|
func SearchPostIds(ctx context.Context, _ string, args ...any) (ids PostIds, err error) {
|
||||||
ctx := args[0].(context.Context)
|
q := args[0].(*model.QueryCondition)
|
||||||
q := args[1].(*model.QueryCondition)
|
page := args[1].(int)
|
||||||
page := args[2].(int)
|
pageSize := args[2].(int)
|
||||||
pageSize := args[3].(int)
|
|
||||||
q.Fields = "ID"
|
q.Fields = "ID"
|
||||||
res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize)
|
res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize)
|
||||||
for _, posts := range res {
|
for _, posts := range res {
|
||||||
@ -118,11 +116,19 @@ func SearchPostIds(args ...any) (ids PostIds, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxPostId(a ...any) (uint64, error) {
|
func GetMaxPostId(ctx context.Context, _ ...any) (uint64, error) {
|
||||||
ctx := a[0].(context.Context)
|
r, err := model.Finds[models.Posts](ctx,
|
||||||
r, err := model.SimpleFind[models.Posts](ctx,
|
model.Conditions(
|
||||||
model.SqlBuilder{{"post_type", "post"}, {"post_status", "publish"}},
|
model.Where(model.SqlBuilder{
|
||||||
"max(ID) ID",
|
{"post_type", "post"},
|
||||||
|
{"post_status", "publish"}},
|
||||||
|
),
|
||||||
|
model.Fields("ID"),
|
||||||
|
model.Order(model.SqlBuilder{
|
||||||
|
{"ID", "desc"},
|
||||||
|
}),
|
||||||
|
model.Limit(1),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
var id uint64
|
var id uint64
|
||||||
if len(r) > 0 {
|
if len(r) > 0 {
|
||||||
@ -131,9 +137,8 @@ func GetMaxPostId(a ...any) (uint64, error) {
|
|||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecentPosts(a ...any) (r []models.Posts, err error) {
|
func RecentPosts(ctx context.Context, a ...any) (r []models.Posts, err error) {
|
||||||
ctx := a[0].(context.Context)
|
num := helper.ParseArgs(10, a...)
|
||||||
num := a[1].(int)
|
|
||||||
r, err = model.Finds[models.Posts](ctx, model.Conditions(
|
r, err = model.Finds[models.Posts](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{
|
model.Where(model.SqlBuilder{
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
@ -146,15 +151,14 @@ func RecentPosts(a ...any) (r []models.Posts, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPostContext(arg ...any) (r PostContext, err error) {
|
func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, err error) {
|
||||||
ctx := arg[0].(context.Context)
|
t := arg[0].(time.Time)
|
||||||
t := arg[1].(time.Time)
|
|
||||||
next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{
|
next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{
|
||||||
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
||||||
{"post_status", "in", ""},
|
{"post_status", "in", ""},
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
}, "ID,post_title,post_password", nil, []any{"publish"})
|
}, "ID,post_title,post_password", nil, []any{"publish"})
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -165,7 +169,7 @@ func GetPostContext(arg ...any) (r PostContext, err error) {
|
|||||||
{"post_status", "in", ""},
|
{"post_status", "in", ""},
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
}, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
}, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -178,9 +182,8 @@ func GetPostContext(arg ...any) (r PostContext, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonthPost(args ...any) (r []uint64, err error) {
|
func MonthPost(ctx context.Context, _ string, args ...any) (r []uint64, err error) {
|
||||||
ctx := args[0].(context.Context)
|
year, month := args[0].(string), args[1].(string)
|
||||||
year, month := args[1].(string), args[2].(string)
|
|
||||||
where := model.SqlBuilder{
|
where := model.SqlBuilder{
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
{"post_status", "publish"},
|
{"post_status", "publish"},
|
||||||
|
@ -7,31 +7,26 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetUserById(a ...any) (r models.Users, err error) {
|
func GetUserById(ctx context.Context, uid uint64, _ ...any) (r models.Users, err error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
uid := a[1].(uint64)
|
|
||||||
r, err = model.FindOneById[models.Users](ctx, uid)
|
r, err = model.FindOneById[models.Users](ctx, uid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AllUsername(a ...any) (map[string]struct{}, error) {
|
func AllUsername(ctx context.Context, _ ...any) (map[string]uint64, error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{
|
r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{
|
||||||
{"user_status", "=", "0", "int"},
|
{"user_status", "=", "0", "int"},
|
||||||
}, "user_login")
|
}, "display_name,ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return slice.ToMap(r, func(t models.Users) (string, struct{}) {
|
return slice.ToMap(r, func(t models.Users) (string, uint64) {
|
||||||
return t.UserLogin, struct{}{}
|
return t.DisplayName, t.Id
|
||||||
}, true), nil
|
}, true), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByName(a ...any) (r models.Users, err error) {
|
func GetUserByName(ctx context.Context, u string, _ ...any) (r models.Users, err error) {
|
||||||
u := a[1].(string)
|
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
r, err = model.FirstOne[models.Users](ctx, model.SqlBuilder{{
|
r, err = model.FirstOne[models.Users](ctx, model.SqlBuilder{{
|
||||||
"user_login", u,
|
"display_name", u,
|
||||||
}}, "*", nil)
|
}}, "*", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,21 @@ package db
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"log"
|
"log"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var safeDb = safety.NewVar[*sqlx.DB](nil)
|
var safeDb = safety.NewVar[*sqlx.DB](nil)
|
||||||
|
var showQuerySql func() bool
|
||||||
|
|
||||||
|
func GetSqlxDB() *sqlx.DB {
|
||||||
|
return safeDb.Load()
|
||||||
|
}
|
||||||
|
|
||||||
func InitDb() (*safety.Var[*sqlx.DB], error) {
|
func InitDb() (*safety.Var[*sqlx.DB], error) {
|
||||||
c := config.GetConfig()
|
c := config.GetConfig()
|
||||||
@ -36,6 +43,11 @@ func InitDb() (*safety.Var[*sqlx.DB], error) {
|
|||||||
if preDb != nil {
|
if preDb != nil {
|
||||||
_ = preDb.Close()
|
_ = preDb.Close()
|
||||||
}
|
}
|
||||||
|
if showQuerySql == nil {
|
||||||
|
showQuerySql = reload.BuildFnVal("showQuerySql", false, func() bool {
|
||||||
|
return config.GetConfig().ShowQuerySql
|
||||||
|
})
|
||||||
|
}
|
||||||
return safeDb, err
|
return safeDb, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +56,20 @@ func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
|
|||||||
nil,
|
nil,
|
||||||
nil))
|
nil))
|
||||||
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
|
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
|
||||||
if config.GetConfig().ShowQuerySql {
|
if showQuerySql() {
|
||||||
go log.Println(model.FormatSql(s, args...))
|
_, f, l, _ := runtime.Caller(5)
|
||||||
|
go func() {
|
||||||
|
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return query.Selects(ctx, a, s, args...)
|
return query.Selects(ctx, a, s, args...)
|
||||||
})
|
})
|
||||||
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
|
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
|
||||||
if config.GetConfig().ShowQuerySql {
|
if showQuerySql() {
|
||||||
go log.Println(model.FormatSql(s, args...))
|
_, f, l, _ := runtime.Caller(5)
|
||||||
|
go func() {
|
||||||
|
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return query.Gets(ctx, a, s, args...)
|
return query.Gets(ctx, a, s, args...)
|
||||||
})
|
})
|
||||||
|
@ -15,20 +15,24 @@ var logs = safety.NewVar[*log.Logger](nil)
|
|||||||
var logFile = safety.NewVar[*os.File](nil)
|
var logFile = safety.NewVar[*os.File](nil)
|
||||||
|
|
||||||
func InitLogger() error {
|
func InitLogger() error {
|
||||||
|
c := config.GetConfig()
|
||||||
|
return SetLogger(c.LogOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLogger(loggerFile string) error {
|
||||||
|
if loggerFile == "" {
|
||||||
|
loggerFile = "stderr"
|
||||||
|
}
|
||||||
preFD := logFile.Load()
|
preFD := logFile.Load()
|
||||||
l := &log.Logger{}
|
l := &log.Logger{}
|
||||||
c := config.GetConfig()
|
|
||||||
if c.LogOutput == "" {
|
|
||||||
c.LogOutput = "stderr"
|
|
||||||
}
|
|
||||||
var out io.Writer
|
var out io.Writer
|
||||||
switch c.LogOutput {
|
switch loggerFile {
|
||||||
case "stdout":
|
case "stdout":
|
||||||
out = os.Stdout
|
out = os.Stdout
|
||||||
case "stderr":
|
case "stderr":
|
||||||
out = os.Stderr
|
out = os.Stderr
|
||||||
default:
|
default:
|
||||||
file, err := os.OpenFile(c.LogOutput, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0777)
|
file, err := os.OpenFile(loggerFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@ type Comments struct {
|
|||||||
CommentParent uint64 `gorm:"column:comment_parent" db:"comment_parent" json:"comment_parent" form:"comment_parent"`
|
CommentParent uint64 `gorm:"column:comment_parent" db:"comment_parent" json:"comment_parent" form:"comment_parent"`
|
||||||
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
|
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
|
||||||
//扩展字段
|
//扩展字段
|
||||||
PostTitle string `db:"post_title"`
|
PostTitle string `db:"post_title"`
|
||||||
|
UpdateTime time.Time `gorm:"update_time" form:"update_time" json:"update_time" db:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Comments) PrimaryKey() string {
|
func (w Comments) PrimaryKey() string {
|
||||||
@ -29,3 +30,8 @@ func (w Comments) PrimaryKey() string {
|
|||||||
func (w Comments) Table() string {
|
func (w Comments) Table() string {
|
||||||
return "wp_comments"
|
return "wp_comments"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PostComments struct {
|
||||||
|
Comments
|
||||||
|
Children []uint64
|
||||||
|
}
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommentHandler struct {
|
type CommentHandler struct {
|
||||||
*gin.Context
|
*gin.Context
|
||||||
comments []*Comments
|
comments []*Comments
|
||||||
maxDepth int
|
maxDepth int
|
||||||
depth int
|
depth int
|
||||||
isTls bool
|
isTls bool
|
||||||
i CommentHtml
|
i CommentHtml
|
||||||
|
isThreadComments bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Comments struct {
|
type Comments struct {
|
||||||
@ -26,19 +28,18 @@ type Comments struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommentHtml interface {
|
type CommentHtml interface {
|
||||||
Sort(i, j *Comments) bool
|
FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string
|
||||||
FormatLi(c *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string
|
FloorOrder(i, j models.Comments) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
|
func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
|
||||||
tree := treeComments(comments)
|
tree := treeComments(comments)
|
||||||
u := c.Request.Header.Get("Referer")
|
|
||||||
var isTls bool
|
var isTls bool
|
||||||
if u != "" {
|
if c.Request.TLS != nil {
|
||||||
uu, _ := url.Parse(u)
|
isTls = true
|
||||||
if uu.Scheme == "https" {
|
} else {
|
||||||
isTls = true
|
isTls = "https" == strings.ToLower(c.Request.Header.Get("X-Forwarded-Proto"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
h := CommentHandler{
|
h := CommentHandler{
|
||||||
Context: c,
|
Context: c,
|
||||||
@ -48,15 +49,21 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m
|
|||||||
isTls: isTls,
|
isTls: isTls,
|
||||||
i: i,
|
i: i,
|
||||||
}
|
}
|
||||||
return h.formatComment(h.comments, true)
|
return h.formatComment(h.comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html string) {
|
func (d CommentHandler) formatComment(comments []*Comments) (html string) {
|
||||||
s := str.NewBuilder()
|
s := str.NewBuilder()
|
||||||
if d.depth > d.maxDepth {
|
if d.depth >= d.maxDepth {
|
||||||
comments = d.findComments(comments)
|
comments = d.findComments(comments)
|
||||||
}
|
}
|
||||||
slice.Sort(comments, d.i.Sort)
|
order := wpconfig.GetOption("comment_order")
|
||||||
|
slice.Sort(comments, func(i, j *Comments) bool {
|
||||||
|
if order == "asc" {
|
||||||
|
return i.CommentDate.Sub(j.CommentDate) < 0
|
||||||
|
}
|
||||||
|
return i.CommentDate.Sub(j.CommentDate) > 0
|
||||||
|
})
|
||||||
for i, comment := range comments {
|
for i, comment := range comments {
|
||||||
eo := "even"
|
eo := "even"
|
||||||
if (i+1)%2 == 0 {
|
if (i+1)%2 == 0 {
|
||||||
@ -68,13 +75,11 @@ func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html st
|
|||||||
parent = "parent"
|
parent = "parent"
|
||||||
fl = true
|
fl = true
|
||||||
}
|
}
|
||||||
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.isTls, eo, parent))
|
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.maxDepth, 1, d.isTls, d.isThreadComments, eo, parent))
|
||||||
if fl {
|
if fl {
|
||||||
d.depth++
|
d.depth++
|
||||||
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
|
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`)
|
||||||
if isTop {
|
d.depth--
|
||||||
d.depth = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s.WriteString("</li><!-- #comment-## -->")
|
s.WriteString("</li><!-- #comment-## -->")
|
||||||
}
|
}
|
||||||
@ -87,7 +92,7 @@ func (d CommentHandler) findComments(comments []*Comments) []*Comments {
|
|||||||
var r []*Comments
|
var r []*Comments
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
tmp := *comment
|
tmp := *comment
|
||||||
comment.Children = nil
|
tmp.Children = nil
|
||||||
r = append(r, &tmp)
|
r = append(r, &tmp)
|
||||||
if len(comment.Children) > 0 {
|
if len(comment.Children) > 0 {
|
||||||
t := d.findComments(comment.Children)
|
t := d.findComments(comment.Children)
|
||||||
@ -137,16 +142,35 @@ func CommentRender() CommonCommentFormat {
|
|||||||
type CommonCommentFormat struct {
|
type CommonCommentFormat struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CommonCommentFormat) Sort(i, j *Comments) bool {
|
func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
|
||||||
order := wpconfig.GetOption("comment_order")
|
return FormatLi(li, m, respondsFn, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent)
|
||||||
if order == "asc" {
|
|
||||||
return i.CommentDate.UnixNano() < j.CommentDate.UnixNano()
|
|
||||||
}
|
|
||||||
return i.CommentDate.UnixNano() > j.CommentDate.UnixNano()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CommonCommentFormat) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
|
func (c CommonCommentFormat) FloorOrder(i, j models.Comments) bool {
|
||||||
return FormatLi(CommonLi(), ctx, m, depth, isTls, eo, parent)
|
return i.CommentId > j.CommentId
|
||||||
|
}
|
||||||
|
|
||||||
|
type RespondFn func(m models.Comments, depth, maxDepth int, isThreadComments bool) string
|
||||||
|
|
||||||
|
var respondsFn = Responds(respondTml)
|
||||||
|
|
||||||
|
func RespondsFn() RespondFn {
|
||||||
|
return respondsFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func Responds(respondTml string) RespondFn {
|
||||||
|
return func(m models.Comments, depth, maxDepth int, isThreadComments bool) string {
|
||||||
|
if !isThreadComments || depth >= maxDepth {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
pId := number.IntToString(m.CommentPostId)
|
||||||
|
cId := number.IntToString(m.CommentId)
|
||||||
|
return str.Replace(respondTml, map[string]string{
|
||||||
|
"{{PostId}}": pId,
|
||||||
|
"{{CommentId}}": cId,
|
||||||
|
"{{CommentAuthor}}": m.CommentAuthor,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var li = `
|
var li = `
|
||||||
@ -158,14 +182,11 @@ var li = `
|
|||||||
src="{{Gravatar}}"
|
src="{{Gravatar}}"
|
||||||
srcset="{{Gravatar}} 2x"
|
srcset="{{Gravatar}} 2x"
|
||||||
class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
|
class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
|
||||||
<b class="fn">
|
<b class="fn">{{CommentAuthor}}</b>
|
||||||
<a href="{{CommentAuthorUrl}}" rel="external nofollow ugc"
|
|
||||||
class="url">{{CommentAuthor}}</a>
|
|
||||||
</b>
|
|
||||||
<span class="says">说道:</span></div><!-- .comment-author -->
|
<span class="says">说道:</span></div><!-- .comment-author -->
|
||||||
|
|
||||||
<div class="comment-metadata">
|
<div class="comment-metadata">
|
||||||
<a href="/p/{{PostId}}#comment-{{CommentId}}">
|
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}">
|
||||||
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
|
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
|
||||||
</a></div><!-- .comment-metadata -->
|
</a></div><!-- .comment-metadata -->
|
||||||
|
|
||||||
@ -175,30 +196,34 @@ var li = `
|
|||||||
<p>{{CommentContent}}</p>
|
<p>{{CommentContent}}</p>
|
||||||
</div><!-- .comment-content -->
|
</div><!-- .comment-content -->
|
||||||
|
|
||||||
<div class="reply">
|
{{respond}}
|
||||||
|
</article><!-- .comment-body -->
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
var respondTml = `<div class="reply">
|
||||||
<a rel="nofollow" class="comment-reply-link"
|
<a rel="nofollow" class="comment-reply-link"
|
||||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
||||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
||||||
data-replyto="回复给{{CommentAuthor}}"
|
data-replyto="回复给{{CommentAuthor}}"
|
||||||
aria-label="回复给{{CommentAuthor}}">回复</a>
|
aria-label="回复给{{CommentAuthor}}">回复</a>
|
||||||
</div>
|
</div>`
|
||||||
</article><!-- .comment-body -->
|
|
||||||
|
|
||||||
`
|
func FormatLi(li string, comments models.Comments, respond RespondFn, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
|
||||||
|
|
||||||
func FormatLi(li string, c *gin.Context, comments models.Comments, depth int, isTls bool, eo, parent string) string {
|
|
||||||
for k, v := range map[string]string{
|
for k, v := range map[string]string{
|
||||||
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
|
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
|
||||||
"{{Depth}}": strconv.Itoa(depth),
|
"{{Depth}}": strconv.Itoa(currentDepth),
|
||||||
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
|
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
|
||||||
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
|
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
|
||||||
"{{CommentAuthor}}": comments.CommentAuthor,
|
"{{CommentAuthor}}": comments.CommentAuthor,
|
||||||
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
|
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
|
||||||
|
"{{page}}": strconv.Itoa(page),
|
||||||
"{{CommentDateGmt}}": comments.CommentDateGmt.String(),
|
"{{CommentDateGmt}}": comments.CommentDateGmt.String(),
|
||||||
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
|
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
|
||||||
"{{CommentContent}}": comments.CommentContent,
|
"{{CommentContent}}": comments.CommentContent,
|
||||||
"{{eo}}": eo,
|
"{{eo}}": eo,
|
||||||
"{{parent}}": parent,
|
"{{parent}}": parent,
|
||||||
|
"{{respond}}": respond(comments, currentDepth, maxDepth, isThreadComments),
|
||||||
} {
|
} {
|
||||||
li = strings.Replace(li, k, v, -1)
|
li = strings.Replace(li, k, v, -1)
|
||||||
}
|
}
|
||||||
|
141
app/plugins/devexample/plugintt/redisCache.go.dev
Normal file
141
app/plugins/devexample/plugintt/redisCache.go.dev
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"strconv"
|
||||||
|
str "strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RdmCache[K comparable, V any] struct {
|
||||||
|
expired func() time.Duration
|
||||||
|
rdb *redis.Client
|
||||||
|
keyFn func(K) string
|
||||||
|
name string
|
||||||
|
resFn func(map[string]string) V
|
||||||
|
saveData func(V) map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) SetExpiredTime(f func() time.Duration) {
|
||||||
|
r.expired = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) Get(ctx context.Context, key K) (V, bool) {
|
||||||
|
var re V
|
||||||
|
result, err := r.rdb.Exists(ctx, r.keyFn(key)).Result()
|
||||||
|
if result <= 0 || err != nil {
|
||||||
|
return re, false
|
||||||
|
}
|
||||||
|
|
||||||
|
rr, err := r.rdb.HGetAll(ctx, r.keyFn(key)).Result()
|
||||||
|
|
||||||
|
if errors.Is(err, redis.Nil) {
|
||||||
|
return re, false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return re, false
|
||||||
|
}
|
||||||
|
return r.resFn(rr), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) Set(ctx context.Context, key K, val V) {
|
||||||
|
k := r.keyFn(key)
|
||||||
|
result, err := r.rdb.HSet(ctx, k, r.saveData(val)).Result()
|
||||||
|
b, err := r.rdb.Expire(ctx, k, r.expired()).Result()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(result, b, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) GetExpireTime(ctx context.Context) time.Duration {
|
||||||
|
return r.expired()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) Ttl(ctx context.Context, key K) time.Duration {
|
||||||
|
result, err := r.rdb.TTL(ctx, r.keyFn(key)).Result()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) Flush(ctx context.Context) {
|
||||||
|
fmt.Println("flush redis cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) Del(ctx context.Context, key ...K) {
|
||||||
|
r.rdb.Del(ctx, slice.Map(key, r.keyFn)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RdmCache[K, V]) ClearExpired(ctx context.Context) {
|
||||||
|
fmt.Println("clear expired redis cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedisCache use step:
|
||||||
|
// 1 go build -gcflags all="-N -l" --race -buildmode=plugin -o redisCache.so main.go && cp ./redisCache.so ../wp-go/plugins/
|
||||||
|
// 2 wp-go config add redisCache plugin
|
||||||
|
func RedisCache(h *wp.Handle) {
|
||||||
|
vv, ok := cachemanager.GetMapCache[string, dao.PostIds]("listPostIds")
|
||||||
|
if ok {
|
||||||
|
_, ok := any(vv.Cache).(*RdmCache[string, dao.PostIds])
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reload.AppendOnceFn(func() {
|
||||||
|
err := cachemanager.SetMapCache("listPostIds", vv)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err, "set recovery listPostIds cache err")
|
||||||
|
} else {
|
||||||
|
cachemanager.PushOrSetFlush(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush})
|
||||||
|
cachemanager.PushOrSetClearExpired(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush})
|
||||||
|
fmt.Println("recovery listPostIds cache ok")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
rdm := redis.NewClient(&redis.Options{
|
||||||
|
Addr: "localhost:6379",
|
||||||
|
Password: "", // no password set
|
||||||
|
DB: 0, // use default DB
|
||||||
|
})
|
||||||
|
r := RdmCache[string, dao.PostIds]{
|
||||||
|
expired: func() time.Duration {
|
||||||
|
return time.Minute
|
||||||
|
},
|
||||||
|
keyFn: func(u string) string {
|
||||||
|
return strings.Join("postIds:", u)
|
||||||
|
},
|
||||||
|
rdb: rdm,
|
||||||
|
name: "",
|
||||||
|
resFn: func(m map[string]string) dao.PostIds {
|
||||||
|
return dao.PostIds{
|
||||||
|
Ids: slice.Map(str.Split(m["ids"], ","), strings.ToInt[uint64]),
|
||||||
|
Length: strings.ToInt[int](m["length"]),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveData: func(ids dao.PostIds) map[string]string {
|
||||||
|
t := slice.Map(ids.Ids, number.IntToString[uint64])
|
||||||
|
return map[string]string{
|
||||||
|
"ids": str.Join(t, ","),
|
||||||
|
"length": strconv.Itoa(ids.Length),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cachemanager.NewMapCache[string, dao.PostIds](&r, nil, dao.SearchPostIds, config.GetConfig().CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.PostListCacheTime
|
||||||
|
})
|
||||||
|
fmt.Println("redis cache inited ok")
|
||||||
|
}
|
50
app/plugins/devexample/plugintt/redisCache.go.mod.dev
Normal file
50
app/plugins/devexample/plugintt/redisCache.go.mod.dev
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
module redisCache
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fthvgb1/wp-go v0.0.0-20231210111549-d72bed0c8c4e
|
||||||
|
github.com/redis/go-redis/v9 v9.3.0
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/fthvgb1/wp-go v0.0.0-20231210111549-d72bed0c8c4e => ../wp-go
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/sonic v1.10.2 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||||
|
github.com/elliotchance/phpserialize v1.3.3 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/sessions v0.0.5 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/gorilla/sessions v1.2.2 // indirect
|
||||||
|
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
golang.org/x/arch v0.6.0 // indirect
|
||||||
|
golang.org/x/crypto v0.15.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
|
golang.org/x/net v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
@ -3,35 +3,134 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/cache"
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper"
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/plugin/digest"
|
"github.com/fthvgb1/wp-go/plugin/digest"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var digestCache *cache.MapCache[uint64, string]
|
|
||||||
|
|
||||||
var more = regexp.MustCompile("<!--more(.*?)?-->")
|
var more = regexp.MustCompile("<!--more(.*?)?-->")
|
||||||
|
|
||||||
var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->")
|
var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->")
|
||||||
|
|
||||||
|
type DigestConfig struct {
|
||||||
|
DigestWordCount int `yaml:"digestWordCount"`
|
||||||
|
DigestAllowTag string `yaml:"digestAllowTag"`
|
||||||
|
DigestRegex string `yaml:"digestRegex"`
|
||||||
|
DigestTagOccupyNum []struct {
|
||||||
|
Tag string `yaml:"tag"`
|
||||||
|
Num int `yaml:"num"`
|
||||||
|
ChuckOvered bool `yaml:"chuckOvered"`
|
||||||
|
EscapeCharacter []struct {
|
||||||
|
Tags string `yaml:"tags"`
|
||||||
|
Character []string `yaml:"character"`
|
||||||
|
Num int `yaml:"num"`
|
||||||
|
ChuckOvered bool `yaml:"chuckOvered"`
|
||||||
|
} `yaml:"escapeCharacter"`
|
||||||
|
} `yaml:"digestTagOccupyNum"`
|
||||||
|
specialSolve map[string]digest.SpecialSolveConf
|
||||||
|
}
|
||||||
|
|
||||||
|
var digestConfig *safety.Var[DigestConfig]
|
||||||
|
|
||||||
func InitDigestCache() {
|
func InitDigestCache() {
|
||||||
digestCache = cachemanager.MapCacheBy[uint64](digestRaw, config.GetConfig().CacheTime.DigestCacheTime)
|
cachemanager.NewMemoryMapCache(nil, digestRaw, config.GetConfig().CacheTime.DigestCacheTime, "digestPlugin", func() time.Duration {
|
||||||
|
return config.GetConfig().CacheTime.DigestCacheTime
|
||||||
|
})
|
||||||
|
|
||||||
|
digestConfig = reload.VarsBy(func() DigestConfig {
|
||||||
|
c, err := config.GetCustomizedConfig[DigestConfig]()
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err, "get digest config fail")
|
||||||
|
c.DigestWordCount = config.GetConfig().DigestWordCount
|
||||||
|
c.DigestAllowTag = config.GetConfig().DigestAllowTag
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if c.DigestRegex != "" {
|
||||||
|
digest.SetQutos(c.DigestRegex)
|
||||||
|
}
|
||||||
|
if len(c.DigestTagOccupyNum) <= 1 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
c.specialSolve = ParseDigestConf(c)
|
||||||
|
return c
|
||||||
|
}, "digestConfig")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDigestConf(c DigestConfig) map[string]digest.SpecialSolveConf {
|
||||||
|
specialSolve := map[string]digest.SpecialSolveConf{}
|
||||||
|
for _, item := range c.DigestTagOccupyNum {
|
||||||
|
tags := strings.Split(strings.ReplaceAll(item.Tag, " ", ""), "<")
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ec := make(map[rune]digest.SpecialSolve)
|
||||||
|
specialTags := make(map[string]digest.SpecialSolve)
|
||||||
|
tag = str.Join("<", tag)
|
||||||
|
if len(item.EscapeCharacter) > 0 {
|
||||||
|
for _, esc := range item.EscapeCharacter {
|
||||||
|
for _, i := range esc.Character {
|
||||||
|
s := []rune(i)
|
||||||
|
if len(s) == 1 {
|
||||||
|
ec[s[0]] = digest.SpecialSolve{
|
||||||
|
Num: esc.Num,
|
||||||
|
ChuckOvered: esc.ChuckOvered,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if esc.Tags == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tagss := strings.Split(strings.ReplaceAll(esc.Tags, " ", ""), "<")
|
||||||
|
for _, t := range tagss {
|
||||||
|
if t == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t = str.Join("<", t)
|
||||||
|
specialTags[t] = digest.SpecialSolve{
|
||||||
|
Num: esc.Num,
|
||||||
|
ChuckOvered: esc.ChuckOvered,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v, ok := specialSolve[tag]
|
||||||
|
if !ok {
|
||||||
|
specialSolve[tag] = digest.SpecialSolveConf{
|
||||||
|
Num: item.Num,
|
||||||
|
ChuckOvered: item.ChuckOvered,
|
||||||
|
EscapeCharacter: ec,
|
||||||
|
Tags: specialTags,
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v.Num = item.Num
|
||||||
|
v.ChuckOvered = item.ChuckOvered
|
||||||
|
v.EscapeCharacter = maps.Merge(v.EscapeCharacter, ec)
|
||||||
|
v.Tags = maps.Merge(v.Tags, specialTags)
|
||||||
|
specialSolve[tag] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return specialSolve
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveWpBlock(s string) string {
|
func RemoveWpBlock(s string) string {
|
||||||
return removeWpBlock.ReplaceAllString(s, "")
|
return removeWpBlock.ReplaceAllString(s, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func digestRaw(arg ...any) (string, error) {
|
func digestRaw(ctx context.Context, id uint64, arg ...any) (string, error) {
|
||||||
ctx := arg[0].(context.Context)
|
|
||||||
s := arg[1].(string)
|
s := arg[1].(string)
|
||||||
id := arg[2].(uint64)
|
|
||||||
limit := arg[3].(int)
|
limit := arg[3].(int)
|
||||||
if limit < 0 {
|
if limit < 0 {
|
||||||
return s, nil
|
return s, nil
|
||||||
@ -47,12 +146,22 @@ func digestRaw(arg ...any) (string, error) {
|
|||||||
func Digests(content string, id uint64, limit int, fn func(id uint64, content, closeTag string) string) string {
|
func Digests(content string, id uint64, limit int, fn func(id uint64, content, closeTag string) string) string {
|
||||||
closeTag := ""
|
closeTag := ""
|
||||||
content = RemoveWpBlock(content)
|
content = RemoveWpBlock(content)
|
||||||
tag := config.GetConfig().DigestAllowTag
|
c := digestConfig.Load()
|
||||||
|
tag := c.DigestAllowTag
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = "<a><b><blockquote><br><cite><code><dd><del><div><dl><dt><em><h1><h2><h3><h4><h5><h6><i><img><li><ol><p><pre><span><strong><ul>"
|
tag = "<a><b><blockquote><br><cite><code><dd><del><div><dl><dt><em><h1><h2><h3><h4><h5><h6><i><img><li><ol><p><pre><span><strong><ul>"
|
||||||
}
|
}
|
||||||
content = digest.StripTags(content, tag)
|
content = digest.StripTags(content, tag)
|
||||||
content, closeTag = digest.Html(content, limit)
|
length := utf8.RuneCountInString(content) + 1
|
||||||
|
if length <= limit {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
if len(c.specialSolve) > 0 {
|
||||||
|
content, closeTag = digest.CustomizeHtml(content, limit, c.specialSolve)
|
||||||
|
} else {
|
||||||
|
content, closeTag = digest.Html(content, limit)
|
||||||
|
}
|
||||||
|
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return PostsMore(id, content, closeTag)
|
return PostsMore(id, content, closeTag)
|
||||||
}
|
}
|
||||||
@ -69,7 +178,7 @@ func PostsMore(id uint64, content, closeTag string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Digest(ctx context.Context, post *models.Posts, limit int) {
|
func Digest(ctx context.Context, post *models.Posts, limit int) {
|
||||||
content, _ := digestCache.GetCache(ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit)
|
content, _ := cachemanager.GetBy[string]("digestPlugin", ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit)
|
||||||
post.PostContent = content
|
post.PostContent = content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,12 @@ package plugins
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +22,13 @@ type PageEle struct {
|
|||||||
func TwentyFifteenPagination() PageEle {
|
func TwentyFifteenPagination() PageEle {
|
||||||
return twentyFifteen
|
return twentyFifteen
|
||||||
}
|
}
|
||||||
|
func TwentyFifteenCommentPagination() CommentPageEle {
|
||||||
|
return twentyFifteenComment
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommentPageEle struct {
|
||||||
|
PageEle
|
||||||
|
}
|
||||||
|
|
||||||
var twentyFifteen = PageEle{
|
var twentyFifteen = PageEle{
|
||||||
PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`,
|
PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`,
|
||||||
@ -28,6 +39,12 @@ var twentyFifteen = PageEle{
|
|||||||
CurrentEle: `<span aria-current="page" class="page-numbers current">
|
CurrentEle: `<span aria-current="page" class="page-numbers current">
|
||||||
<span class="meta-nav screen-reader-text">页 </span>%d</span>`,
|
<span class="meta-nav screen-reader-text">页 </span>%d</span>`,
|
||||||
}
|
}
|
||||||
|
var twentyFifteenComment = CommentPageEle{
|
||||||
|
PageEle{
|
||||||
|
PrevEle: `<div class="nav-previous"><a href="%s">%s</a></div>`,
|
||||||
|
NextEle: `<div class="nav-next"><a href="%s">%s</a></div>`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func (p PageEle) Current(page, totalPage, totalRow int) string {
|
func (p PageEle) Current(page, totalPage, totalRow int) string {
|
||||||
return fmt.Sprintf(p.CurrentEle, page)
|
return fmt.Sprintf(p.CurrentEle, page)
|
||||||
@ -50,8 +67,22 @@ func (p PageEle) Middle(page int, url string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var reg = regexp.MustCompile(`(/page)/(\d+)`)
|
var reg = regexp.MustCompile(`(/page)/(\d+)`)
|
||||||
|
var commentReg = regexp.MustCompile(`/comment-page-(\d+)`)
|
||||||
|
|
||||||
|
var queryParam = []string{"paged", "cat", "m", "author", "tag"}
|
||||||
|
|
||||||
|
func (p PageEle) Urls(u url.URL, page int, isTLS bool) string {
|
||||||
|
var path, query = u.Path, u.RawQuery
|
||||||
|
if path == "/" {
|
||||||
|
v := u.Query()
|
||||||
|
for _, q := range queryParam {
|
||||||
|
if v.Get(q) != "" {
|
||||||
|
v.Set("paged", strconv.Itoa(page))
|
||||||
|
return str.Join(path, "?", v.Encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p PageEle) Url(path, query string, page int) string {
|
|
||||||
if !strings.Contains(path, "/page/") {
|
if !strings.Contains(path, "/page/") {
|
||||||
path = fmt.Sprintf("%s%s", path, "/page/1")
|
path = fmt.Sprintf("%s%s", path, "/page/1")
|
||||||
}
|
}
|
||||||
@ -65,5 +96,81 @@ func (p PageEle) Url(path, query string, page int) string {
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/"
|
path = "/"
|
||||||
}
|
}
|
||||||
return str.Join(path, query)
|
if query != "" {
|
||||||
|
return str.Join(path, "?", query)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CommentPageEle) Urls(u url.URL, page int, isTLS bool) string {
|
||||||
|
var path, query = u.Path, u.RawQuery
|
||||||
|
if path == "/" {
|
||||||
|
v := u.Query()
|
||||||
|
if v.Get("p") != "" {
|
||||||
|
v.Set("cpage", strconv.Itoa(page))
|
||||||
|
return str.Join(path, "?", v.Encode(), "#comments")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(path, "/comment-page-") {
|
||||||
|
path = fmt.Sprintf("%s%s", path, "/comment-page-1")
|
||||||
|
}
|
||||||
|
path = commentReg.ReplaceAllString(path, fmt.Sprintf("/comment-page-%d", page))
|
||||||
|
path = strings.Replace(path, "//", "/", -1)
|
||||||
|
ur := path
|
||||||
|
if query != "" {
|
||||||
|
ur = str.Join(path, "?", query)
|
||||||
|
}
|
||||||
|
ur = str.Join(ur, "#comments")
|
||||||
|
return ur
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CommentPageEle) Middle(page int, url string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (p CommentPageEle) Dots() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (p CommentPageEle) Current(page, totalPage, totalRow int) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (p CommentPageEle) Prev(url string) string {
|
||||||
|
return fmt.Sprintf(p.PrevEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较早评论", "较新评论"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CommentPageEle) Next(url string) string {
|
||||||
|
return fmt.Sprintf(p.NextEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较新评论", "较早评论"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaginationNav struct {
|
||||||
|
Currents func(page, totalPage, totalRows int) string
|
||||||
|
Prevs func(url string) string
|
||||||
|
Nexts func(url string) string
|
||||||
|
Dotss func() string
|
||||||
|
Middles func(page int, url string) string
|
||||||
|
Urlss func(u url.URL, page int, isTLS bool) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Current(page, totalPage, totalRows int) string {
|
||||||
|
return p.Currents(page, totalPage, totalRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Prev(url string) string {
|
||||||
|
return p.Prevs(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Next(url string) string {
|
||||||
|
return p.Nexts(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Dots() string {
|
||||||
|
return p.Dotss()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Middle(page int, url string) string {
|
||||||
|
return p.Middles(page, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PaginationNav) Urls(u url.URL, page int, isTLS bool) string {
|
||||||
|
return p.Urlss(u, page, isTLS)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,29 @@ package apply
|
|||||||
|
|
||||||
import "github.com/fthvgb1/wp-go/safety"
|
import "github.com/fthvgb1/wp-go/safety"
|
||||||
|
|
||||||
var fn safety.Var[any]
|
var contains = safety.NewMap[string, any]()
|
||||||
|
|
||||||
func SetFn(f any) {
|
func SetVal(key string, val any) {
|
||||||
fn.Store(f)
|
contains.Store(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UsePlugins() any {
|
func DelVal(key string) {
|
||||||
return fn.Load()
|
contains.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVal[V any](key string) (V, bool) {
|
||||||
|
v, ok := contains.Load(key)
|
||||||
|
if !ok {
|
||||||
|
var vv V
|
||||||
|
return vv, ok
|
||||||
|
}
|
||||||
|
return v.(V), ok
|
||||||
|
}
|
||||||
|
func GetRawVal(key string) (any, bool) {
|
||||||
|
return contains.Load(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPlugins() any {
|
||||||
|
v, _ := contains.Load("wp-plugins")
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ type Options struct {
|
|||||||
Linehover bool `json:"linehover,omitempty"`
|
Linehover bool `json:"linehover,omitempty"`
|
||||||
RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"`
|
RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"`
|
||||||
TextOverflow string `json:"textOverflow,omitempty"`
|
TextOverflow string `json:"textOverflow,omitempty"`
|
||||||
Linenumbers int64 `json:"linenumbers,omitempty"`
|
Linenumbers bool `json:"linenumbers,omitempty"`
|
||||||
Theme string `json:"theme,omitempty"`
|
Theme string `json:"theme,omitempty"`
|
||||||
Language string `json:"language,omitempty"`
|
Language string `json:"language,omitempty"`
|
||||||
RetainCssClasses bool `json:"retainCssClasses,omitempty"`
|
RetainCssClasses bool `json:"retainCssClasses,omitempty"`
|
||||||
@ -58,7 +58,7 @@ func EnlighterJS(h *wp.Handle) {
|
|||||||
Linehover: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linehover", true),
|
Linehover: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linehover", true),
|
||||||
RawcodeDbclick: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-rawcodedbclick", true),
|
RawcodeDbclick: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-rawcodedbclick", true),
|
||||||
TextOverflow: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-textoverflow", "break"),
|
TextOverflow: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-textoverflow", "break"),
|
||||||
Linenumbers: maps.GetStrAnyValWithDefaults[int64](opp, "enlighterjs-linenumbers", 1),
|
Linenumbers: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linenumbers", true),
|
||||||
Theme: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-theme", "enlighter"),
|
Theme: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-theme", "enlighter"),
|
||||||
Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"),
|
Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"),
|
||||||
RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false),
|
RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false),
|
||||||
|
@ -66,7 +66,7 @@ func LoadPlugins() {
|
|||||||
}
|
}
|
||||||
RegisterPlugin(name, plu)
|
RegisterPlugin(name, plu)
|
||||||
}
|
}
|
||||||
apply.SetFn(func(h *wp.Handle) {
|
apply.SetVal("wp-plugins", func(h *wp.Handle) {
|
||||||
UsePlugins(h)
|
UsePlugins(h)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func HiddenLogin(h *wp.Handle) {
|
func HiddenLogin(h *wp.Handle) {
|
||||||
h.PushComponentFilterFn(widgets.Meta, func(h *wp.Handle, s string, args ...any) string {
|
h.AddActionFilter(widgets.Meta, func(h *wp.Handle, s string, args ...any) string {
|
||||||
return str.Replace(s, map[string]string{
|
return str.Replace(s, map[string]string{
|
||||||
`<li><a href="/wp-login.php">登录</a></li>`: "",
|
`<li><a href="/wp-login.php">登录</a></li>`: "",
|
||||||
`<li><a href="/feed">登录</a></li>`: "",
|
`<li><a href="/feed">登录</a></li>`: "",
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Tt(h *wp.Handle) {
|
func Tt(h *wp.Handle) {
|
||||||
h.HookHandle(constraints.PipeMiddleware, func(call wp.HandleCall) (wp.HandleCall, bool) {
|
h.HookHandle(constraints.PipeMiddleware, constraints.AllScene, func(call wp.HandleCall) (wp.HandleCall, bool) {
|
||||||
return call, false
|
return call, false
|
||||||
})
|
})
|
||||||
/*h.PushPipeHook(constraints.Home, func(pipe wp.Pipe) (wp.Pipe, bool) {
|
/*h.PushPipeHook(constraints.Home, func(pipe wp.Pipe) (wp.Pipe, bool) {
|
||||||
|
147
app/route/route.go
Normal file
147
app/route/route.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/app/actions"
|
||||||
|
"github.com/fthvgb1/wp-go/app/middleware"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
|
"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"
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GinSetter func(*gin.Engine)
|
||||||
|
|
||||||
|
var setters mockmap.Map[string, GinSetter]
|
||||||
|
|
||||||
|
var setterHooks []func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool)
|
||||||
|
|
||||||
|
// SetGinAction 方便插件在init时使用
|
||||||
|
func SetGinAction(name string, hook GinSetter, orders ...float64) {
|
||||||
|
setters.Set(name, hook, orders...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HookGinSetter(fn func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool)) {
|
||||||
|
setterHooks = append(setterHooks, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelGinSetter 方便插件在init时使用
|
||||||
|
func DelGinSetter(name string) {
|
||||||
|
setterHooks = append(setterHooks, func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool) {
|
||||||
|
return item, item.Name != name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupRouter() *gin.Engine {
|
||||||
|
// Disable Console Color
|
||||||
|
// gin.DisableConsoleColor()
|
||||||
|
r := gin.New()
|
||||||
|
c := config.GetConfig()
|
||||||
|
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)
|
||||||
|
|
||||||
|
siteFlowLimitMiddleware, siteFlow := middleware.FlowLimit(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
|
||||||
|
reload.Append(func() {
|
||||||
|
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)
|
||||||
|
|
||||||
|
for _, hook := range setterHooks {
|
||||||
|
setters = slice.FilterAndMap(setters, hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.SimpleSort(setters, slice.DESC, func(t mockmap.Item[string, GinSetter]) float64 {
|
||||||
|
return t.Order
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, fn := range setters {
|
||||||
|
fn.Value(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
@ -3,6 +3,7 @@ package theme
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"github.com/fthvgb1/wp-go/multipTemplate"
|
"github.com/fthvgb1/wp-go/multipTemplate"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -12,20 +13,36 @@ import (
|
|||||||
//go:embed *[^.go]
|
//go:embed *[^.go]
|
||||||
var TemplateFs embed.FS
|
var TemplateFs embed.FS
|
||||||
|
|
||||||
var templates map[string]*template.Template //方便外部获取模板render后的字符串,不然在gin中获取不了
|
var templates = safety.NewMap[string, *template.Template]() //方便外部获取模板render后的字符串,不然在gin中获取不了
|
||||||
|
|
||||||
func Template() *multipTemplate.MultipleFsTemplate {
|
var multiple *multipTemplate.MultipleFsTemplate
|
||||||
t := multipTemplate.NewFsTemplate(TemplateFs)
|
|
||||||
templates = t.Template
|
func BuildTemplate() *multipTemplate.MultipleFsTemplate {
|
||||||
t.FuncMap = FuncMap()
|
if multiple != nil {
|
||||||
commonTemplate(t)
|
tt := multipTemplate.NewFsTemplate(TemplateFs)
|
||||||
/*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml"). //单个主题设置
|
commonTemplate(tt)
|
||||||
AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml")*/
|
for k, v := range map[string]*template.Template(any(tt.Template).(multipTemplate.TemplateMaps)) {
|
||||||
return t
|
multiple.Template.Store(k, v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
multiple = multipTemplate.NewFsTemplates(TemplateFs, templates)
|
||||||
|
commonTemplate(multiple)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml","wp/template.gohtml"). //单个主题设置
|
||||||
|
AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml","wp/template.gohtml")*/
|
||||||
|
return multiple
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMultipleTemplate() *multipTemplate.MultipleFsTemplate {
|
||||||
|
if multiple == nil {
|
||||||
|
BuildTemplate()
|
||||||
|
}
|
||||||
|
return multiple
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTemplate(name string) (*template.Template, bool) {
|
func GetTemplate(name string) (*template.Template, bool) {
|
||||||
t, ok := templates[name]
|
t, ok := templates.Load(name)
|
||||||
return t, ok
|
return t, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,10 +52,11 @@ func commonTemplate(t *multipTemplate.MultipleFsTemplate) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
funMap := FuncMap()
|
||||||
for _, main := range m {
|
for _, main := range m {
|
||||||
file := filepath.Base(main)
|
file := filepath.Base(main)
|
||||||
dir := strings.Split(main, "/")[0]
|
dir := strings.Split(main, "/")[0]
|
||||||
templ := template.Must(template.New(file).Funcs(t.FuncMap).ParseFS(t.Fs, main, filepath.Join(dir, "layout/*.gohtml"), "wp/template.gohtml"))
|
templ := template.Must(template.New(file).Funcs(funMap).ParseFS(t.Fs, main, filepath.Join(dir, "layout/*.gohtml"), "wp/template.gohtml"))
|
||||||
t.SetTemplate(main, templ)
|
t.SetTemplate(main, templ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +73,10 @@ func IsTemplateDirExists(tml string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsTemplateExists(tml string) bool {
|
func IsTemplateExists(tml string) bool {
|
||||||
t, ok := templates[tml]
|
t, ok := templates.Load(tml)
|
||||||
return ok && t != nil
|
return ok && t != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetTemplate(name string, val *template.Template) {
|
||||||
|
templates.Store(name, val)
|
||||||
|
}
|
||||||
|
@ -1,25 +1,35 @@
|
|||||||
package theme
|
package theme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/theme/twentyfifteen"
|
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
)
|
)
|
||||||
|
|
||||||
var themeMap = safety.NewMap[string, func(*wp.Handle)]()
|
var themeMap = safety.NewMap[string, func(*wp.Handle)]()
|
||||||
|
|
||||||
func AddThemeHookFunc(name string, fn func(handle *wp.Handle)) {
|
func AddTheme(name string, fn func(handle *wp.Handle)) {
|
||||||
if _, ok := themeMap.Load(name); ok {
|
|
||||||
panic("exists same name theme")
|
|
||||||
}
|
|
||||||
themeMap.Store(name, fn)
|
themeMap.Store(name, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DelTheme(name string) {
|
||||||
|
themeMap.Delete(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTheme(name string) (func(*wp.Handle), bool) {
|
||||||
|
return themeMap.Load(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsThemeHookFuncExist(name string) bool {
|
||||||
|
_, ok := themeMap.Load(name)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func Hook(themeName string, h *wp.Handle) {
|
func Hook(themeName string, h *wp.Handle) {
|
||||||
fn, ok := themeMap.Load(themeName)
|
fn, ok := themeMap.Load(themeName)
|
||||||
if ok && fn != nil {
|
if ok && fn != nil {
|
||||||
fn(h)
|
fn(h)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
twentyfifteen.Hook(h)
|
panic(str.Join("theme ", themeName, " don't exist"))
|
||||||
}
|
}
|
||||||
|
@ -7,37 +7,31 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var comFn = template.FuncMap{
|
|
||||||
"unescaped": func(s string) any {
|
|
||||||
return template.HTML(s)
|
|
||||||
},
|
|
||||||
"dateCh": func(t time.Time) any {
|
|
||||||
return t.Format("2006年 01月 02日")
|
|
||||||
},
|
|
||||||
"timeFormat": func(t time.Time, format string) any {
|
|
||||||
return t.Format(format)
|
|
||||||
},
|
|
||||||
"getOption": func(k string) string {
|
|
||||||
return wpconfig.GetOption(k)
|
|
||||||
},
|
|
||||||
"getLang": wpconfig.GetLang,
|
|
||||||
"postsFn": postsFn,
|
|
||||||
"exec": func(fn func() string) template.HTML {
|
|
||||||
return template.HTML(fn())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func postsFn(fn func(models.Posts) string, a models.Posts) string {
|
func postsFn(fn func(models.Posts) string, a models.Posts) string {
|
||||||
return fn(a)
|
return fn(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FuncMap() template.FuncMap {
|
func FuncMap() template.FuncMap {
|
||||||
return comFn
|
return template.FuncMap{
|
||||||
}
|
"unescaped": func(s string) any {
|
||||||
|
return template.HTML(s)
|
||||||
func addTemplateFunc(fnName string, fn any) {
|
},
|
||||||
if _, ok := comFn[fnName]; ok {
|
"dateCh": func(t time.Time) any {
|
||||||
panic("exists same name func")
|
return t.Format("2006年 01月 02日")
|
||||||
|
},
|
||||||
|
"timeFormat": func(t time.Time, format string) any {
|
||||||
|
return t.Format(format)
|
||||||
|
},
|
||||||
|
"getOption": func(k string) string {
|
||||||
|
return wpconfig.GetOption(k)
|
||||||
|
},
|
||||||
|
"getLang": wpconfig.GetLang,
|
||||||
|
"postsFn": postsFn,
|
||||||
|
"exec": func(fn func() string) template.HTML {
|
||||||
|
return template.HTML(fn())
|
||||||
|
},
|
||||||
|
"callFuncString": func(fn func(string) string, s string) template.HTML {
|
||||||
|
return template.HTML(fn(s))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
comFn[fnName] = fn
|
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func InitTheme() {
|
func InitTheme() {
|
||||||
AddThemeHookFunc(twentyfifteen.ThemeName, twentyfifteen.Hook)
|
AddTheme(twentyfifteen.ThemeName, twentyfifteen.Hook)
|
||||||
AddThemeHookFunc(twentyseventeen.ThemeName, twentyseventeen.Hook)
|
AddTheme(twentyseventeen.ThemeName, twentyseventeen.Hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentTemplateName() string {
|
func GetCurrentTheme() string {
|
||||||
tmlp := config.GetConfig().Theme
|
themeName := config.GetConfig().Theme
|
||||||
if tmlp == "" {
|
if themeName == "" {
|
||||||
tmlp = wpconfig.GetOption("template")
|
themeName = wpconfig.GetOption("template")
|
||||||
}
|
}
|
||||||
if !IsTemplateDirExists(tmlp) {
|
if !IsTemplateDirExists(themeName) {
|
||||||
tmlp = "twentyfifteen"
|
themeName = "twentyfifteen"
|
||||||
}
|
}
|
||||||
return tmlp
|
return themeName
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package twentyfifteen
|
package twentyfifteen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ var imgStyle = `.site-header {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var header = reload.Vars(constraints.Defaults)
|
var header = reload.Vars(constraints.Defaults, "twentyfifteen-customheader")
|
||||||
|
|
||||||
func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) {
|
func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) {
|
||||||
img, rand := h.GetCustomHeaderImg()
|
img, rand := h.GetCustomHeaderImg()
|
||||||
|
@ -31,11 +31,7 @@
|
|||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<footer id="colophon" class="site-footer">
|
{{template "common/colophon" .}}
|
||||||
<div class="site-info">
|
|
||||||
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{template "layout/footer" .}}
|
{{template "layout/footer" .}}
|
||||||
|
@ -52,14 +52,30 @@
|
|||||||
|
|
||||||
{{ if .showComment}}
|
{{ if .showComment}}
|
||||||
<div id="comments" class="comments-area">
|
<div id="comments" class="comments-area">
|
||||||
{{ if gt .post.CommentCount 0}}
|
{{ if ne .comments ""}}
|
||||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
|
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </h2>
|
||||||
<ol class="comment-list">
|
{{if gt .totalCommentPage 1}}
|
||||||
{{.comments|unescaped}}
|
<nav class="navigation comment-navigation">
|
||||||
</ol>
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
|
<ol class="comment-list">
|
||||||
|
{{.comments|unescaped}}
|
||||||
|
</ol>
|
||||||
|
{{if gt .totalCommentPage 1}}
|
||||||
|
<nav class="navigation comment-navigation">
|
||||||
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}}
|
{{if eq .post.CommentStatus "open"}}
|
||||||
<div id="respond" class="comment-respond">
|
<div id="respond" class="comment-respond">
|
||||||
<h3 id="reply-title" class="comment-reply-title">发表回复
|
<h3 id="reply-title" class="comment-reply-title">发表回复
|
||||||
<small>
|
<small>
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp/components"
|
"github.com/fthvgb1/wp-go/app/theme/wp/components"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
|
"github.com/fthvgb1/wp-go/app/theme/wp/middleware"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -18,20 +18,17 @@ func Hook(h *wp.Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configs(h *wp.Handle) {
|
func configs(h *wp.Handle) {
|
||||||
h.PushComponentFilterFn(widgets.Search, func(h *wp.Handle, s string, args ...any) string {
|
h.AddActionFilter(widgets.Search, func(h *wp.Handle, s string, args ...any) string {
|
||||||
return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`)
|
return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`)
|
||||||
})
|
})
|
||||||
wp.InitPipe(h)
|
wp.InitPipe(h)
|
||||||
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
|
middleware.CommonMiddleware(h)
|
||||||
wp.NewHandleFn(widget.IsCategory, 100, "widget.IsCategory"))
|
setPaginationAndRender(h)
|
||||||
|
|
||||||
h.Index.SetPageEle(plugins.TwentyFifteenPagination())
|
|
||||||
h.PushCacheGroupHeadScript(constraints.AllScene, "CalCustomBackGround", 10.005, CalCustomBackGround)
|
h.PushCacheGroupHeadScript(constraints.AllScene, "CalCustomBackGround", 10.005, CalCustomBackGround)
|
||||||
h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss)
|
h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss)
|
||||||
h.CommonComponents()
|
h.CommonComponents()
|
||||||
components.WidgetArea(h)
|
components.WidgetArea(h)
|
||||||
wp.ReplyCommentJs(h)
|
h.PushRender(constraints.AllScene, wp.NewHandleFn(renderCustomHeader, 20.5, "renderCustomHeader"))
|
||||||
h.SetData("customHeader", customHeader(h))
|
|
||||||
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 50.005, "wp.IndexRender"))
|
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 50.005, "wp.IndexRender"))
|
||||||
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender"))
|
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender"))
|
||||||
h.PushRender(constraints.Detail, wp.NewHandleFn(postThumb, 60.005, "postThumb"))
|
h.PushRender(constraints.Detail, wp.NewHandleFn(postThumb, 60.005, "postThumb"))
|
||||||
@ -41,8 +38,26 @@ func configs(h *wp.Handle) {
|
|||||||
h.PushRender(constraints.AllScene, wp.NewHandleFn(wp.PreTemplate, 70.005, "wp.PreTemplate"))
|
h.PushRender(constraints.AllScene, wp.NewHandleFn(wp.PreTemplate, 70.005, "wp.PreTemplate"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPaginationAndRender(h *wp.Handle) {
|
||||||
|
h.PushHandler(constraints.PipeRender, constraints.Detail, wp.NewHandleFn(func(hh *wp.Handle) {
|
||||||
|
d := hh.GetDetailHandle()
|
||||||
|
d.CommentRender = plugins.CommentRender()
|
||||||
|
d.CommentPageEle = plugins.TwentyFifteenCommentPagination()
|
||||||
|
}, 150, "setPaginationAndRender"))
|
||||||
|
|
||||||
|
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(func(hh *wp.Handle) {
|
||||||
|
i := hh.GetIndexHandle()
|
||||||
|
i.SetPageEle(plugins.TwentyFifteenPagination())
|
||||||
|
}, 150, "setPaginationAndRender"))
|
||||||
|
}
|
||||||
|
|
||||||
func postThumb(h *wp.Handle) {
|
func postThumb(h *wp.Handle) {
|
||||||
if h.Detail.Post.Thumbnail.Path != "" {
|
d := h.GetDetailHandle()
|
||||||
h.Detail.Post.Thumbnail = wpconfig.Thumbnail(h.Detail.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
|
if d.Post.Thumbnail.Path != "" {
|
||||||
|
d.Post.Thumbnail = wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func renderCustomHeader(h *wp.Handle) {
|
||||||
|
h.SetData("customHeader", customHeader(h))
|
||||||
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{{define "layout/colophon"}}
|
|
||||||
<footer id="colophon" class="site-footer">
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="site-info">
|
|
||||||
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
{{end}}
|
|
@ -64,12 +64,21 @@
|
|||||||
|
|
||||||
{{ if .showComment}}
|
{{ if .showComment}}
|
||||||
<div id="comments" class="comments-area">
|
<div id="comments" class="comments-area">
|
||||||
{{ if gt .post.CommentCount 0}}
|
{{ if ne .comments ""}}
|
||||||
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.post.CommentCount}}个回复 </h2>
|
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.totalCommentNum}}个回复 </h2>
|
||||||
<ol class="comment-list">
|
<ol class="comment-list">
|
||||||
{{.comments|unescaped}}
|
{{.comments|unescaped}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
{{if gt .totalCommentPage 1}}
|
||||||
|
<nav class="navigation comments-pagination" aria-label="评论">
|
||||||
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .post.CommentStatus "open"}}
|
{{if eq .post.CommentStatus "open"}}
|
||||||
{{template "respond" .}}
|
{{template "respond" .}}
|
||||||
@ -123,7 +132,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{template "layout/colophon"}}
|
{{template "common/colophon" .}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -77,6 +77,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{template "layout/colophon"}}
|
{{template "common/colophon" .}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
@ -1,8 +1,8 @@
|
|||||||
package twentyseventeen
|
package twentyseventeen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
@ -10,11 +10,12 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp/components"
|
"github.com/fthvgb1/wp-go/app/theme/wp/components"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
|
"github.com/fthvgb1/wp-go/app/theme/wp/middleware"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper"
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/fthvgb1/wp-go/plugin/pagination"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,23 +28,33 @@ var paginate = func() plugins.PageEle {
|
|||||||
p.NextEle = strings.Replace(p.NextEle, "下一页", `<span class="screen-reader-text">下一页</span>
|
p.NextEle = strings.Replace(p.NextEle, "下一页", `<span class="screen-reader-text">下一页</span>
|
||||||
<svg class="icon icon-arrow-right" aria-hidden="true" role="img"> <use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use>
|
<svg class="icon icon-arrow-right" aria-hidden="true" role="img"> <use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use>
|
||||||
</svg>`, 1)
|
</svg>`, 1)
|
||||||
|
commentPageEle = plugins.PaginationNav{
|
||||||
|
Currents: p.Current,
|
||||||
|
Prevs: p.Prev,
|
||||||
|
Nexts: p.Next,
|
||||||
|
Dotss: p.Dots,
|
||||||
|
Middles: p.Middle,
|
||||||
|
Urlss: plugins.TwentyFifteenCommentPagination().Urls,
|
||||||
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var commentPageEle pagination.Render
|
||||||
|
|
||||||
func Hook(h *wp.Handle) {
|
func Hook(h *wp.Handle) {
|
||||||
wp.Run(h, configs)
|
wp.Run(h, configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configs(h *wp.Handle) {
|
func configs(h *wp.Handle) {
|
||||||
wp.InitPipe(h)
|
wp.InitPipe(h)
|
||||||
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
|
middleware.CommonMiddleware(h)
|
||||||
wp.NewHandleFn(widget.IsCategory, 100.006, "widget.IsCategory"))
|
h.AddActionFilter("bodyClass", calClass)
|
||||||
h.PushComponentFilterFn("bodyClass", calClass)
|
|
||||||
h.PushCacheGroupHeadScript(constraints.AllScene, "colorScheme-customHeader", 10, colorScheme, customHeader)
|
h.PushCacheGroupHeadScript(constraints.AllScene, "colorScheme-customHeader", 10, colorScheme, customHeader)
|
||||||
components.WidgetArea(h)
|
components.WidgetArea(h)
|
||||||
pushScripts(h)
|
pushScripts(h)
|
||||||
h.PushRender(constraints.AllStats, wp.NewHandleFn(calCustomHeader, 10.005, "calCustomHeader"))
|
h.PushRender(constraints.AllStats, wp.NewHandleFn(calCustomHeader, 10.005, "calCustomHeader"))
|
||||||
h.SetComponentsArgs(widgets.Widget, map[string]string{
|
wp.SetComponentsArgs(widgets.Widget, map[string]string{
|
||||||
"{$before_widget}": `<section id="%s" class="%s">`,
|
"{$before_widget}": `<section id="%s" class="%s">`,
|
||||||
"{$after_widget}": `</section>`,
|
"{$after_widget}": `</section>`,
|
||||||
})
|
})
|
||||||
@ -52,12 +63,11 @@ func configs(h *wp.Handle) {
|
|||||||
wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"),
|
wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"),
|
||||||
)
|
)
|
||||||
videoHeader(h)
|
videoHeader(h)
|
||||||
h.Detail.CommentRender = commentFormat
|
h.SetData("colophon", colophon)
|
||||||
|
setPaginationAndRender(h)
|
||||||
h.CommonComponents()
|
h.CommonComponents()
|
||||||
h.Index.SetPageEle(paginate)
|
|
||||||
wp.ReplyCommentJs(h)
|
|
||||||
h.PushPostPlugin(postThumbnail)
|
h.PushPostPlugin(postThumbnail)
|
||||||
wp.SetComponentsArgsForMap(h, widgets.Search, "{$form}", searchForm)
|
wp.SetComponentsArgsForMap(widgets.Search, "{$form}", searchForm)
|
||||||
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 10.005, "wp.IndexRender"))
|
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 10.005, "wp.IndexRender"))
|
||||||
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 10.005, "wp.DetailRender"))
|
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 10.005, "wp.DetailRender"))
|
||||||
h.PushDataHandler(constraints.Detail, wp.NewHandleFn(wp.Detail, 100.005, "wp.Detail"), wp.NewHandleFn(postThumb, 90.005, "{theme}.postThumb"))
|
h.PushDataHandler(constraints.Detail, wp.NewHandleFn(wp.Detail, 100.005, "wp.Detail"), wp.NewHandleFn(postThumb, 90.005, "{theme}.postThumb"))
|
||||||
@ -65,6 +75,19 @@ func configs(h *wp.Handle) {
|
|||||||
h.PushDataHandler(constraints.AllScene, wp.NewHandleFn(wp.PreCodeAndStats, 90.005, "wp.PreCodeAndStats"))
|
h.PushDataHandler(constraints.AllScene, wp.NewHandleFn(wp.PreCodeAndStats, 90.005, "wp.PreCodeAndStats"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPaginationAndRender(h *wp.Handle) {
|
||||||
|
h.PushHandler(constraints.PipeRender, constraints.Detail, wp.NewHandleFn(func(hh *wp.Handle) {
|
||||||
|
d := hh.GetDetailHandle()
|
||||||
|
d.CommentRender = commentFormat
|
||||||
|
d.CommentPageEle = commentPageEle
|
||||||
|
d.CommentStep = 2
|
||||||
|
}, 150, "setPaginationAndRender"))
|
||||||
|
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(func(hh *wp.Handle) {
|
||||||
|
i := hh.GetIndexHandle()
|
||||||
|
i.SetPageEle(paginate)
|
||||||
|
}, 150, "setPaginationAndRender"))
|
||||||
|
}
|
||||||
|
|
||||||
var searchForm = `<form role="search" method="get" class="search-form" action="/">
|
var searchForm = `<form role="search" method="get" class="search-form" action="/">
|
||||||
<label for="search-form-1">
|
<label for="search-form-1">
|
||||||
<span class="screen-reader-text">{$label}:</span>
|
<span class="screen-reader-text">{$label}:</span>
|
||||||
@ -85,7 +108,7 @@ func errorsHandle(h *wp.Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func postThumb(h *wp.Handle) {
|
func postThumb(h *wp.Handle) {
|
||||||
d := h.Detail
|
d := h.GetDetailHandle()
|
||||||
if d.Post.Thumbnail.Path != "" {
|
if d.Post.Thumbnail.Path != "" {
|
||||||
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
|
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
|
||||||
img.Sizes = "100vw"
|
img.Sizes = "100vw"
|
||||||
@ -100,20 +123,28 @@ type comment struct {
|
|||||||
plugins.CommonCommentFormat
|
plugins.CommonCommentFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
|
var commentLi = plugins.CommonLi()
|
||||||
templ := plugins.CommonLi()
|
|
||||||
templ = strings.ReplaceAll(templ, `<a rel="nofollow" class="comment-reply-link"
|
var respondFn = plugins.Responds(respondStr)
|
||||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
|
||||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
func (c comment) FormatLi(_ context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
|
||||||
data-replyto="回复给{{CommentAuthor}}"
|
return plugins.FormatLi(commentLi, m, respondFn, depth, maxDepth, page, isTls, isThreadComments, eo, parent)
|
||||||
aria-label="回复给{{CommentAuthor}}">回复</a>`, `<a rel="nofollow" class="comment-reply-link"
|
|
||||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
|
||||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
|
||||||
data-replyto="回复给{{CommentAuthor}}"
|
|
||||||
aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>`)
|
|
||||||
return plugins.FormatLi(templ, ctx, m, depth, isTls, eo, parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var colophon = `<footer id="colophon" class="site-footer">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="site-info">
|
||||||
|
<a href="https://github.com/fthvgb1/wp-go" class="imprint">自豪地采用 wp-go</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>`
|
||||||
|
|
||||||
|
var respondStr = `<a rel="nofollow" class="comment-reply-link"
|
||||||
|
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
||||||
|
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
||||||
|
data-replyto="回复给{{CommentAuthor}}"
|
||||||
|
aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>`
|
||||||
|
|
||||||
func postThumbnail(h *wp.Handle, posts *models.Posts) {
|
func postThumbnail(h *wp.Handle, posts *models.Posts) {
|
||||||
if posts.Thumbnail.Path != "" {
|
if posts.Thumbnail.Path != "" {
|
||||||
posts.Thumbnail.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
posts.Thumbnail.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||||
@ -123,7 +154,7 @@ func postThumbnail(h *wp.Handle, posts *models.Posts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var header = reload.Vars(models.PostThumbnail{})
|
var header = reload.Vars(models.PostThumbnail{}, "twentyseventeen-headerImage")
|
||||||
|
|
||||||
func calCustomHeader(h *wp.Handle) {
|
func calCustomHeader(h *wp.Handle) {
|
||||||
h.SetData("HeaderImage", getHeaderImage(h))
|
h.SetData("HeaderImage", getHeaderImage(h))
|
||||||
@ -176,7 +207,7 @@ func calClass(h *wp.Handle, s string, _ ...any) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func videoHeader(h *wp.Handle) {
|
func videoHeader(h *wp.Handle) {
|
||||||
h.PushComponentFilterFn("videoSetting", videoPlay)
|
h.AddActionFilter("videoSetting", videoPlay)
|
||||||
wp.CustomVideo(h, constraints.Home)
|
wp.CustomVideo(h, constraints.Home)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,14 +33,14 @@ func (h *Handle) BodyClass() string {
|
|||||||
|
|
||||||
case constraints.Search:
|
case constraints.Search:
|
||||||
s := "search-no-results"
|
s := "search-no-results"
|
||||||
if len(h.Index.Posts) > 0 {
|
if len(h.GetIndexHandle().Posts) > 0 {
|
||||||
s = "search-results"
|
s = "search-results"
|
||||||
}
|
}
|
||||||
class = append(class, "search", s)
|
class = append(class, "search", s)
|
||||||
|
|
||||||
case constraints.Category, constraints.Tag:
|
case constraints.Category, constraints.Tag:
|
||||||
class = append(class, "archive", "category")
|
class = append(class, "archive", "category")
|
||||||
cat := h.Index.Param.Category
|
cat := h.GetIndexHandle().Param.Category
|
||||||
if cat == "" {
|
if cat == "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -54,16 +54,16 @@ func (h *Handle) BodyClass() string {
|
|||||||
|
|
||||||
case constraints.Author:
|
case constraints.Author:
|
||||||
class = append(class, "archive", "author")
|
class = append(class, "archive", "author")
|
||||||
author := h.Index.Param.Author
|
author := h.GetIndexHandle().Param.Author
|
||||||
user, _ := cache.GetUserByName(h.C, author)
|
user, _ := cache.GetUserByName(h.C, author)
|
||||||
class = append(class, str.Join("author-", number.IntToString(user.Id)))
|
class = append(class, str.Join("author-", number.IntToString(user.Id)))
|
||||||
if user.UserLogin[0] != '%' {
|
if user.DisplayName[0] != '%' {
|
||||||
class = append(class, str.Join("author-", user.UserLogin))
|
class = append(class, str.Join("author-", user.DisplayName))
|
||||||
}
|
}
|
||||||
|
|
||||||
case constraints.Detail:
|
case constraints.Detail:
|
||||||
class = append(class, "post-template-default", "single", "single-post")
|
class = append(class, "post-template-default", "single", "single-post")
|
||||||
class = append(class, str.Join("postid-", number.IntToString(h.Detail.Post.Id)))
|
class = append(class, str.Join("postid-", number.IntToString(h.GetDetailHandle().Post.Id)))
|
||||||
if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
|
if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
|
||||||
class = append(class, "single-format-standard")
|
class = append(class, "single-format-standard")
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ func (h *Handle) BodyClass() string {
|
|||||||
if h.themeMods.ThemeSupport.ResponsiveEmbeds {
|
if h.themeMods.ThemeSupport.ResponsiveEmbeds {
|
||||||
class = append(class, "wp-embed-responsive")
|
class = append(class, "wp-embed-responsive")
|
||||||
}
|
}
|
||||||
return h.ComponentFilterFnHook("bodyClass", strings.Join(class, " "))
|
return h.DoActionFilter("bodyClass", strings.Join(class, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func postClass(h *Handle) func(posts models.Posts) string {
|
func postClass(h *Handle) func(posts models.Posts) string {
|
||||||
@ -113,7 +113,7 @@ func (h *Handle) PostClass(posts models.Posts) string {
|
|||||||
class = append(class, TermClass(term))
|
class = append(class, TermClass(term))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.ComponentFilterFnHook("postClass", strings.Join(class, " "))
|
return h.DoActionFilter("postClass", strings.Join(class, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TermClass(term models.TermsMy) string {
|
func TermClass(term models.TermsMy) string {
|
||||||
|
126
app/theme/wp/comments.go
Normal file
126
app/theme/wp/comments.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package wp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenderComment(ctx context.Context, page int, render plugins.CommentHtml, ids []uint64, timeout time.Duration, isTLS bool) (string, error) {
|
||||||
|
ca, _ := cachemanager.GetMapCache[uint64, models.Comments]("postCommentData")
|
||||||
|
children, _ := cachemanager.GetMapCache[uint64, []uint64]("commentChildren")
|
||||||
|
h := CommentHandle{
|
||||||
|
maxDepth: str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5),
|
||||||
|
depth: 1,
|
||||||
|
isTls: isTLS,
|
||||||
|
html: render,
|
||||||
|
order: wpconfig.GetOption("comment_order"),
|
||||||
|
ca: ca,
|
||||||
|
children: children,
|
||||||
|
threadComments: wpconfig.GetOption("thread_comments") == "1",
|
||||||
|
page: page,
|
||||||
|
}
|
||||||
|
return h.formatComments(ctx, ids, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommentHandle struct {
|
||||||
|
maxDepth int
|
||||||
|
depth int
|
||||||
|
isTls bool
|
||||||
|
html plugins.CommentHtml
|
||||||
|
order string
|
||||||
|
page int
|
||||||
|
ca *cache.MapCache[uint64, models.Comments]
|
||||||
|
children *cache.MapCache[uint64, []uint64]
|
||||||
|
threadComments bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommentHandle) findGrandchildComments(ctx context.Context, timeout time.Duration, comments []models.Comments) ([]models.Comments, error) {
|
||||||
|
parentIds := slice.Map(comments, func(t models.Comments) uint64 {
|
||||||
|
return t.CommentId
|
||||||
|
})
|
||||||
|
children, err := c.children.GetCacheBatch(ctx, parentIds, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rr := slice.FilterAndMap(children, func(t []uint64) ([]uint64, bool) {
|
||||||
|
return t, len(t) > 0
|
||||||
|
})
|
||||||
|
if len(rr) < 1 {
|
||||||
|
return comments, nil
|
||||||
|
}
|
||||||
|
ids := slice.Decompress(rr)
|
||||||
|
r, err := c.ca.GetCacheBatch(ctx, ids, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rrr, err := c.findGrandchildComments(ctx, timeout, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
comments = append(comments, rrr...)
|
||||||
|
slice.Sort(comments, func(i, j models.Comments) bool {
|
||||||
|
return c.html.FloorOrder(i, j)
|
||||||
|
})
|
||||||
|
return comments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommentHandle) formatComments(ctx context.Context, ids []uint64, timeout time.Duration) (html string, err error) {
|
||||||
|
comments, err := c.ca.GetCacheBatch(ctx, ids, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if c.depth > 1 && c.depth < c.maxDepth {
|
||||||
|
slice.Sort(comments, func(i, j models.Comments) bool {
|
||||||
|
return c.html.FloorOrder(i, j)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fixChildren := false
|
||||||
|
if c.depth >= c.maxDepth {
|
||||||
|
comments, err = c.findGrandchildComments(ctx, timeout, comments)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fixChildren = true
|
||||||
|
}
|
||||||
|
s := str.NewBuilder()
|
||||||
|
for i, comment := range comments {
|
||||||
|
eo := "even"
|
||||||
|
if (i+1)%2 == 0 {
|
||||||
|
eo = "odd"
|
||||||
|
}
|
||||||
|
parent := ""
|
||||||
|
fl := false
|
||||||
|
var children []uint64
|
||||||
|
if !fixChildren {
|
||||||
|
children, err = c.children.GetCache(ctx, comment.CommentId, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.threadComments && len(children) > 0 && c.depth < c.maxDepth+1 {
|
||||||
|
parent = "parent"
|
||||||
|
fl = true
|
||||||
|
}
|
||||||
|
s.WriteString(c.html.FormatLi(ctx, comment, c.depth, c.maxDepth, c.page, c.isTls, c.threadComments, eo, parent))
|
||||||
|
if fl {
|
||||||
|
c.depth++
|
||||||
|
ss, err := c.formatComments(ctx, children, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s.WriteString(`<ol class="children">`, ss, `</ol>`)
|
||||||
|
c.depth--
|
||||||
|
}
|
||||||
|
s.WriteString("</li><!-- #comment-## -->")
|
||||||
|
}
|
||||||
|
|
||||||
|
html = s.String()
|
||||||
|
return
|
||||||
|
}
|
@ -1,62 +1,103 @@
|
|||||||
package wp
|
package wp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handle) DeleteComponents(scene, name string) {
|
var handleComponents = safety.NewMap[string, map[string][]Components[string]]()
|
||||||
h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
|
var handleComponentHook = safety.NewMap[string, map[string][]func(Components[string]) (Components[string], bool)]()
|
||||||
return c, c.Name != name
|
|
||||||
|
var componentsArgs = safety.NewMap[string, any]()
|
||||||
|
var componentFilterFns = safety.NewMap[string, []func(*Handle, string, ...any) string]()
|
||||||
|
|
||||||
|
func (h *Handle) DeleteComponents(scene, componentKey, componentName string) {
|
||||||
|
v, ok := handleComponentHook.Load(scene)
|
||||||
|
if !ok {
|
||||||
|
v = make(map[string][]func(Components[string]) (Components[string], bool))
|
||||||
|
}
|
||||||
|
|
||||||
|
v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) {
|
||||||
|
return c, c.Name != componentName
|
||||||
})
|
})
|
||||||
|
handleComponentHook.Store(scene, v)
|
||||||
}
|
}
|
||||||
func (h *Handle) ReplaceComponents(scene, name string, components Components[string]) {
|
func (h *Handle) ReplaceComponents(scene, componentKey, componentName string, components Components[string]) {
|
||||||
h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
|
v, ok := handleComponentHook.Load(scene)
|
||||||
if c.Name == name {
|
if !ok {
|
||||||
|
v = make(map[string][]func(Components[string]) (Components[string], bool))
|
||||||
|
}
|
||||||
|
v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) {
|
||||||
|
if c.Name == componentName {
|
||||||
c = components
|
c = components
|
||||||
}
|
}
|
||||||
return c, true
|
return c, true
|
||||||
})
|
})
|
||||||
|
handleComponentHook.Store(scene, v)
|
||||||
}
|
}
|
||||||
func (h *Handle) HookComponents(scene string, fn func(Components[string]) (Components[string], bool)) {
|
func (h *Handle) PushComponentHooks(scene, componentKey string, fn func(Components[string]) (Components[string], bool)) {
|
||||||
h.componentHook[scene] = append(h.componentHook[scene], fn)
|
v, ok := handleComponentHook.Load(scene)
|
||||||
|
if !ok {
|
||||||
|
v = make(map[string][]func(Components[string]) (Components[string], bool))
|
||||||
|
}
|
||||||
|
v[componentKey] = append(v[componentKey], fn)
|
||||||
|
handleComponentHook.Store(scene, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalComponents(h *Handle) {
|
var GetAndHookComponents = reload.BuildMapFnWithAnyParams[string]("calComponents", HookComponent)
|
||||||
componentss := reload.GetAnyValMapBy("scene-components", str.Join("allScene-", h.scene), h, func(h *Handle) map[string][]Components[string] {
|
|
||||||
return maps.MergeBy(func(k string, v1, v2 []Components[string]) ([]Components[string], bool) {
|
func HookComponent(a ...any) []Components[string] {
|
||||||
vv := append(v1, v2...)
|
componentKey := a[0].(string)
|
||||||
return vv, vv != nil
|
scene := a[1].(string)
|
||||||
}, nil, h.components[h.scene], h.components[constraints.AllScene])
|
mut := reload.GetGlobeMutex()
|
||||||
|
mut.Lock()
|
||||||
|
defer mut.Unlock()
|
||||||
|
components := GetComponents(scene, componentKey)
|
||||||
|
r := slice.FilterAndMap(components, func(component Components[string]) (Components[string], bool) {
|
||||||
|
keyHooks, ok := handleComponentHook.Load(scene)
|
||||||
|
if !ok {
|
||||||
|
return component, true
|
||||||
|
}
|
||||||
|
hooks := keyHooks[componentKey]
|
||||||
|
for _, fn := range hooks {
|
||||||
|
hookedComponent, ok := fn(component)
|
||||||
|
if !ok { // DeleteComponents fn
|
||||||
|
return hookedComponent, false
|
||||||
|
}
|
||||||
|
component = hookedComponent // ReplaceComponents fn
|
||||||
|
}
|
||||||
|
return component, true
|
||||||
})
|
})
|
||||||
for k, components := range componentss {
|
slice.SimpleSort(r, slice.DESC, func(t Components[string]) float64 {
|
||||||
key := str.Join("calComponents-", h.scene, "-", k)
|
return t.Order
|
||||||
ss := reload.GetAnyValMapBy("calComponents", key, h, func(h *Handle) []Components[string] {
|
})
|
||||||
r := slice.FilterAndMap(components, func(t Components[string]) (Components[string], bool) {
|
return r
|
||||||
fns, ok := h.componentHook[k]
|
}
|
||||||
if !ok {
|
|
||||||
return t, true
|
func GetComponents(scene, key string) (r []Components[string]) {
|
||||||
}
|
sceneComponents, _ := handleComponents.Load(scene)
|
||||||
for _, fn := range fns {
|
allSceneComponents, _ := handleComponents.Load(constraints.AllScene)
|
||||||
c, ok := fn(t)
|
r = append(sceneComponents[key], allSceneComponents[key]...)
|
||||||
if !ok {
|
return
|
||||||
return c, false
|
}
|
||||||
}
|
|
||||||
t = c
|
var CacheComponent = reload.BuildMapFnWithAnyParams[string]("cacheComponents", cacheComponentFn)
|
||||||
}
|
|
||||||
return t, true
|
func cacheComponentFn(a ...any) string {
|
||||||
})
|
return a[0].(Components[string]).Fn(a[1].(*Handle))
|
||||||
slice.Sort(r, func(i, j Components[string]) bool {
|
}
|
||||||
return i.Order > j.Order
|
|
||||||
})
|
func CalComponent(h *Handle) func(string) string {
|
||||||
return r
|
return func(componentKey string) string {
|
||||||
})
|
cacheKey := str.Join("get-hook-Components-", h.scene, "-", componentKey)
|
||||||
var s = make([]string, 0, len(ss))
|
hookedComponents := GetAndHookComponents(cacheKey, componentKey, h.scene)
|
||||||
for _, component := range ss {
|
var s = make([]string, 0, len(hookedComponents))
|
||||||
|
for _, component := range hookedComponents {
|
||||||
if component.Val != "" {
|
if component.Val != "" {
|
||||||
s = append(s, component.Val)
|
s = append(s, component.Val)
|
||||||
continue
|
continue
|
||||||
@ -64,7 +105,8 @@ func CalComponents(h *Handle) {
|
|||||||
if component.Fn != nil {
|
if component.Fn != nil {
|
||||||
v := ""
|
v := ""
|
||||||
if component.Cached {
|
if component.Cached {
|
||||||
v = reload.GetAnyValMapBy("cacheComponents", component.Name, h, component.Fn)
|
key := str.Join(h.scene, "-", componentKey, "-", component.Name)
|
||||||
|
v = CacheComponent(key, component, h)
|
||||||
} else {
|
} else {
|
||||||
v = component.Fn(h)
|
v = component.Fn(h)
|
||||||
}
|
}
|
||||||
@ -73,17 +115,17 @@ func CalComponents(h *Handle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.ginH[k] = strings.Join(s, "\n")
|
return strings.Join(s, "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushComponents(scene, componentType string, components ...Components[string]) {
|
func (h *Handle) PushComponents(scene, componentType string, components ...Components[string]) {
|
||||||
c, ok := h.components[scene]
|
c, ok := handleComponents.Load(scene)
|
||||||
if !ok {
|
if !ok {
|
||||||
c = make(map[string][]Components[string])
|
c = make(map[string][]Components[string])
|
||||||
h.components[scene] = c
|
|
||||||
}
|
}
|
||||||
c[componentType] = append(c[componentType], components...)
|
c[componentType] = append(c[componentType], components...)
|
||||||
|
handleComponents.Store(scene, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushGroupComponentStr(scene, componentType, name string, order float64, strs ...string) {
|
func (h *Handle) PushGroupComponentStr(scene, componentType, name string, order float64, strs ...string) {
|
||||||
@ -139,8 +181,8 @@ func (h *Handle) PushGroupHeadScript(scene, name string, order float64, str ...s
|
|||||||
h.PushGroupComponentStr(scene, constraints.HeadScript, name, order, str...)
|
h.PushGroupComponentStr(scene, constraints.HeadScript, name, order, str...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetComponentsArgs[T any](h *Handle, k string, defaults T) T {
|
func GetComponentsArgs[T any](k string, defaults T) T {
|
||||||
v, ok := h.componentsArgs[k]
|
v, ok := componentsArgs.Load(k)
|
||||||
if ok {
|
if ok {
|
||||||
vv, ok := v.(T)
|
vv, ok := v.(T)
|
||||||
if ok {
|
if ok {
|
||||||
@ -150,60 +192,61 @@ func GetComponentsArgs[T any](h *Handle, k string, defaults T) T {
|
|||||||
return defaults
|
return defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
func PushComponentsArgsForSlice[T any](h *Handle, name string, v ...T) {
|
func PushComponentsArgsForSlice[T any](name string, v ...T) {
|
||||||
val, ok := h.componentsArgs[name]
|
val, ok := componentsArgs.Load(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
var vv []T
|
var vv []T
|
||||||
vv = append(vv, v...)
|
vv = append(vv, v...)
|
||||||
h.componentsArgs[name] = vv
|
componentsArgs.Store(name, vv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vv, ok := val.([]T)
|
vv, ok := val.([]T)
|
||||||
if ok {
|
if ok {
|
||||||
vv = append(vv, v...)
|
vv = append(vv, v...)
|
||||||
h.componentsArgs[name] = vv
|
componentsArgs.Store(name, vv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func SetComponentsArgsForMap[K comparable, V any](h *Handle, name string, key K, v V) {
|
func SetComponentsArgsForMap[K comparable, V any](name string, key K, v V) {
|
||||||
val, ok := h.componentsArgs[name]
|
val, ok := componentsArgs.Load(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
vv := make(map[K]V)
|
vv := make(map[K]V)
|
||||||
vv[key] = v
|
vv[key] = v
|
||||||
h.componentsArgs[name] = vv
|
componentsArgs.Store(name, vv)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vv, ok := val.(map[K]V)
|
vv, ok := val.(map[K]V)
|
||||||
if ok {
|
if ok {
|
||||||
vv[key] = v
|
vv[key] = v
|
||||||
h.componentsArgs[name] = vv
|
componentsArgs.Store(name, vv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func MergeComponentsArgsForMap[K comparable, V any](h *Handle, name string, m map[K]V) {
|
func MergeComponentsArgsForMap[K comparable, V any](name string, m map[K]V) {
|
||||||
val, ok := h.componentsArgs[name]
|
val, ok := componentsArgs.Load(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
h.componentsArgs[name] = m
|
componentsArgs.Store(name, m)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vv, ok := val.(map[K]V)
|
vv, ok := val.(map[K]V)
|
||||||
if ok {
|
if ok {
|
||||||
h.componentsArgs[name] = maps.Merge(vv, m)
|
componentsArgs.Store(name, maps.Merge(vv, m))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) SetComponentsArgs(key string, value any) {
|
func SetComponentsArgs(key string, value any) {
|
||||||
h.componentsArgs[key] = value
|
componentsArgs.Store(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) ComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
|
func (h *Handle) GetComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
|
||||||
fn, ok := h.componentFilterFn[name]
|
return componentFilterFns.Load(name)
|
||||||
return fn, ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushComponentFilterFn(name string, fns ...func(*Handle, string, ...any) string) {
|
func (h *Handle) AddActionFilter(name string, fns ...func(*Handle, string, ...any) string) {
|
||||||
h.componentFilterFn[name] = append(h.componentFilterFn[name], fns...)
|
v, _ := componentFilterFns.Load(name)
|
||||||
|
v = append(v, fns...)
|
||||||
|
componentFilterFns.Store(name, v)
|
||||||
}
|
}
|
||||||
func (h *Handle) ComponentFilterFnHook(name, s string, args ...any) string {
|
func (h *Handle) DoActionFilter(name, s string, args ...any) string {
|
||||||
calls, ok := h.componentFilterFn[name]
|
calls, ok := componentFilterFns.Load(name)
|
||||||
if ok {
|
if ok {
|
||||||
return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string {
|
return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string {
|
||||||
return fn(h, r, args...)
|
return fn(h, r, args...)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package components
|
package components
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp/components/block"
|
"github.com/fthvgb1/wp-go/app/theme/wp/components/block"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
@ -29,6 +30,7 @@ func Block(id string) (func(*wp.Handle) string, string) {
|
|||||||
if ok {
|
if ok {
|
||||||
s, err := fn(h, id, parserBlock)
|
s, err := fn(h, id, parserBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logs.Error(err, str.Join("parse block", parserBlock.Name, " fail "), parserBlock)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, s())
|
out = append(out, s())
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
constraints2 "github.com/fthvgb1/wp-go/app/pkg/constraints"
|
constraints2 "github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
@ -12,6 +11,7 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"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/components/widget"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
@ -50,7 +50,7 @@ func parseAttr(attr map[any]any) string {
|
|||||||
}
|
}
|
||||||
style := maps.GetAnyAnyValWithDefaults[map[any]any](attr, nil, "style", "typography")
|
style := maps.GetAnyAnyValWithDefaults[map[any]any](attr, nil, "style", "typography")
|
||||||
if len(style) > 0 {
|
if len(style) > 0 {
|
||||||
styless := maps.AnyAnyMap(style, func(k, v any) (string, string, bool) {
|
styless := maps.AnyAnyMapTo(style, func(k, v any) (string, string, bool) {
|
||||||
kk, ok := k.(string)
|
kk, ok := k.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", "", false
|
return "", "", false
|
||||||
@ -71,48 +71,61 @@ func parseAttr(attr map[any]any) string {
|
|||||||
return strings.Join(attrs, " ")
|
return strings.Join(attrs, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetCategoryAttr = reload.BuildValFn("block-category-attr", parseAttr)
|
||||||
|
|
||||||
|
var GetCategoryConf = reload.BuildValFnWithConfirm("block-category-conf", categoryConfFn, 5)
|
||||||
|
|
||||||
|
func categoryConfFn(blockParser ParserBlock) (map[any]any, bool) {
|
||||||
|
var con any
|
||||||
|
err := json.Unmarshal([]byte(blockParser.Attrs), &con)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err, "解析category attr错误", blockParser.Attrs)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
var conf map[any]any
|
||||||
|
switch con.(type) {
|
||||||
|
case map[any]any:
|
||||||
|
conf = con.(map[any]any)
|
||||||
|
case map[string]any:
|
||||||
|
conf = maps.StrAnyToAnyAny(con.(map[string]any))
|
||||||
|
}
|
||||||
|
conf = maps.FilterZeroMerge(categoryConf(), conf)
|
||||||
|
|
||||||
|
if maps.GetAnyAnyValWithDefaults(conf, false, "showPostCounts") {
|
||||||
|
conf["count"] = int64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if maps.GetAnyAnyValWithDefaults(conf, false, "displayAsDropdown") {
|
||||||
|
conf["dropdown"] = int64(1)
|
||||||
|
}
|
||||||
|
if maps.GetAnyAnyValWithDefaults(conf, false, "showHierarchy") {
|
||||||
|
conf["hierarchical"] = int64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
class := maps.GetAnyAnyValWithDefaults(conf, "", "className")
|
||||||
|
classes := strings.Split(class, " ")
|
||||||
|
classes = append(classes, "wp-block-categories")
|
||||||
|
if conf["dropdown"].(int64) == 1 {
|
||||||
|
classes = append(classes, "wp-block-categories-dropdown")
|
||||||
|
conf["className"] = strings.Join(classes, " ")
|
||||||
|
} else {
|
||||||
|
classes = append(classes, "wp-block-categories-list")
|
||||||
|
conf["className"] = strings.Join(classes, " ")
|
||||||
|
}
|
||||||
|
return conf, true
|
||||||
|
}
|
||||||
|
|
||||||
|
var GetCategoryArgs = reload.BuildValFnWithAnyParams("block-category-args", categoryArgs)
|
||||||
|
|
||||||
|
func categoryArgs(_ ...any) map[string]string {
|
||||||
|
args := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
|
return maps.FilterZeroMerge(categoryDefaultArgs(), args)
|
||||||
|
}
|
||||||
|
|
||||||
func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) {
|
func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) {
|
||||||
counter := number.Counters[int]()
|
counter := number.Counters[int]()
|
||||||
var err error
|
var err error
|
||||||
conf := reload.GetAnyValBys("block-category-conf", h, func(h *wp.Handle) map[any]any {
|
conf := GetCategoryConf(blockParser)
|
||||||
var con any
|
|
||||||
err = json.Unmarshal([]byte(blockParser.Attrs), &con)
|
|
||||||
if err != nil {
|
|
||||||
logs.Error(err, "解析category attr错误", blockParser.Attrs)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var conf map[any]any
|
|
||||||
switch con.(type) {
|
|
||||||
case map[any]any:
|
|
||||||
conf = con.(map[any]any)
|
|
||||||
case map[string]any:
|
|
||||||
conf = maps.StrAnyToAnyAny(con.(map[string]any))
|
|
||||||
}
|
|
||||||
conf = maps.FilterZeroMerge(categoryConf(), conf)
|
|
||||||
|
|
||||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showPostCounts") {
|
|
||||||
conf["count"] = int64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if maps.GetAnyAnyValWithDefaults(conf, false, "displayAsDropdown") {
|
|
||||||
conf["dropdown"] = int64(1)
|
|
||||||
}
|
|
||||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showHierarchy") {
|
|
||||||
conf["hierarchical"] = int64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
class := maps.GetAnyAnyValWithDefaults(conf, "", "className")
|
|
||||||
classes := strings.Split(class, " ")
|
|
||||||
classes = append(classes, "wp-block-categories")
|
|
||||||
if conf["dropdown"].(int64) == 1 {
|
|
||||||
classes = append(classes, "wp-block-categories-dropdown")
|
|
||||||
conf["className"] = strings.Join(classes, " ")
|
|
||||||
} else {
|
|
||||||
classes = append(classes, "wp-block-categories-list")
|
|
||||||
conf["className"] = strings.Join(classes, " ")
|
|
||||||
}
|
|
||||||
return conf
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -127,10 +140,7 @@ func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string,
|
|||||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") {
|
if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") {
|
||||||
h.C.Set("showOnlyTopLevel", true)
|
h.C.Set("showOnlyTopLevel", true)
|
||||||
}
|
}
|
||||||
args := reload.GetAnyValBys("block-category-args", h, func(h *wp.Handle) map[string]string {
|
args := GetCategoryArgs()
|
||||||
args := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
|
||||||
return maps.FilterZeroMerge(categoryDefaultArgs(), args)
|
|
||||||
})
|
|
||||||
|
|
||||||
return func() string {
|
return func() string {
|
||||||
return category(h, id, counter, args, conf)
|
return category(h, id, counter, args, conf)
|
||||||
@ -150,21 +160,21 @@ func category(h *wp.Handle, id string, counter number.Counter[int], args map[str
|
|||||||
return str.Join(before, out, args["{$after_widget}"])
|
return str.Join(before, out, args["{$after_widget}"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func categoryUl(h *wp.Handle, categories []models.TermsMy, conf map[any]any) string {
|
func categoryUl(h *wp.Handle, categories []models.TermsMy, confAttr map[any]any) string {
|
||||||
s := str.NewBuilder()
|
s := str.NewBuilder()
|
||||||
li := widget.CategoryLi(h, conf, categories)
|
li := widget.CategoryLi(h, confAttr, categories)
|
||||||
attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
|
attrs := GetCategoryAttr(confAttr)
|
||||||
s.Sprintf(`<ul %s>%s</ul>`, attrs, li)
|
s.Sprintf(`<ul %s>%s</ul>`, attrs, li)
|
||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropdown(h *wp.Handle, categories []models.TermsMy, id int, args map[string]string, conf map[any]any) string {
|
func dropdown(h *wp.Handle, categories []models.TermsMy, id int, args map[string]string, confAttr map[any]any) string {
|
||||||
s := str.NewBuilder()
|
s := str.NewBuilder()
|
||||||
ids := fmt.Sprintf(`wp-block-categories-%v`, id)
|
ids := fmt.Sprintf(`wp-block-categories-%v`, id)
|
||||||
args = maps.Copy(args)
|
args = maps.Copy(args)
|
||||||
args["{$selectId}"] = ids
|
args["{$selectId}"] = ids
|
||||||
attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
|
attrs := GetCategoryAttr(confAttr)
|
||||||
selects := widget.DropdownCategories(h, args, conf, categories)
|
selects := widget.DropdownCategories(h, args, confAttr, categories)
|
||||||
s.Sprintf(`<div %s><label class="screen-reader-text" for="%s">%s</label>%s%s</div>`, attrs, ids, args["{$title}"], selects, strings.ReplaceAll(categoryDropdownScript, "{$id}", ids))
|
s.Sprintf(`<div %s><label class="screen-reader-text" for="%s">%s</label>%s%s</div>`, attrs, ids, args["{$title}"], selects, strings.ReplaceAll(categoryDropdownScript, "{$id}", ids))
|
||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
@ -40,21 +40,29 @@ var archivesConfig = map[any]any{
|
|||||||
"title": "归档",
|
"title": "归档",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetArchiveConf = BuildconfigFn(archivesConfig, "widget_archives", int64(2))
|
||||||
|
var GetArchiveArgs = reload.BuildValFnWithAnyParams("widget_archive-args", archiveArgsFn)
|
||||||
|
|
||||||
|
func archiveArgsFn(a ...any) map[string]string {
|
||||||
|
h := a[0].(*wp.Handle)
|
||||||
|
conf := a[1].(map[any]any)
|
||||||
|
id := a[2].(string)
|
||||||
|
archiveArgs := archiveArgs()
|
||||||
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, CommonArgs())
|
||||||
|
args := wp.GetComponentsArgs(widgets.Archive, archiveArgs)
|
||||||
|
args = maps.FilterZeroMerge(archiveArgs, CommonArgs(), commonArgs, args)
|
||||||
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("archives-", id), str.Join("widget widget_", "archive"))
|
||||||
|
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||||
|
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"].(string))
|
||||||
|
args["{$navCloser}"] = "</nav>"
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
func Archive(h *wp.Handle, id string) string {
|
func Archive(h *wp.Handle, id string) string {
|
||||||
conf := configs(archivesConfig, "widget_archives", int64(2))
|
conf := GetArchiveConf()
|
||||||
args := reload.GetAnyValBys("widget-archive-args", h, func(h *wp.Handle) map[string]string {
|
args := GetArchiveArgs(h, conf, id)
|
||||||
archiveArgs := archiveArgs()
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, CommonArgs())
|
|
||||||
args := wp.GetComponentsArgs(h, widgets.Archive, archiveArgs)
|
|
||||||
args = maps.FilterZeroMerge(archiveArgs, CommonArgs(), commonArgs, args)
|
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("archives-", id), str.Join("widget widget_", "archive"))
|
|
||||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
|
||||||
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
|
||||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"].(string))
|
|
||||||
args["{$navCloser}"] = "</nav>"
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
|
|
||||||
s := archiveTemplate
|
s := archiveTemplate
|
||||||
if int64(1) == conf["dropdown"].(int64) {
|
if int64(1) == conf["dropdown"].(int64) {
|
||||||
@ -62,7 +70,7 @@ func Archive(h *wp.Handle, id string) string {
|
|||||||
} else {
|
} else {
|
||||||
s = strings.ReplaceAll(s, "{$html}", archiveUl(h, conf, args, cache.Archives(h.C)))
|
s = strings.ReplaceAll(s, "{$html}", archiveUl(h, conf, args, cache.Archives(h.C)))
|
||||||
}
|
}
|
||||||
return h.ComponentFilterFnHook(widgets.Archive, str.Replace(s, args))
|
return h.DoActionFilter(widgets.Archive, str.Replace(s, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
var dropdownScript = `
|
var dropdownScript = `
|
||||||
@ -83,11 +91,12 @@ var dropdownScript = `
|
|||||||
func archiveDropDown(h *wp.Handle, conf map[any]any, args map[string]string, archives []models.PostArchive) string {
|
func archiveDropDown(h *wp.Handle, conf map[any]any, args map[string]string, archives []models.PostArchive) string {
|
||||||
option := str.NewBuilder()
|
option := str.NewBuilder()
|
||||||
option.Sprintf(`<option value="">%s</option>`, args["{$dropdown_label}"])
|
option.Sprintf(`<option value="">%s</option>`, args["{$dropdown_label}"])
|
||||||
month := strings.TrimLeft(h.Index.Param.Month, "0")
|
i := h.GetIndexHandle()
|
||||||
|
month := strings.TrimLeft(i.Param.Month, "0")
|
||||||
showCount := conf["count"].(int64)
|
showCount := conf["count"].(int64)
|
||||||
for _, archive := range archives {
|
for _, archive := range archives {
|
||||||
sel := ""
|
sel := ""
|
||||||
if h.Index.Param.Year == archive.Year && month == archive.Month {
|
if i.Param.Year == archive.Year && month == archive.Month {
|
||||||
sel = "selected"
|
sel = "selected"
|
||||||
}
|
}
|
||||||
count := ""
|
count := ""
|
||||||
|
@ -2,17 +2,16 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/helper/tree"
|
"github.com/fthvgb1/wp-go/helper/tree"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,22 +44,29 @@ func categoryArgs() map[string]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetCategoryConf = BuildconfigFn(categoryConfig, "widget_categories", int64(2))
|
||||||
|
|
||||||
|
var GetCategoryArgs = reload.BuildValFnWithAnyParams("widget-category-args", categoryArgsFn)
|
||||||
|
|
||||||
|
func categoryArgsFn(a ...any) map[string]string {
|
||||||
|
h := a[0].(*wp.Handle)
|
||||||
|
conf := a[1].(map[any]any)
|
||||||
|
id := a[2].(string)
|
||||||
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
|
args := wp.GetComponentsArgs(widgets.Categories, categoryArgs())
|
||||||
|
args = maps.FilterZeroMerge(categoryArgs(), CommonArgs(), commonArgs, args)
|
||||||
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("categories-", id), str.Join("widget widget_", "categories"))
|
||||||
|
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||||
|
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{title}"])
|
||||||
|
args["{$navCloser}"] = "</nav>"
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
func Category(h *wp.Handle, id string) string {
|
func Category(h *wp.Handle, id string) string {
|
||||||
conf := configs(categoryConfig, "widget_categories", int64(2))
|
conf := GetCategoryConf()
|
||||||
|
args := GetCategoryArgs(h, conf, id)
|
||||||
args := reload.GetAnyValBys("widget-category-args", h, func(h *wp.Handle) map[string]string {
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
|
||||||
args := wp.GetComponentsArgs(h, widgets.Categories, categoryArgs())
|
|
||||||
args = maps.FilterZeroMerge(categoryArgs(), CommonArgs(), commonArgs, args)
|
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("categories-", id), str.Join("widget widget_", "categories"))
|
|
||||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
|
||||||
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
|
||||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{title}"])
|
|
||||||
args["{$navCloser}"] = "</nav>"
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
|
|
||||||
t := categoryTemplate
|
t := categoryTemplate
|
||||||
dropdown := conf["dropdown"].(int64)
|
dropdown := conf["dropdown"].(int64)
|
||||||
categories := cache.CategoriesTags(h.C, constraints.Category)
|
categories := cache.CategoriesTags(h.C, constraints.Category)
|
||||||
@ -69,7 +75,7 @@ func Category(h *wp.Handle, id string) string {
|
|||||||
} else {
|
} else {
|
||||||
t = strings.ReplaceAll(t, "{$html}", categoryUL(h, args, conf, categories))
|
t = strings.ReplaceAll(t, "{$html}", categoryUL(h, args, conf, categories))
|
||||||
}
|
}
|
||||||
return h.ComponentFilterFnHook(widgets.Categories, str.Replace(t, args))
|
return h.DoActionFilter(widgets.Categories, str.Replace(t, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CategoryLi(h *wp.Handle, conf map[any]any, categories []models.TermsMy) string {
|
func CategoryLi(h *wp.Handle, conf map[any]any, categories []models.TermsMy) string {
|
||||||
@ -202,8 +208,9 @@ func DropdownCategories(h *wp.Handle, args map[string]string, conf map[any]any,
|
|||||||
s.Sprintf(` <option value="-1">%s</option>
|
s.Sprintf(` <option value="-1">%s</option>
|
||||||
`, args["{$show_option_none}"])
|
`, args["{$show_option_none}"])
|
||||||
currentCategory := ""
|
currentCategory := ""
|
||||||
|
i := h.GetIndexHandle()
|
||||||
if h.Scene() == constraints.Category {
|
if h.Scene() == constraints.Category {
|
||||||
currentCategory = h.Index.Param.Category
|
currentCategory = i.Param.Category
|
||||||
}
|
}
|
||||||
showCount := conf["count"].(int64)
|
showCount := conf["count"].(int64)
|
||||||
fn := func(category models.TermsMy, deep int) {
|
fn := func(category models.TermsMy, deep int) {
|
||||||
@ -232,19 +239,11 @@ func DropdownCategories(h *wp.Handle, args map[string]string, conf map[any]any,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
s.WriteString(" </select>\n")
|
s.WriteString(" </select>\n")
|
||||||
return h.ComponentFilterFnHook("wp_dropdown_cats", s.String())
|
return h.DoActionFilter("wp_dropdown_cats", s.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsCategory(h *wp.Handle) {
|
func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) {
|
||||||
name, ok := parseDropdownCate(h)
|
cate := wp.GetComponentsArgs[map[string]string](widgets.Categories, categoryArgs())
|
||||||
if ok {
|
|
||||||
h.C.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/p/category/%s", name))
|
|
||||||
h.Abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDropdownCate(h *wp.Handle) (cateName string, r bool) {
|
|
||||||
cate := wp.GetComponentsArgs[map[string]string](h, widgets.Categories, categoryArgs())
|
|
||||||
name, ok := cate["{$name}"]
|
name, ok := cate["{$name}"]
|
||||||
if !ok || name == "" {
|
if !ok || name == "" {
|
||||||
return
|
return
|
||||||
@ -264,6 +263,15 @@ func parseDropdownCate(h *wp.Handle) (cateName string, r bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
r = true
|
r = true
|
||||||
cateName = cc.Name
|
category = cc
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CategoryQueryName(h *wp.Handle) string {
|
||||||
|
cate := wp.GetComponentsArgs[map[string]string](widgets.Categories, categoryArgs())
|
||||||
|
name, ok := cate["{$name}"]
|
||||||
|
if ok {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
)
|
)
|
||||||
@ -14,9 +14,13 @@ func Fn(id string, fn func(*wp.Handle, string) string) func(h *wp.Handle) string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func configs[M ~map[K]V, K comparable, V any](m M, key string, a ...any) M {
|
func configFns[K comparable, V any](m map[K]V, key string, a ...any) func(_ ...any) map[K]V {
|
||||||
return reload.GetAnyValBys(str.Join("widget-config-", key), key, func(_ string) M {
|
return func(_ ...any) map[K]V {
|
||||||
c := wpconfig.GetPHPArrayVal[M](key, nil, a...)
|
c := wpconfig.GetPHPArrayVal[map[K]V](key, nil, a...)
|
||||||
return maps.FilterZeroMerge(maps.Copy(m), c)
|
return maps.FilterZeroMerge(maps.Copy(m), c)
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildconfigFn[K comparable, V any](m map[K]V, key string, a ...any) func(_ ...any) map[K]V {
|
||||||
|
return reload.BuildValFnWithAnyParams(str.Join("widget-config-", key), configFns(m, key, a...))
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
@ -21,34 +21,39 @@ var metaTemplate = `{$before_widget}
|
|||||||
{$navCloser}
|
{$navCloser}
|
||||||
{$after_widget}`
|
{$after_widget}`
|
||||||
|
|
||||||
func metaArgs() map[string]string {
|
func defaultMetaArgs() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"{$aria_label}": "",
|
"{$aria_label}": "",
|
||||||
"{$title}": "",
|
"{$title}": "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Meta(h *wp.Handle, id string) string {
|
var GetMetaArgs = reload.BuildValFnWithAnyParams("widget-meta-args", ParseMetaArgs)
|
||||||
args := reload.GetAnyValBys("widget-meta-args", h, func(h *wp.Handle) map[string]string {
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
|
||||||
metaArgs := metaArgs()
|
|
||||||
args := wp.GetComponentsArgs(h, widgets.Meta, metaArgs)
|
|
||||||
args = maps.FilterZeroMerge(metaArgs, CommonArgs(), commonArgs, args)
|
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("meta-", id), str.Join("widget widget_", "meta"))
|
|
||||||
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_meta", "其它操作", int64(2), "title")
|
|
||||||
if args["{$title}"] == "" {
|
|
||||||
args["{$title}"] = "其他操作"
|
|
||||||
}
|
|
||||||
if args["{$title}"] != "" {
|
|
||||||
args["{$h2title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
|
||||||
}
|
|
||||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
|
||||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{$title}"])
|
|
||||||
args["{$navCloser}"] = "</nav>"
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
|
|
||||||
|
func ParseMetaArgs(a ...any) map[string]string {
|
||||||
|
h := a[0].(*wp.Handle)
|
||||||
|
id := a[1].(string)
|
||||||
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
|
metaArgs := defaultMetaArgs()
|
||||||
|
args := wp.GetComponentsArgs(widgets.Meta, metaArgs)
|
||||||
|
args = maps.FilterZeroMerge(metaArgs, CommonArgs(), commonArgs, args)
|
||||||
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("meta-", id), str.Join("widget widget_", "meta"))
|
||||||
|
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_meta", "其它操作", int64(2), "title")
|
||||||
|
if args["{$title}"] == "" {
|
||||||
|
args["{$title}"] = "其他操作"
|
||||||
|
}
|
||||||
|
if args["{$title}"] != "" {
|
||||||
|
args["{$h2title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
||||||
|
}
|
||||||
|
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{$title}"])
|
||||||
|
args["{$navCloser}"] = "</nav>"
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func Meta(h *wp.Handle, id string) string {
|
||||||
|
args := GetMetaArgs(h, id)
|
||||||
ss := str.NewBuilder()
|
ss := str.NewBuilder()
|
||||||
if str.ToInteger(wpconfig.GetOption("users_can_register"), 0) > 0 {
|
if str.ToInteger(wpconfig.GetOption("users_can_register"), 0) > 0 {
|
||||||
ss.Sprintf(`<li><a href="/wp-login.php?action=register">注册</li>`)
|
ss.Sprintf(`<li><a href="/wp-login.php?action=register">注册</li>`)
|
||||||
@ -57,5 +62,5 @@ func Meta(h *wp.Handle, id string) string {
|
|||||||
ss.Sprintf(`<li><a href="%s">条目feed</a></li>`, "/feed")
|
ss.Sprintf(`<li><a href="%s">条目feed</a></li>`, "/feed")
|
||||||
ss.Sprintf(`<li><a href="%s">评论feed</a></li>`, "/comments/feed")
|
ss.Sprintf(`<li><a href="%s">评论feed</a></li>`, "/comments/feed")
|
||||||
s := strings.ReplaceAll(metaTemplate, "{$li}", ss.String())
|
s := strings.ReplaceAll(metaTemplate, "{$li}", ss.String())
|
||||||
return h.ComponentFilterFnHook(widgets.Meta, str.Replace(s, args))
|
return h.DoActionFilter(widgets.Meta, str.Replace(s, args))
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
@ -39,30 +39,37 @@ var recentCommentsTemplate = `{$before_widget}
|
|||||||
{$after_widget}
|
{$after_widget}
|
||||||
`
|
`
|
||||||
|
|
||||||
func RecentComments(h *wp.Handle, id string) string {
|
var GetRecentCommentConf = BuildconfigFn(recentCommentConf, "widget_recent-comments", int64(2))
|
||||||
conf := configs(recentCommentConf, "widget_recent-comments", int64(2))
|
|
||||||
|
|
||||||
args := reload.GetAnyValBys("widget-recent-comment-args", h, func(h *wp.Handle) map[string]string {
|
var GetRecentCommentArgs = reload.BuildValFnWithAnyParams("widget-recent-comment-args", RecentCommentArgs)
|
||||||
commentsArgs := recentCommentsArgs()
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
func RecentCommentArgs(a ...any) map[string]string {
|
||||||
args := wp.GetComponentsArgs(h, widgets.RecentComments, commentsArgs)
|
h := a[0].(*wp.Handle)
|
||||||
args = maps.FilterZeroMerge(commentsArgs, CommonArgs(), commonArgs, args)
|
conf := a[1].(map[any]any)
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-comments-", id), str.Join("widget widget_", "recent_comments"))
|
id := a[2].(string)
|
||||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
commentsArgs := recentCommentsArgs()
|
||||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
args := wp.GetComponentsArgs(widgets.RecentComments, commentsArgs)
|
||||||
args["{$navCloser}"] = "</nav>"
|
args = maps.FilterZeroMerge(commentsArgs, CommonArgs(), commonArgs, args)
|
||||||
}
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-comments-", id), str.Join("widget widget_", "recent_comments"))
|
||||||
return args
|
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||||
})
|
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
||||||
|
args["{$navCloser}"] = "</nav>"
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
func RecentComments(h *wp.Handle, id string) string {
|
||||||
|
conf := GetRecentCommentConf()
|
||||||
|
args := GetRecentCommentArgs(h, conf, id)
|
||||||
|
|
||||||
comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string {
|
comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string {
|
||||||
return fmt.Sprintf(` <li>
|
return fmt.Sprintf(` <li>
|
||||||
<span class="comment-author-link">%s</span>发表在《
|
<span class="comment-author-link">%s</span>发表在《
|
||||||
<a href="/p/%v#comment-%v">%s</a>
|
<a href="%s">%s</a>
|
||||||
》
|
》
|
||||||
</li>`, t.CommentAuthor, t.CommentPostId, t.CommentId, t.PostTitle)
|
</li>`, t.CommentAuthor, t.CommentAuthorUrl, t.PostTitle)
|
||||||
})
|
})
|
||||||
s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n"))
|
s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n"))
|
||||||
return h.ComponentFilterFnHook(widgets.RecentComments, str.Replace(s, args))
|
return h.DoActionFilter(widgets.RecentComments, str.Replace(s, args))
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,13 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
@ -25,7 +25,7 @@ var recentPostsTemplate = `{$before_widget}
|
|||||||
{$after_widget}
|
{$after_widget}
|
||||||
`
|
`
|
||||||
|
|
||||||
func recentPostsArgs() map[string]string {
|
func DefaultRecentPostsArgs() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"{$before_sidebar}": "",
|
"{$before_sidebar}": "",
|
||||||
"{$after_sidebar}": "",
|
"{$after_sidebar}": "",
|
||||||
@ -35,7 +35,7 @@ func recentPostsArgs() map[string]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func recentConf() map[any]any {
|
func DefaultRecentConf() map[any]any {
|
||||||
return map[any]any{
|
return map[any]any{
|
||||||
"number": int64(5),
|
"number": int64(5),
|
||||||
"show_date": false,
|
"show_date": false,
|
||||||
@ -43,28 +43,37 @@ func recentConf() map[any]any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetRecentPostConf = reload.BuildValFnWithAnyParams("widget-recent-posts-conf", RecentPostConf)
|
||||||
|
|
||||||
|
func RecentPostConf(_ ...any) map[any]any {
|
||||||
|
recent := DefaultRecentConf()
|
||||||
|
conf := wpconfig.GetPHPArrayVal[map[any]any]("widget_recent-posts", recent, int64(2))
|
||||||
|
conf = maps.FilterZeroMerge(recent, conf)
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
var GetRecentPostArgs = reload.BuildValFnWithAnyParams("widget-recent-posts-args", ParseRecentPostArgs)
|
||||||
|
|
||||||
|
func ParseRecentPostArgs(a ...any) map[string]string {
|
||||||
|
h := a[0].(*wp.Handle)
|
||||||
|
conf := a[1].(map[any]any)
|
||||||
|
id := a[2].(string)
|
||||||
|
recent := DefaultRecentPostsArgs()
|
||||||
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
|
args := wp.GetComponentsArgs(widgets.RecentPosts, recent)
|
||||||
|
args = maps.FilterZeroMerge(recent, CommonArgs(), commonArgs, args)
|
||||||
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-posts-", id), str.Join("widget widget_", "recent_entries"))
|
||||||
|
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||||
|
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
||||||
|
args["{$navCloser}"] = "</nav>"
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
func RecentPosts(h *wp.Handle, id string) string {
|
func RecentPosts(h *wp.Handle, id string) string {
|
||||||
conf := reload.GetAnyValBys("widget-recent-posts-conf", h, func(h *wp.Handle) map[any]any {
|
conf := GetRecentPostConf()
|
||||||
recent := recentConf()
|
args := GetRecentPostArgs(h, conf, id)
|
||||||
conf := wpconfig.GetPHPArrayVal[map[any]any]("widget_recent-posts", recent, int64(2))
|
|
||||||
conf = maps.FilterZeroMerge(recent, conf)
|
|
||||||
return conf
|
|
||||||
})
|
|
||||||
|
|
||||||
args := reload.GetAnyValBys("widget-recent-posts-args", h, func(h *wp.Handle) map[string]string {
|
|
||||||
recent := recentPostsArgs()
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
|
||||||
args := wp.GetComponentsArgs(h, widgets.RecentPosts, recent)
|
|
||||||
args = maps.FilterZeroMerge(recent, CommonArgs(), commonArgs, args)
|
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-posts-", id), str.Join("widget widget_", "recent_entries"))
|
|
||||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
|
||||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
|
||||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
|
||||||
args["{$navCloser}"] = "</nav>"
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
|
|
||||||
currentPostId := uint64(0)
|
currentPostId := uint64(0)
|
||||||
if h.Scene() == constraints.Detail {
|
if h.Scene() == constraints.Detail {
|
||||||
currentPostId = str.ToInteger(h.C.Param("id"), uint64(0))
|
currentPostId = str.ToInteger(h.C.Param("id"), uint64(0))
|
||||||
@ -85,5 +94,5 @@ func RecentPosts(h *wp.Handle, id string) string {
|
|||||||
</li>`, t.Id, ariaCurrent, t.PostTitle, date)
|
</li>`, t.Id, ariaCurrent, t.PostTitle, date)
|
||||||
})
|
})
|
||||||
s := strings.ReplaceAll(recentPostsTemplate, "{$li}", strings.Join(posts, "\n"))
|
s := strings.ReplaceAll(recentPostsTemplate, "{$li}", strings.Join(posts, "\n"))
|
||||||
return h.ComponentFilterFnHook(widgets.RecentPosts, str.Replace(s, args))
|
return h.DoActionFilter(widgets.RecentPosts, str.Replace(s, args))
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/html"
|
"github.com/fthvgb1/wp-go/helper/html"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
@ -47,35 +47,41 @@ func searchArgs() map[string]string {
|
|||||||
|
|
||||||
var form = html5SearchForm
|
var form = html5SearchForm
|
||||||
|
|
||||||
|
var GetSearchArgs = reload.BuildValFnWithAnyParams("widget-search-args", ParseSearchArgs)
|
||||||
|
|
||||||
|
func ParseSearchArgs(a ...any) map[string]string {
|
||||||
|
h := a[0].(*wp.Handle)
|
||||||
|
id := a[1].(string)
|
||||||
|
search := searchArgs()
|
||||||
|
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||||
|
args := wp.GetComponentsArgs(widgets.Search, search)
|
||||||
|
args = maps.FilterZeroMerge(search, CommonArgs(), commonArgs, args)
|
||||||
|
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("search-", id), str.Join("widget widget_", "search"))
|
||||||
|
if args["{$title}"] == "" {
|
||||||
|
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_search", "", int64(2), "title")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args["{$title}"] != "" {
|
||||||
|
args["{$title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
||||||
|
}
|
||||||
|
if args["{$form}"] != "" {
|
||||||
|
form = args["{$form}"]
|
||||||
|
delete(args, "{$form}")
|
||||||
|
}
|
||||||
|
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||||
|
form = xmlSearchForm
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
func Search(h *wp.Handle, id string) string {
|
func Search(h *wp.Handle, id string) string {
|
||||||
args := reload.GetAnyValBys("widget-search-args", h, func(h *wp.Handle) map[string]string {
|
args := GetSearchArgs(h, id)
|
||||||
search := searchArgs()
|
|
||||||
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
|
|
||||||
args := wp.GetComponentsArgs(h, widgets.Search, search)
|
|
||||||
args = maps.FilterZeroMerge(search, CommonArgs(), commonArgs, args)
|
|
||||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("search-", id), str.Join("widget widget_", "search"))
|
|
||||||
if args["{$title}"] == "" {
|
|
||||||
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_search", "", int64(2), "title")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args["{$title}"] != "" {
|
|
||||||
args["{$title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
|
||||||
}
|
|
||||||
if args["{$form}"] != "" {
|
|
||||||
form = args["{$form}"]
|
|
||||||
delete(args, "{$form}")
|
|
||||||
}
|
|
||||||
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
|
||||||
form = xmlSearchForm
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
})
|
|
||||||
s := strings.ReplaceAll(searchTemplate, "{$form}", form)
|
s := strings.ReplaceAll(searchTemplate, "{$form}", form)
|
||||||
val := ""
|
val := ""
|
||||||
if h.Scene() == constraints.Search {
|
if h.Scene() == constraints.Search {
|
||||||
val = html.SpecialChars(h.Index.Param.Search)
|
val = html.SpecialChars(h.GetIndexHandle().Param.Search)
|
||||||
}
|
}
|
||||||
s = strings.ReplaceAll(s, "{$value}", val)
|
s = strings.ReplaceAll(s, "{$value}", val)
|
||||||
return h.ComponentFilterFnHook(widgets.Search, str.Replace(s, args))
|
return h.DoActionFilter(widgets.Search, str.Replace(s, args))
|
||||||
}
|
}
|
||||||
|
@ -3,32 +3,38 @@ package wp
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"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"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handle) DisplayHeaderText() bool {
|
func (h *Handle) DisplayHeaderText() bool {
|
||||||
return h.themeMods.ThemeSupport.CustomHeader.HeaderText && "blank" != h.themeMods.HeaderTextcolor
|
return h.themeMods.ThemeSupport.CustomHeader.HeaderText && "blank" != h.themeMods.HeaderTextcolor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetCustomHeaderImgFn = reload.BuildValFnWithConfirm("headerImages", customHeadImag, 5)
|
||||||
|
|
||||||
|
func customHeadImag(h *Handle) ([]models.PostThumbnail, bool) {
|
||||||
|
hs, err := h.GetHeaderImages(h.theme)
|
||||||
|
if err != nil {
|
||||||
|
h.SetErr(fmt.Errorf("get customheadimage err: %v", err), Low)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return hs, true
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handle) GetCustomHeaderImg() (r models.PostThumbnail, isRand bool) {
|
func (h *Handle) GetCustomHeaderImg() (r models.PostThumbnail, isRand bool) {
|
||||||
var err error
|
var err error
|
||||||
img := reload.GetAnyValBys("headerImages", h.theme, func(theme string) []models.PostThumbnail {
|
img := GetCustomHeaderImgFn(h)
|
||||||
hs, er := h.GetHeaderImages(h.theme)
|
err = h.Err()
|
||||||
if er != nil {
|
if err != nil && strings.Contains(err.Error(), "get customheadimage err") {
|
||||||
err = er
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return hs
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logs.Error(err, "获取页眉背景图失败")
|
logs.Error(err, "获取页眉背景图失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -85,7 +91,7 @@ func GetVideoSetting(h *Handle, u string) (string, error) {
|
|||||||
if is := videoReg.FindString(u); is != "" {
|
if is := videoReg.FindString(u); is != "" {
|
||||||
v.MimeType = "video/x-youtube"
|
v.MimeType = "video/x-youtube"
|
||||||
}
|
}
|
||||||
_ = h.ComponentFilterFnHook("videoSetting", "", &v)
|
_ = h.DoActionFilter("videoSetting", "", &v)
|
||||||
s, err := json.Marshal(v)
|
s, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -134,13 +140,14 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
|
|||||||
}
|
}
|
||||||
scripts = slice.Map(scripts, func(t string) string {
|
scripts = slice.Map(scripts, func(t string) string {
|
||||||
return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script>
|
return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script>
|
||||||
`, t, str.Replaces(t, [][]string{
|
`, t, str.Replaces(t, []string{
|
||||||
{"/wp-includes/js/dist/vendor/"},
|
"/wp-includes/js/dist/vendor/",
|
||||||
{"/wp-includes/js/dist/"},
|
"/wp-includes/js/dist/",
|
||||||
{"/wp-includes/js/"},
|
"/wp-includes/js/",
|
||||||
{".min.js"},
|
".min.js",
|
||||||
{".js"},
|
".js",
|
||||||
{"wp-", ""},
|
"wp-",
|
||||||
|
"",
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package wp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
)
|
)
|
||||||
@ -42,10 +42,10 @@ func CalCustomLogo(h *Handle) (r string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetCustomLog = reload.BuildValFn("customLogo", CalCustomLogo)
|
||||||
|
|
||||||
func customLogo(h *Handle) func() string {
|
func customLogo(h *Handle) func() string {
|
||||||
return func() string {
|
return func() string {
|
||||||
return reload.GetAnyValBys("customLogo", h, func(h *Handle) string {
|
return GetCustomLog(h)
|
||||||
return CalCustomLogo(h)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,18 +10,34 @@ import (
|
|||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/plugin/pagination"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DetailHandle struct {
|
type DetailHandle struct {
|
||||||
*Handle
|
*Handle
|
||||||
CommentRender plugins.CommentHtml
|
CommentRender plugins.CommentHtml
|
||||||
Comments []models.Comments
|
Comments []uint64
|
||||||
Post models.Posts
|
Page int
|
||||||
|
Limit int
|
||||||
|
Post models.Posts
|
||||||
|
CommentPageEle pagination.Render
|
||||||
|
TotalRaw int
|
||||||
|
TotalPage int
|
||||||
|
CommentStep int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDetailHandle(handle *Handle) *DetailHandle {
|
func NewDetailHandle(handle *Handle) *DetailHandle {
|
||||||
return &DetailHandle{Handle: handle}
|
return &DetailHandle{
|
||||||
|
Handle: handle,
|
||||||
|
Page: 1,
|
||||||
|
Limit: 5,
|
||||||
|
CommentStep: 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DetailHandle) BuildDetailData() (err error) {
|
func (d *DetailHandle) BuildDetailData() (err error) {
|
||||||
@ -30,7 +46,7 @@ func (d *DetailHandle) BuildDetailData() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.Comment()
|
d.CommentData()
|
||||||
d.ContextPost()
|
d.ContextPost()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -68,12 +84,66 @@ func (d *DetailHandle) PasswordProject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (d *DetailHandle) Comment() {
|
func (h *Handle) GetDetailHandle() *DetailHandle {
|
||||||
comments, err := cache.PostComments(d.C, d.Post.Id)
|
v, ok := h.C.Get("detailHandle")
|
||||||
logs.IfError(err, "get d.Post comment", d.Post.Id)
|
if !ok {
|
||||||
d.ginH["comments"] = comments
|
vv := NewDetailHandle(h)
|
||||||
d.Comments = comments
|
h.C.Set("detailHandle", vv)
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
return v.(*DetailHandle)
|
||||||
|
}
|
||||||
|
func (d *DetailHandle) CommentData() {
|
||||||
|
d.ginH["totalCommentNum"] = 0
|
||||||
|
d.ginH["totalCommentPage"] = 1
|
||||||
|
d.ginH["commentPageNav"] = ""
|
||||||
|
order := wpconfig.GetOption("comment_order")
|
||||||
|
d.ginH["commentOrder"] = order
|
||||||
|
d.Limit = str.ToInteger(wpconfig.GetOption("comments_per_page"), 5)
|
||||||
|
pageComments := wpconfig.GetOption("page_comments")
|
||||||
|
num, err := cachemanager.GetBy[int]("commentNumber", d.C, d.Post.Id, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
d.SetErr(err, Low)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if num < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
topNum, err := cachemanager.GetBy[int]("postTopCommentsNum", d.C, d.Post.Id, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
d.SetErr(err, Low)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.TotalPage = number.DivideCeil(topNum, d.Limit)
|
||||||
|
if !strings.Contains(d.C.Request.URL.Path, "comment-page") {
|
||||||
|
defaultCommentsPage := wpconfig.GetOption("default_comments_page")
|
||||||
|
if order == "desc" && defaultCommentsPage == "oldest" || order == "asc" && defaultCommentsPage == "newest" {
|
||||||
|
d.C.AddParam("page", number.IntToString(d.TotalPage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.Page = str.ToInteger(d.C.Param("page"), 1)
|
||||||
|
d.ginH["currentPage"] = d.Page
|
||||||
|
var key string
|
||||||
|
if pageComments != "1" {
|
||||||
|
key = number.IntToString(d.Post.Id)
|
||||||
|
d.Limit = 0
|
||||||
|
} else {
|
||||||
|
key = fmt.Sprintf("%d-%d-%d", d.Post.Id, d.Page, d.Limit)
|
||||||
|
}
|
||||||
|
d.ginH["page_comments"] = pageComments
|
||||||
|
d.ginH["totalCommentPage"] = d.TotalPage
|
||||||
|
if d.TotalPage < d.Page {
|
||||||
|
d.SetErr(errors.New("curren page above total page"), High)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := cache.PostTopLevelCommentIds(d.C, d.Post.Id, d.Page, d.Limit, topNum, order, key)
|
||||||
|
if err != nil {
|
||||||
|
d.SetErr(err, Low)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.TotalRaw = topNum
|
||||||
|
d.ginH["totalCommentNum"] = num
|
||||||
|
d.Comments = data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DetailHandle) RenderComment() {
|
func (d *DetailHandle) RenderComment() {
|
||||||
@ -86,9 +156,21 @@ func (d *DetailHandle) RenderComment() {
|
|||||||
ableComment = false
|
ableComment = false
|
||||||
}
|
}
|
||||||
d.ginH["showComment"] = ableComment
|
d.ginH["showComment"] = ableComment
|
||||||
if len(d.Comments) > 0 && ableComment {
|
d.ginH["comments"] = ""
|
||||||
dep := str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5)
|
if len(d.Comments) < 0 || !ableComment {
|
||||||
d.ginH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep)
|
return
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
d.ginH["comments"], err = RenderComment(d.C, d.Page, d.CommentRender, d.Comments, 2*time.Second, d.IsHttps())
|
||||||
|
if err != nil {
|
||||||
|
d.SetErr(err, High)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.CommentPageEle == nil {
|
||||||
|
d.CommentPageEle = plugins.TwentyFifteenCommentPagination()
|
||||||
|
}
|
||||||
|
if wpconfig.GetOption("page_comments") == "1" && d.TotalPage > 1 {
|
||||||
|
d.ginH["commentPageNav"] = pagination.Paginate(d.CommentPageEle, d.TotalRaw, d.Limit, d.Page, d.CommentStep, *d.C.Request.URL, d.IsHttps())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,16 +185,17 @@ func DetailRender(h *Handle) {
|
|||||||
if h.Stats != constraints.Ok {
|
if h.Stats != constraints.Ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := h.Detail
|
d := h.GetDetailHandle()
|
||||||
d.PasswordProject()
|
d.PasswordProject()
|
||||||
d.RenderComment()
|
d.RenderComment()
|
||||||
d.ginH["post"] = d.Post
|
d.ginH["post"] = d.Post
|
||||||
}
|
}
|
||||||
|
|
||||||
func Detail(h *Handle) {
|
func Detail(h *Handle) {
|
||||||
err := h.Detail.BuildDetailData()
|
d := h.GetDetailHandle()
|
||||||
|
err := d.BuildDetailData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.Detail.SetErr(err)
|
d.SetErr(err, High)
|
||||||
}
|
}
|
||||||
h.SetData("scene", h.Scene())
|
h.SetData("scene", h.Scene())
|
||||||
}
|
}
|
||||||
@ -120,7 +203,7 @@ func Detail(h *Handle) {
|
|||||||
func ReplyCommentJs(h *Handle) {
|
func ReplyCommentJs(h *Handle) {
|
||||||
h.PushFooterScript(constraints.Detail, NewComponent("comment-reply.js", "", false, 10, func(h *Handle) string {
|
h.PushFooterScript(constraints.Detail, NewComponent("comment-reply.js", "", false, 10, func(h *Handle) string {
|
||||||
reply := ""
|
reply := ""
|
||||||
if h.Detail.Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" {
|
if h.GetDetailHandle().Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" {
|
||||||
reply = `<script src='/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>`
|
reply = `<script src='/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>`
|
||||||
}
|
}
|
||||||
return reply
|
return reply
|
||||||
|
8
app/theme/wp/errorlevel.go
Normal file
8
app/theme/wp/errorlevel.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package wp
|
||||||
|
|
||||||
|
const (
|
||||||
|
None = iota
|
||||||
|
Low
|
||||||
|
High
|
||||||
|
Fatal
|
||||||
|
)
|
@ -2,13 +2,14 @@ package wp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fnMap map[string]map[string]any
|
var fnMap = safety.NewMap[string, map[string]any]()
|
||||||
var fnHook map[string]map[string]any
|
var fnHook = safety.NewMap[string, map[string]any]()
|
||||||
|
|
||||||
func GetFn[T any](fnType string, name string) []T {
|
func GetFn[T any](fnType string, name string) []T {
|
||||||
v, ok := fnMap[fnType]
|
v, ok := fnMap.Load(fnType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -19,7 +20,7 @@ func GetFn[T any](fnType string, name string) []T {
|
|||||||
return vv.([]T)
|
return vv.([]T)
|
||||||
}
|
}
|
||||||
func GetFnHook[T any](fnType string, name string) []T {
|
func GetFnHook[T any](fnType string, name string) []T {
|
||||||
v, ok := fnHook[fnType]
|
v, ok := fnHook.Load(fnType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -31,10 +32,10 @@ func GetFnHook[T any](fnType string, name string) []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PushFn[T any](fnType string, name string, fns ...T) error {
|
func PushFn[T any](fnType string, name string, fns ...T) error {
|
||||||
v, ok := fnMap[fnType]
|
v, ok := fnMap.Load(fnType)
|
||||||
if !ok {
|
if !ok {
|
||||||
v = make(map[string]any)
|
v = make(map[string]any)
|
||||||
fnMap[fnType] = v
|
fnMap.Store(fnType, v)
|
||||||
v[name] = fns
|
v[name] = fns
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -52,10 +53,10 @@ func PushFn[T any](fnType string, name string, fns ...T) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PushFnHook[T any](fnType string, name string, fns ...T) error {
|
func PushFnHook[T any](fnType string, name string, fns ...T) error {
|
||||||
v, ok := fnHook[fnType]
|
v, ok := fnHook.Load(fnType)
|
||||||
if !ok {
|
if !ok {
|
||||||
v = make(map[string]any)
|
v = make(map[string]any)
|
||||||
fnHook[fnType] = v
|
fnHook.Store(fnType, v)
|
||||||
v[name] = fns
|
v[name] = fns
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@ package wp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
@ -19,11 +20,21 @@ type IndexHandle struct {
|
|||||||
*Handle
|
*Handle
|
||||||
Param *IndexParams
|
Param *IndexParams
|
||||||
Posts []models.Posts
|
Posts []models.Posts
|
||||||
pageEle pagination.Elements
|
pageEle pagination.Render
|
||||||
TotalRows int
|
TotalRows int
|
||||||
postsPlugin PostsPlugin
|
postsPlugin PostsPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handle) GetIndexHandle() *IndexHandle {
|
||||||
|
v, ok := h.C.Get("indexHandle")
|
||||||
|
if !ok {
|
||||||
|
vv := NewIndexHandle(h)
|
||||||
|
h.C.Set("indexHandle", vv)
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
return v.(*IndexHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *IndexHandle) ListPlugin() func(*Handle, *models.Posts) {
|
func (i *IndexHandle) ListPlugin() func(*Handle, *models.Posts) {
|
||||||
return i.postsPlugin
|
return i.postsPlugin
|
||||||
}
|
}
|
||||||
@ -32,11 +43,11 @@ func (i *IndexHandle) SetListPlugin(listPlugin func(*Handle, *models.Posts)) {
|
|||||||
i.postsPlugin = listPlugin
|
i.postsPlugin = listPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IndexHandle) PageEle() pagination.Elements {
|
func (i *IndexHandle) PageEle() pagination.Render {
|
||||||
return i.pageEle
|
return i.pageEle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IndexHandle) SetPageEle(pageEle pagination.Elements) {
|
func (i *IndexHandle) SetPageEle(pageEle pagination.Render) {
|
||||||
i.pageEle = pageEle
|
i.pageEle = pageEle
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,14 +99,14 @@ func (i *IndexHandle) GetIndexData() (posts []models.Posts, totalRaw int, err er
|
|||||||
switch i.scene {
|
switch i.scene {
|
||||||
case constraints.Home, constraints.Category, constraints.Tag, constraints.Author:
|
case constraints.Home, constraints.Category, constraints.Tag, constraints.Author:
|
||||||
|
|
||||||
posts, totalRaw, err = cache.PostLists(i.C, i.Param.CacheKey, i.C, q, i.Param.Page, i.Param.PageSize)
|
posts, totalRaw, err = cache.PostLists(i.C, i.Param.CacheKey, q, i.Param.Page, i.Param.PageSize)
|
||||||
if i.scene == constraints.Home && i.Param.Page == 1 {
|
if i.scene == constraints.Home && i.Param.Page == 1 {
|
||||||
i.MarkSticky(&posts)
|
i.MarkSticky(&posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
case constraints.Search:
|
case constraints.Search:
|
||||||
|
|
||||||
posts, totalRaw, err = cache.SearchPost(i.C, i.Param.CacheKey, i.C, q, i.Param.Page, i.Param.PageSize)
|
posts, totalRaw, err = cache.SearchPost(i.C, i.Param.CacheKey, q, i.Param.Page, i.Param.PageSize)
|
||||||
|
|
||||||
case constraints.Archive:
|
case constraints.Archive:
|
||||||
i.ginH["archiveYear"] = i.Param.Year
|
i.ginH["archiveYear"] = i.Param.Year
|
||||||
@ -115,34 +126,36 @@ func (i *IndexHandle) Pagination() {
|
|||||||
if q != "" {
|
if q != "" {
|
||||||
q = fmt.Sprintf("?%s", q)
|
q = fmt.Sprintf("?%s", q)
|
||||||
}
|
}
|
||||||
paginations := pagination.NewParsePagination(i.TotalRows, i.Param.PageSize, i.Param.Page, i.Param.PaginationStep, q, i.C.Request.URL.Path)
|
i.ginH["pagination"] = pagination.Paginate(i.pageEle, i.TotalRows, i.Param.PageSize, i.Param.Page, i.Param.PaginationStep, *i.C.Request.URL, i.IsHttps())
|
||||||
i.ginH["pagination"] = pagination.Paginate(i.pageEle, paginations)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IndexHandle) BuildIndexData(parm *IndexParams) (err error) {
|
func (i *IndexHandle) BuildIndexData() (err error) {
|
||||||
err = i.ParseIndex(parm)
|
if i.Param == nil {
|
||||||
|
i.Param = NewIndexParams(i.C)
|
||||||
|
}
|
||||||
|
err = i.ParseIndex(i.Param)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.Stats = constraints.ParamError
|
i.Stats = constraints.ParamError
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
posts, totalRows, err := i.GetIndexData()
|
posts, totalRows, err := i.GetIndexData()
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
i.Stats = constraints.Error404
|
i.Stats = constraints.Error404
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.Posts = posts
|
i.Posts = posts
|
||||||
i.TotalRows = totalRows
|
i.TotalRows = totalRows
|
||||||
i.ginH["totalPage"] = number.CalTotalPage(totalRows, i.Param.PageSize)
|
i.ginH["totalPage"] = number.DivideCeil(totalRows, i.Param.PageSize)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GetPostsPlugin = reload.BuildValFnWithAnyParams("postPlugins", UsePostsPlugins)
|
||||||
|
|
||||||
func (i *IndexHandle) ExecPostsPlugin() {
|
func (i *IndexHandle) ExecPostsPlugin() {
|
||||||
fn := i.postsPlugin
|
fn := i.postsPlugin
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
fn = reload.GetAnyValBys("postPlugins", i, func(a *IndexHandle) PostsPlugin {
|
fn = GetPostsPlugin()
|
||||||
return UsePostsPlugins()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
for j := range i.Posts {
|
for j := range i.Posts {
|
||||||
fn(i.Handle, &i.Posts[j])
|
fn(i.Handle, &i.Posts[j])
|
||||||
@ -150,27 +163,27 @@ func (i *IndexHandle) ExecPostsPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IndexRender(h *Handle) {
|
func IndexRender(h *Handle) {
|
||||||
i := h.Index
|
i := h.GetIndexHandle()
|
||||||
i.ExecPostsPlugin()
|
i.ExecPostsPlugin()
|
||||||
i.Pagination()
|
i.Pagination()
|
||||||
i.ginH["posts"] = i.Posts
|
i.ginH["posts"] = i.Posts
|
||||||
}
|
}
|
||||||
|
|
||||||
func Index(h *Handle) {
|
func Index(h *Handle) {
|
||||||
i := h.Index
|
i := h.GetIndexHandle()
|
||||||
err := i.BuildIndexData(NewIndexParams(i.C))
|
err := i.BuildIndexData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.SetErr(err)
|
i.SetErr(err, High)
|
||||||
}
|
}
|
||||||
h.SetData("scene", h.Scene())
|
h.SetData("scene", h.Scene())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IndexHandle) MarkSticky(posts *[]models.Posts) {
|
func (i *IndexHandle) MarkSticky(posts *[]models.Posts) {
|
||||||
a := i.StickPosts()
|
a := GetStickPosts(i.Handle)
|
||||||
if len(a) < 1 {
|
if len(a) < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m := i.StickMapPosts()
|
m := GetStickMapPosts(i.Handle)
|
||||||
*posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool {
|
*posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool {
|
||||||
_, ok := m[post.Id]
|
_, ok := m[post.Id]
|
||||||
return !ok
|
return !ok
|
||||||
|
@ -105,18 +105,16 @@ func NewIndexParams(ctx *gin.Context) *IndexParams {
|
|||||||
|
|
||||||
func (i *IndexParams) ParseSearchs() {
|
func (i *IndexParams) ParseSearchs() {
|
||||||
s := i.Ctx.Query("s")
|
s := i.Ctx.Query("s")
|
||||||
if s != "" {
|
q := str.Join("%", s, "%")
|
||||||
q := str.Join("%", s, "%")
|
i.Where = append(i.Where, []string{
|
||||||
i.Where = append(i.Where, []string{
|
"and", "post_title", "like", q, "",
|
||||||
"and", "post_title", "like", q, "",
|
"or", "post_content", "like", q, "",
|
||||||
"or", "post_content", "like", q, "",
|
"or", "post_excerpt", "like", q, "",
|
||||||
"or", "post_excerpt", "like", q, "",
|
}, []string{"post_password", ""})
|
||||||
}, []string{"post_password", ""})
|
i.PostType = append(i.PostType, "Page", "attachment")
|
||||||
i.PostType = append(i.PostType, "Page", "attachment")
|
i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
|
||||||
i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
|
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
|
||||||
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
|
i.Search = s
|
||||||
i.Search = s
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func (i *IndexParams) ParseArchives() error {
|
func (i *IndexParams) ParseArchives() error {
|
||||||
year := i.Ctx.Param("year")
|
year := i.Ctx.Param("year")
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package wp
|
package wp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins"
|
"github.com/fthvgb1/wp-go/app/plugins"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
"github.com/fthvgb1/wp-go/app/plugins/wpposts"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
)
|
)
|
||||||
@ -23,7 +23,7 @@ func PostsPlugins(initial PostsPlugin, calls ...func(PostsPlugin, *Handle, *mode
|
|||||||
var pluginFns = reload.Vars(map[string]func(PostsPlugin, *Handle, *models.Posts){
|
var pluginFns = reload.Vars(map[string]func(PostsPlugin, *Handle, *models.Posts){
|
||||||
"passwordProject": PasswordProject,
|
"passwordProject": PasswordProject,
|
||||||
"digest": Digest,
|
"digest": Digest,
|
||||||
})
|
}, "list-post-plugins-fns")
|
||||||
|
|
||||||
func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) {
|
func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) {
|
||||||
m := pluginFns.Load()
|
m := pluginFns.Load()
|
||||||
@ -53,7 +53,7 @@ func Digest(next PostsPlugin, h *Handle, post *models.Posts) {
|
|||||||
next(h, post)
|
next(h, post)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ordinaryPlugin = reload.Vars([]PostsPlugin{})
|
var ordinaryPlugin = reload.Vars([]PostsPlugin{}, "ordinaryPlugin")
|
||||||
|
|
||||||
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
|
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
|
||||||
p := ordinaryPlugin.Load()
|
p := ordinaryPlugin.Load()
|
||||||
@ -69,7 +69,7 @@ func PostPlugin(calls ...PostsPlugin) PostsPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func UsePostsPlugins() PostsPlugin {
|
func UsePostsPlugins(_ ...any) PostsPlugin {
|
||||||
m := pluginFns.Load()
|
m := pluginFns.Load()
|
||||||
pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
|
pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
|
||||||
f, ok := m[t]
|
f, ok := m[t]
|
||||||
|
167
app/theme/wp/middleware/middleware.go
Normal file
167
app/theme/wp/middleware/middleware.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
|
"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"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var plainRouteParam = reload.Vars([]Plain{
|
||||||
|
{
|
||||||
|
Action: "p",
|
||||||
|
Param: map[string]string{
|
||||||
|
"p": "id",
|
||||||
|
"cpage": "page",
|
||||||
|
},
|
||||||
|
Scene: constraints.Detail,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "s",
|
||||||
|
Scene: constraints.Search,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Scene: constraints.Category,
|
||||||
|
Fn: func(h *wp.Handle) bool {
|
||||||
|
c, ok := widget.IsCategory(h)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.C.AddParam("category", c.Name)
|
||||||
|
h.C.AddParam("page", h.C.Query("paged"))
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Scene: constraints.Tag,
|
||||||
|
Action: "tag",
|
||||||
|
Param: map[string]string{
|
||||||
|
"tag": "tag",
|
||||||
|
"paged": "page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Scene: constraints.Archive,
|
||||||
|
Fn: func(h *wp.Handle) bool {
|
||||||
|
m := h.C.Query("m")
|
||||||
|
if m == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t, err := time.Parse("200601", m)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.C.AddParam("year", strconv.Itoa(t.Year()))
|
||||||
|
h.C.AddParam("month", number.IntToString(t.Month()))
|
||||||
|
h.C.AddParam("page", h.C.Query("paged"))
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Scene: constraints.Author,
|
||||||
|
Fn: func(h *wp.Handle) bool {
|
||||||
|
u := h.C.Query("author")
|
||||||
|
if u == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
users := GetUsersIds(h)
|
||||||
|
name, ok := users[str.ToInteger[uint64](u, 0)]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.C.AddParam("author", name)
|
||||||
|
h.C.AddParam("page", h.C.Query("paged"))
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, "plainRouteParam")
|
||||||
|
|
||||||
|
var GetUsersIds = reload.BuildValFnWithConfirm("usersIds", func(h *wp.Handle) (map[uint64]string, bool) {
|
||||||
|
users, err := cache.GetAllUsername(h.C)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return maps.Flip(users), true
|
||||||
|
}, 10)
|
||||||
|
|
||||||
|
func SetExplainRouteParam(p []Plain) {
|
||||||
|
plainRouteParam.Store(p)
|
||||||
|
}
|
||||||
|
func GetExplainRouteParam() []Plain {
|
||||||
|
return plainRouteParam.Load()
|
||||||
|
}
|
||||||
|
func PushExplainRouteParam(explain ...Plain) {
|
||||||
|
v := plainRouteParam.Load()
|
||||||
|
v = append(v, explain...)
|
||||||
|
plainRouteParam.Store(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Plain struct {
|
||||||
|
Action string
|
||||||
|
Param map[string]string
|
||||||
|
Scene string
|
||||||
|
Fn func(h *wp.Handle) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func MixWithPlain(h *wp.Handle) {
|
||||||
|
for _, explain := range plainRouteParam.Load() {
|
||||||
|
if explain.Action == "" && explain.Fn == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if explain.Fn != nil {
|
||||||
|
if !explain.Fn(h) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if explain.Scene != "" {
|
||||||
|
h.SetScene(explain.Scene)
|
||||||
|
}
|
||||||
|
wp.Run(h, nil)
|
||||||
|
h.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if explain.Scene == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q := h.C.Query(explain.Action)
|
||||||
|
if q == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h.SetScene(explain.Scene)
|
||||||
|
for query, param := range explain.Param {
|
||||||
|
h.C.AddParam(param, h.C.Query(query))
|
||||||
|
}
|
||||||
|
wp.Run(h, nil)
|
||||||
|
h.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShowPreComment(h *wp.Handle) {
|
||||||
|
v, ok := cache.NewCommentCache().Get(h.C, h.C.Request.URL.RawQuery)
|
||||||
|
if ok {
|
||||||
|
h.C.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
h.C.Writer.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = h.C.Writer.Write([]byte(v))
|
||||||
|
h.Abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonMiddleware(h *wp.Handle) {
|
||||||
|
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
|
||||||
|
wp.NewHandleFn(MixWithPlain, 100, "middleware.MixWithPlain"),
|
||||||
|
)
|
||||||
|
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"),
|
||||||
|
)
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
package wp
|
package wp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/helper"
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
)
|
)
|
||||||
@ -22,7 +21,7 @@ func NewPipe(name string, order float64, fn HandlePipeFn[*Handle]) Pipe {
|
|||||||
|
|
||||||
// HandlePipe 方便把功能写在其它包里
|
// HandlePipe 方便把功能写在其它包里
|
||||||
func HandlePipe[T any](initial func(T), fns ...HandlePipeFn[T]) HandleFn[T] {
|
func HandlePipe[T any](initial func(T), fns ...HandlePipeFn[T]) HandleFn[T] {
|
||||||
return slice.ReverseReduce(fns, func(next HandlePipeFn[T], f func(t T)) func(t T) {
|
return slice.ReverseReduce(fns, func(next HandlePipeFn[T], f HandleFn[T]) HandleFn[T] {
|
||||||
return func(t T) {
|
return func(t T) {
|
||||||
next(f, t)
|
next(f, t)
|
||||||
}
|
}
|
||||||
@ -52,10 +51,12 @@ func (h *Handle) ReplacePipe(scene, pipeName string, pipe Pipe) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushHandler(pipScene string, scene string, fns ...HandleCall) {
|
func (h *Handle) PushHandler(pipScene string, scene string, fns ...HandleCall) {
|
||||||
if _, ok := h.handlers[pipScene]; !ok {
|
v, ok := handlerss.Load(pipScene)
|
||||||
h.handlers[pipScene] = make(map[string][]HandleCall)
|
if !ok {
|
||||||
|
v = make(map[string][]HandleCall)
|
||||||
}
|
}
|
||||||
h.handlers[pipScene][scene] = append(h.handlers[pipScene][scene], fns...)
|
v[scene] = append(v[scene], fns...)
|
||||||
|
handlerss.Store(pipScene, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushRender(statsOrScene string, fns ...HandleCall) {
|
func (h *Handle) PushRender(statsOrScene string, fns ...HandleCall) {
|
||||||
@ -65,27 +66,16 @@ func (h *Handle) PushDataHandler(scene string, fns ...HandleCall) {
|
|||||||
h.PushHandler(constraints.PipeData, scene, fns...)
|
h.PushHandler(constraints.PipeData, scene, fns...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildPipe(pipeScene string, keyFn func(*Handle, string) string, fn func(*Handle, map[string][]HandleCall, string) []HandleCall) func(HandleFn[*Handle], *Handle) {
|
func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string,
|
||||||
|
handleHookFn func(*Handle, string,
|
||||||
|
map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
|
map[string][]HandleCall) []HandleCall) func(HandleFn[*Handle], *Handle) {
|
||||||
|
|
||||||
|
pipeHandlerFn := reload.BuildMapFn[string]("pipeHandlers", BuildHandler(pipeScene, keyFn, handleHookFn))
|
||||||
|
|
||||||
return func(next HandleFn[*Handle], h *Handle) {
|
return func(next HandleFn[*Handle], h *Handle) {
|
||||||
key := keyFn(h, pipeScene)
|
key := keyFn(h, pipeScene)
|
||||||
handlers := reload.GetAnyValMapBy("pipeHandlers", key, h, func(h *Handle) []HandleCall {
|
handlers := pipeHandlerFn(key, h)
|
||||||
conf := h.handleHook[pipeScene]
|
|
||||||
calls := fn(h, h.handlers[pipeScene], key)
|
|
||||||
calls = slice.FilterAndMap(calls, func(call HandleCall) (HandleCall, bool) {
|
|
||||||
ok := true
|
|
||||||
for _, hook := range conf {
|
|
||||||
call, ok = hook(call)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return call, ok
|
|
||||||
})
|
|
||||||
slice.Sort(calls, func(i, j HandleCall) bool {
|
|
||||||
return i.Order > j.Order
|
|
||||||
})
|
|
||||||
return calls
|
|
||||||
})
|
|
||||||
for _, handler := range handlers {
|
for _, handler := range handlers {
|
||||||
handler.Fn(h)
|
handler.Fn(h)
|
||||||
if h.abort {
|
if h.abort {
|
||||||
@ -98,87 +88,141 @@ func BuildPipe(pipeScene string, keyFn func(*Handle, string) string, fn func(*Ha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PipeKey(h *Handle, pipScene string) string {
|
func BuildHandler(pipeScene string, keyFn func(*Handle, string) string,
|
||||||
key := str.Join("pipekey", "-", pipScene, "-", h.scene, "-", h.Stats)
|
handleHook func(*Handle, string,
|
||||||
return h.ComponentFilterFnHook("pipeKey", key, pipScene)
|
map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
|
map[string][]HandleCall) []HandleCall) func(*Handle) []HandleCall {
|
||||||
|
|
||||||
|
return func(h *Handle) []HandleCall {
|
||||||
|
key := keyFn(h, pipeScene)
|
||||||
|
mut := reload.GetGlobeMutex()
|
||||||
|
mut.Lock()
|
||||||
|
pipeHookers, _ := handleHooks.Load(pipeScene)
|
||||||
|
pipeHandlers, _ := handlerss.Load(pipeScene)
|
||||||
|
mut.Unlock()
|
||||||
|
calls := handleHook(h, key, pipeHookers, pipeHandlers)
|
||||||
|
slice.SimpleSort(calls, slice.DESC, func(t HandleCall) float64 {
|
||||||
|
return t.Order
|
||||||
|
})
|
||||||
|
return calls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HookHandles(hooks map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
|
handlers map[string][]HandleCall, sceneOrStats ...string) []HandleCall {
|
||||||
|
|
||||||
|
hookedHandlers := slice.FilterAndMap(sceneOrStats, func(k string) ([]HandleCall, bool) {
|
||||||
|
r := handlers[k]
|
||||||
|
for _, hook := range hooks[k] {
|
||||||
|
r = slice.FilterAndMap(r, hook)
|
||||||
|
}
|
||||||
|
return r, true
|
||||||
|
})
|
||||||
|
return slice.Decompress(hookedHandlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PipeKey(h *Handle, pipScene string) string {
|
||||||
|
key := str.Join("pipekey", "-", pipScene, "-", h.scene, "-", h.Stats)
|
||||||
|
return h.DoActionFilter("pipeKey", key, pipScene)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipeInitFn = reload.BuildMapFn[string]("pipeInit", BuildPipe)
|
||||||
|
|
||||||
func Run(h *Handle, conf func(*Handle)) {
|
func Run(h *Handle, conf func(*Handle)) {
|
||||||
if !helper.GetContextVal(h.C, "inited", false) {
|
if !h.isInited {
|
||||||
InitHandle(conf, h)
|
InitHandle(conf, h)
|
||||||
}
|
}
|
||||||
reload.GetAnyValBys(str.Join("pipeInit-", h.scene), h, func(h *Handle) func(*Handle) {
|
pipeInitFn(h.scene, h.scene)(h)
|
||||||
p := GetFn[Pipe]("pipe", constraints.AllScene)
|
}
|
||||||
p = append(p, GetFn[Pipe]("pipe", h.scene)...)
|
|
||||||
pipes := slice.FilterAndMap(p, func(pipe Pipe) (Pipe, bool) {
|
func BuildPipe(scene string) func(*Handle) {
|
||||||
var ok bool
|
pipees := GetFn[Pipe]("pipe", constraints.AllScene)
|
||||||
hooks := GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", constraints.AllScene)
|
pipees = append(pipees, GetFn[Pipe]("pipe", scene)...)
|
||||||
hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", h.scene)...)
|
pipes := slice.FilterAndMap(pipees, func(pipe Pipe) (Pipe, bool) {
|
||||||
for _, fn := range hooks {
|
var ok bool
|
||||||
pipe, ok = fn(pipe)
|
mut := reload.GetGlobeMutex()
|
||||||
if !ok {
|
mut.Lock()
|
||||||
return pipe, false
|
hooks := GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", constraints.AllScene)
|
||||||
}
|
hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", scene)...)
|
||||||
|
mut.Unlock()
|
||||||
|
for _, fn := range hooks {
|
||||||
|
pipe, ok = fn(pipe)
|
||||||
|
if !ok {
|
||||||
|
return pipe, false
|
||||||
}
|
}
|
||||||
return pipe, pipe.Fn != nil
|
}
|
||||||
})
|
return pipe, pipe.Fn != nil
|
||||||
slice.Sort(pipes, func(i, j Pipe) bool {
|
})
|
||||||
return i.Order > j.Order
|
slice.SimpleSort(pipes, slice.DESC, func(t Pipe) float64 {
|
||||||
})
|
return t.Order
|
||||||
arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] {
|
})
|
||||||
return t.Fn
|
|
||||||
})
|
arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] {
|
||||||
return HandlePipe(NothingToDo, arr...)
|
return t.Fn
|
||||||
})(h)
|
})
|
||||||
|
return HandlePipe(NothingToDo, arr...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MiddlewareKey(h *Handle, pipScene string) string {
|
func MiddlewareKey(h *Handle, pipScene string) string {
|
||||||
return h.ComponentFilterFnHook("middleware", "middleware", pipScene)
|
return h.DoActionFilter("middleware", str.Join("pipe-middleware-", h.scene), pipScene)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PipeMiddlewareHandle(h *Handle, middlewares map[string][]HandleCall, key string) (handlers []HandleCall) {
|
func PipeMiddlewareHandle(h *Handle, key string,
|
||||||
handlers = append(handlers, middlewares[h.scene]...)
|
hooks map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
handlers = append(handlers, middlewares[constraints.AllScene]...)
|
handlers map[string][]HandleCall) []HandleCall {
|
||||||
handlers = h.PipeHandleHook("PipeMiddlewareHandle", handlers, middlewares, key)
|
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
|
||||||
return
|
return h.PipeHandleHook("PipeMiddlewareHandle", hookedHandles, handlers, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PipeDataHandle(h *Handle, dataHandlers map[string][]HandleCall, key string) (handlers []HandleCall) {
|
func PipeDataHandle(h *Handle, key string,
|
||||||
handlers = append(handlers, dataHandlers[h.scene]...)
|
hooks map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
handlers = append(handlers, dataHandlers[constraints.AllScene]...)
|
handlers map[string][]HandleCall) []HandleCall {
|
||||||
handlers = h.PipeHandleHook("PipeDataHandle", handlers, dataHandlers, key)
|
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
|
||||||
return
|
return h.PipeHandleHook("PipeDataHandle", hookedHandles, handlers, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PipeRender(h *Handle, renders map[string][]HandleCall, key string) (handlers []HandleCall) {
|
func PipeRender(h *Handle, key string,
|
||||||
handlers = append(handlers, renders[h.Stats]...)
|
hooks map[string][]func(HandleCall) (HandleCall, bool),
|
||||||
handlers = append(handlers, renders[h.scene]...)
|
handlers map[string][]HandleCall) []HandleCall {
|
||||||
handlers = append(handlers, renders[constraints.AllStats]...)
|
hookedHandles := HookHandles(hooks, handlers, h.scene, h.Stats, constraints.AllScene, constraints.AllStats)
|
||||||
handlers = append(handlers, renders[constraints.AllScene]...)
|
return h.PipeHandleHook("PipeRender", hookedHandles, handlers, key)
|
||||||
handlers = h.PipeHandleHook("PipeRender", handlers, renders, key)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHandle 写插件的时候用
|
// DeleteHandle 写插件的时候用
|
||||||
func (h *Handle) DeleteHandle(pipeScene string, name string) {
|
func (h *Handle) DeleteHandle(pipeScene, scene, name string) {
|
||||||
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
|
v, ok := handleHooks.Load(pipeScene)
|
||||||
|
if !ok {
|
||||||
|
v = make(map[string][]func(HandleCall) (HandleCall, bool))
|
||||||
|
}
|
||||||
|
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
|
||||||
return call, name != call.Name
|
return call, name != call.Name
|
||||||
})
|
})
|
||||||
|
handleHooks.Store(pipeScene, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplaceHandle 写插件的时候用
|
// ReplaceHandle 写插件的时候用
|
||||||
func (h *Handle) ReplaceHandle(pipeScene, name string, fn HandleFn[*Handle]) {
|
func (h *Handle) ReplaceHandle(pipeScene, scene, name string, fn HandleFn[*Handle]) {
|
||||||
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
|
v, ok := handleHooks.Load(pipeScene)
|
||||||
|
if !ok {
|
||||||
|
v = make(map[string][]func(HandleCall) (HandleCall, bool))
|
||||||
|
}
|
||||||
|
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
|
||||||
if name == call.Name {
|
if name == call.Name {
|
||||||
call.Fn = fn
|
call.Fn = fn
|
||||||
}
|
}
|
||||||
return call, true
|
return call, true
|
||||||
})
|
})
|
||||||
|
handleHooks.Store(pipeScene, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookHandle 写插件的时候用
|
// HookHandle 写插件的时候用
|
||||||
func (h *Handle) HookHandle(pipeScene string, hook func(HandleCall) (HandleCall, bool)) {
|
func (h *Handle) HookHandle(pipeScene, scene string, hook func(HandleCall) (HandleCall, bool)) {
|
||||||
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], hook)
|
v, ok := handleHooks.Load(pipeScene)
|
||||||
|
if !ok {
|
||||||
|
v = make(map[string][]func(HandleCall) (HandleCall, bool))
|
||||||
|
}
|
||||||
|
v[scene] = append(v[scene], hook)
|
||||||
|
handleHooks.Store(pipeScene, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) PushPipeHandleHook(name string, fn ...func([]HandleCall) []HandleCall) error {
|
func (h *Handle) PushPipeHandleHook(name string, fn ...func([]HandleCall) []HandleCall) error {
|
||||||
@ -193,11 +237,11 @@ func (h *Handle) PipeHandleHook(name string, calls []HandleCall, m map[string][]
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitPipe(h *Handle) {
|
func InitPipe(h *Handle) {
|
||||||
h.PushPipe(constraints.Home, NewPipe(constraints.PipeMiddleware, 300,
|
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeMiddleware, 300,
|
||||||
BuildPipe(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
|
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
|
||||||
|
|
||||||
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200,
|
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200,
|
||||||
BuildPipe(constraints.PipeData, PipeKey, PipeDataHandle)))
|
BuildHandlers(constraints.PipeData, PipeKey, PipeDataHandle)))
|
||||||
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100,
|
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100,
|
||||||
BuildPipe(constraints.PipeRender, PipeKey, PipeRender)))
|
BuildHandlers(constraints.PipeRender, PipeKey, PipeRender)))
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Route
|
||||||
|
// Type value equal const or reg
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Path string
|
Path string
|
||||||
Scene string
|
Scene string
|
||||||
@ -20,18 +23,18 @@ var routeHook []func(Route) (Route, bool)
|
|||||||
var regRoutes *safety.Map[string, *regexp.Regexp]
|
var regRoutes *safety.Map[string, *regexp.Regexp]
|
||||||
var routes = func() *safety.Map[string, Route] {
|
var routes = func() *safety.Map[string, Route] {
|
||||||
r := safety.NewMap[string, Route]()
|
r := safety.NewMap[string, Route]()
|
||||||
reload.Push(func() {
|
reload.Append(func() {
|
||||||
r.Flush()
|
r.Flush()
|
||||||
regRoutes.Flush()
|
regRoutes.Flush()
|
||||||
})
|
}, "wp-routers")
|
||||||
regRoutes = safety.NewMap[string, *regexp.Regexp]()
|
regRoutes = safety.NewMap[string, *regexp.Regexp]()
|
||||||
return r
|
return r
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// PushRoute path can be const or regex string
|
// PushRoute path can be const or regex string
|
||||||
//
|
//
|
||||||
// eg: `(?P<control>\w+)/(?P<method>\w+)`, route.Route{
|
// eg: `(?P<controller>\w+)/(?P<method>\w+)`, route.Route{
|
||||||
// Path: `(?P<control>\w+)/(?P<method>\w+)`,
|
// Path: `(?P<controller>\w+)/(?P<method>\w+)`,
|
||||||
// Scene: constraints.Home,
|
// Scene: constraints.Home,
|
||||||
// Method: []string{"GET"},
|
// Method: []string{"GET"},
|
||||||
// Type: "reg",
|
// Type: "reg",
|
||||||
@ -70,60 +73,74 @@ func Hook(path string, fn func(Route) Route) {
|
|||||||
return r, path == r.Path
|
return r, path == r.Path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var RegRouteFn = reload.BuildValFnWithAnyParams("regexRoute", RegRouteHook)
|
||||||
|
|
||||||
|
func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp) {
|
||||||
|
m := map[string]Route{}
|
||||||
|
rrs := map[string]*regexp.Regexp{}
|
||||||
|
routes.Range(func(key string, value Route) bool {
|
||||||
|
vv, _ := regRoutes.Load(key)
|
||||||
|
if len(routeHook) > 0 {
|
||||||
|
for _, fn := range routeHook {
|
||||||
|
v, ok := fn(value)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[v.Path] = v
|
||||||
|
if v.Type != "reg" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v.Path != key {
|
||||||
|
vvv, err := regexp.Compile(v.Path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
vv = vvv
|
||||||
|
}
|
||||||
|
rrs[v.Path] = vv
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m[key] = value
|
||||||
|
rrs[key] = vv
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return func() (map[string]Route, map[string]*regexp.Regexp) {
|
||||||
|
return m, rrs
|
||||||
|
}
|
||||||
|
}
|
||||||
func ResolveRoute(h *wp.Handle) {
|
func ResolveRoute(h *wp.Handle) {
|
||||||
requestURI := h.C.Request.RequestURI
|
requestURI := h.C.Request.RequestURI
|
||||||
rs, rrs := reload.GetAnyValBys("route",
|
rs, rrs := RegRouteFn()()
|
||||||
struct{}{},
|
|
||||||
func(_ struct{}) func() (map[string]Route, map[string]*regexp.Regexp) {
|
|
||||||
m := map[string]Route{}
|
|
||||||
rrs := map[string]*regexp.Regexp{}
|
|
||||||
routes.Range(func(key string, value Route) bool {
|
|
||||||
vv, _ := regRoutes.Load(key)
|
|
||||||
if len(routeHook) > 0 {
|
|
||||||
for _, fn := range routeHook {
|
|
||||||
v, ok := fn(value)
|
|
||||||
if ok {
|
|
||||||
m[v.Path] = v
|
|
||||||
if v.Type == "reg" {
|
|
||||||
if v.Path != key {
|
|
||||||
vvv, err := regexp.Compile(v.Path)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
vv = vvv
|
|
||||||
}
|
|
||||||
rrs[v.Path] = vv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m[key] = value
|
|
||||||
rrs[key] = vv
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return func() (map[string]Route, map[string]*regexp.Regexp) {
|
|
||||||
return m, rrs
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
v, ok := rs[requestURI]
|
v, ok := rs[requestURI]
|
||||||
if ok && slice.IsContained(v.Method, h.C.Request.Method) {
|
if ok && slice.IsContained(v.Method, h.C.Request.Method) {
|
||||||
h.SetScene(v.Scene)
|
h.SetScene(v.Scene)
|
||||||
wp.Run(h, nil)
|
wp.Run(h, nil)
|
||||||
|
h.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for path, reg := range rrs {
|
for path, reg := range rrs {
|
||||||
r := reg.FindAllStringSubmatch(requestURI, -1)
|
r := reg.FindStringSubmatch(requestURI)
|
||||||
if len(r) < 1 {
|
if len(r) < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rr := rs[path]
|
rr := rs[path]
|
||||||
if slice.IsContained(rr.Method, h.C.Request.Method) {
|
if slice.IsContained(rr.Method, h.C.Request.Method) {
|
||||||
h.SetScene(rr.Scene)
|
h.SetScene(rr.Scene)
|
||||||
h.C.Set("route", r)
|
for i, name := range reg.SubexpNames() {
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h.C.AddParam(name, r[i])
|
||||||
|
}
|
||||||
|
h.C.Set("regRoute", reg)
|
||||||
|
h.C.Set("regRouteRes", r)
|
||||||
wp.Run(h, nil)
|
wp.Run(h, nil)
|
||||||
|
h.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h.C.Status(http.StatusNotFound)
|
||||||
|
h.Abort()
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sizes = []string{"site_icon-270", "site_icon-32", "site_icon-192", "site_icon-180"}
|
var iconSizes = []string{"site_icon-270", "site_icon-32", "site_icon-192", "site_icon-180"}
|
||||||
|
|
||||||
func CalSiteIcon(h *Handle) (r string) {
|
func CalSiteIcon(h *Handle) (r string) {
|
||||||
id := str.ToInteger[uint64](wpconfig.GetOption("site_icon"), 0)
|
id := str.ToInteger[uint64](wpconfig.GetOption("site_icon"), 0)
|
||||||
@ -21,7 +21,7 @@ func CalSiteIcon(h *Handle) (r string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
m := strings.Join(strings.Split(icon.AttachmentMetadata.File, "/")[:2], "/")
|
m := strings.Join(strings.Split(icon.AttachmentMetadata.File, "/")[:2], "/")
|
||||||
size := slice.FilterAndMap(sizes, func(t string) (string, bool) {
|
size := slice.FilterAndMap(iconSizes, func(t string) (string, bool) {
|
||||||
s, ok := icon.AttachmentMetadata.Sizes[t]
|
s, ok := icon.AttachmentMetadata.Sizes[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", false
|
return "", false
|
||||||
|
@ -3,45 +3,46 @@ package wp
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/elliotchance/phpserialize"
|
"github.com/elliotchance/phpserialize"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handle) StickPosts() []models.Posts {
|
var GetStickPosts = reload.BuildValFnWithConfirm("stickPostsSlice", ParseStickPosts)
|
||||||
return reload.GetAnyValBys("stickPostsSlice", h, func(h *Handle) (r []models.Posts) {
|
|
||||||
v := wpconfig.GetOption("sticky_posts")
|
func ParseStickPosts(h *Handle) (r []models.Posts, ok bool) {
|
||||||
if v == "" {
|
v := wpconfig.GetOption("sticky_posts")
|
||||||
return
|
if v == "" {
|
||||||
}
|
|
||||||
array, err := phpserialize.UnmarshalIndexedArray([]byte(v))
|
|
||||||
if err != nil {
|
|
||||||
logs.Error(err, "解析option sticky_posts错误", v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = slice.FilterAndMap(array, func(t any) (models.Posts, bool) {
|
|
||||||
id := str.ToInt[uint64](fmt.Sprintf("%v", t))
|
|
||||||
post, err := cache.GetPostById(h.C, id)
|
|
||||||
post.IsSticky = true
|
|
||||||
return post, err == nil
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
array, err := phpserialize.UnmarshalIndexedArray([]byte(v))
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err, "解析option sticky_posts错误", v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r = slice.FilterAndMap(array, func(t any) (models.Posts, bool) {
|
||||||
|
id := str.ToInt[uint64](fmt.Sprintf("%v", t))
|
||||||
|
post, err := cache.GetPostById(h.C, id)
|
||||||
|
post.IsSticky = true
|
||||||
|
return post, err == nil
|
||||||
})
|
})
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) StickMapPosts() map[uint64]models.Posts {
|
var GetStickMapPosts = reload.BuildValFn("stickPostsMap", StickMapPosts)
|
||||||
return reload.GetAnyValBys("stickPostsMap", h, func(h *Handle) map[uint64]models.Posts {
|
|
||||||
return slice.SimpleToMap(h.StickPosts(), func(v models.Posts) uint64 {
|
func StickMapPosts(h *Handle) map[uint64]models.Posts {
|
||||||
return v.Id
|
return slice.SimpleToMap(GetStickPosts(h), func(v models.Posts) uint64 {
|
||||||
})
|
return v.Id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) IsStick(id uint64) bool {
|
func (h *Handle) IsStick(id uint64) bool {
|
||||||
return maps.IsExists(h.StickMapPosts(), id)
|
_, ok := GetStickMapPosts(h)[id]
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
{{define "common/head"}}
|
{{define "common/head"}}
|
||||||
|
|
||||||
{{if .headScript}}
|
{{ callFuncString .calComponent "headScript"}}
|
||||||
{{.headScript|unescaped}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .externHead}}
|
{{if .externHead}}
|
||||||
{{.externHead|unescaped}}
|
{{.externHead|unescaped}}
|
||||||
@ -10,9 +8,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "common/footer"}}
|
{{define "common/footer"}}
|
||||||
{{if .footerScript}}
|
{{ callFuncString .calComponent "footerScript"}}
|
||||||
{{.footerScript|unescaped}}
|
|
||||||
{{end}}
|
|
||||||
{{if .externFooter}}
|
{{if .externFooter}}
|
||||||
{{.externFooter|unescaped}}
|
{{.externFooter|unescaped}}
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -20,7 +16,17 @@
|
|||||||
|
|
||||||
|
|
||||||
{{define "common/sidebarWidget"}}
|
{{define "common/sidebarWidget"}}
|
||||||
{{if .sidebarsWidgets}}
|
{{ callFuncString .calComponent "sidebarsWidgets"}}
|
||||||
{{.sidebarsWidgets|unescaped}}
|
{{end}}
|
||||||
|
|
||||||
|
{{define "common/colophon"}}
|
||||||
|
{{if .colophon}}
|
||||||
|
{{.colophon|unescaped}}
|
||||||
|
{{else}}
|
||||||
|
<footer id="colophon" class="site-footer">
|
||||||
|
<div class="site-info">
|
||||||
|
<a href="https://github.com/fthvgb1/wp-go" class="imprint">自豪地采用 wp-go</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
@ -1,44 +1,47 @@
|
|||||||
package wp
|
package wp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
"errors"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle/apply"
|
"github.com/fthvgb1/wp-go/app/plugins/wphandle/apply"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handle struct {
|
type Handle struct {
|
||||||
Index *IndexHandle
|
C *gin.Context
|
||||||
Detail *DetailHandle
|
theme string
|
||||||
C *gin.Context
|
isInited bool
|
||||||
theme string
|
Session sessions.Session
|
||||||
Session sessions.Session
|
ginH gin.H
|
||||||
ginH gin.H
|
password string
|
||||||
password string
|
scene string
|
||||||
scene string
|
Code int
|
||||||
Code int
|
Stats string
|
||||||
Stats string
|
templ string
|
||||||
templ string
|
themeMods wpconfig.ThemeMods
|
||||||
components map[string]map[string][]Components[string]
|
err error
|
||||||
componentHook map[string][]func(Components[string]) (Components[string], bool)
|
errLevel int8
|
||||||
themeMods wpconfig.ThemeMods
|
abort bool
|
||||||
handlers map[string]map[string][]HandleCall
|
stopPipe bool
|
||||||
handleHook map[string][]func(HandleCall) (HandleCall, bool)
|
}
|
||||||
err error
|
|
||||||
abort bool
|
var handlerss = safety.NewMap[string, map[string][]HandleCall]()
|
||||||
stopPipe bool
|
var handleHooks = safety.NewMap[string, map[string][]func(HandleCall) (HandleCall, bool)]()
|
||||||
componentsArgs map[string]any
|
|
||||||
componentFilterFn map[string][]func(*Handle, string, ...any) string
|
func (h *Handle) Theme() string {
|
||||||
template *template.Template
|
return h.theme
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) GinH() gin.H {
|
func (h *Handle) GinH() gin.H {
|
||||||
@ -49,28 +52,20 @@ func (h *Handle) SetScene(scene string) {
|
|||||||
h.scene = scene
|
h.scene = scene
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) Components() map[string]map[string][]Components[string] {
|
func (h *Handle) Components() *safety.Map[string, map[string][]Components[string]] {
|
||||||
return h.components
|
return handleComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) ComponentHook() map[string][]func(Components[string]) (Components[string], bool) {
|
func (h *Handle) ComponentHook() *safety.Map[string, map[string][]func(Components[string]) (Components[string], bool)] {
|
||||||
return h.componentHook
|
return handleComponentHook
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) Handlers() map[string]map[string][]HandleCall {
|
func (h *Handle) Handlers() *safety.Map[string, map[string][]HandleCall] {
|
||||||
return h.handlers
|
return handlerss
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) HandleHook() map[string][]func(HandleCall) (HandleCall, bool) {
|
func (h *Handle) HandleHook() *safety.Map[string, map[string][]func(HandleCall) (HandleCall, bool)] {
|
||||||
return h.handleHook
|
return handleHooks
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handle) SetTemplate(template *template.Template) {
|
|
||||||
h.template = template
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handle) Template() *template.Template {
|
|
||||||
return h.template
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandlePlugins map[string]HandleFn[*Handle]
|
type HandlePlugins map[string]HandleFn[*Handle]
|
||||||
@ -92,44 +87,50 @@ type HandleCall struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitHandle(fn func(*Handle), h *Handle) {
|
var isFirstRequest = true
|
||||||
var inited = false
|
|
||||||
hh := reload.GetAnyValBys("themeArgAndConfig", h, func(h *Handle) Handle {
|
func SetConfigHandle(a ...any) Handle {
|
||||||
h.components = make(map[string]map[string][]Components[string])
|
configFn := a[0].(func(*Handle))
|
||||||
h.componentsArgs = make(map[string]any)
|
hh := a[1].(*Handle)
|
||||||
h.componentFilterFn = make(map[string][]func(*Handle, string, ...any) string)
|
h := &Handle{}
|
||||||
h.handlers = make(map[string]map[string][]HandleCall)
|
handleComponents.Flush()
|
||||||
h.handleHook = make(map[string][]func(HandleCall) (HandleCall, bool))
|
componentsArgs.Flush()
|
||||||
h.ginH = gin.H{}
|
handleComponentHook.Flush()
|
||||||
fnMap = map[string]map[string]any{}
|
componentFilterFns.Flush()
|
||||||
fnHook = map[string]map[string]any{}
|
handlerss.Flush()
|
||||||
fn(h)
|
handleHooks.Flush()
|
||||||
v := apply.UsePlugins()
|
h.ginH = gin.H{}
|
||||||
pluginFn, ok := v.(func(*Handle))
|
fnMap.Flush()
|
||||||
if ok {
|
fnHook.Flush()
|
||||||
pluginFn(h)
|
if isFirstRequest {
|
||||||
}
|
isFirstRequest = false
|
||||||
h.C.Set("inited", true)
|
} else {
|
||||||
inited = true
|
reload.Reload()
|
||||||
return *h
|
}
|
||||||
})
|
h.C = hh.C
|
||||||
|
h.theme = hh.theme
|
||||||
|
configFn(h)
|
||||||
|
v := apply.GetPlugins()
|
||||||
|
pluginFn, ok := v.(func(*Handle))
|
||||||
|
if ok {
|
||||||
|
pluginFn(h)
|
||||||
|
}
|
||||||
|
return *h
|
||||||
|
}
|
||||||
|
|
||||||
|
var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, false)
|
||||||
|
|
||||||
|
func InitHandle(configFn func(*Handle), h *Handle) {
|
||||||
|
hh := GetInitHandleFn(configFn, h)
|
||||||
|
mods, err := wpconfig.GetThemeMods(h.theme)
|
||||||
|
logs.IfError(err, "获取mods失败")
|
||||||
|
h.themeMods = mods
|
||||||
h.ginH = maps.Copy(hh.ginH)
|
h.ginH = maps.Copy(hh.ginH)
|
||||||
h.ginH["calPostClass"] = postClass(h)
|
h.ginH["calPostClass"] = postClass(h)
|
||||||
h.ginH["calBodyClass"] = bodyClass(h)
|
h.ginH["calBodyClass"] = bodyClass(h)
|
||||||
h.ginH["customLogo"] = customLogo(h)
|
h.ginH["customLogo"] = customLogo(h)
|
||||||
if inited {
|
h.ginH["calComponent"] = CalComponent(h)
|
||||||
return
|
h.isInited = true
|
||||||
}
|
|
||||||
h.components = hh.components
|
|
||||||
h.Index.postsPlugin = hh.Index.postsPlugin
|
|
||||||
h.Index.pageEle = hh.Index.pageEle
|
|
||||||
h.Detail.CommentRender = hh.Detail.CommentRender
|
|
||||||
h.handlers = hh.handlers
|
|
||||||
h.handleHook = hh.handleHook
|
|
||||||
h.componentHook = hh.componentHook
|
|
||||||
h.componentsArgs = hh.componentsArgs
|
|
||||||
h.componentFilterFn = hh.componentFilterFn
|
|
||||||
h.C.Set("inited", true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) Abort() {
|
func (h *Handle) Abort() {
|
||||||
@ -151,13 +152,25 @@ func (h *Handle) Err() error {
|
|||||||
return h.err
|
return h.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) SetErr(err error) {
|
func (h *Handle) SetErr(err error, level int8) {
|
||||||
h.err = err
|
h.err = errors.Join(err)
|
||||||
|
h.errLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) ErrLevel() int8 {
|
||||||
|
return h.errLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) SetErrLevel(errLevel int8) {
|
||||||
|
h.errLevel = errLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handle) SetTempl(templ string) {
|
func (h *Handle) SetTempl(templ string) {
|
||||||
h.templ = templ
|
h.templ = templ
|
||||||
}
|
}
|
||||||
|
func (h *Handle) GetTempl() string {
|
||||||
|
return h.templ
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handle) Scene() string {
|
func (h *Handle) Scene() string {
|
||||||
return h.scene
|
return h.scene
|
||||||
@ -171,15 +184,12 @@ func (h *Handle) SetData(k string, v any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHandle(c *gin.Context, scene string, theme string) *Handle {
|
func NewHandle(c *gin.Context, scene string, theme string) *Handle {
|
||||||
mods, err := wpconfig.GetThemeMods(theme)
|
|
||||||
logs.IfError(err, "获取mods失败")
|
|
||||||
return &Handle{
|
return &Handle{
|
||||||
C: c,
|
C: c,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
Session: sessions.Default(c),
|
Session: sessions.Default(c),
|
||||||
scene: scene,
|
scene: scene,
|
||||||
Stats: constraints.Ok,
|
Stats: constraints.Ok,
|
||||||
themeMods: mods,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +234,12 @@ func (h *Handle) RenderHtml(t *template.Template, statsCode int, name string) {
|
|||||||
header["Content-Type"] = htmlContentType
|
header["Content-Type"] = htmlContentType
|
||||||
}
|
}
|
||||||
h.C.Status(statsCode)
|
h.C.Status(statsCode)
|
||||||
err := t.ExecuteTemplate(h.C.Writer, name, h.ginH)
|
var err error
|
||||||
|
if name == "" {
|
||||||
|
err = t.Execute(h.C.Writer, h.ginH)
|
||||||
|
} else {
|
||||||
|
err = t.ExecuteTemplate(h.C.Writer, name, h.ginH)
|
||||||
|
}
|
||||||
h.Abort()
|
h.Abort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -239,8 +254,8 @@ func (h *Handle) PushHandlers(pipeScene string, call HandleCall, statsOrScene ..
|
|||||||
|
|
||||||
func (h *Handle) CommonComponents() {
|
func (h *Handle) CommonComponents() {
|
||||||
h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss)
|
h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss)
|
||||||
h.PushRender(constraints.AllStats, NewHandleFn(CalComponents, 10.001, "wp.CalComponents"))
|
|
||||||
h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate"))
|
h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate"))
|
||||||
|
ReplyCommentJs(h)
|
||||||
AdditionScript(h)
|
AdditionScript(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,3 +296,10 @@ func NewHandleFn(fn HandleFn[*Handle], order float64, name string) HandleCall {
|
|||||||
func NothingToDo(h *Handle) {
|
func NothingToDo(h *Handle) {
|
||||||
h.Abort()
|
h.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handle) IsHttps() bool {
|
||||||
|
if h.C.Request.TLS != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return "https" == strings.ToLower(h.C.Request.Header.Get("X-Forwarded-Proto"))
|
||||||
|
}
|
||||||
|
@ -39,7 +39,13 @@ func GetOption(k string) string {
|
|||||||
if ok {
|
if ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
vv, err := model.GetField[models.Options](ctx, "option_value", model.Conditions(model.Where(model.SqlBuilder{{"option_name", k}})))
|
vv, err := model.GetField[models.Options](ctx, "option_value",
|
||||||
|
model.Conditions(
|
||||||
|
model.Where(
|
||||||
|
model.SqlBuilder{{"option_name", k}},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
options.Store(k, vv)
|
options.Store(k, vv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -3,10 +3,10 @@ package wpconfig
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/app/phphelper"
|
"github.com/fthvgb1/wp-go/app/phphelper"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -73,6 +73,7 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
|
|||||||
up := strings.Split(metadata.File, "/")
|
up := strings.Split(metadata.File, "/")
|
||||||
if metadata.File != "" && Type == "full" {
|
if metadata.File != "" && Type == "full" {
|
||||||
mimeType := metadata.Sizes["thumbnail"].MimeType
|
mimeType := metadata.Sizes["thumbnail"].MimeType
|
||||||
|
metadata.Sizes = maps.Copy(metadata.Sizes)
|
||||||
metadata.Sizes["full"] = models.MetaDataFileSize{
|
metadata.Sizes["full"] = models.MetaDataFileSize{
|
||||||
File: filepath.Base(metadata.File),
|
File: filepath.Base(metadata.File),
|
||||||
Width: metadata.Width,
|
Width: metadata.Width,
|
||||||
@ -111,10 +112,10 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
|
|||||||
var themeModes = func() *safety.Map[string, ThemeMods] {
|
var themeModes = func() *safety.Map[string, ThemeMods] {
|
||||||
m := safety.NewMap[string, ThemeMods]()
|
m := safety.NewMap[string, ThemeMods]()
|
||||||
themeModsRaw = safety.NewMap[string, map[string]any]()
|
themeModsRaw = safety.NewMap[string, map[string]any]()
|
||||||
reload.Push(func() {
|
reload.Append(func() {
|
||||||
m.Flush()
|
m.Flush()
|
||||||
themeModsRaw.Flush()
|
themeModsRaw.Flush()
|
||||||
})
|
}, "theme-modes")
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}()
|
}()
|
||||||
|
44
cache/cache.go
vendored
44
cache/cache.go
vendored
@ -2,15 +2,49 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cache[K comparable, V any] interface {
|
type Cache[K comparable, V any] interface {
|
||||||
Get(ctx context.Context, key K) (V, bool)
|
Get(ctx context.Context, key K) (V, bool)
|
||||||
Set(ctx context.Context, key K, val V, expire time.Duration)
|
Set(ctx context.Context, key K, val V)
|
||||||
Ttl(ctx context.Context, key K, expire time.Duration) time.Duration
|
GetExpireTime(ctx context.Context) time.Duration
|
||||||
Ver(ctx context.Context, key K) int
|
Ttl(ctx context.Context, key K) time.Duration
|
||||||
Flush(ctx context.Context)
|
Flush(ctx context.Context)
|
||||||
Delete(ctx context.Context, key K)
|
Del(ctx context.Context, key ...K)
|
||||||
ClearExpired(ctx context.Context, expire time.Duration)
|
ClearExpired(ctx context.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expend[K comparable, V any] interface {
|
||||||
|
Gets(ctx context.Context, k []K) (map[K]V, error)
|
||||||
|
Sets(ctx context.Context, m map[K]V)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetTime interface {
|
||||||
|
SetExpiredTime(func() time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnyCache[T any] interface {
|
||||||
|
Get(ctx context.Context) (T, bool)
|
||||||
|
Set(ctx context.Context, v T)
|
||||||
|
Flush(ctx context.Context)
|
||||||
|
GetLastSetTime(ctx context.Context) time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Refresh[K comparable, V any] interface {
|
||||||
|
Refresh(ctx context.Context, k K, a ...any)
|
||||||
|
}
|
||||||
|
type RefreshVar[T any] interface {
|
||||||
|
Refresh(ctx context.Context, a ...any)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Lockss[K comparable] interface {
|
||||||
|
GetLock(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type LockFn[K comparable] func(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
|
||||||
|
|
||||||
|
type LocksNum interface {
|
||||||
|
SetLockNum(num int)
|
||||||
}
|
}
|
||||||
|
163
cache/cachemanager/manger.go
vendored
Normal file
163
cache/cachemanager/manger.go
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package cachemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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/mockmap"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mutex sync.Mutex
|
||||||
|
|
||||||
|
type Fn func(context.Context)
|
||||||
|
|
||||||
|
type clearExpired interface {
|
||||||
|
ClearExpired(ctx context.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
var clears = safety.NewVar(mockmap.Map[string, Fn]{})
|
||||||
|
|
||||||
|
var flushes = safety.NewVar(mockmap.Map[string, Fn]{})
|
||||||
|
|
||||||
|
func Flush() {
|
||||||
|
ctx := context.WithValue(context.Background(), "execFlushBy", "mangerFlushFn")
|
||||||
|
for _, f := range flushes.Load() {
|
||||||
|
f.Value(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flushes(ctx context.Context, names ...string) {
|
||||||
|
execute(ctx, flushes, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Value != nil {
|
||||||
|
queue.Value(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseArgs(args ...any) (string, func() time.Duration) {
|
||||||
|
var name string
|
||||||
|
var fn func() time.Duration
|
||||||
|
for _, arg := range args {
|
||||||
|
v, ok := arg.(string)
|
||||||
|
if ok {
|
||||||
|
name = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vv, ok := arg.(func() time.Duration)
|
||||||
|
if ok {
|
||||||
|
fn = vv
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return name, fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLockFn[K comparable](args ...any) cache.LockFn[K] {
|
||||||
|
lockFn := helper.ParseArgs(cache.LockFn[K](nil), args...)
|
||||||
|
name := helper.ParseArgs("", args...)
|
||||||
|
num := helper.ParseArgs(runtime.NumCPU(), args...)
|
||||||
|
loFn := func() int {
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
loFn = helper.ParseArgs(loFn, args...)
|
||||||
|
if name != "" {
|
||||||
|
loFn = reload.BuildFnVal(str.Join("cachesLocksNum-", name), num, loFn)
|
||||||
|
}
|
||||||
|
if lockFn == nil {
|
||||||
|
looo := helper.ParseArgs(cache.Lockss[K](nil), args...)
|
||||||
|
if looo != nil {
|
||||||
|
lockFn = looo.GetLock
|
||||||
|
loo, ok := any(looo).(cache.LocksNum)
|
||||||
|
if ok && loo != nil {
|
||||||
|
loo.SetLockNum(num)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lo := cache.NewLocks[K](loFn)
|
||||||
|
lockFn = lo.GetLock
|
||||||
|
PushOrSetFlush(mockmap.Item[string, Fn]{
|
||||||
|
Name: name,
|
||||||
|
Value: lo.Flush,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return lockFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expireTimeFn func() time.Duration) {
|
||||||
|
if name == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fn := reload.BuildFnVal(str.Join("cacheManger-", name, "-expiredTime"), expireTime, expireTimeFn)
|
||||||
|
c.SetExpiredTime(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) {
|
||||||
|
for _, s := range name {
|
||||||
|
reload.SetFnVal(s, t, coverConf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.Value != nil {
|
||||||
|
qu.Set(queue.Name, queue.Value)
|
||||||
|
} else {
|
||||||
|
qu = append(qu, queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.Store(qu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushOrSetFlush will execute flush func when call Flush or Flushes
|
||||||
|
func PushOrSetFlush(queues ...mockmap.Item[string, Fn]) {
|
||||||
|
pushOrSet(flushes, queues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushOrSetClearExpired will execute clearExpired func when call ClearExpired or ClearExpireds
|
||||||
|
func PushOrSetClearExpired(queues ...mockmap.Item[string, Fn]) {
|
||||||
|
pushOrSet(clears, queues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func del(q *safety.Var[mockmap.Map[string, Fn]], names ...string) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
queues := q.Load()
|
||||||
|
for _, name := range names {
|
||||||
|
queues.Del(name)
|
||||||
|
}
|
||||||
|
q.Store(queues)
|
||||||
|
}
|
||||||
|
func DelFlush(names ...string) {
|
||||||
|
del(flushes, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DelClearExpired(names ...string) {
|
||||||
|
del(clears, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearExpireds(ctx context.Context, names ...string) {
|
||||||
|
execute(ctx, clears, names...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearExpired() {
|
||||||
|
ctx := context.WithValue(context.Background(), "execClearExpired", "mangerClearExpiredFn")
|
||||||
|
for _, queue := range clears.Load() {
|
||||||
|
queue.Value(ctx)
|
||||||
|
}
|
||||||
|
}
|
193
cache/cachemanager/manger_test.go
vendored
Normal file
193
cache/cachemanager/manger_test.go
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package cachemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/taskPools"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
|
func TestFlushMapVal(t *testing.T) {
|
||||||
|
_ = number.Range(1, 5, 0)
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
count := 0
|
||||||
|
vv := NewMemoryMapCache(func(ctx2 context.Context, ks []int, a ...any) (map[int]int, error) {
|
||||||
|
r := make(map[int]int)
|
||||||
|
for _, k := range ks {
|
||||||
|
r[k] = k * k
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
return r, nil
|
||||||
|
}, nil, time.Second, "test")
|
||||||
|
|
||||||
|
gets, err := GetBatchBy[int]("test", ctx, number.Range(1, 10), time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(t, "err:", err)
|
||||||
|
}
|
||||||
|
p := taskPools.NewPools(10)
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
i := i
|
||||||
|
p.Execute(func() {
|
||||||
|
if i%2 == 0 {
|
||||||
|
vv.Get(ctx, 5)
|
||||||
|
} else {
|
||||||
|
vv.Set(ctx, i, i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
p.Wait()
|
||||||
|
fmt.Println(gets, count)
|
||||||
|
DelMapCacheVal("test", 3, 4)
|
||||||
|
fmt.Println(vv.Get(ctx, 3))
|
||||||
|
fmt.Println(vv.Get(ctx, 4))
|
||||||
|
get, err := GetBy[int]("test", ctx, 3, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(t, "err", err)
|
||||||
|
}
|
||||||
|
fmt.Println(get, count)
|
||||||
|
fmt.Println(vv.Get(ctx, 5))
|
||||||
|
Flushes(ctx, "test")
|
||||||
|
fmt.Println(vv.Get(ctx, 5))
|
||||||
|
fmt.Println(vv.Get(ctx, 6))
|
||||||
|
//fmt.Println(GetVarCache("test"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetExpireTime(t *testing.T) {
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
c := NewMemoryMapCache[string, string](func(ctx2 context.Context, strings []string, a ...any) (map[string]string, error) {
|
||||||
|
return slice.ToMap(strings, func(v string) (string, string) {
|
||||||
|
return v, str.Join(v, "__", v)
|
||||||
|
}, false), nil
|
||||||
|
}, nil, time.Second, "xx")
|
||||||
|
c.Set(ctx, "xx", "yy")
|
||||||
|
fmt.Println(c.Get(ctx, "xx"))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
fmt.Println(c.Get(ctx, "xx"))
|
||||||
|
ChangeExpireTime(3*time.Second, true, "xx")
|
||||||
|
c.Set(ctx, "xx", "yyy")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
fmt.Println(c.Get(ctx, "xx"))
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
fmt.Println(c.Get(ctx, "xx"))
|
||||||
|
cc, _ := GetMapCache[string, string]("xx")
|
||||||
|
fmt.Println(reflect.DeepEqual(c, cc))
|
||||||
|
cc.Set(ctx, "fff", "xxxx")
|
||||||
|
cc.Set(ctx, "ffx", "eex")
|
||||||
|
cc.Set(ctx, "ww", "vv")
|
||||||
|
m, err := cc.GetBatchToMap(ctx, []string{"fff", "ffx", "ww", "kkkk"}, time.Second)
|
||||||
|
fmt.Println(m, err)
|
||||||
|
fmt.Println(GetBatchByToMap[string]("xx", ctx, []string{"fff", "ffx", "ww", "kkkk"}, time.Second))
|
||||||
|
v := NewVarMemoryCache(func(ct context.Context, a ...any) (string, error) {
|
||||||
|
return "ssss", nil
|
||||||
|
}, 3*time.Second, "ff")
|
||||||
|
vv, _ := GetVarCache[string]("ff")
|
||||||
|
fmt.Println(reflect.DeepEqual(v, vv))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetMapCache(t *testing.T) {
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
x := NewMemoryMapCache(nil, func(ctx2 context.Context, k string, a ...any) (string, error) {
|
||||||
|
fmt.Println("memory cache")
|
||||||
|
return strings.Repeat(k, 2), nil
|
||||||
|
}, time.Hour, "test")
|
||||||
|
fmt.Println(GetBy[string]("test", ctx, "test", time.Second))
|
||||||
|
|
||||||
|
NewMapCache[string, string](xx[string, string]{m: map[string]string{}}, nil, func(ctx2 context.Context, k string, a ...any) (string, error) {
|
||||||
|
fmt.Println("other cache drives. eg: redis,file.....")
|
||||||
|
return strings.Repeat(k, 2), nil
|
||||||
|
}, "test", time.Hour)
|
||||||
|
|
||||||
|
if err := SetMapCache("kkk", x); err != nil {
|
||||||
|
t.Errorf("SetMapCache() error = %v, wantErr %v", err, nil)
|
||||||
|
}
|
||||||
|
fmt.Println(GetBy[string]("test", ctx, "test", time.Second))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type xx[K comparable, V any] struct {
|
||||||
|
m map[K]V
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) Get(ctx context.Context, key K) (V, bool) {
|
||||||
|
v, ok := x.m[key]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) Set(ctx context.Context, key K, val V) {
|
||||||
|
x.m[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) GetExpireTime(ctx context.Context) time.Duration {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) Ttl(ctx context.Context, key K) time.Duration {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) Flush(ctx context.Context) {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) Del(ctx context.Context, key ...K) {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x xx[K, V]) ClearExpired(ctx context.Context) {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetVarCache(t *testing.T) {
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
bak := NewVarMemoryCache(func(ctx2 context.Context, a ...any) (string, error) {
|
||||||
|
fmt.Println("memory cache")
|
||||||
|
return "xxx", nil
|
||||||
|
}, time.Hour, "test")
|
||||||
|
fmt.Println(GetVarVal[string]("test", ctx, time.Second))
|
||||||
|
NewVarCache[string](oo[string]{}, func(ctx2 context.Context, a ...any) (string, error) {
|
||||||
|
fmt.Println("other cache drives. eg: redis,file.....")
|
||||||
|
return "ooo", nil
|
||||||
|
}, "test")
|
||||||
|
if err := SetVarCache("xx", bak); err != nil {
|
||||||
|
t.Errorf("SetVarCache() error = %v, wantErr %v", err, nil)
|
||||||
|
}
|
||||||
|
fmt.Println(GetVarVal[string]("test", ctx, time.Second))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type oo[T any] struct {
|
||||||
|
val T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o oo[T]) Get(ctx context.Context) (T, bool) {
|
||||||
|
return o.val, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o oo[T]) Set(ctx context.Context, v T) {
|
||||||
|
o.val = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o oo[T]) Flush(ctx context.Context) {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o oo[T]) GetLastSetTime(ctx context.Context) time.Time {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
148
cache/cachemanager/mapcache.go
vendored
Normal file
148
cache/cachemanager/mapcache.go
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package cachemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapDelFuncs = safety.NewMap[string, func(any)]()
|
||||||
|
|
||||||
|
var mapCache = safety.NewMap[string, any]()
|
||||||
|
|
||||||
|
func SetMapCache[K comparable, V any](name string, ca *cache.MapCache[K, V]) error {
|
||||||
|
v, ok := mapCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
mapCache.Store(name, ca)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, ok = v.(*cache.MapCache[K, V])
|
||||||
|
if !ok {
|
||||||
|
return errors.New(str.Join("cache ", name, " type err"))
|
||||||
|
}
|
||||||
|
mapCache.Store(name, ca)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushMangerMap will del mapCache val with name When call DelMapCacheVal
|
||||||
|
func PushMangerMap[K comparable, V any](name string, m *cache.MapCache[K, V]) {
|
||||||
|
if name == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mapCache.Store(name, m)
|
||||||
|
mapDelFuncs.Store(name, func(a any) {
|
||||||
|
k, ok := a.([]K)
|
||||||
|
if ok && len(k) > 0 {
|
||||||
|
mm, ok := mapCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, ok := mm.(*cache.MapCache[K, V])
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := context.WithValue(context.Background(), "ctx", "registerFlush")
|
||||||
|
c.Del(ctx, k...)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBy[T any, K comparable](name string, ct context.Context, key K, timeout time.Duration, params ...any) (r T, err error) {
|
||||||
|
ct = context.WithValue(ct, "getCache", name)
|
||||||
|
ca, err := getMap[K, T](name)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
vv, err := ca.GetCache(ct, key, timeout, params...)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
r = vv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMap[K comparable, T any](name string) (*cache.MapCache[K, T], error) {
|
||||||
|
m, ok := mapCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(str.Join("cache ", name, " doesn't exist"))
|
||||||
|
}
|
||||||
|
vk, ok := m.(*cache.MapCache[K, T])
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(str.Join("cache ", name, " type error"))
|
||||||
|
}
|
||||||
|
return vk, nil
|
||||||
|
}
|
||||||
|
func GetBatchBy[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r []T, err error) {
|
||||||
|
ct = context.WithValue(ct, "getCache", name)
|
||||||
|
ca, err := getMap[K, T](name)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
vv, err := ca.GetCacheBatch(ct, key, timeout, params...)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
r = vv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func GetBatchByToMap[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r map[K]T, err error) {
|
||||||
|
ct = context.WithValue(ct, "getCache", name)
|
||||||
|
ca, err := getMap[K, T](name)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
vv, err := ca.GetBatchToMap(ct, key, timeout, params...)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
r = vv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] {
|
||||||
|
inc := helper.ParseArgs((*cache.IncreaseUpdate[K, V])(nil), args...)
|
||||||
|
m := cache.NewMapCache[K, V](data, fn, batchFn, inc, buildLockFn[K](args...), args...)
|
||||||
|
name, f := parseArgs(args...)
|
||||||
|
if name != "" {
|
||||||
|
PushMangerMap(name, m)
|
||||||
|
}
|
||||||
|
PushOrSetFlush(mockmap.Item[string, Fn]{
|
||||||
|
Name: name,
|
||||||
|
Value: m.Flush,
|
||||||
|
})
|
||||||
|
PushOrSetClearExpired(mockmap.Item[string, Fn]{
|
||||||
|
Name: name,
|
||||||
|
Value: m.ClearExpired,
|
||||||
|
})
|
||||||
|
if f != nil && name != "" {
|
||||||
|
SetExpireTime(any(data).(cache.SetTime), name, 0, f)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemoryMapCache[K comparable, V any](batchFn cache.MapBatchFn[K, V],
|
||||||
|
fn cache.MapSingleFn[K, V], expireTime time.Duration, args ...any) *cache.MapCache[K, V] {
|
||||||
|
|
||||||
|
c := NewMapCache[K, V](cache.NewMemoryMapCache[K, V](func() time.Duration {
|
||||||
|
return expireTime
|
||||||
|
}), batchFn, fn, args...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) {
|
||||||
|
vv, err := getMap[K, V](name)
|
||||||
|
return vv, err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DelMapCacheVal[T any](name string, keys ...T) {
|
||||||
|
v, ok := mapDelFuncs.Load(name)
|
||||||
|
if !ok || len(keys) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v(keys)
|
||||||
|
}
|
57
cache/cachemanager/pagination.go
vendored
Normal file
57
cache/cachemanager/pagination.go
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package cachemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/fthvgb1/wp-go/cache"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.PaginationData[V]], maxNum int,
|
||||||
|
dbFn cache.DbFn[K, V], localFn cache.LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, fetchNum int, name string, a ...any) *cache.Pagination[K, V] {
|
||||||
|
fn := helper.ParseArgs([]func() int(nil), a...)
|
||||||
|
var ma, fet func() int
|
||||||
|
if len(fn) > 0 {
|
||||||
|
ma = fn[0]
|
||||||
|
if len(fn) > 1 {
|
||||||
|
fet = fn[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ma == nil {
|
||||||
|
ma = reload.BuildFnVal(str.Join("paginationCache-", name, "-maxNum"), maxNum, nil)
|
||||||
|
}
|
||||||
|
if fet == nil {
|
||||||
|
fet = reload.BuildFnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil)
|
||||||
|
}
|
||||||
|
p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name)
|
||||||
|
mapCache.Store(name, p)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationCache[K comparable, V any](name string) (*cache.Pagination[K, V], bool) {
|
||||||
|
v, err := getPagination[K, V](name)
|
||||||
|
return v, err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pagination[V any, K comparable](name string, ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) {
|
||||||
|
v, err := getPagination[K, V](name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return v.Pagination(ctx, timeout, k, page, limit, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPagination[K comparable, T any](name string) (*cache.Pagination[K, T], error) {
|
||||||
|
m, ok := mapCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(str.Join("cache ", name, " doesn't exist"))
|
||||||
|
}
|
||||||
|
vk, ok := m.(*cache.Pagination[K, T])
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(str.Join("cache ", name, " type error"))
|
||||||
|
}
|
||||||
|
return vk, nil
|
||||||
|
}
|
83
cache/cachemanager/varcache.go
vendored
Normal file
83
cache/cachemanager/varcache.go
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package cachemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var varCache = safety.NewMap[string, any]()
|
||||||
|
|
||||||
|
func SetVarCache[T any](name string, v *cache.VarCache[T]) error {
|
||||||
|
vv, ok := varCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
varCache.Store(name, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, ok = vv.(*cache.VarCache[T])
|
||||||
|
if ok {
|
||||||
|
varCache.Store(name, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(str.Join("cache ", name, " type err"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T, error), a ...any) *cache.VarCache[T] {
|
||||||
|
inc := helper.ParseArgs((*cache.IncreaseUpdateVar[T])(nil), a...)
|
||||||
|
ref := helper.ParseArgs(cache.RefreshVar[T](nil), a...)
|
||||||
|
v := cache.NewVarCache(c, fn, inc, ref, a...)
|
||||||
|
|
||||||
|
name, _ := parseArgs(a...)
|
||||||
|
if name != "" {
|
||||||
|
varCache.Store(name, v)
|
||||||
|
}
|
||||||
|
PushOrSetFlush(mockmap.Item[string, Fn]{
|
||||||
|
Name: name,
|
||||||
|
Value: v.Flush,
|
||||||
|
})
|
||||||
|
cc, ok := any(c).(clearExpired)
|
||||||
|
if ok {
|
||||||
|
PushOrSetClearExpired(mockmap.Item[string, Fn]{
|
||||||
|
Name: name,
|
||||||
|
Value: cc.ClearExpired,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVarVal[T any](name string, ctx context.Context, duration time.Duration, a ...any) (r T, err error) {
|
||||||
|
ctx = context.WithValue(ctx, "getCache", name)
|
||||||
|
ca, ok := GetVarCache[T](name)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New(str.Join("cache ", name, " is not exist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v, err := ca.GetCache(ctx, duration, a...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r = v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVarMemoryCache[T any](fn func(context.Context, ...any) (T, error), expired time.Duration, a ...any) *cache.VarCache[T] {
|
||||||
|
c := cache.NewVarMemoryCache[T](nil)
|
||||||
|
name, e := parseArgs(a...)
|
||||||
|
SetExpireTime(c, name, expired, e)
|
||||||
|
v := NewVarCache[T](c, fn, a...)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVarCache[T any](name string) (*cache.VarCache[T], bool) {
|
||||||
|
v, ok := varCache.Load(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
vv, ok := v.(*cache.VarCache[T])
|
||||||
|
return vv, ok
|
||||||
|
}
|
78
cache/locks.go
vendored
Normal file
78
cache/locks.go
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Locks[K comparable] struct {
|
||||||
|
numFn func() int
|
||||||
|
locks []*sync.Mutex
|
||||||
|
m *safety.Map[K, *sync.Mutex]
|
||||||
|
counter *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Locks[K]) Flush(_ context.Context) {
|
||||||
|
l.m.Flush()
|
||||||
|
atomic.StoreInt64(l.counter, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Locks[K]) SetNumFn(numFn func() int) {
|
||||||
|
l.numFn = numFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocks[K comparable](num func() int) *Locks[K] {
|
||||||
|
var i int64
|
||||||
|
return &Locks[K]{numFn: num, m: safety.NewMap[K, *sync.Mutex](), counter: &i}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Locks[K]) SetLockNum(num int) {
|
||||||
|
if num > 0 {
|
||||||
|
l.locks = make([]*sync.Mutex, num)
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
l.locks[i] = &sync.Mutex{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Locks[K]) GetLock(ctx context.Context, gMut *sync.Mutex, keys ...K) *sync.Mutex {
|
||||||
|
k := keys[0]
|
||||||
|
lo, ok := l.m.Load(k)
|
||||||
|
if ok {
|
||||||
|
return lo
|
||||||
|
}
|
||||||
|
num := l.numFn()
|
||||||
|
if num == 1 {
|
||||||
|
return gMut
|
||||||
|
}
|
||||||
|
gMut.Lock()
|
||||||
|
defer gMut.Unlock()
|
||||||
|
lo, ok = l.m.Load(k)
|
||||||
|
if ok {
|
||||||
|
return lo
|
||||||
|
}
|
||||||
|
if num <= 0 {
|
||||||
|
lo = &sync.Mutex{}
|
||||||
|
l.m.Store(k, lo)
|
||||||
|
return lo
|
||||||
|
}
|
||||||
|
if len(l.locks) == 0 {
|
||||||
|
l.SetLockNum(num)
|
||||||
|
}
|
||||||
|
counter := int(atomic.LoadInt64(l.counter))
|
||||||
|
if counter > len(l.locks)-1 {
|
||||||
|
atomic.StoreInt64(l.counter, 0)
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
lo = l.locks[counter]
|
||||||
|
l.m.Store(k, lo)
|
||||||
|
atomic.AddInt64(l.counter, 1)
|
||||||
|
if len(l.locks) < num {
|
||||||
|
for i := 0; i < num-len(l.locks); i++ {
|
||||||
|
l.locks = append(l.locks, &sync.Mutex{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lo
|
||||||
|
}
|
612
cache/map.go
vendored
612
cache/map.go
vendored
@ -4,54 +4,200 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MapCache[K comparable, V any] struct {
|
type MapCache[K comparable, V any] struct {
|
||||||
data Cache[K, V]
|
Cache[K, V]
|
||||||
mux sync.Mutex
|
mux *sync.Mutex
|
||||||
cacheFunc func(...any) (V, error)
|
muFn func(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
|
||||||
batchCacheFn func(...any) (map[K]V, error)
|
cacheFunc MapSingleFn[K, V]
|
||||||
expireTime time.Duration
|
batchCacheFn MapBatchFn[K, V]
|
||||||
|
getCacheBatch func(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error)
|
||||||
|
getCacheBatchToMap func(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error)
|
||||||
|
increaseUpdate *IncreaseUpdate[K, V]
|
||||||
|
refresh Refresh[K, V]
|
||||||
|
gets func(ctx context.Context, key K) (V, bool)
|
||||||
|
sets func(ctx context.Context, key K, val V)
|
||||||
|
getExpireTimes func(ctx context.Context) time.Duration
|
||||||
|
ttl func(ctx context.Context, key K) time.Duration
|
||||||
|
flush func(ctx context.Context)
|
||||||
|
del func(ctx context.Context, key ...K)
|
||||||
|
clearExpired func(ctx context.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
|
func (m *MapCache[K, V]) Get(ctx context.Context, key K) (V, bool) {
|
||||||
|
return m.gets(ctx, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapCache[K, V]) Set(ctx context.Context, key K, val V) {
|
||||||
|
m.sets(ctx, key, val)
|
||||||
|
}
|
||||||
|
func (m *MapCache[K, V]) Ttl(ctx context.Context, key K) time.Duration {
|
||||||
|
return m.ttl(ctx, key)
|
||||||
|
}
|
||||||
|
func (m *MapCache[K, V]) GetExpireTime(ctx context.Context) time.Duration {
|
||||||
|
return m.getExpireTimes(ctx)
|
||||||
|
}
|
||||||
|
func (m *MapCache[K, V]) Del(ctx context.Context, key ...K) {
|
||||||
|
m.del(ctx, key...)
|
||||||
|
}
|
||||||
|
func (m *MapCache[K, V]) ClearExpired(ctx context.Context) {
|
||||||
|
m.clearExpired(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IncreaseUpdate[K comparable, V any] struct {
|
||||||
|
CycleTime func() time.Duration
|
||||||
|
Fn IncreaseFn[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIncreaseUpdate[K comparable, V any](name string, fn IncreaseFn[K, V], cycleTime time.Duration, tFn func() time.Duration) *IncreaseUpdate[K, V] {
|
||||||
|
tFn = reload.BuildFnVal(name, cycleTime, tFn)
|
||||||
|
return &IncreaseUpdate[K, V]{CycleTime: tFn, Fn: fn}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MapSingleFn[K, V any] func(context.Context, K, ...any) (V, error)
|
||||||
|
type MapBatchFn[K comparable, V any] func(context.Context, []K, ...any) (map[K]V, error)
|
||||||
|
type IncreaseFn[K comparable, V any] func(c context.Context, currentData V, k K, t time.Time, a ...any) (data V, save bool, refresh bool, err error)
|
||||||
|
|
||||||
|
func NewMapCache[K comparable, V any](ca Cache[K, V], cacheFunc MapSingleFn[K, V], batchCacheFn MapBatchFn[K, V], inc *IncreaseUpdate[K, V], lockFn LockFn[K], a ...any) *MapCache[K, V] {
|
||||||
|
r := &MapCache[K, V]{
|
||||||
|
Cache: ca,
|
||||||
|
mux: &sync.Mutex{},
|
||||||
|
cacheFunc: cacheFunc,
|
||||||
|
batchCacheFn: batchCacheFn,
|
||||||
|
increaseUpdate: inc,
|
||||||
|
muFn: lockFn,
|
||||||
|
}
|
||||||
|
if cacheFunc == nil && batchCacheFn != nil {
|
||||||
|
r.setDefaultCacheFn(batchCacheFn)
|
||||||
|
} else if batchCacheFn == nil && cacheFunc != nil {
|
||||||
|
r.SetDefaultBatchFunc(cacheFunc)
|
||||||
|
}
|
||||||
|
ex, ok := any(ca).(Expend[K, V])
|
||||||
|
if !ok {
|
||||||
|
r.getCacheBatch = r.getCacheBatchs
|
||||||
|
r.getCacheBatchToMap = r.getBatchToMapes
|
||||||
|
} else {
|
||||||
|
r.getCacheBatch = r.getBatches(ex)
|
||||||
|
r.getCacheBatchToMap = r.getBatchToMap(ex)
|
||||||
|
}
|
||||||
|
re, ok := any(ca).(Refresh[K, V])
|
||||||
|
if ok {
|
||||||
|
r.refresh = re
|
||||||
|
}
|
||||||
|
initCache(r, a...)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCache[K comparable, V any](r *MapCache[K, V], a ...any) {
|
||||||
|
gets := helper.ParseArgs[func(Cache[K, V], context.Context, K) (V, bool)](nil, a...)
|
||||||
|
if gets == nil {
|
||||||
|
r.gets = r.Cache.Get
|
||||||
|
} else {
|
||||||
|
r.gets = func(ctx context.Context, key K) (V, bool) {
|
||||||
|
return gets(r.Cache, ctx, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sets := helper.ParseArgs[func(Cache[K, V], context.Context, K, V)](nil, a...)
|
||||||
|
if sets == nil {
|
||||||
|
r.sets = r.Cache.Set
|
||||||
|
} else {
|
||||||
|
r.sets = func(ctx context.Context, key K, val V) {
|
||||||
|
sets(r.Cache, ctx, key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getExpireTimes := helper.ParseArgs[func(Cache[K, V], context.Context) time.Duration](nil, a...)
|
||||||
|
if getExpireTimes == nil {
|
||||||
|
r.getExpireTimes = r.Cache.GetExpireTime
|
||||||
|
} else {
|
||||||
|
r.getExpireTimes = func(ctx context.Context) time.Duration {
|
||||||
|
return getExpireTimes(r.Cache, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl := helper.ParseArgs[func(Cache[K, V], context.Context, K) time.Duration](nil, a...)
|
||||||
|
if ttl == nil {
|
||||||
|
r.ttl = r.Cache.Ttl
|
||||||
|
} else {
|
||||||
|
r.ttl = func(ctx context.Context, k K) time.Duration {
|
||||||
|
return ttl(r.Cache, ctx, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
del := helper.ParseArgs[func(Cache[K, V], context.Context, ...K)](nil, a...)
|
||||||
|
if del == nil {
|
||||||
|
r.del = r.Cache.Del
|
||||||
|
} else {
|
||||||
|
r.del = func(ctx context.Context, key ...K) {
|
||||||
|
del(r.Cache, ctx, key...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flushAndClearExpired := helper.ParseArgs[[]func(Cache[K, V], context.Context)](nil, a...)
|
||||||
|
if flushAndClearExpired == nil {
|
||||||
|
r.flush = r.Cache.Flush
|
||||||
|
r.clearExpired = r.Cache.ClearExpired
|
||||||
|
} else {
|
||||||
|
r.flush = func(ctx context.Context) {
|
||||||
|
flushAndClearExpired[0](r.Cache, ctx)
|
||||||
|
}
|
||||||
|
if len(flushAndClearExpired) > 1 {
|
||||||
|
r.clearExpired = func(ctx context.Context) {
|
||||||
|
flushAndClearExpired[1](r.Cache, ctx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.clearExpired = r.Cache.ClearExpired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapCache[K, V]) SetDefaultBatchFunc(fn MapSingleFn[K, V]) {
|
||||||
|
m.batchCacheFn = func(ctx context.Context, ids []K, a ...any) (map[K]V, error) {
|
||||||
|
var err error
|
||||||
|
rr := make(map[K]V)
|
||||||
|
for _, id := range ids {
|
||||||
|
v, er := fn(ctx, id, a...)
|
||||||
|
if er != nil {
|
||||||
|
err = errors.Join(er)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rr[id] = v
|
||||||
|
}
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapCache[K, V]) SetCacheFunc(fn MapSingleFn[K, V]) {
|
||||||
m.cacheFunc = fn
|
m.cacheFunc = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) Ttl(ctx context.Context, k K) time.Duration {
|
|
||||||
return m.data.Ttl(ctx, k, m.expireTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
|
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
|
||||||
tt := m.data.Ttl(ctx, k, m.expireTime)
|
tt := m.Ttl(ctx, k)
|
||||||
if tt <= 0 {
|
if tt <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return time.Now().Add(m.data.Ttl(ctx, k, m.expireTime)).Add(-m.expireTime)
|
return time.Now().Add(m.Ttl(ctx, k)).Add(-m.GetExpireTime(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) SetCacheBatchFn(fn func(...any) (map[K]V, error)) {
|
func (m *MapCache[K, V]) SetCacheBatchFn(fn MapBatchFn[K, V]) {
|
||||||
m.batchCacheFn = fn
|
m.batchCacheFn = fn
|
||||||
if m.cacheFunc == nil {
|
if m.cacheFunc == nil {
|
||||||
m.setCacheFn(fn)
|
m.setDefaultCacheFn(fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
|
func (m *MapCache[K, V]) setDefaultCacheFn(fn MapBatchFn[K, V]) {
|
||||||
m.cacheFunc = func(a ...any) (V, error) {
|
m.cacheFunc = func(ctx context.Context, k K, a ...any) (V, error) {
|
||||||
var err error
|
var err error
|
||||||
var r map[K]V
|
var r map[K]V
|
||||||
var k K
|
r, err = fn(ctx, []K{k}, a...)
|
||||||
ctx, ok := a[0].(context.Context)
|
|
||||||
if ok {
|
|
||||||
k, ok = a[1].(K)
|
|
||||||
if ok {
|
|
||||||
r, err = fn(ctx, []K{k})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var rr V
|
var rr V
|
||||||
@ -61,114 +207,145 @@ func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMapCacheByFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
|
|
||||||
return &MapCache[K, V]{
|
|
||||||
mux: sync.Mutex{},
|
|
||||||
cacheFunc: fn,
|
|
||||||
expireTime: expireTime,
|
|
||||||
data: cacheType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func NewMapCacheByBatchFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
|
|
||||||
r := &MapCache[K, V]{
|
|
||||||
mux: sync.Mutex{},
|
|
||||||
batchCacheFn: fn,
|
|
||||||
expireTime: expireTime,
|
|
||||||
data: cacheType,
|
|
||||||
}
|
|
||||||
r.setCacheFn(fn)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapCache[K, V]) Flush(ctx context.Context) {
|
func (m *MapCache[K, V]) Flush(ctx context.Context) {
|
||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
defer m.mux.Unlock()
|
defer m.mux.Unlock()
|
||||||
m.data.Flush(ctx)
|
m.flush(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) Get(ctx context.Context, k K) (V, bool) {
|
func (m *MapCache[K, V]) increaseUpdates(c context.Context, timeout time.Duration, data V, key K, params ...any) (V, error) {
|
||||||
return m.data.Get(ctx, k)
|
var err error
|
||||||
}
|
nowTime := time.Now()
|
||||||
|
if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() {
|
||||||
func (m *MapCache[K, V]) Set(ctx context.Context, k K, v V) {
|
return data, err
|
||||||
m.data.Set(ctx, k, v, m.expireTime)
|
}
|
||||||
|
fn := func() {
|
||||||
|
l := m.muFn(c, m.mux, key)
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dat, save, refresh, er := m.increaseUpdate.Fn(c, data, key, m.GetLastSetTime(c, key), params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if refresh {
|
||||||
|
m.refresh.Refresh(c, key, params...)
|
||||||
|
}
|
||||||
|
if save {
|
||||||
|
m.Set(c, key, dat)
|
||||||
|
data = dat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
er := helper.RunFnWithTimeout(c, timeout, fn)
|
||||||
|
if err == nil && er != nil {
|
||||||
|
return data, fmt.Errorf("increateUpdate cache %v err:[%s]", key, er)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
|
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
|
||||||
data, ok := m.data.Get(c, key)
|
data, ok := m.Get(c, key)
|
||||||
var err error
|
var err error
|
||||||
if !ok || m.data.Ttl(c, key, m.expireTime) <= 0 {
|
if ok {
|
||||||
ver := m.data.Ver(c, key)
|
if m.increaseUpdate == nil || m.refresh == nil {
|
||||||
call := func() {
|
return data, err
|
||||||
m.mux.Lock()
|
|
||||||
defer m.mux.Unlock()
|
|
||||||
if m.data.Ver(c, key) > ver {
|
|
||||||
data, _ = m.data.Get(c, key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data, err = m.cacheFunc(params...)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m.Set(c, key, data)
|
|
||||||
}
|
}
|
||||||
if timeout > 0 {
|
return m.increaseUpdates(c, timeout, data, key, params...)
|
||||||
ctx, cancel := context.WithTimeout(c, timeout)
|
}
|
||||||
defer cancel()
|
call := func() {
|
||||||
done := make(chan struct{}, 1)
|
l := m.muFn(c, m.mux, key)
|
||||||
go func() {
|
l.Lock()
|
||||||
call()
|
defer l.Unlock()
|
||||||
done <- struct{}{}
|
if data, ok = m.Get(c, key); ok {
|
||||||
}()
|
return
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
call()
|
|
||||||
}
|
}
|
||||||
|
data, err = m.cacheFunc(c, key, params...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.Set(c, key, data)
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
er := helper.RunFnWithTimeout(c, timeout, call, fmt.Sprintf("get cache %v ", key))
|
||||||
|
if err == nil && er != nil {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call()
|
||||||
}
|
}
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
|
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
|
||||||
var res []V
|
return m.getCacheBatch(c, key, timeout, params...)
|
||||||
ver := 0
|
}
|
||||||
needFlush := slice.FilterAndMap(key, func(k K) (r K, ok bool) {
|
|
||||||
if _, ok := m.data.Get(c, k); !ok {
|
func (m *MapCache[K, V]) GetBatchToMap(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
|
||||||
return k, true
|
return m.getCacheBatchToMap(c, key, timeout, params...)
|
||||||
}
|
}
|
||||||
ver += m.data.Ver(c, k)
|
func (m *MapCache[K, V]) getBatchToMap(e Expend[K, V]) func(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
|
||||||
return
|
return func(ctx context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
|
||||||
})
|
var res map[K]V
|
||||||
|
var err error
|
||||||
|
mm, err := e.Gets(ctx, key)
|
||||||
|
if err != nil || len(key) == len(mm) {
|
||||||
|
return mm, err
|
||||||
|
}
|
||||||
|
var needIndex = make(map[K]int)
|
||||||
|
res = mm
|
||||||
|
var flushKeys []K
|
||||||
|
for i, k := range key {
|
||||||
|
_, ok := mm[k]
|
||||||
|
if !ok {
|
||||||
|
flushKeys = append(flushKeys, k)
|
||||||
|
needIndex[k] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
if len(needFlush) > 0 {
|
|
||||||
call := func() {
|
call := func() {
|
||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
defer m.mux.Unlock()
|
defer m.mux.Unlock()
|
||||||
|
mmm, er := e.Gets(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
vers := slice.Reduce(needFlush, func(t K, r int) int {
|
return k, true
|
||||||
return r + m.data.Ver(c, t)
|
}))
|
||||||
}, 0)
|
if er != nil {
|
||||||
|
|
||||||
if vers > ver {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r, er := m.batchCacheFn(params...)
|
|
||||||
if err != nil {
|
|
||||||
err = er
|
err = er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for k, v := range r {
|
for k, v := range mmm {
|
||||||
m.Set(c, k, v)
|
res[k] = v
|
||||||
|
delete(needIndex, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(needIndex) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r, er := m.batchCacheFn(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
|
return k, true
|
||||||
|
}), params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.Sets(ctx, r)
|
||||||
|
|
||||||
|
for k := range needIndex {
|
||||||
|
v, ok := r[k]
|
||||||
|
if ok {
|
||||||
|
res[k] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if timeout > 0 {
|
if timeout > 0 {
|
||||||
ctx, cancel := context.WithTimeout(c, timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
done := make(chan struct{}, 1)
|
done := make(chan struct{}, 1)
|
||||||
go func() {
|
go func() {
|
||||||
@ -178,20 +355,227 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
||||||
|
return nil, err
|
||||||
case <-done:
|
case <-done:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
call()
|
call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
res = slice.FilterAndMap(key, func(k K) (V, bool) {
|
}
|
||||||
return m.data.Get(c, k)
|
func (m *MapCache[K, V]) getBatchToMapes(c context.Context, key []K, timeout time.Duration, params ...any) (r map[K]V, err error) {
|
||||||
})
|
r = make(map[K]V)
|
||||||
|
var needIndex = make(map[K]int)
|
||||||
|
for i, k := range key {
|
||||||
|
v, ok := m.Get(c, k)
|
||||||
|
if !ok {
|
||||||
|
needIndex[k] = i
|
||||||
|
} else {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(needIndex) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
call := func() {
|
||||||
|
l := m.muFn(c, m.mux, key...)
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
|
vv, ok := m.Get(c, k)
|
||||||
|
if ok {
|
||||||
|
r[k] = vv
|
||||||
|
delete(needIndex, k)
|
||||||
|
return k, false
|
||||||
|
}
|
||||||
|
return k, true
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(needFlushs) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rr, er := m.batchCacheFn(c, needFlushs, params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k := range needIndex {
|
||||||
|
v, ok := rr[k]
|
||||||
|
if ok {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
m.Set(c, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel := context.WithTimeout(c, timeout)
|
||||||
|
defer cancel()
|
||||||
|
done := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
call()
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
||||||
|
return nil, err
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
|
||||||
|
var res = make([]V, 0, len(key))
|
||||||
|
var needIndex = make(map[K]int)
|
||||||
|
for i, k := range key {
|
||||||
|
v, ok := m.Get(c, k)
|
||||||
|
if !ok {
|
||||||
|
needIndex[k] = i
|
||||||
|
}
|
||||||
|
res = append(res, v)
|
||||||
|
}
|
||||||
|
if len(needIndex) < 1 {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
call := func() {
|
||||||
|
l := m.muFn(c, m.mux, key...)
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
|
vv, ok := m.Get(c, k)
|
||||||
|
if ok {
|
||||||
|
res[needIndex[k]] = vv
|
||||||
|
delete(needIndex, k)
|
||||||
|
return k, false
|
||||||
|
}
|
||||||
|
return k, true
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(needFlushs) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r, er := m.batchCacheFn(c, needFlushs, params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, i := range needIndex {
|
||||||
|
v, ok := r[k]
|
||||||
|
if ok {
|
||||||
|
res[i] = v
|
||||||
|
m.Set(c, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel := context.WithTimeout(c, timeout)
|
||||||
|
defer cancel()
|
||||||
|
done := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
call()
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
||||||
|
return nil, err
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call()
|
||||||
|
}
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapCache[K, V]) ClearExpired(ctx context.Context) {
|
func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
|
||||||
m.mux.Lock()
|
cc := e
|
||||||
defer m.mux.Unlock()
|
return func(ctx context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
|
||||||
m.data.ClearExpired(ctx, m.expireTime)
|
var res = make([]V, 0, len(key))
|
||||||
|
var needIndex = make(map[K]int)
|
||||||
|
var err error
|
||||||
|
mm, err := cc.Gets(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var flushKeys []K
|
||||||
|
for i, k := range key {
|
||||||
|
v, ok := mm[k]
|
||||||
|
if !ok {
|
||||||
|
flushKeys = append(flushKeys, k)
|
||||||
|
needIndex[k] = i
|
||||||
|
var vv V
|
||||||
|
v = vv
|
||||||
|
}
|
||||||
|
res = append(res, v)
|
||||||
|
}
|
||||||
|
if len(needIndex) < 1 {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
call := func() {
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
mmm, er := cc.Gets(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
|
return k, true
|
||||||
|
}))
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range mmm {
|
||||||
|
res[needIndex[k]] = v
|
||||||
|
delete(needIndex, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(needIndex) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r, er := m.batchCacheFn(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
|
||||||
|
return k, true
|
||||||
|
}), params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cc.Sets(ctx, r)
|
||||||
|
|
||||||
|
for k, i := range needIndex {
|
||||||
|
v, ok := r[k]
|
||||||
|
if ok {
|
||||||
|
res[i] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
done := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
call()
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
|
||||||
|
return nil, err
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
39
cache/map_test.go
vendored
39
cache/map_test.go
vendored
@ -12,27 +12,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ca MapCache[string, string]
|
var ca MapCache[string, string]
|
||||||
var fn func(a ...any) (string, error)
|
var fn MapSingleFn[string, string]
|
||||||
var batchFn func(a ...any) (map[string]string, error)
|
var batchFn MapBatchFn[string, string]
|
||||||
var ct context.Context
|
var ct context.Context
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
fn = func(a ...any) (string, error) {
|
fn = func(ctx context.Context, aa string, a ...any) (string, error) {
|
||||||
aa := a[1].(string)
|
|
||||||
return strings.Repeat(aa, 2), nil
|
return strings.Repeat(aa, 2), nil
|
||||||
}
|
}
|
||||||
ct = context.Background()
|
ct = context.Background()
|
||||||
batchFn = func(a ...any) (map[string]string, error) {
|
batchFn = func(ctx context.Context, arr []string, a ...any) (map[string]string, error) {
|
||||||
fmt.Println(a)
|
fmt.Println(a)
|
||||||
arr := a[1].([]string)
|
return slice.FilterAndToMap(arr, func(t string, _ int) (string, string, bool) {
|
||||||
return slice.FilterAndToMap(arr, func(t string) (string, string, bool) {
|
|
||||||
return t, strings.Repeat(t, 2), true
|
return t, strings.Repeat(t, 2), true
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
ca = *NewMemoryMapCacheByFn[string, string](fn, time.Second*2)
|
|
||||||
ca.SetCacheBatchFn(batchFn)
|
|
||||||
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
|
|
||||||
_, _ = ca.GetCache(ct, "bb", time.Second, ct, "bb")
|
|
||||||
}
|
}
|
||||||
func TestMapCache_ClearExpired(t *testing.T) {
|
func TestMapCache_ClearExpired(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -72,7 +67,9 @@ func TestMapCache_Flush(t *testing.T) {
|
|||||||
m MapCache[K, V]
|
m MapCache[K, V]
|
||||||
args args
|
args args
|
||||||
}
|
}
|
||||||
ca := *NewMemoryMapCacheByFn[string, string](fn, time.Second)
|
ca := *NewMapCache[string, string](NewMemoryMapCache[string, string](func() time.Duration {
|
||||||
|
return time.Second
|
||||||
|
}), fn, nil, nil, nil)
|
||||||
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
|
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
|
||||||
tests := []testCase[string, string]{
|
tests := []testCase[string, string]{
|
||||||
{
|
{
|
||||||
@ -293,7 +290,7 @@ func TestMapCache_Set(t *testing.T) {
|
|||||||
|
|
||||||
func TestMapCache_SetCacheBatchFn(t *testing.T) {
|
func TestMapCache_SetCacheBatchFn(t *testing.T) {
|
||||||
type args[K comparable, V any] struct {
|
type args[K comparable, V any] struct {
|
||||||
fn func(...any) (map[K]V, error)
|
fn MapBatchFn[K, V]
|
||||||
}
|
}
|
||||||
type testCase[K comparable, V any] struct {
|
type testCase[K comparable, V any] struct {
|
||||||
name string
|
name string
|
||||||
@ -315,19 +312,19 @@ func TestMapCache_SetCacheBatchFn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapCache_SetCacheFunc(t *testing.T) {
|
func TestMapCache_SetCacheFunc(t *testing.T) {
|
||||||
type args[V any] struct {
|
type args[K comparable, V any] struct {
|
||||||
fn func(...any) (V, error)
|
fn MapSingleFn[K, V]
|
||||||
}
|
}
|
||||||
type testCase[K comparable, V any] struct {
|
type testCase[K comparable, V any] struct {
|
||||||
name string
|
name string
|
||||||
m MapCache[K, V]
|
m MapCache[K, V]
|
||||||
args args[V]
|
args args[K, V]
|
||||||
}
|
}
|
||||||
tests := []testCase[string, string]{
|
tests := []testCase[string, string]{
|
||||||
{
|
{
|
||||||
name: "t1",
|
name: "t1",
|
||||||
m: ca,
|
m: ca,
|
||||||
args: args[string]{fn: fn},
|
args: args[string, string]{fn: fn},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -355,12 +352,12 @@ func TestMapCache_Ttl(t *testing.T) {
|
|||||||
name: "t1",
|
name: "t1",
|
||||||
m: ca,
|
m: ca,
|
||||||
args: args[string]{ct, "aa"},
|
args: args[string]{ct, "aa"},
|
||||||
want: ca.expireTime - tx.Sub(txx),
|
want: ca.GetExpireTime(ct) - tx.Sub(txx),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
fmt.Printf("过期时间=%v \nttl=%v \n当前时间 =%v\n最后设置时间=%v\n当时时间-最后设置时间=%v ", ca.expireTime, ca.Ttl(ct, "aa"), tx, txx, tx.Sub(txx))
|
fmt.Printf("过期时间=%v \nttl=%v \n当前时间 =%v\n最后设置时间=%v\n当时时间-最后设置时间=%v ", ca.GetExpireTime(ct), ca.Ttl(ct, "aa"), tx, txx, tx.Sub(txx))
|
||||||
if got := tt.m.Ttl(tt.args.ct, tt.args.k); got != tt.want {
|
if got := tt.m.Ttl(tt.args.ct, tt.args.k); got != tt.want {
|
||||||
t.Errorf("Ttl() = %v, want %v", got, tt.want)
|
t.Errorf("Ttl() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
@ -370,7 +367,7 @@ func TestMapCache_Ttl(t *testing.T) {
|
|||||||
|
|
||||||
func TestMapCache_setCacheFn(t *testing.T) {
|
func TestMapCache_setCacheFn(t *testing.T) {
|
||||||
type args[K comparable, V any] struct {
|
type args[K comparable, V any] struct {
|
||||||
fn func(...any) (map[K]V, error)
|
fn MapBatchFn[K, V]
|
||||||
}
|
}
|
||||||
type testCase[K comparable, V any] struct {
|
type testCase[K comparable, V any] struct {
|
||||||
name string
|
name string
|
||||||
@ -387,7 +384,7 @@ func TestMapCache_setCacheFn(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
ca.cacheFunc = nil
|
ca.cacheFunc = nil
|
||||||
tt.m.setCacheFn(tt.args.fn)
|
tt.m.setDefaultCacheFn(tt.args.fn)
|
||||||
fmt.Println(ca.GetCache(ct, "xx", time.Second, ct, "xx"))
|
fmt.Println(ca.GetCache(ct, "xx", time.Second, ct, "xx"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
69
cache/memorymapcache.go
vendored
69
cache/memorymapcache.go
vendored
@ -2,37 +2,22 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MemoryMapCache[K comparable, V any] struct {
|
type MemoryMapCache[K comparable, V any] struct {
|
||||||
*safety.Map[K, mapVal[V]]
|
*safety.Map[K, mapVal[V]]
|
||||||
|
expireTime func() time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryMapCacheByFn[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
|
func NewMemoryMapCache[K comparable, V any](expireTime func() time.Duration) *MemoryMapCache[K, V] {
|
||||||
return &MapCache[K, V]{
|
return &MemoryMapCache[K, V]{
|
||||||
data: NewMemoryMapCache[K, V](),
|
Map: safety.NewMap[K, mapVal[V]](),
|
||||||
cacheFunc: fn,
|
|
||||||
expireTime: expireTime,
|
expireTime: expireTime,
|
||||||
mux: sync.Mutex{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func NewMemoryMapCacheByBatchFn[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
|
|
||||||
r := &MapCache[K, V]{
|
|
||||||
data: NewMemoryMapCache[K, V](),
|
|
||||||
batchCacheFn: fn,
|
|
||||||
expireTime: expireTime,
|
|
||||||
mux: sync.Mutex{},
|
|
||||||
}
|
|
||||||
r.setCacheFn(fn)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemoryMapCache[K comparable, V any]() *MemoryMapCache[K, V] {
|
|
||||||
return &MemoryMapCache[K, V]{Map: safety.NewMap[K, mapVal[V]]()}
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapVal[T any] struct {
|
type mapVal[T any] struct {
|
||||||
setTime time.Time
|
setTime time.Time
|
||||||
@ -40,15 +25,28 @@ type mapVal[T any] struct {
|
|||||||
data T
|
data T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MemoryMapCache[K, V]) SetExpiredTime(f func() time.Duration) {
|
||||||
|
m.expireTime = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemoryMapCache[K, V]) GetExpireTime(_ context.Context) time.Duration {
|
||||||
|
return m.expireTime()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) Get(_ context.Context, key K) (r V, ok bool) {
|
func (m *MemoryMapCache[K, V]) Get(_ context.Context, key K) (r V, ok bool) {
|
||||||
v, ok := m.Load(key)
|
v, ok := m.Load(key)
|
||||||
if ok {
|
if !ok {
|
||||||
return v.data, true
|
return
|
||||||
|
}
|
||||||
|
r = v.data
|
||||||
|
t := m.expireTime() - time.Now().Sub(v.setTime)
|
||||||
|
if t <= 0 {
|
||||||
|
ok = false
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) Set(_ context.Context, key K, val V, _ time.Duration) {
|
func (m *MemoryMapCache[K, V]) Set(_ context.Context, key K, val V) {
|
||||||
v, ok := m.Load(key)
|
v, ok := m.Load(key)
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
if ok {
|
if ok {
|
||||||
@ -65,12 +63,12 @@ func (m *MemoryMapCache[K, V]) Set(_ context.Context, key K, val V, _ time.Durat
|
|||||||
m.Store(key, v)
|
m.Store(key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) Ttl(_ context.Context, key K, expire time.Duration) time.Duration {
|
func (m *MemoryMapCache[K, V]) Ttl(_ context.Context, key K) time.Duration {
|
||||||
v, ok := m.Load(key)
|
v, ok := m.Load(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return time.Duration(-1)
|
return time.Duration(-1)
|
||||||
}
|
}
|
||||||
return expire - time.Now().Sub(v.setTime)
|
return m.expireTime() - time.Now().Sub(v.setTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int {
|
func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int {
|
||||||
@ -85,17 +83,28 @@ func (m *MemoryMapCache[K, V]) Flush(context.Context) {
|
|||||||
m.Map.Flush()
|
m.Map.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) Delete(_ context.Context, key K) {
|
func (m *MemoryMapCache[K, V]) Del(_ context.Context, keys ...K) {
|
||||||
m.Map.Delete(key)
|
for _, key := range keys {
|
||||||
|
m.Map.Delete(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryMapCache[K, V]) ClearExpired(_ context.Context, expire time.Duration) {
|
func (m *MemoryMapCache[K, V]) ClearExpired(_ context.Context) {
|
||||||
now := time.Duration(time.Now().UnixNano())
|
now := time.Duration(time.Now().UnixNano())
|
||||||
|
|
||||||
m.Range(func(k K, v mapVal[V]) bool {
|
m.Range(func(k K, v mapVal[V]) bool {
|
||||||
if now > time.Duration(v.setTime.UnixNano())+expire {
|
if now > time.Duration(v.setTime.UnixNano())+m.expireTime() {
|
||||||
m.Map.Delete(k)
|
m.Map.Delete(k)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MemoryMapCache[K, V]) Refresh(_ context.Context, k K, a ...any) {
|
||||||
|
v, ok := m.Load(k)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := helper.ParseArgs(time.Now(), a...)
|
||||||
|
v.setTime = t
|
||||||
|
m.Store(k, v)
|
||||||
|
}
|
||||||
|
14
cache/memorymapcache_test.go
vendored
14
cache/memorymapcache_test.go
vendored
@ -15,7 +15,7 @@ var ttt time.Time
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
mm = *NewMemoryMapCache[string, string]()
|
mm = *NewMemoryMapCache[string, string](3 * time.Second)
|
||||||
ttt = time.Now()
|
ttt = time.Now()
|
||||||
mm.Store("aa", mapVal[string]{
|
mm.Store("aa", mapVal[string]{
|
||||||
setTime: ttt,
|
setTime: ttt,
|
||||||
@ -53,7 +53,7 @@ func TestMemoryMapCache_ClearExpired(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
fmt.Println(tt.m)
|
fmt.Println(tt.m)
|
||||||
tt.m.ClearExpired(tt.args.in0, tt.args.expire)
|
tt.m.ClearExpired(tt.args.in0)
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
fmt.Println(tt.m)
|
fmt.Println(tt.m)
|
||||||
})
|
})
|
||||||
@ -83,7 +83,7 @@ func TestMemoryMapCache_Delete(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
fmt.Println(mm.Get(ctx, "aa"))
|
fmt.Println(mm.Get(ctx, "aa"))
|
||||||
tt.m.Delete(tt.args.in0, tt.args.key)
|
tt.m.Del(tt.args.in0, tt.args.key)
|
||||||
fmt.Println(mm.Get(ctx, "aa"))
|
fmt.Println(mm.Get(ctx, "aa"))
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -111,7 +111,7 @@ func TestMemoryMapCache_Flush(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.m.Flush(tt.args.in0)
|
tt.m.Flush(tt.args.in0)
|
||||||
mm.Set(ctx, "aa", "xx", time.Second)
|
mm.Set(ctx, "aa", "xx")
|
||||||
fmt.Println(mm.Get(ctx, "aa"))
|
fmt.Println(mm.Get(ctx, "aa"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ func TestMemoryMapCache_Set(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.m.Set(tt.args.in0, tt.args.key, tt.args.val, tt.args.in3)
|
tt.m.Set(tt.args.in0, tt.args.key, tt.args.val)
|
||||||
fmt.Println(tt.m.Get(ctx, tt.args.key))
|
fmt.Println(tt.m.Get(ctx, tt.args.key))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ func TestMemoryMapCache_Ttl(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := tt.m.Ttl(tt.args.in0, tt.args.key, tt.args.expire); got != tt.want {
|
if got := tt.m.Ttl(tt.args.in0, tt.args.key); got != tt.want {
|
||||||
t.Errorf("Ttl() = %v, want %v", got, tt.want)
|
t.Errorf("Ttl() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -227,7 +227,7 @@ func TestMemoryMapCache_Ver(t *testing.T) {
|
|||||||
args args[K]
|
args args[K]
|
||||||
want int
|
want int
|
||||||
}
|
}
|
||||||
mm.Set(ctx, "aa", "ff", time.Second)
|
mm.Set(ctx, "aa", "ff")
|
||||||
tests := []testCase[string, string]{
|
tests := []testCase[string, string]{
|
||||||
{
|
{
|
||||||
name: "t1",
|
name: "t1",
|
||||||
|
186
cache/pagination.go
vendored
Normal file
186
cache/pagination.go
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pagination[K comparable, V any] struct {
|
||||||
|
*MapCache[string, helper.PaginationData[V]]
|
||||||
|
maxNum func() int
|
||||||
|
isSwitch *safety.Map[K, bool]
|
||||||
|
dbFn func(ctx context.Context, k K, page, limit, totalRaw int, a ...any) ([]V, int, error)
|
||||||
|
localFn func(ctx context.Context, data []V, k K, page, limit int, a ...any) ([]V, int, error)
|
||||||
|
batchFetchNum func() int
|
||||||
|
localKeyFn func(K K, a ...any) string
|
||||||
|
dbKeyFn func(K K, a ...any) string
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
var switchDb = errors.New("switch Db")
|
||||||
|
|
||||||
|
type DbFn[K comparable, V any] func(ctx context.Context, k K, page, limit, totalRaw int, a ...any) ([]V, int, error)
|
||||||
|
|
||||||
|
type LocalFn[K comparable, V any] func(ctx context.Context, data []V, k K, page, limit int, a ...any) ([]V, int, error)
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) IsSwitchDB(k K) bool {
|
||||||
|
v, _ := p.isSwitch.Load(k)
|
||||||
|
return v == true
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPagination[K comparable, V any](m *MapCache[string, helper.PaginationData[V]], maxNum func() int,
|
||||||
|
dbFn DbFn[K, V], localFn LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string,
|
||||||
|
batchFetchNum func() int, name string) *Pagination[K, V] {
|
||||||
|
if dbKeyFn == nil {
|
||||||
|
dbKeyFn = func(k K, a ...any) string {
|
||||||
|
s := str.NewBuilder()
|
||||||
|
for _, v := range append([]any{k}, a...) {
|
||||||
|
s.Sprintf("%v|", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimRight(s.String(), "|")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if localKeyFn == nil {
|
||||||
|
localKeyFn = func(k K, a ...any) string {
|
||||||
|
return fmt.Sprintf("%v", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Pagination[K, V]{
|
||||||
|
MapCache: m,
|
||||||
|
maxNum: maxNum,
|
||||||
|
isSwitch: safety.NewMap[K, bool](),
|
||||||
|
dbFn: dbFn,
|
||||||
|
localFn: localFn,
|
||||||
|
batchFetchNum: batchFetchNum,
|
||||||
|
name: name,
|
||||||
|
dbKeyFn: dbKeyFn,
|
||||||
|
localKeyFn: localKeyFn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) Pagination(ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) {
|
||||||
|
if is, _ := p.isSwitch.Load(k); is {
|
||||||
|
return p.paginationByDB(ctx, timeout, k, page, limit, 0, a...)
|
||||||
|
}
|
||||||
|
data, total, err := p.paginationByLocal(ctx, timeout, k, page, limit, a...)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, switchDb) {
|
||||||
|
p.isSwitch.Store(k, true)
|
||||||
|
err = nil
|
||||||
|
return p.paginationByDB(ctx, timeout, k, page, limit, total, a...)
|
||||||
|
}
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return data, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) paginationByLocal(ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) {
|
||||||
|
key := p.localKeyFn(k)
|
||||||
|
data, ok := p.Get(ctx, key)
|
||||||
|
if ok {
|
||||||
|
if p.increaseUpdate != nil && p.refresh != nil {
|
||||||
|
dat, err := p.increaseUpdates(ctx, timeout, data, key, a...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if dat.TotalRaw >= p.maxNum() {
|
||||||
|
return nil, 0, switchDb
|
||||||
|
}
|
||||||
|
data = dat
|
||||||
|
}
|
||||||
|
return p.localFn(ctx, data.Data, k, page, limit, a...)
|
||||||
|
}
|
||||||
|
p.mux.Lock()
|
||||||
|
defer p.mux.Unlock()
|
||||||
|
data, ok = p.Get(ctx, key)
|
||||||
|
if ok {
|
||||||
|
return data.Data, data.TotalRaw, nil
|
||||||
|
}
|
||||||
|
batchNum := p.batchFetchNum()
|
||||||
|
da, totalRaw, err := p.fetchDb(ctx, timeout, k, 1, 0, 0, a...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if totalRaw < 1 {
|
||||||
|
data.Data = nil
|
||||||
|
data.TotalRaw = 0
|
||||||
|
p.Set(ctx, key, data)
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
if totalRaw >= p.maxNum() {
|
||||||
|
return nil, totalRaw, switchDb
|
||||||
|
}
|
||||||
|
totalPage := number.DivideCeil(totalRaw, batchNum)
|
||||||
|
for i := 1; i <= totalPage; i++ {
|
||||||
|
daa, _, err := p.fetchDb(ctx, timeout, k, i, batchNum, totalRaw, a...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
da = append(da, daa...)
|
||||||
|
}
|
||||||
|
data.Data = da
|
||||||
|
data.TotalRaw = totalRaw
|
||||||
|
p.Set(ctx, key, data)
|
||||||
|
|
||||||
|
return p.localFn(ctx, data.Data, k, page, limit, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) dbGet(ctx context.Context, key string) (helper.PaginationData[V], bool) {
|
||||||
|
data, ok := p.Get(ctx, key)
|
||||||
|
if ok && p.increaseUpdate != nil && p.increaseUpdate.CycleTime() > p.GetExpireTime(ctx)-p.Ttl(ctx, key) {
|
||||||
|
return data, true
|
||||||
|
}
|
||||||
|
return data, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) paginationByDB(ctx context.Context, timeout time.Duration, k K, page, limit, totalRaw int, a ...any) ([]V, int, error) {
|
||||||
|
key := p.dbKeyFn(k, append([]any{page, limit}, a...)...)
|
||||||
|
data, ok := p.dbGet(ctx, key)
|
||||||
|
if ok {
|
||||||
|
return data.Data, data.TotalRaw, nil
|
||||||
|
}
|
||||||
|
p.mux.Lock()
|
||||||
|
defer p.mux.Unlock()
|
||||||
|
data, ok = p.dbGet(ctx, key)
|
||||||
|
if ok {
|
||||||
|
return data.Data, data.TotalRaw, nil
|
||||||
|
}
|
||||||
|
dat, total, err := p.fetchDb(ctx, timeout, k, page, limit, totalRaw, a...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
data.Data, data.TotalRaw = dat, total
|
||||||
|
p.Set(ctx, key, data)
|
||||||
|
return data.Data, data.TotalRaw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pagination[K, V]) fetchDb(ctx context.Context, timeout time.Duration, k K, page, limit, totalRaw int, a ...any) ([]V, int, error) {
|
||||||
|
var data helper.PaginationData[V]
|
||||||
|
var err error
|
||||||
|
fn := func() {
|
||||||
|
da, total, er := p.dbFn(ctx, k, page, limit, totalRaw, a...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data.Data = da
|
||||||
|
data.TotalRaw = total
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
er := helper.RunFnWithTimeout(ctx, timeout, fn, fmt.Sprintf("fetch %s-[%v]-page[%d]-limit[%d] from db fail", p.name, k, page, limit))
|
||||||
|
if err == nil && er != nil {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
return data.Data, data.TotalRaw, err
|
||||||
|
}
|
544
cache/reload/reload.go
vendored
Normal file
544
cache/reload/reload.go
vendored
Normal file
@ -0,0 +1,544 @@
|
|||||||
|
package reload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Queue struct {
|
||||||
|
Fn func()
|
||||||
|
Order float64
|
||||||
|
Name string
|
||||||
|
AutoExec bool
|
||||||
|
Once bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var mut = &sync.Mutex{}
|
||||||
|
|
||||||
|
func GetGlobeMutex() *sync.Mutex {
|
||||||
|
return mut
|
||||||
|
}
|
||||||
|
|
||||||
|
var reloadQueues = safety.NewSlice[Queue]()
|
||||||
|
|
||||||
|
var reloadQueueHookFns = safety.NewVar[[]func(queue Queue) (Queue, bool)](nil)
|
||||||
|
|
||||||
|
var setFnVal = safety.NewMap[string, any]()
|
||||||
|
|
||||||
|
func DeleteReloadQueue(names ...string) {
|
||||||
|
hooks := reloadQueueHookFns.Load()
|
||||||
|
for _, name := range names {
|
||||||
|
hooks = append(hooks, func(queue Queue) (Queue, bool) {
|
||||||
|
if name != queue.Name {
|
||||||
|
return queue, true
|
||||||
|
}
|
||||||
|
return queue, false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
reloadQueueHookFns.Store(hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HookReloadQueue(fn func(queue Queue) (Queue, bool)) {
|
||||||
|
a := reloadQueueHookFns.Load()
|
||||||
|
a = append(a, fn)
|
||||||
|
reloadQueueHookFns.Store(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetReloadFn(name string) func() {
|
||||||
|
hookQueue()
|
||||||
|
i, queue := slice.SearchFirst(reloadQueues.Load(), func(queue Queue) bool {
|
||||||
|
return queue.Name == name
|
||||||
|
})
|
||||||
|
if i > -1 && queue.Fn != nil {
|
||||||
|
return queue.Fn
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hookQueue() {
|
||||||
|
hooks := reloadQueueHookFns.Load()
|
||||||
|
queues := reloadQueues.Load()
|
||||||
|
length := len(queues)
|
||||||
|
for _, hook := range hooks {
|
||||||
|
queues = slice.FilterAndMap(queues, hook)
|
||||||
|
}
|
||||||
|
if len(queues) != length {
|
||||||
|
reloadQueues.Store(queues)
|
||||||
|
}
|
||||||
|
reloadQueueHookFns.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SafetyVar[T, A any] struct {
|
||||||
|
Val *safety.Var[Val[T]]
|
||||||
|
Mutex sync.Mutex
|
||||||
|
}
|
||||||
|
type Val[T any] struct {
|
||||||
|
V T
|
||||||
|
Ok bool
|
||||||
|
}
|
||||||
|
type SafetyMap[K comparable, V, A any] struct {
|
||||||
|
Val *safety.Map[K, V]
|
||||||
|
Mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var safetyMaps = safety.NewMap[string, any]()
|
||||||
|
var safetyMapLock = sync.Mutex{}
|
||||||
|
|
||||||
|
var deleteMapFn = safety.NewMap[string, func(any)]()
|
||||||
|
|
||||||
|
// GetValMap can get stored map value with namespace which called BuildSafetyMap, BuildMapFnWithConfirm, BuildMapFn, BuildMapFnWithAnyParams
|
||||||
|
func GetValMap[K comparable, V any](namespace string) (*safety.Map[K, V], bool) {
|
||||||
|
m, ok := safetyMaps.Load(namespace)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
v, ok := m.(*safety.Map[K, V])
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteMapVal[T any](namespace string, key ...T) {
|
||||||
|
fn, ok := deleteMapFn.Load(namespace)
|
||||||
|
if !ok || len(key) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fn(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reloads(namespaces ...string) {
|
||||||
|
mut.Lock()
|
||||||
|
defer mut.Unlock()
|
||||||
|
hookQueue()
|
||||||
|
queues := reloadQueues.Load()
|
||||||
|
for _, name := range namespaces {
|
||||||
|
i, queue := slice.SearchFirst(queues, func(queue Queue) bool {
|
||||||
|
return name == queue.Name
|
||||||
|
})
|
||||||
|
if i < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
queue.Fn()
|
||||||
|
if queue.Once {
|
||||||
|
slice.Delete(&queues, i)
|
||||||
|
reloadQueues.Store(queues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildMapFnWithConfirm same as BuildMapFn
|
||||||
|
func BuildMapFnWithConfirm[K comparable, V, A any](namespace string, fn func(A) (V, bool), a ...any) func(key K, args A) V {
|
||||||
|
m := BuildSafetyMap[K, V, A](namespace, a...)
|
||||||
|
return func(key K, a A) V {
|
||||||
|
v, ok := m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
m.Mutex.Lock()
|
||||||
|
defer m.Mutex.Unlock()
|
||||||
|
v, ok = m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
v, ok = fn(a)
|
||||||
|
if ok {
|
||||||
|
m.Val.Store(key, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildMapFn build given fn with a new fn which returned value can be saved and flushed when called Reload or Reloads
|
||||||
|
// with namespace
|
||||||
|
//
|
||||||
|
// if give a float then can be reloaded early or lately, more bigger more earlier
|
||||||
|
//
|
||||||
|
// if give a bool false will not flushed when called Reload, then can called GetValMap to flush manually
|
||||||
|
func BuildMapFn[K comparable, V, A any](namespace string, fn func(A) V, a ...any) func(key K, args A) V {
|
||||||
|
m := BuildSafetyMap[K, V, A](namespace, a...)
|
||||||
|
return func(key K, a A) V {
|
||||||
|
v, ok := m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
m.Mutex.Lock()
|
||||||
|
defer m.Mutex.Unlock()
|
||||||
|
v, ok = m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
v = fn(a)
|
||||||
|
m.Val.Store(key, v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildMapFnWithAnyParams same as BuildMapFn use multiple params
|
||||||
|
func BuildMapFnWithAnyParams[K comparable, V any](namespace string, fn func(...any) V, a ...any) func(key K, a ...any) V {
|
||||||
|
m := BuildSafetyMap[K, V, any](namespace, a...)
|
||||||
|
return func(key K, a ...any) V {
|
||||||
|
v, ok := m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
m.Mutex.Lock()
|
||||||
|
defer m.Mutex.Unlock()
|
||||||
|
v, ok = m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
v = fn(a...)
|
||||||
|
m.Val.Store(key, v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildSafetyMap[K comparable, V, A any](namespace string, args ...any) *SafetyMap[K, V, A] {
|
||||||
|
vv, ok := safetyMaps.Load(namespace)
|
||||||
|
var m *SafetyMap[K, V, A]
|
||||||
|
if ok {
|
||||||
|
m = vv.(*SafetyMap[K, V, A])
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
safetyMapLock.Lock()
|
||||||
|
defer safetyMapLock.Unlock()
|
||||||
|
vv, ok = safetyMaps.Load(namespace)
|
||||||
|
if ok {
|
||||||
|
m = vv.(*SafetyMap[K, V, A])
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
m = &SafetyMap[K, V, A]{safety.NewMap[K, V](), sync.Mutex{}}
|
||||||
|
args = append(args, namespace)
|
||||||
|
deleteMapFn.Store(namespace, func(a any) {
|
||||||
|
k, ok := a.([]K)
|
||||||
|
if !ok && len(k) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, key := range k {
|
||||||
|
m.Val.Delete(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Append(m.Val.Flush, args...)
|
||||||
|
safetyMaps.Store(namespace, m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAnyValMapBy[K comparable, V, A any](namespace string, key K, a A, fn func(A) (V, bool), args ...any) V {
|
||||||
|
m := BuildSafetyMap[K, V, A](namespace, args...)
|
||||||
|
v, ok := m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
m.Mutex.Lock()
|
||||||
|
defer m.Mutex.Unlock()
|
||||||
|
v, ok = m.Val.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
v, ok = fn(a)
|
||||||
|
if ok {
|
||||||
|
m.Val.Store(key, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildAnyVal[T, A any](namespace string, args ...any) *SafetyVar[T, A] {
|
||||||
|
var vv *SafetyVar[T, A]
|
||||||
|
vvv, ok := safetyMaps.Load(namespace)
|
||||||
|
if ok {
|
||||||
|
vv = vvv.(*SafetyVar[T, A])
|
||||||
|
}
|
||||||
|
safetyMapLock.Lock()
|
||||||
|
defer safetyMapLock.Unlock()
|
||||||
|
vvv, ok = safetyMaps.Load(namespace)
|
||||||
|
if ok {
|
||||||
|
vv = vvv.(*SafetyVar[T, A])
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
v := Val[T]{}
|
||||||
|
vv = &SafetyVar[T, A]{safety.NewVar(v), sync.Mutex{}}
|
||||||
|
args = append(args, namespace)
|
||||||
|
Append(vv.Val.Flush, args...)
|
||||||
|
safetyMaps.Store(namespace, vv)
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAnyValBys[T, A any](namespace string, a A, fn func(A) (T, bool), args ...any) T {
|
||||||
|
var vv = BuildAnyVal[T, A](namespace, args...)
|
||||||
|
v := vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
vv.Mutex.Lock()
|
||||||
|
defer vv.Mutex.Unlock()
|
||||||
|
v = vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
v.V, v.Ok = fn(a)
|
||||||
|
vv.Val.Store(v)
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildValFnWithConfirm same as BuildValFn
|
||||||
|
//
|
||||||
|
// if give a int and value bigger than 1 will be a times which built fn called return false
|
||||||
|
func BuildValFnWithConfirm[T, A any](namespace string, fn func(A) (T, bool), args ...any) func(A) T {
|
||||||
|
var vv = BuildAnyVal[T, A](namespace, args...)
|
||||||
|
tryTimes := helper.ParseArgs(1, args...)
|
||||||
|
var counter int64
|
||||||
|
if tryTimes > 1 {
|
||||||
|
Append(func() {
|
||||||
|
atomic.StoreInt64(&counter, 0)
|
||||||
|
}, str.Join("reload-valFn-counter-", namespace))
|
||||||
|
}
|
||||||
|
return func(a A) T {
|
||||||
|
v := vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
vv.Mutex.Lock()
|
||||||
|
defer vv.Mutex.Unlock()
|
||||||
|
v = vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
v.V, v.Ok = fn(a)
|
||||||
|
if v.Ok {
|
||||||
|
vv.Val.Store(v)
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
if atomic.LoadInt64(&counter) <= 1 {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
atomic.AddInt64(&counter, 1)
|
||||||
|
if atomic.LoadInt64(&counter) >= int64(tryTimes) {
|
||||||
|
v.Ok = true
|
||||||
|
vv.Val.Store(v)
|
||||||
|
}
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildValFn build given fn a new fn which return value can be saved and flushed when called Reload or Reloads
|
||||||
|
// with namespace.
|
||||||
|
//
|
||||||
|
// note: namespace should be not same as BuildMapFn and related fn, they stored same safety.Map[string,any].
|
||||||
|
//
|
||||||
|
// if give a float then can be reloaded early or lately, more bigger more earlier
|
||||||
|
//
|
||||||
|
// if give a bool false will not flushed when called Reload, but can call GetValMap or Reloads to flush manually
|
||||||
|
func BuildValFn[T, A any](namespace string, fn func(A) T, args ...any) func(A) T {
|
||||||
|
var vv = BuildAnyVal[T, A](namespace, args...)
|
||||||
|
return func(a A) T {
|
||||||
|
v := vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
vv.Mutex.Lock()
|
||||||
|
defer vv.Mutex.Unlock()
|
||||||
|
v = vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
v.V = fn(a)
|
||||||
|
v.Ok = true
|
||||||
|
vv.Val.Store(v)
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildValFnWithAnyParams same as BuildValFn use multiple params
|
||||||
|
func BuildValFnWithAnyParams[T any](namespace string, fn func(...any) T, args ...any) func(...any) T {
|
||||||
|
var vv = BuildAnyVal[T, any](namespace, args...)
|
||||||
|
return func(a ...any) T {
|
||||||
|
v := vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
vv.Mutex.Lock()
|
||||||
|
defer vv.Mutex.Unlock()
|
||||||
|
v = vv.Val.Load()
|
||||||
|
if v.Ok {
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
v.V = fn(a...)
|
||||||
|
v.Ok = true
|
||||||
|
vv.Val.Store(v)
|
||||||
|
return v.V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vars get default value and whenever reloaded assign default value
|
||||||
|
//
|
||||||
|
// args same as Append
|
||||||
|
//
|
||||||
|
// if give a name, then can be flushed by calls Reloads
|
||||||
|
//
|
||||||
|
// if give a float then can be reloaded early or lately, more bigger more earlier
|
||||||
|
//
|
||||||
|
// if give a bool false will not flushed when called Reload, but can call GetValMap or Reloads to flush manually
|
||||||
|
//
|
||||||
|
// if give a int 1 will only execute once when called Reload or Reloads and then delete the flush fn
|
||||||
|
func Vars[T any](defaults T, args ...any) *safety.Var[T] {
|
||||||
|
ss := safety.NewVar(defaults)
|
||||||
|
|
||||||
|
Append(func() {
|
||||||
|
ss.Store(defaults)
|
||||||
|
}, args...)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseArgs(a ...any) (ord float64, name string) {
|
||||||
|
if len(a) > 0 {
|
||||||
|
for _, arg := range a {
|
||||||
|
v, ok := arg.(float64)
|
||||||
|
if ok {
|
||||||
|
ord = v
|
||||||
|
}
|
||||||
|
vv, ok := arg.(string)
|
||||||
|
if ok {
|
||||||
|
name = vv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ord, name
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarsBy
|
||||||
|
//
|
||||||
|
// args same as Append
|
||||||
|
// if give a name, then can be flushed by calls Reloads
|
||||||
|
func VarsBy[T any](fn func() T, args ...any) *safety.Var[T] {
|
||||||
|
ss := safety.NewVar(fn())
|
||||||
|
Append(func() {
|
||||||
|
ss.Store(fn())
|
||||||
|
}, args...)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
func MapBy[K comparable, T any](fn func(*safety.Map[K, T]), args ...any) *safety.Map[K, T] {
|
||||||
|
m := safety.NewMap[K, T]()
|
||||||
|
if fn != nil {
|
||||||
|
fn(m)
|
||||||
|
}
|
||||||
|
Append(func() {
|
||||||
|
m.Flush()
|
||||||
|
if fn != nil {
|
||||||
|
fn(m)
|
||||||
|
}
|
||||||
|
}, args...)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeMap[K comparable, T any](args ...any) *safety.Map[K, T] {
|
||||||
|
m := safety.NewMap[K, T]()
|
||||||
|
Append(m.Flush, args...)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the func that will be called whenever Reload called
|
||||||
|
//
|
||||||
|
// if give a name, then can be called by called Reloads
|
||||||
|
//
|
||||||
|
// if give a float then can be called early or lately when called Reload, more bigger more earlier
|
||||||
|
//
|
||||||
|
// if give a bool false will not execute when called Reload, then can called Reloads to execute manually
|
||||||
|
//
|
||||||
|
// if give a int 1 will only execute once when called Reload or Reloads and then delete the Queue
|
||||||
|
func Append(fn func(), a ...any) {
|
||||||
|
ord, name := parseArgs(a...)
|
||||||
|
autoExec := helper.ParseArgs(true, a...)
|
||||||
|
once := helper.ParseArgs(0, a...)
|
||||||
|
queues := reloadQueues.Load()
|
||||||
|
queue := Queue{fn, ord, name, autoExec, once == 1}
|
||||||
|
if name != "" {
|
||||||
|
i, _ := slice.SearchFirst(queues, func(queue Queue) bool {
|
||||||
|
return queue.Name == name
|
||||||
|
})
|
||||||
|
if i > -1 {
|
||||||
|
queues[i] = queue
|
||||||
|
reloadQueues.Store(queues)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reloadQueues.Append(queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendOnceFn function and args same as Append, but func will execute only once when called Reload or Reloads and then will be deleted. Especially suitable for using to develop plugins, when uninstall plugin can clean or recover some progress's self relative data or behavior which was changed by plugin.
|
||||||
|
func AppendOnceFn(fn func(), a ...any) {
|
||||||
|
a = append([]any{1}, a...)
|
||||||
|
Append(fn, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reload() {
|
||||||
|
mut.Lock()
|
||||||
|
defer mut.Unlock()
|
||||||
|
deleteMapFn.Flush()
|
||||||
|
hookQueue()
|
||||||
|
queues := reloadQueues.Load()
|
||||||
|
length := len(queues)
|
||||||
|
slice.SimpleSort(queues, slice.DESC, func(t Queue) float64 {
|
||||||
|
return t.Order
|
||||||
|
})
|
||||||
|
for i, queue := range queues {
|
||||||
|
if !queue.AutoExec {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
queue.Fn()
|
||||||
|
if queue.Once {
|
||||||
|
slice.Delete(&queues, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length != len(queues) {
|
||||||
|
reloadQueues.Store(queues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Any[T any] struct {
|
||||||
|
fn func() T
|
||||||
|
v *safety.Var[T]
|
||||||
|
isManual *safety.Var[bool]
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFnVal build a new fn which can be set value by SetFnVal with name or set default value by given fn when called Reload
|
||||||
|
func BuildFnVal[T any](name string, t T, fn func() T) func() T {
|
||||||
|
if fn == nil {
|
||||||
|
fn = func() T {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t = fn()
|
||||||
|
}
|
||||||
|
p := safety.NewVar(t)
|
||||||
|
e := Any[T]{
|
||||||
|
fn: fn,
|
||||||
|
v: p,
|
||||||
|
isManual: safety.NewVar(false),
|
||||||
|
}
|
||||||
|
Append(func() {
|
||||||
|
if !e.isManual.Load() {
|
||||||
|
e.v.Store(fn())
|
||||||
|
}
|
||||||
|
}, str.Join("fnval-", name))
|
||||||
|
setFnVal.Store(name, e)
|
||||||
|
return func() T {
|
||||||
|
return e.v.Load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFnVal[T any](name string, val T, onlyManual bool) {
|
||||||
|
v, ok := setFnVal.Load(name)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vv, ok := v.(Any[T])
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if onlyManual && !vv.isManual.Load() {
|
||||||
|
vv.isManual.Store(true)
|
||||||
|
}
|
||||||
|
vv.v.Store(val)
|
||||||
|
}
|
47
cache/reload/reload_test.go
vendored
Normal file
47
cache/reload/reload_test.go
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package reload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFlushMapVal(t *testing.T) {
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
c := 0
|
||||||
|
v := GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) {
|
||||||
|
c++
|
||||||
|
return 33, true
|
||||||
|
})
|
||||||
|
fmt.Println(v)
|
||||||
|
DeleteMapVal("key", 2)
|
||||||
|
|
||||||
|
v = GetAnyValMapBy("key", 2, struct{}{}, func(a struct{}) (int, bool) {
|
||||||
|
fmt.Println("xxxxx")
|
||||||
|
return 33, true
|
||||||
|
})
|
||||||
|
fmt.Println(v)
|
||||||
|
Reloads("key")
|
||||||
|
v = GetAnyValMapBy[int, int, struct{}]("key", 2, struct{}{}, func(a struct{}) (int, bool) {
|
||||||
|
fmt.Println("yyyy")
|
||||||
|
return 33, true
|
||||||
|
})
|
||||||
|
fmt.Println(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAnyMapFnBys(t *testing.T) {
|
||||||
|
var i int
|
||||||
|
t.Run("t1", func(t *testing.T) {
|
||||||
|
v := BuildMapFnWithConfirm[int]("name", func(a int) (int, bool) {
|
||||||
|
i++
|
||||||
|
return a + 1, true
|
||||||
|
})
|
||||||
|
vv := v(1, 2)
|
||||||
|
vvv := v(2, 3)
|
||||||
|
fmt.Println(vv, vvv)
|
||||||
|
v(1, 2)
|
||||||
|
DeleteMapVal("name", 2)
|
||||||
|
v(2, 3)
|
||||||
|
fmt.Println(i)
|
||||||
|
})
|
||||||
|
}
|
239
cache/vars.go
vendored
239
cache/vars.go
vendored
@ -2,98 +2,197 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"fmt"
|
|
||||||
"github.com/fthvgb1/wp-go/safety"
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VarCache[T any] struct {
|
type VarCache[T any] struct {
|
||||||
v *safety.Var[vars[T]]
|
AnyCache[T]
|
||||||
|
setCacheFunc func(context.Context, ...any) (T, error)
|
||||||
|
mutex sync.Mutex
|
||||||
|
increaseUpdate *IncreaseUpdateVar[T]
|
||||||
|
refresh RefreshVar[T]
|
||||||
|
get func(ctx context.Context) (T, bool)
|
||||||
|
set func(ctx context.Context, v T)
|
||||||
|
flush func(ctx context.Context)
|
||||||
|
getLastSetTime func(ctx context.Context) time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type vars[T any] struct {
|
type IncreaseUpdateVar[T any] struct {
|
||||||
data T
|
CycleTime func() time.Duration
|
||||||
mutex *sync.Mutex
|
Fn IncreaseVarFn[T]
|
||||||
setCacheFunc func(...any) (T, error)
|
|
||||||
expireTime time.Duration
|
|
||||||
setTime time.Time
|
|
||||||
incr int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *VarCache[T]) GetLastSetTime() time.Time {
|
type IncreaseVarFn[T any] func(c context.Context, currentData T, t time.Time, a ...any) (data T, save bool, refresh bool, err error)
|
||||||
return c.v.Load().setTime
|
|
||||||
|
func (t *VarCache[T]) Get(ctx context.Context) (T, bool) {
|
||||||
|
return t.get(ctx)
|
||||||
|
}
|
||||||
|
func (t *VarCache[T]) Set(ctx context.Context, v T) {
|
||||||
|
t.set(ctx, v)
|
||||||
|
}
|
||||||
|
func (t *VarCache[T]) Flush(ctx context.Context) {
|
||||||
|
t.flush(ctx)
|
||||||
|
}
|
||||||
|
func (t *VarCache[T]) GetLastSetTime(ctx context.Context) time.Time {
|
||||||
|
return t.getLastSetTime(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVarCache[T any](fun func(...any) (T, error), duration time.Duration) *VarCache[T] {
|
func initVarCache[T any](t *VarCache[T], a ...any) {
|
||||||
return &VarCache[T]{
|
gets := helper.ParseArgs[func(AnyCache[T], context.Context) (T, bool)](nil, a...)
|
||||||
v: safety.NewVar(vars[T]{
|
if gets == nil {
|
||||||
mutex: &sync.Mutex{},
|
t.get = t.AnyCache.Get
|
||||||
setCacheFunc: fun,
|
} else {
|
||||||
expireTime: duration,
|
t.get = func(ctx context.Context) (T, bool) {
|
||||||
}),
|
return gets(t.AnyCache, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set := helper.ParseArgs[func(AnyCache[T], context.Context, T)](nil, a...)
|
||||||
|
if set == nil {
|
||||||
|
t.set = t.AnyCache.Set
|
||||||
|
} else {
|
||||||
|
t.set = func(ctx context.Context, v T) {
|
||||||
|
set(t.AnyCache, ctx, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flush := helper.ParseArgs[func(AnyCache[T], context.Context)](nil, a...)
|
||||||
|
if flush == nil {
|
||||||
|
t.flush = t.AnyCache.Flush
|
||||||
|
} else {
|
||||||
|
t.flush = func(ctx context.Context) {
|
||||||
|
flush(t.AnyCache, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLastSetTime := helper.ParseArgs[func(AnyCache[T], context.Context) time.Time](nil, a...)
|
||||||
|
if getLastSetTime == nil {
|
||||||
|
t.getLastSetTime = t.AnyCache.GetLastSetTime
|
||||||
|
} else {
|
||||||
|
t.getLastSetTime = func(ctx context.Context) time.Time {
|
||||||
|
return getLastSetTime(t.AnyCache, ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *VarCache[T]) IsExpired() bool {
|
func NewVarCache[T any](cache AnyCache[T], fn func(context.Context, ...any) (T, error), inc *IncreaseUpdateVar[T], ref RefreshVar[T], a ...any) *VarCache[T] {
|
||||||
v := c.v.Load()
|
r := &VarCache[T]{
|
||||||
return time.Duration(v.setTime.UnixNano())+v.expireTime < time.Duration(time.Now().UnixNano())
|
AnyCache: cache, setCacheFunc: fn, mutex: sync.Mutex{},
|
||||||
|
increaseUpdate: inc,
|
||||||
|
refresh: ref,
|
||||||
|
}
|
||||||
|
initVarCache(r, a...)
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *VarCache[T]) Flush() {
|
func (t *VarCache[T]) GetCache(ctx context.Context, timeout time.Duration, params ...any) (T, error) {
|
||||||
v := c.v.Load()
|
data, ok := t.Get(ctx)
|
||||||
mu := v.mutex
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
var vv T
|
|
||||||
v.data = vv
|
|
||||||
c.v.Store(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VarCache[T]) GetCache(ctx context.Context, timeout time.Duration, params ...any) (T, error) {
|
|
||||||
v := c.v.Load()
|
|
||||||
data := v.data
|
|
||||||
var err error
|
var err error
|
||||||
if v.expireTime <= 0 || ((time.Duration(v.setTime.UnixNano()) + v.expireTime) < time.Duration(time.Now().UnixNano())) {
|
if ok {
|
||||||
t := v.incr
|
if t.increaseUpdate != nil && t.refresh != nil {
|
||||||
call := func() {
|
nowTime := time.Now()
|
||||||
v.mutex.Lock()
|
if t.increaseUpdate.CycleTime() > nowTime.Sub(t.GetLastSetTime(ctx)) {
|
||||||
defer v.mutex.Unlock()
|
return data, nil
|
||||||
vv := c.v.Load()
|
|
||||||
if vv.incr > t {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
r, er := vv.setCacheFunc(params...)
|
fn := func() {
|
||||||
if err != nil {
|
t.mutex.Lock()
|
||||||
err = er
|
defer t.mutex.Unlock()
|
||||||
return
|
da, save, refresh, er := t.increaseUpdate.Fn(ctx, data, t.GetLastSetTime(ctx), params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if save {
|
||||||
|
t.Set(ctx, da)
|
||||||
|
}
|
||||||
|
if refresh {
|
||||||
|
t.refresh.Refresh(ctx, params...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
er := helper.RunFnWithTimeout(ctx, timeout, fn, "increaseUpdate cache fail")
|
||||||
|
if err == nil && er != nil {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn()
|
||||||
}
|
}
|
||||||
vv.setTime = time.Now()
|
|
||||||
vv.data = r
|
|
||||||
data = r
|
|
||||||
vv.incr++
|
|
||||||
c.v.Store(vv)
|
|
||||||
}
|
}
|
||||||
if timeout > 0 {
|
return data, nil
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
}
|
||||||
defer cancel()
|
call := func() {
|
||||||
done := make(chan struct{}, 1)
|
t.mutex.Lock()
|
||||||
go func() {
|
defer t.mutex.Unlock()
|
||||||
call()
|
dat, ok := t.Get(ctx)
|
||||||
done <- struct{}{}
|
if ok {
|
||||||
close(done)
|
data = dat
|
||||||
}()
|
return
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
err = errors.New(fmt.Sprintf("get cache %s", ctx.Err().Error()))
|
|
||||||
case <-done:
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
call()
|
|
||||||
}
|
}
|
||||||
|
r, er := t.setCacheFunc(ctx, params...)
|
||||||
|
if er != nil {
|
||||||
|
err = er
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Set(ctx, r)
|
||||||
|
data = r
|
||||||
|
}
|
||||||
|
if timeout > 0 {
|
||||||
|
er := helper.RunFnWithTimeout(ctx, timeout, call, "get cache fail")
|
||||||
|
if err == nil && er != nil {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call()
|
||||||
}
|
}
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VarMemoryCache[T any] struct {
|
||||||
|
v *safety.Var[vars[T]]
|
||||||
|
expireTime func() time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) ClearExpired(ctx context.Context) {
|
||||||
|
_, ok := c.Get(ctx)
|
||||||
|
if !ok {
|
||||||
|
c.Flush(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVarMemoryCache[T any](expireTime func() time.Duration) *VarMemoryCache[T] {
|
||||||
|
return &VarMemoryCache[T]{v: safety.NewVar(vars[T]{}), expireTime: expireTime}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) Get(_ context.Context) (T, bool) {
|
||||||
|
v := c.v.Load()
|
||||||
|
return v.data, c.expireTime() >= time.Now().Sub(v.setTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) Set(_ context.Context, v T) {
|
||||||
|
vv := c.v.Load()
|
||||||
|
vv.data = v
|
||||||
|
vv.setTime = time.Now()
|
||||||
|
vv.incr++
|
||||||
|
c.v.Store(vv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) SetExpiredTime(f func() time.Duration) {
|
||||||
|
c.expireTime = f
|
||||||
|
}
|
||||||
|
|
||||||
|
type vars[T any] struct {
|
||||||
|
data T
|
||||||
|
setTime time.Time
|
||||||
|
incr int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) GetLastSetTime(_ context.Context) time.Time {
|
||||||
|
return c.v.Load().setTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VarMemoryCache[T]) Flush(_ context.Context) {
|
||||||
|
c.v.Flush()
|
||||||
|
}
|
||||||
|
41
cache/vars_test.go
vendored
41
cache/vars_test.go
vendored
@ -7,9 +7,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cc = *NewVarCache(func(a ...any) (int, error) {
|
var cc = *NewVarCache[int](NewVarMemoryCache[int](func() time.Duration {
|
||||||
|
return time.Minute
|
||||||
|
}), func(ctx context.Context, a ...any) (int, error) {
|
||||||
return 1, nil
|
return 1, nil
|
||||||
}, time.Minute)
|
})
|
||||||
|
|
||||||
func TestVarCache_Flush(t *testing.T) {
|
func TestVarCache_Flush(t *testing.T) {
|
||||||
type testCase[T any] struct {
|
type testCase[T any] struct {
|
||||||
@ -26,41 +28,8 @@ func TestVarCache_Flush(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
fmt.Println(tt.c.GetCache(c, time.Second))
|
fmt.Println(tt.c.GetCache(c, time.Second))
|
||||||
tt.c.Flush()
|
tt.c.Flush(ctx)
|
||||||
fmt.Println(tt.c.GetCache(c, time.Second))
|
fmt.Println(tt.c.GetCache(c, time.Second))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVarCache_IsExpired(t *testing.T) {
|
|
||||||
type testCase[T any] struct {
|
|
||||||
name string
|
|
||||||
c VarCache[T]
|
|
||||||
want bool
|
|
||||||
}
|
|
||||||
tests := []testCase[int]{
|
|
||||||
{
|
|
||||||
name: "expired",
|
|
||||||
c: cc,
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not expired",
|
|
||||||
c: func() VarCache[int] {
|
|
||||||
v := *NewVarCache(func(a ...any) (int, error) {
|
|
||||||
return 1, nil
|
|
||||||
}, time.Minute)
|
|
||||||
_, _ = v.GetCache(context.Background(), time.Second)
|
|
||||||
return v
|
|
||||||
}(),
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := tt.c.IsExpired(); got != tt.want {
|
|
||||||
t.Errorf("IsExpired() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user