Compare commits

..

47 Commits

Author SHA1 Message Date
8aeec60181 optimize code and update package version 2024-08-31 20:01:52 +08:00
58d3e4f645 optimize code and update package version 2024-08-21 21:11:27 +08:00
e592a753bf add some func 2024-06-24 21:19:24 +08:00
d598fb5ce5 add some func 2024-06-23 21:30:57 +08:00
63f769796f add some func 2024-06-23 19:18:54 +08:00
7cbfdf8601 update and add func 2024-06-22 11:32:48 +08:00
9c60d10568 optimize sign mechanism 2024-06-19 16:10:42 +08:00
dcbe760f09 optimize code 2024-06-12 22:38:49 +08:00
d220bb2a6c optimize code 2024-06-12 17:51:47 +08:00
eb7ccd4f2d remake safety slice 2024-06-12 00:14:07 +08:00
39c9dc1b09 add rwmap 2024-06-11 22:10:34 +08:00
68beb0fcbb optimize code 2024-06-11 02:21:57 +08:00
1f5f7f465d add httptool.BodyBuffer 2024-06-10 22:50:18 +08:00
b7c09cb5a2 add zeroDefault func and optimize httptool and update package 2024-06-10 17:44:41 +08:00
5942f76972 fix bug 2024-06-06 22:59:03 +08:00
1f40810b78 optimize wp reg route 2024-04-18 11:48:49 +08:00
da858d55e0 fix bug 2024-04-18 11:26:58 +08:00
166bb08ed0 optimize code and add hook gin action and set noroute 2024-04-18 01:39:36 +08:00
5eaf798a6c optimize reload schema and optimize cachemanager package 2024-04-15 15:25:19 +08:00
e2e6bcc8ce fix bug 2024-04-14 23:08:22 +08:00
daa7101493 optimize reload package add append executed once func when reload and add hook scheme 2024-04-14 22:49:40 +08:00
6f51f1c295 update docker golang version 2024-04-13 22:12:20 +08:00
a8d1dcdd5b optimize code and fix bug 2024-04-10 23:48:23 +08:00
9af2257650 fix bug 2024-04-10 22:41:26 +08:00
63591899bb optimize code 2024-04-10 22:31:08 +08:00
ee9ba3fcf0 revise digest add more complex config 2024-04-09 23:51:45 +08:00
a78815f3d3 revise digest add set tag occupied number 2024-04-07 21:30:57 +08:00
6209f2b5e5 optimize digest 2024-04-07 20:02:39 +08:00
08d3bca39c upgrade dependency packages 2024-04-06 20:41:45 +08:00
9b8e597066 remove SetSqlxDB func 2024-04-06 16:44:56 +08:00
3215b0c8ea add get customize config func and add wpHandle add err level 2024-04-06 16:43:18 +08:00
c7c97c469f optimize code 2024-04-05 16:55:34 +08:00
b45ad0e54e fix enlighterjs config 2024-04-05 16:23:59 +08:00
a0c6e48e83 add customheader err measure 2024-04-05 11:53:44 +08:00
3c3ed73971 db use context method 2024-03-27 13:09:46 +08:00
be58a35245 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	README.md
#	readme_en.md
2024-03-22 12:24:51 +08:00
67bdf1e070 change logo size 2024-03-22 12:23:34 +08:00
981e0ad85b change logo size 2024-03-22 12:21:59 +08:00
74159940b5 add thanks jetbrains product license support 2024-03-22 12:14:12 +08:00
206e5902d1 add running description 2024-03-21 17:09:11 +08:00
2d38b36525 optimize pagination interface and update dependence package 2024-03-18 19:12:16 +08:00
af712f06a5 fix bug and optimize code 2024-01-25 14:12:51 +08:00
73575965d3 update docker go version 2024-01-24 15:25:12 +08:00
f49ad731e3 optimize code and fix bug 2024-01-24 15:13:29 +08:00
802c7c7dcc optimize code 2024-01-24 00:25:37 +08:00
20ea36c727 fix bug and optimize code and update dependence package version 2024-01-23 22:59:15 +08:00
fd3199a83c remove sitetest 2024-01-22 21:14:16 +08:00
66 changed files with 1893 additions and 1090 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
err.log
/plugins/
/config.json
go.sum

View File

@ -1,7 +1,6 @@
FROM golang:1.21.4-alpine as gobulidIso
FROM golang:1.22.2-alpine as gobulidIso
COPY ./ /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
FROM alpine:latest

View File

@ -20,6 +20,11 @@
- kill -SIGUSR1 PID 更新配置和清空缓存
- kill -SIGUSR2 PID 清空缓存
#### 运行
```
go run app/cmd/main.go [-c configpath] [-p port]
```
#### 数据显示支持程度
| 页表 | 支持程度 |
@ -75,3 +80,8 @@
#### 其它
用的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>

View File

@ -8,7 +8,7 @@ import (
func ThemeHook(scene string) func(*gin.Context) {
return func(c *gin.Context) {
t := theme.GetCurrentTemplateName()
t := theme.GetCurrentTheme()
h := wp.NewHandle(c, scene, t)
theme.Hook(t, h)
}

View File

@ -3,7 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/fthvgb1/wp-go/app/mail"
"github.com/fthvgb1/wp-go/app/ossigns"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/db"
@ -14,14 +14,10 @@ import (
"github.com/fthvgb1/wp-go/app/theme"
"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/model"
"log"
"os"
"os/signal"
"regexp"
"strings"
"syscall"
"time"
)
@ -89,52 +85,6 @@ 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.Reloads("themeArgAndConfig")
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() {
defer func() {
if r := recover(); r != nil {
@ -143,7 +93,8 @@ func main() {
}
}()
inits()
go signalNotify()
ossigns.SetConfPath(confPath)
go ossigns.SignalNotify()
Gin := route.SetupRouter()
c := config.GetConfig()
if c.Ssl.Key != "" && c.Ssl.Cert != "" {

View File

@ -1,111 +0,0 @@
package main
import (
"flag"
"fmt"
"github.com/fthvgb1/wp-go/helper/httptool"
strings2 "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"github.com/fthvgb1/wp-go/taskPools"
"net/http"
"os"
"regexp"
"strings"
"sync"
)
var reg = regexp.MustCompile(`<a.*href="([^"]+?)".*?>`)
var m = safety.NewMap[string, bool]()
var mut = sync.Mutex{}
func parseHtml(ss string) {
r := reg.FindAllStringSubmatch(ss, -1)
for _, href := range r {
if href[1] == "/" {
continue
}
if strings.ContainsAny(href[1], ".") {
continue
}
if string([]rune(href[1])[0:3]) == "http" {
continue
}
mut.Lock()
if _, ok := m.Load(href[1]); !ok {
m.Store(href[1], false)
}
mut.Unlock()
}
}
func siteFetch(c int, u string) {
u = strings.TrimRight(u, "/")
ss, code, err := httptool.GetString(u, nil)
if err != nil || code != http.StatusOK {
panic(err)
}
parseHtml(ss)
p := taskPools.NewPools(c)
for {
m.Range(func(key string, value bool) bool {
if value {
return true
}
u := strings2.Join(u, key)
p.Execute(func() {
ss, code, err := httptool.GetString(u, nil)
fmt.Println(u, code)
if err != nil || code != http.StatusOK {
panic(err)
return
}
parseHtml(ss)
m.Store(key, true)
})
return true
})
var x bool
m.Range(func(key string, value bool) bool {
if !value {
x = true
return false
}
return true
})
if !x {
break
}
}
p.Wait()
m.Flush()
}
var c int
var u string
var t int
func main() {
flag.IntVar(&c, "c", 10, "concurrency num")
flag.StringVar(&u, "url", "http://127.0.0.1:8081", "test url")
flag.IntVar(&t, "t", 1, "request full site times")
flag.Parse()
if u == "" {
fmt.Println("url can't emtpy")
os.Exit(2)
}
if c < 1 {
fmt.Println("concurrency num must >= 1")
os.Exit(2)
}
if t < 1 {
for {
siteFetch(c, u)
}
}
for i := 0; i < t; i++ {
siteFetch(c, u)
}
}

View File

@ -8,9 +8,9 @@ import (
func SearchLimit(num int64) func(ctx *gin.Context) {
fn, reFn := IpLimit(num)
reload.Push(func() {
reload.Append(func() {
reFn(config.GetConfig().SingleIpSearchNum)
})
}, "search-ip-limit-number")
return func(c *gin.Context) {
if c.Query("s") != "" {
fn(c)

View File

@ -19,7 +19,7 @@ func ValidateServerNames() func(ctx *gin.Context) {
}
}
return m
})
}, "site-names")
return func(c *gin.Context) {
m := sites.Load()

69
app/ossigns/signs.go Normal file
View 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()
}

View File

@ -7,7 +7,7 @@ import (
"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/safety"
"github.com/fthvgb1/wp-go/cache/reload"
"time"
)
@ -102,9 +102,9 @@ type Arch struct {
month time.Month
}
var arch = safety.NewVar(Arch{
var arch = reload.Vars(Arch{
fn: dao.Archives,
})
}, "archives-year-month-data")
func Archives(ctx context.Context) []models.PostArchive {
a := arch.Load()

View File

@ -85,6 +85,14 @@ type Mysql struct {
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 {
if conf == "" {
conf = "config.yaml"
@ -96,6 +104,7 @@ func InitConfig(conf string) error {
if err != nil {
return err
}
defer get.Body.Close()
file, err = io.ReadAll(get.Body)
} else {
file, err = os.ReadFile(conf)
@ -103,6 +112,7 @@ func InitConfig(conf string) error {
if err != nil {
return err
}
fileData.Store(file)
var c Config
err = yaml.Unmarshal(file, &c)
if err != nil {

View File

@ -15,6 +15,10 @@ import (
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) {
c := config.GetConfig()
dsn := c.Mysql.Dsn.GetDsn()

View File

@ -15,20 +15,24 @@ var logs = safety.NewVar[*log.Logger](nil)
var logFile = safety.NewVar[*os.File](nil)
func InitLogger() error {
c := config.GetConfig()
return SetLogger(c.LogOutput)
}
func SetLogger(loggerFile string) error {
if loggerFile == "" {
loggerFile = "stderr"
}
preFD := logFile.Load()
l := &log.Logger{}
c := config.GetConfig()
if c.LogOutput == "" {
c.LogOutput = "stderr"
}
var out io.Writer
switch c.LogOutput {
switch loggerFile {
case "stdout":
out = os.Stdout
case "stderr":
out = os.Stderr
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 {
return err
}

View File

@ -20,6 +20,7 @@ type Comments struct {
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
//扩展字段
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 {

View File

@ -6,14 +6,16 @@ import (
"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"
strings2 "strings"
str "strings"
"time"
)
@ -57,7 +59,6 @@ func (r *RdmCache[K, V]) Set(ctx context.Context, key K, val V) {
return
}
fmt.Println(result, err)
}
func (r *RdmCache[K, V]) GetExpireTime(ctx context.Context) time.Duration {
@ -73,7 +74,7 @@ func (r *RdmCache[K, V]) Ttl(ctx context.Context, key K) time.Duration {
}
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) {
@ -81,12 +82,13 @@ func (r *RdmCache[K, V]) Del(ctx context.Context, key ...K) {
}
func (r *RdmCache[K, V]) ClearExpired(ctx context.Context) {
fmt.Println("clear expired redis cache")
}
// use step:
//1 go build -gcflags all="-N -l" --race -buildmode=plugin -o redisCache.so main.go && cp ./redisCache.so wp-go/plugins/
// 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 Re(h *wp.Handle) {
func RedisCache(h *wp.Handle) {
vv, ok := cachemanager.GetMapCache[string, dao.PostIds]("listPostIds")
if ok {
_, ok := any(vv.Cache).(*RdmCache[string, dao.PostIds])
@ -94,6 +96,16 @@ func Re(h *wp.Handle) {
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
@ -110,14 +122,14 @@ func Re(h *wp.Handle) {
name: "",
resFn: func(m map[string]string) dao.PostIds {
return dao.PostIds{
Ids: slice.Map(strings2.Split(m["ids"], ","), strings.ToInt[uint64]),
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": strings2.Join(t, ","),
"ids": str.Join(t, ","),
"length": strconv.Itoa(ids.Length),
}
},

View File

@ -4,10 +4,15 @@ import (
"context"
"fmt"
"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/cache/cachemanager"
"github.com/fthvgb1/wp-go/cache/reload"
"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/safety"
"regexp"
"strings"
"time"
@ -18,10 +23,106 @@ var more = regexp.MustCompile("<!--more(.*?)?-->")
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() {
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 {
@ -45,7 +146,8 @@ func digestRaw(ctx context.Context, id uint64, arg ...any) (string, error) {
func Digests(content string, id uint64, limit int, fn func(id uint64, content, closeTag string) string) string {
closeTag := ""
content = RemoveWpBlock(content)
tag := config.GetConfig().DigestAllowTag
c := digestConfig.Load()
tag := c.DigestAllowTag
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>"
}
@ -54,7 +156,12 @@ func Digests(content string, id uint64, limit int, fn func(id uint64, content, c
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 {
return PostsMore(id, content, closeTag)
}

View File

@ -62,10 +62,6 @@ func (p PageEle) Dots() string {
return p.DotsEle
}
func (p PageEle) Step() int {
return 0
}
func (p PageEle) Middle(page int, url string) string {
return fmt.Sprintf(p.MiddleEle, url, page)
}
@ -142,10 +138,6 @@ func (p CommentPageEle) Prev(url string) string {
return fmt.Sprintf(p.PrevEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较早评论", "较新评论"))
}
func (p CommentPageEle) Step() int {
return 0
}
func (p CommentPageEle) Next(url string) string {
return fmt.Sprintf(p.NextEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较新评论", "较早评论"))
}
@ -157,7 +149,6 @@ type PaginationNav struct {
Dotss func() string
Middles func(page int, url string) string
Urlss func(u url.URL, page int, isTLS bool) string
Steps func() int
}
func (p PaginationNav) Current(page, totalPage, totalRows int) string {
@ -183,7 +174,3 @@ func (p PaginationNav) Middle(page int, url string) string {
func (p PaginationNav) Urls(u url.URL, page int, isTLS bool) string {
return p.Urlss(u, page, isTLS)
}
func (p PaginationNav) Step() int {
return p.Steps()
}

View File

@ -2,12 +2,29 @@ package apply
import "github.com/fthvgb1/wp-go/safety"
var fn safety.Var[any]
var contains = safety.NewMap[string, any]()
func SetFn(f any) {
fn.Store(f)
func SetVal(key string, val any) {
contains.Store(key, val)
}
func UsePlugins() any {
return fn.Load()
func DelVal(key string) {
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
}

View File

@ -22,7 +22,7 @@ type Options struct {
Linehover bool `json:"linehover,omitempty"`
RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"`
TextOverflow string `json:"textOverflow,omitempty"`
Linenumbers int64 `json:"linenumbers,omitempty"`
Linenumbers bool `json:"linenumbers,omitempty"`
Theme string `json:"theme,omitempty"`
Language string `json:"language,omitempty"`
RetainCssClasses bool `json:"retainCssClasses,omitempty"`
@ -58,7 +58,7 @@ func EnlighterJS(h *wp.Handle) {
Linehover: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linehover", true),
RawcodeDbclick: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-rawcodedbclick", true),
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"),
Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"),
RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false),

View File

@ -66,7 +66,7 @@ func LoadPlugins() {
}
RegisterPlugin(name, plu)
}
apply.SetFn(func(h *wp.Handle) {
apply.SetVal("wp-plugins", func(h *wp.Handle) {
UsePlugins(h)
})
}

View File

@ -8,6 +8,8 @@ import (
"github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/pprof"
@ -16,11 +18,26 @@ import (
"github.com/gin-gonic/gin"
)
var hooker []func(r *gin.Engine)
type GinSetter func(*gin.Engine)
// Hook 方便插件在init时使用
func Hook(fn ...func(r *gin.Engine)) {
hooker = append(hooker, fn...)
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 {
@ -28,16 +45,27 @@ func SetupRouter() *gin.Engine {
// 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)
r.HTMLRender = theme.Template()
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(),
@ -45,13 +73,18 @@ func SetupRouter() *gin.Engine {
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")
}
@ -61,9 +94,14 @@ func SetupRouter() *gin.Engine {
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))
@ -81,19 +119,29 @@ func SetupRouter() *gin.Engine {
r.GET("/p/:id/feed", actions.PostFeed)
r.GET("/feed", actions.SiteFeed)
r.GET("/comments/feed", actions.CommentsFeed)
//r.NoRoute(actions.ThemeHook(constraints.NoRoute))
commentMiddleWare, _ := middleware.FlowLimit(c.MaxRequestSleepNum, 5, c.CacheTime.SleepTime)
r.POST("/comment", commentMiddleWare, actions.PostComment)
r.NoRoute(actions.ThemeHook(constraints.NoRoute))
}, 84.6)
SetGinAction("setpprof", func(r *gin.Engine) {
if c.Pprof != "" {
pprof.Register(r, c.Pprof)
}
for _, fn := range hooker {
fn(r)
}, 80.8)
for _, hook := range setterHooks {
setters = slice.FilterAndMap(setters, hook)
}
reload.Push(func() {
c := config.GetConfig()
siteFlow(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
slice.SimpleSort(setters, slice.DESC, func(t mockmap.Item[string, GinSetter]) float64 {
return t.Order
})
for _, fn := range setters {
fn.Value(r)
}
return r
}

View File

@ -3,6 +3,7 @@ package theme
import (
"embed"
"github.com/fthvgb1/wp-go/multipTemplate"
"github.com/fthvgb1/wp-go/safety"
"html/template"
"io/fs"
"path/filepath"
@ -12,20 +13,36 @@ import (
//go:embed *[^.go]
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 {
t := multipTemplate.NewFsTemplate(TemplateFs)
templates = t.Template
t.FuncMap = FuncMap()
commonTemplate(t)
/*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml"). //单个主题设置
AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml")*/
return t
var multiple *multipTemplate.MultipleFsTemplate
func BuildTemplate() *multipTemplate.MultipleFsTemplate {
if multiple != nil {
tt := multipTemplate.NewFsTemplate(TemplateFs)
commonTemplate(tt)
for k, v := range map[string]*template.Template(any(tt.Template).(multipTemplate.TemplateMaps)) {
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) {
t, ok := templates[name]
t, ok := templates.Load(name)
return t, ok
}
@ -35,10 +52,11 @@ func commonTemplate(t *multipTemplate.MultipleFsTemplate) {
if err != nil {
panic(err)
}
funMap := FuncMap()
for _, main := range m {
file := filepath.Base(main)
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)
}
}
@ -55,6 +73,10 @@ func IsTemplateDirExists(tml string) bool {
}
func IsTemplateExists(tml string) bool {
t, ok := templates[tml]
t, ok := templates.Load(tml)
return ok && t != nil
}
func SetTemplate(name string, val *template.Template) {
templates.Store(name, val)
}

View File

@ -8,13 +8,18 @@ import (
var themeMap = safety.NewMap[string, func(*wp.Handle)]()
func AddThemeHookFunc(name string, fn func(handle *wp.Handle)) {
if _, ok := themeMap.Load(name); ok {
panic("exists same name theme")
}
func AddTheme(name string, fn func(handle *wp.Handle)) {
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

View File

@ -7,7 +7,12 @@ import (
"time"
)
var comFn = template.FuncMap{
func postsFn(fn func(models.Posts) string, a models.Posts) string {
return fn(a)
}
func FuncMap() template.FuncMap {
return template.FuncMap{
"unescaped": func(s string) any {
return template.HTML(s)
},
@ -29,18 +34,4 @@ var comFn = template.FuncMap{
return template.HTML(fn(s))
},
}
func postsFn(fn func(models.Posts) string, a models.Posts) string {
return fn(a)
}
func FuncMap() template.FuncMap {
return comFn
}
func addTemplateFunc(fnName string, fn any) {
if _, ok := comFn[fnName]; ok {
panic("exists same name func")
}
comFn[fnName] = fn
}

View File

@ -8,17 +8,17 @@ import (
)
func InitTheme() {
AddThemeHookFunc(twentyfifteen.ThemeName, twentyfifteen.Hook)
AddThemeHookFunc(twentyseventeen.ThemeName, twentyseventeen.Hook)
AddTheme(twentyfifteen.ThemeName, twentyfifteen.Hook)
AddTheme(twentyseventeen.ThemeName, twentyseventeen.Hook)
}
func GetCurrentTemplateName() string {
tmlp := config.GetConfig().Theme
if tmlp == "" {
tmlp = wpconfig.GetOption("template")
func GetCurrentTheme() string {
themeName := config.GetConfig().Theme
if themeName == "" {
themeName = wpconfig.GetOption("template")
}
if !IsTemplateDirExists(tmlp) {
tmlp = "twentyfifteen"
if !IsTemplateDirExists(themeName) {
themeName = "twentyfifteen"
}
return tmlp
return themeName
}

View File

@ -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) {
img, rand := h.GetCustomHeaderImg()

View File

@ -3,13 +3,11 @@ package twentyfifteen
import (
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/plugins"
"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/middleware"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"strings"
)
@ -56,16 +54,10 @@ func setPaginationAndRender(h *wp.Handle) {
func postThumb(h *wp.Handle) {
d := h.GetDetailHandle()
if d.Post.Thumbnail.Path != "" {
d.Post.Thumbnail = getPostThumbs(d.Post.Id, d.Post)
d.Post.Thumbnail = wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
}
}
var getPostThumbs = reload.BuildMapFn[uint64]("twentyFifteen-post-thumbnail", postThumbs)
func postThumbs(post models.Posts) models.PostThumbnail {
return wpconfig.Thumbnail(post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
}
func renderCustomHeader(h *wp.Handle) {
h.SetData("customHeader", customHeader(h))
}

View File

@ -34,9 +34,6 @@ var paginate = func() plugins.PageEle {
Nexts: p.Next,
Dotss: p.Dots,
Middles: p.Middle,
Steps: func() int {
return 2
},
Urlss: plugins.TwentyFifteenCommentPagination().Urls,
}
@ -83,6 +80,7 @@ func setPaginationAndRender(h *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()
@ -112,17 +110,11 @@ func errorsHandle(h *wp.Handle) {
func postThumb(h *wp.Handle) {
d := h.GetDetailHandle()
if d.Post.Thumbnail.Path != "" {
d.Post.Thumbnail = getPostThumbs(d.Post.Id, d.Post)
}
}
var getPostThumbs = reload.BuildMapFn[uint64]("post-thumbnail", postThumbs)
func postThumbs(post models.Posts) models.PostThumbnail {
img := wpconfig.Thumbnail(post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
img.Sizes = "100vw"
img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset)
return img
d.Post.Thumbnail = img
}
}
var commentFormat = comment{}
@ -162,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) {
h.SetData("HeaderImage", getHeaderImage(h))

View File

@ -12,6 +12,7 @@ import (
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model"
"regexp"
"strings"
)
func (h *Handle) DisplayHeaderText() bool {
@ -23,7 +24,7 @@ var GetCustomHeaderImgFn = reload.BuildValFnWithConfirm("headerImages", customHe
func customHeadImag(h *Handle) ([]models.PostThumbnail, bool) {
hs, err := h.GetHeaderImages(h.theme)
if err != nil {
h.SetErr(err)
h.SetErr(fmt.Errorf("get customheadimage err: %v", err), Low)
return nil, false
}
return hs, true
@ -33,7 +34,7 @@ func (h *Handle) GetCustomHeaderImg() (r models.PostThumbnail, isRand bool) {
var err error
img := GetCustomHeaderImgFn(h)
err = h.Err()
if err != nil {
if err != nil && strings.Contains(err.Error(), "get customheadimage err") {
logs.Error(err, "获取页眉背景图失败")
return
}

View File

@ -28,10 +28,16 @@ type DetailHandle struct {
CommentPageEle pagination.Render
TotalRaw int
TotalPage int
CommentStep int
}
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) {
@ -97,7 +103,7 @@ func (d *DetailHandle) CommentData() {
pageComments := wpconfig.GetOption("page_comments")
num, err := cachemanager.GetBy[int]("commentNumber", d.C, d.Post.Id, time.Second)
if err != nil {
d.SetErr(err)
d.SetErr(err, Low)
return
}
if num < 1 {
@ -105,7 +111,7 @@ func (d *DetailHandle) CommentData() {
}
topNum, err := cachemanager.GetBy[int]("postTopCommentsNum", d.C, d.Post.Id, time.Second)
if err != nil {
d.SetErr(err)
d.SetErr(err, Low)
return
}
d.TotalPage = number.DivideCeil(topNum, d.Limit)
@ -127,12 +133,12 @@ func (d *DetailHandle) CommentData() {
d.ginH["page_comments"] = pageComments
d.ginH["totalCommentPage"] = d.TotalPage
if d.TotalPage < d.Page {
d.SetErr(errors.New("curren page above total 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)
d.SetErr(err, Low)
return
}
d.TotalRaw = topNum
@ -157,14 +163,14 @@ func (d *DetailHandle) RenderComment() {
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)
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, 1, *d.C.Request.URL, d.IsHttps())
d.ginH["commentPageNav"] = pagination.Paginate(d.CommentPageEle, d.TotalRaw, d.Limit, d.Page, d.CommentStep, *d.C.Request.URL, d.IsHttps())
}
}
@ -189,7 +195,7 @@ func Detail(h *Handle) {
d := h.GetDetailHandle()
err := d.BuildDetailData()
if err != nil {
d.SetErr(err)
d.SetErr(err, High)
}
h.SetData("scene", h.Scene())
}

View File

@ -0,0 +1,8 @@
package wp
const (
None = iota
Low
High
Fatal
)

View File

@ -173,7 +173,7 @@ func Index(h *Handle) {
i := h.GetIndexHandle()
err := i.BuildIndexData()
if err != nil {
i.SetErr(err)
i.SetErr(err, High)
}
h.SetData("scene", h.Scene())
}

View File

@ -23,7 +23,7 @@ func PostsPlugins(initial PostsPlugin, calls ...func(PostsPlugin, *Handle, *mode
var pluginFns = reload.Vars(map[string]func(PostsPlugin, *Handle, *models.Posts){
"passwordProject": PasswordProject,
"digest": Digest,
})
}, "list-post-plugins-fns")
func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) {
m := pluginFns.Load()
@ -53,7 +53,7 @@ func Digest(next PostsPlugin, h *Handle, post *models.Posts) {
next(h, post)
}
var ordinaryPlugin = reload.Vars([]PostsPlugin{})
var ordinaryPlugin = reload.Vars([]PostsPlugin{}, "ordinaryPlugin")
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
p := ordinaryPlugin.Load()

View File

@ -5,6 +5,7 @@ import (
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/theme/wp/route"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/number"
@ -81,7 +82,7 @@ var plainRouteParam = reload.Vars([]Plain{
return true
},
},
})
}, "plainRouteParam")
var GetUsersIds = reload.BuildValFnWithConfirm("usersIds", func(h *wp.Handle) (map[uint64]string, bool) {
users, err := cache.GetAllUsername(h.C)
@ -160,4 +161,7 @@ func CommonMiddleware(h *wp.Handle) {
h.PushHandler(constraints.PipeMiddleware, constraints.Detail,
wp.NewHandleFn(ShowPreComment, 100, "middleware.ShowPreComment"),
)
h.PushHandler(constraints.PipeMiddleware, constraints.NoRoute,
wp.NewHandleFn(route.ResolveRoute, 100, "route.ResolveRoute"),
)
}

View File

@ -67,10 +67,11 @@ func (h *Handle) PushDataHandler(scene string, fns ...HandleCall) {
}
func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string,
handleHook func(*Handle, map[string][]func(HandleCall) (HandleCall, bool)) []func(HandleCall) (HandleCall, bool),
handlesFn func(*Handle, map[string][]HandleCall, string) []HandleCall) func(HandleFn[*Handle], *Handle) {
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, handleHook, handlesFn))
pipeHandlerFn := reload.BuildMapFn[string]("pipeHandlers", BuildHandler(pipeScene, keyFn, handleHookFn))
return func(next HandleFn[*Handle], h *Handle) {
key := keyFn(h, pipeScene)
@ -88,8 +89,9 @@ func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string,
}
func BuildHandler(pipeScene string, keyFn func(*Handle, string) string,
handleHook func(*Handle, map[string][]func(HandleCall) (HandleCall, bool)) []func(HandleCall) (HandleCall, bool),
handlesFn func(*Handle, map[string][]HandleCall, string) []HandleCall) func(*Handle) []HandleCall {
handleHook func(*Handle, string,
map[string][]func(HandleCall) (HandleCall, bool),
map[string][]HandleCall) []HandleCall) func(*Handle) []HandleCall {
return func(h *Handle) []HandleCall {
key := keyFn(h, pipeScene)
@ -98,18 +100,7 @@ func BuildHandler(pipeScene string, keyFn func(*Handle, string) string,
pipeHookers, _ := handleHooks.Load(pipeScene)
pipeHandlers, _ := handlerss.Load(pipeScene)
mut.Unlock()
hookers := handleHook(h, pipeHookers)
calls := handlesFn(h, pipeHandlers, key)
calls = slice.FilterAndMap(calls, func(call HandleCall) (HandleCall, bool) {
ok := true
for _, hook := range hookers {
call, ok = hook(call)
if !ok {
break
}
}
return call, ok
})
calls := handleHook(h, key, pipeHookers, pipeHandlers)
slice.SimpleSort(calls, slice.DESC, func(t HandleCall) float64 {
return t.Order
})
@ -117,6 +108,19 @@ func BuildHandler(pipeScene string, keyFn func(*Handle, string) string,
}
}
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)
@ -163,36 +167,25 @@ func MiddlewareKey(h *Handle, pipScene string) string {
return h.DoActionFilter("middleware", str.Join("pipe-middleware-", h.scene), pipScene)
}
func PipeMiddlewareHandle(h *Handle, middlewares map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, middlewares[h.scene]...)
handlers = append(handlers, middlewares[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeMiddlewareHandle", handlers, middlewares, key)
return
func PipeMiddlewareHandle(h *Handle, key string,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
return h.PipeHandleHook("PipeMiddlewareHandle", hookedHandles, handlers, key)
}
func PipeDataHandle(h *Handle, dataHandlers map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, dataHandlers[h.scene]...)
handlers = append(handlers, dataHandlers[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeDataHandle", handlers, dataHandlers, key)
return
func PipeDataHandle(h *Handle, key string,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
return h.PipeHandleHook("PipeDataHandle", hookedHandles, handlers, key)
}
func PipeRender(h *Handle, renders map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, renders[h.Stats]...)
handlers = append(handlers, renders[h.scene]...)
handlers = append(handlers, renders[constraints.AllStats]...)
handlers = append(handlers, renders[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeRender", handlers, renders, key)
return
}
func HandleHook(h *Handle, hooks map[string][]func(HandleCall) (HandleCall, bool)) []func(HandleCall) (HandleCall, bool) {
var r []func(HandleCall) (HandleCall, bool)
r = append(r, hooks[h.scene]...)
r = append(r, hooks[h.Stats]...)
r = append(r, hooks[constraints.AllScene]...)
r = append(r, hooks[constraints.AllStats]...)
return r
func PipeRender(h *Handle, key string,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, h.Stats, constraints.AllScene, constraints.AllStats)
return h.PipeHandleHook("PipeRender", hookedHandles, handlers, key)
}
// DeleteHandle 写插件的时候用
@ -245,10 +238,10 @@ func (h *Handle) PipeHandleHook(name string, calls []HandleCall, m map[string][]
func InitPipe(h *Handle) {
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeMiddleware, 300,
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, HandleHook, PipeMiddlewareHandle)))
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200,
BuildHandlers(constraints.PipeData, PipeKey, HandleHook, PipeDataHandle)))
BuildHandlers(constraints.PipeData, PipeKey, PipeDataHandle)))
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100,
BuildHandlers(constraints.PipeRender, PipeKey, HandleHook, PipeRender)))
BuildHandlers(constraints.PipeRender, PipeKey, PipeRender)))
}

View File

@ -5,9 +5,12 @@ import (
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/safety"
"net/http"
"regexp"
)
// Route
// Type value equal const or reg
type Route struct {
Path string
Scene string
@ -20,18 +23,18 @@ var routeHook []func(Route) (Route, bool)
var regRoutes *safety.Map[string, *regexp.Regexp]
var routes = func() *safety.Map[string, Route] {
r := safety.NewMap[string, Route]()
reload.Push(func() {
reload.Append(func() {
r.Flush()
regRoutes.Flush()
})
}, "wp-routers")
regRoutes = safety.NewMap[string, *regexp.Regexp]()
return r
}()
// PushRoute path can be const or regex string
//
// eg: `(?P<control>\w+)/(?P<method>\w+)`, route.Route{
// Path: `(?P<control>\w+)/(?P<method>\w+)`,
// eg: `(?P<controller>\w+)/(?P<method>\w+)`, route.Route{
// Path: `(?P<controller>\w+)/(?P<method>\w+)`,
// Scene: constraints.Home,
// Method: []string{"GET"},
// Type: "reg",
@ -118,17 +121,26 @@ func ResolveRoute(h *wp.Handle) {
return
}
for path, reg := range rrs {
r := reg.FindAllStringSubmatch(requestURI, -1)
r := reg.FindStringSubmatch(requestURI)
if len(r) < 1 {
return
}
rr := rs[path]
if slice.IsContained(rr.Method, h.C.Request.Method) {
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)
h.Abort()
return
}
}
h.C.Status(http.StatusNotFound)
h.Abort()
}

View File

@ -32,6 +32,7 @@ type Handle struct {
templ string
themeMods wpconfig.ThemeMods
err error
errLevel int8
abort bool
stopPipe bool
}
@ -86,6 +87,8 @@ type HandleCall struct {
Name string
}
var isFirstRequest = true
func SetConfigHandle(a ...any) Handle {
configFn := a[0].(func(*Handle))
hh := a[1].(*Handle)
@ -99,27 +102,29 @@ func SetConfigHandle(a ...any) Handle {
h.ginH = gin.H{}
fnMap.Flush()
fnHook.Flush()
if isFirstRequest {
isFirstRequest = false
} else {
reload.Reload()
}
h.C = hh.C
h.theme = hh.theme
configFn(h)
v := apply.UsePlugins()
v := apply.GetPlugins()
pluginFn, ok := v.(func(*Handle))
if ok {
pluginFn(h)
}
reload.Reload()
return *h
}
var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, false)
type ConfigParm struct {
ConfigFn func(*Handle)
H *Handle
}
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["calPostClass"] = postClass(h)
h.ginH["calBodyClass"] = bodyClass(h)
@ -147,8 +152,17 @@ func (h *Handle) Err() error {
return h.err
}
func (h *Handle) SetErr(err error) {
func (h *Handle) SetErr(err error, level int8) {
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) {
@ -170,15 +184,12 @@ func (h *Handle) SetData(k string, v any) {
}
func NewHandle(c *gin.Context, scene string, theme string) *Handle {
mods, err := wpconfig.GetThemeMods(theme)
logs.IfError(err, "获取mods失败")
return &Handle{
C: c,
theme: theme,
Session: sessions.Default(c),
scene: scene,
Stats: constraints.Ok,
themeMods: mods,
}
}
@ -223,7 +234,12 @@ func (h *Handle) RenderHtml(t *template.Template, statsCode int, name string) {
header["Content-Type"] = htmlContentType
}
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()
if err != nil {
panic(err)

View File

@ -73,6 +73,7 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
up := strings.Split(metadata.File, "/")
if metadata.File != "" && Type == "full" {
mimeType := metadata.Sizes["thumbnail"].MimeType
metadata.Sizes = maps.Copy(metadata.Sizes)
metadata.Sizes["full"] = models.MetaDataFileSize{
File: filepath.Base(metadata.File),
Width: metadata.Width,
@ -111,10 +112,10 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
var themeModes = func() *safety.Map[string, ThemeMods] {
m := safety.NewMap[string, ThemeMods]()
themeModsRaw = safety.NewMap[string, map[string]any]()
reload.Push(func() {
reload.Append(func() {
m.Flush()
themeModsRaw.Flush()
})
}, "theme-modes")
return m
}()

View File

@ -2,156 +2,50 @@ package cachemanager
import (
"context"
"errors"
"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 mapFlush = safety.NewMap[string, func(any)]()
var anyFlush = safety.NewMap[string, func()]()
var mutex sync.Mutex
var varCache = safety.NewMap[string, any]()
var mapCache = 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 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
}
type flush interface {
Flush(ctx context.Context)
}
type Fn func(context.Context)
type clearExpired interface {
ClearExpired(ctx context.Context)
}
var clears []clearExpired
var clears = safety.NewVar(mockmap.Map[string, Fn]{})
var flushes []flush
var flushes = safety.NewVar(mockmap.Map[string, Fn]{})
func Flush() {
ctx := context.WithValue(context.Background(), "execFlushBy", "mangerFlushFn")
for _, f := range flushes {
f.Flush(ctx)
for _, f := range flushes.Load() {
f.Value(ctx)
}
}
func FlushMapVal[T any](name string, keys ...T) {
v, ok := mapFlush.Load(name)
if !ok || len(keys) < 1 {
return
}
v(keys)
func Flushes(ctx context.Context, names ...string) {
execute(ctx, flushes, names...)
}
func FlushAnyVal(name ...string) {
for _, s := range name {
v, ok := anyFlush.Load(s)
if ok {
v()
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 PushMangerMap[K comparable, V any](name string, m *cache.MapCache[K, V]) {
if name == "" {
return
}
mapCache.Store(name, m)
anyFlush.Store(name, func() {
ctx := context.WithValue(context.Background(), "ctx", "registerFlush")
m.Flush(ctx)
})
mapFlush.Store(name, func(a any) {
k, ok := a.([]K)
if ok && len(k) > 0 {
ctx := context.WithValue(context.Background(), "ctx", "registerFlush")
m.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 parseArgs(args ...any) (string, func() time.Duration) {
var name string
var fn func() time.Duration
@ -170,42 +64,6 @@ func parseArgs(args ...any) (string, func() time.Duration) {
return name, fn
}
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 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...)
FlushPush(m)
ClearPush(m)
name, f := parseArgs(args...)
if name != "" {
PushMangerMap(name, m)
}
if f != nil && name != "" {
SetExpireTime(any(data).(cache.SetTime), name, 0, f)
}
return m
}
func buildLockFn[K comparable](args ...any) cache.LockFn[K] {
lockFn := helper.ParseArgs(cache.LockFn[K](nil), args...)
name := helper.ParseArgs("", args...)
@ -228,20 +86,15 @@ func buildLockFn[K comparable](args ...any) cache.LockFn[K] {
} else {
lo := cache.NewLocks[K](loFn)
lockFn = lo.GetLock
FlushPush(lo)
PushOrSetFlush(mockmap.Item[string, Fn]{
Name: name,
Value: lo.Flush,
})
}
}
return lockFn
}
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 SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expireTimeFn func() time.Duration) {
if name == "" {
@ -256,95 +109,55 @@ func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) {
reload.SetFnVal(s, t, coverConf)
}
}
func FlushPush(f ...flush) {
flushes = append(flushes, f...)
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)
}
func ClearPush(c ...clearExpired) {
clears = append(clears, c...)
}
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 _, c := range clears {
c.ClearExpired(ctx)
for _, queue := range clears.Load() {
queue.Value(ctx)
}
}
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...)
FlushPush(v)
name, _ := parseArgs(a...)
if name != "" {
varCache.Store(name, v)
}
cc, ok := any(c).(clearExpired)
if ok {
ClearPush(cc)
}
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
}
func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) {
vv, err := getMap[K, V](name)
return vv, err == nil
}
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
}

View File

@ -45,7 +45,7 @@ func TestFlushMapVal(t *testing.T) {
}
p.Wait()
fmt.Println(gets, count)
FlushMapVal("test", 3, 4)
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)
@ -54,7 +54,7 @@ func TestFlushMapVal(t *testing.T) {
}
fmt.Println(get, count)
fmt.Println(vv.Get(ctx, 5))
FlushAnyVal("test")
Flushes(ctx, "test")
fmt.Println(vv.Get(ctx, 5))
fmt.Println(vv.Get(ctx, 6))
//fmt.Println(GetVarCache("test"))

148
cache/cachemanager/mapcache.go vendored Normal file
View 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
View 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
View 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
}

175
cache/reload/reload.go vendored
View File

@ -2,16 +2,19 @@ package reload
import (
"github.com/fthvgb1/wp-go/helper"
"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/safety"
"sync"
"sync/atomic"
)
type queue struct {
fn func()
order float64
name string
type Queue struct {
Fn func()
Order float64
Name string
AutoExec bool
Once bool
}
var mut = &sync.Mutex{}
@ -20,11 +23,55 @@ func GetGlobeMutex() *sync.Mutex {
return mut
}
var waitReloadCalls = safety.NewSlice(make([]queue, 0))
var callMap = safety.NewMap[string, func()]()
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
@ -64,12 +111,20 @@ func DeleteMapVal[T any](namespace string, key ...T) {
func Reloads(namespaces ...string) {
mut.Lock()
defer mut.Unlock()
for _, namespace := range namespaces {
fn, ok := callMap.Load(namespace)
if !ok {
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
}
fn()
queue.Fn()
if queue.Once {
slice.Delete(&queues, i)
reloadQueues.Store(queues)
}
}
}
@ -165,7 +220,7 @@ func BuildSafetyMap[K comparable, V, A any](namespace string, args ...any) *Safe
m.Val.Delete(key)
}
})
Push(m.Val.Flush, args...)
Append(m.Val.Flush, args...)
safetyMaps.Store(namespace, m)
return m
}
@ -189,7 +244,7 @@ func GetAnyValMapBy[K comparable, V, A any](namespace string, key K, a A, fn fun
return v
}
func BuildAnyVal[T, A any](namespace string, counter bool, args ...any) *SafetyVar[T, A] {
func BuildAnyVal[T, A any](namespace string, args ...any) *SafetyVar[T, A] {
var vv *SafetyVar[T, A]
vvv, ok := safetyMaps.Load(namespace)
if ok {
@ -205,13 +260,13 @@ func BuildAnyVal[T, A any](namespace string, counter bool, args ...any) *SafetyV
v := Val[T]{}
vv = &SafetyVar[T, A]{safety.NewVar(v), sync.Mutex{}}
args = append(args, namespace)
Push(vv.Val.Flush, args...)
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, false, args...)
var vv = BuildAnyVal[T, A](namespace, args...)
v := vv.Val.Load()
if v.Ok {
return v.V
@ -231,11 +286,13 @@ func GetAnyValBys[T, A any](namespace string, a A, fn func(A) (T, bool), args ..
//
// 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, false, args...)
var vv = BuildAnyVal[T, A](namespace, args...)
tryTimes := helper.ParseArgs(1, args...)
var counter func() int
var counter int64
if tryTimes > 1 {
counter = number.Counters[int]()
Append(func() {
atomic.StoreInt64(&counter, 0)
}, str.Join("reload-valFn-counter-", namespace))
}
return func(a A) T {
v := vv.Val.Load()
@ -253,11 +310,11 @@ func BuildValFnWithConfirm[T, A any](namespace string, fn func(A) (T, bool), arg
vv.Val.Store(v)
return v.V
}
if counter == nil {
if atomic.LoadInt64(&counter) <= 1 {
return v.V
}
times := counter()
if times >= tryTimes {
atomic.AddInt64(&counter, 1)
if atomic.LoadInt64(&counter) >= int64(tryTimes) {
v.Ok = true
vv.Val.Store(v)
}
@ -274,7 +331,7 @@ func BuildValFnWithConfirm[T, A any](namespace string, fn func(A) (T, bool), arg
//
// 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, false, args...)
var vv = BuildAnyVal[T, A](namespace, args...)
return func(a A) T {
v := vv.Val.Load()
if v.Ok {
@ -295,7 +352,7 @@ func BuildValFn[T, A any](namespace string, fn func(A) T, args ...any) func(A) T
// 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, false, args...)
var vv = BuildAnyVal[T, any](namespace, args...)
return func(a ...any) T {
v := vv.Val.Load()
if v.Ok {
@ -316,17 +373,19 @@ func BuildValFnWithAnyParams[T any](namespace string, fn func(...any) T, args ..
// Vars get default value and whenever reloaded assign default value
//
// args same as Push
// 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)
Push(func() {
Append(func() {
ss.Store(defaults)
}, args...)
return ss
@ -350,11 +409,11 @@ func parseArgs(a ...any) (ord float64, name string) {
// VarsBy
//
// args same as Push
// 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())
Push(func() {
Append(func() {
ss.Store(fn())
}, args...)
return ss
@ -364,7 +423,7 @@ func MapBy[K comparable, T any](fn func(*safety.Map[K, T]), args ...any) *safety
if fn != nil {
fn(m)
}
Push(func() {
Append(func() {
m.Flush()
if fn != nil {
fn(m)
@ -375,42 +434,66 @@ func MapBy[K comparable, T any](fn func(*safety.Map[K, T]), args ...any) *safety
func SafeMap[K comparable, T any](args ...any) *safety.Map[K, T] {
m := safety.NewMap[K, T]()
Push(m.Flush, args...)
Append(m.Flush, args...)
return m
}
// Push the func that will be called whenever Reload called
// 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 flushed when called Reload, then can called GetValMap to flush manually
func Push(fn func(), a ...any) {
// 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...)
auto := helper.ParseArgs(true, a...)
if name != "" && !auto {
callMap.Store(name, fn)
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
}
waitReloadCalls.Append(queue{fn, ord, name})
if name != "" {
callMap.Store(name, fn)
}
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()
reloadCalls := waitReloadCalls.Load()
slice.SimpleSort(reloadCalls, slice.DESC, func(t queue) float64 {
return t.order
hookQueue()
queues := reloadQueues.Load()
length := len(queues)
slice.SimpleSort(queues, slice.DESC, func(t Queue) float64 {
return t.Order
})
for _, call := range reloadCalls {
call.fn()
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)
}
return
}
type Any[T any] struct {
@ -434,11 +517,11 @@ func BuildFnVal[T any](name string, t T, fn func() T) func() T {
v: p,
isManual: safety.NewVar(false),
}
Push(func() {
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()

View File

@ -66,6 +66,48 @@ cacheTime:
digestWordCount: 300
# 摘要允许的标签 默认为<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>
digestTag: "<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>"
# 设置html转义实体正则 the html coded character set regex file: plugin/digest/digest.go:12
#digestRegex: "&quot;*|&amp;*|&lt;*|&gt;*|&nbsp;*|&#91;*|&#93;*|&emsp;*"
# 可以设置每个标签或者转义字符占用的字数默认都为0 set tag or escape character occupied num, default every tag occupied 0
#digestTagOccupyNum: [
# {
# tag: "<top>", # 最外层固定tag outermost immovable tag
# num: 0,
# chuckOvered: false,
# escapeCharacter: [
# {
# character: [ "\n","\r","\t" ],
# num: 0
# },
# ]
# },{
# tag: "<img>",
# num: 1,
# chuckOvered: false
# },
# {
# tag: "<pre><code>",
# num: 0,
# escapeCharacter: [
# {
# character: ["\t"],
# num: 4,
# chuckOvered: false,
# },
# {
# character: ["\n","\r"],
# num: 1
# },
# {
# tags: "<br>",
# num: 1
# },
# ]
# },
#]
# 到达指定并发请求数时随机sleep
maxRequestSleepNum: 100
# 全局最大请求数超过直接403

58
go.mod
View File

@ -1,49 +1,53 @@
module github.com/fthvgb1/wp-go
go 1.20
go 1.21.0
toolchain go1.23.0
require (
github.com/dlclark/regexp2 v1.10.0
github.com/elliotchance/phpserialize v1.3.3
github.com/gin-contrib/gzip v0.0.6
github.com/gin-contrib/pprof v1.4.0
github.com/gin-contrib/sessions v0.0.5
github.com/gin-gonic/gin v1.9.1
github.com/dlclark/regexp2 v1.11.4
github.com/elliotchance/phpserialize v1.4.0
github.com/gin-contrib/gzip v1.0.1
github.com/gin-contrib/pprof v1.5.0
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.16.0
github.com/go-sql-driver/mysql v1.7.1
github.com/goccy/go-json v0.10.2
github.com/jmoiron/sqlx v1.3.5
golang.org/x/crypto v0.15.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
github.com/go-playground/validator/v10 v10.22.0
github.com/go-sql-driver/mysql v1.8.1
github.com/goccy/go-json v0.10.3
github.com/jmoiron/sqlx v1.4.0
golang.org/x/crypto v0.26.0
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/sonic v1.12.2 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gin-contrib/sse v0.1.0 // 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/gorilla/sessions v1.3.0 // 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/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.4.0 // 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/pelletier/go-toml/v2 v2.2.3 // 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/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
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.9.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

209
go.sum
View File

@ -1,209 +0,0 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/elliotchance/phpserialize v1.3.3 h1:hV4QVmGdCiYgoBbw+ADt6fNgyZ2mYX0OgpnON1adTCM=
github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg=
github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90=
github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -2,11 +2,13 @@ package helper
import (
"context"
"encoding/json"
"errors"
"fmt"
str "github.com/fthvgb1/wp-go/helper/strings"
"net/url"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
@ -51,13 +53,15 @@ func CutUrlHost(u string) string {
return ur.String()
}
func Defaults[T comparable](v, defaults T) T {
var zero T
if v == zero {
return defaults
}
func Defaults[T comparable](vals ...T) T {
var val T
for _, v := range vals {
if v != val {
return v
}
}
return val
}
func DefaultVal[T any](v, defaults T) T {
var zero T
if reflect.DeepEqual(zero, v) {
@ -129,6 +133,11 @@ func FileExist(path string) bool {
return err == nil
}
func IsFile(file string) bool {
info, err := os.Stat(file)
return err == nil && !info.IsDir()
}
func GetAnyVal[T any](v any, defaults T) T {
vv, ok := v.(T)
if !ok {
@ -188,3 +197,46 @@ func RunFnWithTimeouts[A, V any](ctx context.Context, t time.Duration, ar A, cal
}
return v, err
}
func JsonDecode[T any](byts []byte) (T, error) {
var v T
err := json.Unmarshal(byts, &v)
return v, err
}
func AsError[T any](err error) (T, bool) {
var v T
ok := errors.As(err, &v)
return v, ok
}
func IsDirExistAndMkdir(dir string, perm os.FileMode) error {
stat, err := os.Stat(dir)
if err != nil {
if !os.IsNotExist(err) {
return err
} else {
return os.MkdirAll(dir, perm)
}
}
if !stat.IsDir() {
return fmt.Errorf("%s is exist but not dir", dir)
}
return nil
}
func ReadDir(dir string) ([]string, error) {
fii, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
var files []string
for _, entry := range fii {
name := entry.Name()
if name == "." || name == ".." {
continue
}
files = append(files, filepath.Join(dir, name))
}
return files, nil
}

View File

@ -133,6 +133,13 @@ func StripTagsX(str, allowable string) string {
var selfCloseTags = map[string]string{"area": "", "base": "", "basefont": "", "br": "", "col": "", "command": "", "fecolormatrix": "", "embed": "", "frame": "", "hr": "", "img": "", "input": "", "isindex": "", "link": "", "fecomposite": "", "fefuncr": "", "fefuncg": "", "fefuncb": "", "fefunca": "", "meta": "", "param": "", "!doctype": "", "source": "", "track": "", "wbr": ""}
func GetSelfCloseTags() map[string]string {
return selfCloseTags
}
func SetSelfCloseTags(m map[string]string) {
selfCloseTags = m
}
func CloseTag(str string) string {
tags := tag.FindAllString(str, -1)
if len(tags) < 1 {

View File

@ -36,7 +36,7 @@ func GetString(u string, q map[string]any, a ...any) (r string, code int, err er
}
func Get(u string, q map[string]any, a ...any) (res *http.Response, err error) {
cli, req, err := GetClient(u, q, a...)
cli, req, err := BuildClient(u, "get", q, a...)
res, err = cli.Do(req)
return
}
@ -51,7 +51,7 @@ func GetToJsonAny[T any](u string, q map[string]any, a ...any) (r T, code int, e
if err != nil {
return
}
rr.Body.Close()
defer rr.Body.Close()
err = json.Unmarshal(b, &r)
return
}
@ -66,7 +66,7 @@ func PostWwwString(u string, form map[string]any, a ...any) (r string, code int,
if err != nil {
return "", code, err
}
rr.Body.Close()
defer rr.Body.Close()
r = string(rs)
return
}
@ -80,12 +80,12 @@ func PostFormDataString(u string, form map[string]any, a ...any) (r string, code
if err != nil {
return "", code, err
}
rr.Body.Close()
defer rr.Body.Close()
r = string(rs)
return
}
func GetClient(u string, q map[string]any, a ...any) (res *http.Client, req *http.Request, err error) {
func BuildClient(u, method string, q map[string]any, a ...any) (res *http.Client, req *http.Request, err error) {
parse, err := url.Parse(u)
if err != nil {
return nil, nil, err
@ -95,10 +95,10 @@ func GetClient(u string, q map[string]any, a ...any) (res *http.Client, req *htt
setValue(q, values)
parse.RawQuery = values.Encode()
req = &http.Request{
Method: "GET",
Method: strings.ToUpper(method),
URL: parse,
}
setArgs(&cli, req, a...)
SetArgs(&cli, req, a...)
return &cli, req, err
}
@ -113,17 +113,9 @@ func Post(u string, types int, form map[string]any, a ...any) (res *http.Respons
return
}
func PostClient(u string, types int, form map[string]any, a ...any) (cli *http.Client, req *http.Request, err error) {
parse, err := url.Parse(u)
if err != nil {
return
}
cli = &http.Client{}
req = &http.Request{
Method: "POST",
URL: parse,
Header: http.Header{},
}
// SetBody
// types 1 x-www-form-urlencoded, 2 form-data, 3 json, 4 binary
func SetBody(req *http.Request, types int, form map[string]any) (err error) {
switch types {
case 1:
values := url.Values{}
@ -147,20 +139,104 @@ func PostClient(u string, types int, form map[string]any, a ...any) (cli *http.C
case 3:
fo, err := json.Marshal(form)
if err != nil {
return nil, nil, err
return err
}
b := bytes.NewReader(fo)
req.Body = io.NopCloser(b)
req.Header.Add("Content-Type", "application/json")
case 4:
b, ok := maps.GetStrAnyVal[[]byte](form, "binary")
if !ok {
return nil, nil, errors.New("no binary value")
}
if ok {
req.Body = io.NopCloser(bytes.NewReader(b))
req.Header.Add("Content-Type", "application/octet-stream")
req.ContentLength = int64(len(b))
return
}
setArgs(cli, req, a...)
bb, ok := maps.GetStrAnyVal[*[]byte](form, "binary")
if ok {
req.Body = io.NopCloser(NewBodyBuffer(bb))
req.Header.Add("Content-Type", "application/octet-stream")
req.ContentLength = int64(len(*bb))
return
}
bf, ok := maps.GetStrAnyVal[*BodyBuffer](form, "binary")
if ok {
req.Body = bf
req.Header.Add("Content-Type", "application/octet-stream")
req.ContentLength = int64(len(*bf.Data))
return
}
return errors.New("no binary value")
}
return
}
type BodyBuffer struct {
Offset int
Data *[]byte
ReadFn func([]byte) (int, error)
CloseFn func() error
}
func (b *BodyBuffer) Close() error {
if b.CloseFn != nil {
return b.CloseFn()
}
b.Offset = 0
return nil
}
func (b *BodyBuffer) Read(p []byte) (int, error) {
return b.ReadFn(p)
}
func (b *BodyBuffer) Reads(p []byte) (int, error) {
data := (*b.Data)[b.Offset:]
if len(p) <= len(data) {
copy(p, data[0:len(p)])
b.Offset += len(p)
return len(p), nil
}
if len(data) <= 0 {
b.Offset = 0
return 0, io.EOF
}
copy(p, data)
b.Offset = 0
return len(data), io.EOF
}
func NewBodyBuffer(byt *[]byte, readFn ...func(*BodyBuffer, []byte) (int, error)) *BodyBuffer {
var fn func([]byte) (int, error)
b := &BodyBuffer{Data: byt}
if len(readFn) > 1 {
fn = func(p []byte) (int, error) {
return readFn[0](b, p)
}
} else {
fn = b.Reads
}
b.ReadFn = fn
return b
}
func PostClient(u string, types int, form map[string]any, a ...any) (cli *http.Client, req *http.Request, err error) {
parse, err := url.Parse(u)
if err != nil {
return
}
cli = &http.Client{}
req = &http.Request{
Method: "POST",
URL: parse,
Header: http.Header{},
}
err = SetBody(req, types, form)
if err != nil {
return
}
SetArgs(cli, req, a...)
return
}
@ -174,18 +250,44 @@ func PostToJsonAny[T any](u string, types int, form map[string]any, a ...any) (r
if err != nil {
return
}
rr.Body.Close()
defer rr.Body.Close()
err = json.Unmarshal(b, &r)
return
}
func setArgs(cli *http.Client, req *http.Request, a ...any) {
func SetUrl(u string, req *http.Request) error {
parse, err := url.Parse(u)
if err != nil {
return err
}
req.URL = parse
return nil
}
func RequestToJSON[T any](client *http.Client, req *http.Request, a ...any) (res *http.Response, r T, err error) {
SetArgs(client, req, a...)
res, err = client.Do(req)
if err != nil {
return
}
bt, err := io.ReadAll(res.Body)
if err != nil {
return
}
err = json.Unmarshal(bt, &r)
return
}
func SetArgs(cli *http.Client, req *http.Request, a ...any) {
if len(a) < 1 {
return
}
for _, arg := range a {
h, ok := arg.(map[string]string)
if ok && len(h) > 0 {
if req.Header == nil {
req.Header = make(http.Header)
}
for k, v := range h {
req.Header.Add(k, v)
}
@ -208,6 +310,9 @@ func setArgs(cli *http.Client, req *http.Request, a ...any) {
}
c, ok := arg.(string)
if ok && c != "" {
if req.Header == nil {
req.Header = make(http.Header)
}
req.Header.Add("cookie", c)
}
}

View File

@ -100,6 +100,10 @@ func Divide[T constraints.Integer | constraints.Float](i, j T) T {
return i / j
}
func Ceil[T constraints.Integer](num1, num2 T) int {
return int((num1 + num2 - 1) / num2)
}
func DivideCeil[T constraints.Integer](num1, num2 T) T {
return T(math.Ceil(float64(num1) / float64(num2)))
}
@ -113,8 +117,3 @@ func Counters[T constraints.Integer]() func() T {
return count
}
}
func Round(f float64, precision int) float64 {
p := math.Pow10(precision)
return math.Floor(f*p+0.5) / p
}

View File

@ -0,0 +1,47 @@
package mockmap
import (
"github.com/fthvgb1/wp-go/helper/slice"
)
type Item[K comparable, T any] struct {
Name K
Value T
Order float64
}
type Map[K comparable, T any] []Item[K, T]
func (q *Map[K, T]) Get(name K) Item[K, T] {
_, v := slice.SearchFirst(*q, func(t Item[K, T]) bool {
return name == t.Name
})
return v
}
func (q *Map[K, T]) Set(name K, value T, orders ...float64) {
i := slice.IndexOfBy(*q, func(t Item[K, T]) bool {
return name == t.Name
})
if i > -1 {
(*q)[i].Value = value
return
}
order := float64(0)
if len(orders) > 0 {
order = orders[0]
}
*q = append(*q, Item[K, T]{name, value, order})
}
func (q *Map[K, T]) Del(name K) {
i := slice.IndexOfBy(*q, func(t Item[K, T]) bool {
return name == t.Name
})
if i > -1 {
slice.Delete((*[]Item[K, T])(q), i)
}
}
func (q *Map[K, T]) DelByIndex(i int) {
slice.Delete((*[]Item[K, T])(q), i)
}

View File

@ -22,6 +22,15 @@ func FilterAndMap[N any, T any](arr []T, fn func(T) (N, bool)) (r []N) {
}
return
}
func FilterAndMaps[N any, T any](arr []T, fn func(int, T) (N, bool)) (r []N) {
for i, t := range arr {
x, ok := fn(i, t)
if ok {
r = append(r, x)
}
}
return
}
func Walk[T any](arr []T, fn func(*T)) {
for i := 0; i < len(arr); i++ {
@ -298,6 +307,15 @@ func IndexOf[T comparable](a []T, v T) int {
}
return -1
}
func IndexOfBy[T any](a []T, fn func(T) bool) int {
for i, t := range a {
ok := fn(t)
if ok {
return i
}
}
return -1
}
func ForEach[T any](a []T, fn func(i int, v T)) {
for i, t := range a {

View File

@ -102,3 +102,9 @@ func DecompressBy[T, R any](a [][]T, fn func(T) (R, bool)) (r []R) {
}
return
}
func Replace[T any](a []T, offset int, replacement []T) {
aa := a[offset:]
aa = aa[:0]
aa = append(aa, replacement...)
}

View File

@ -27,7 +27,7 @@ func (r anyArr[T]) Less(i, j int) bool {
return r.fn(r.data[i], r.data[j])
}
// Sort fn i>j 为降序 desc反之为升序 asc
// Sort fn i>j desc ↓i<j asc ↑
func Sort[T any](arr []T, fn func(i, j T) bool) {
slice := anyArr[T]{
data: arr,
@ -84,3 +84,16 @@ func SimpleSort[T any, O constraints.Ordered](a []T, order string, fn func(t T)
}
sort.Sort(slice)
}
func SimpleSorts[T constraints.Ordered](a []T, order string) {
slice := anyArr[T]{
data: a,
fn: func(i, j T) bool {
if order == DESC {
return i > j
}
return i < j
},
}
sort.Sort(slice)
}

View File

@ -41,14 +41,14 @@ func (r *SqlxQuery) Selects(ctx context.Context, dest any, sql string, params ..
if v != "" {
switch v {
case "string":
return ToMapSlice(db, dest.(*[]map[string]string), sql, params...)
return ToMapSlice(ctx, db, dest.(*[]map[string]string), sql, params...)
case "scanner":
fn := ctx.Value("fn")
return Scanner[any](db, dest, sql, params...)(fn.(func(any)))
return Scanner[any](ctx, db, dest, sql, params...)(fn.(func(any)))
}
}
return db.Select(dest, sql, params...)
return db.SelectContext(ctx, dest, sql, params...)
}
func (r *SqlxQuery) Gets(ctx context.Context, dest any, sql string, params ...any) error {
@ -57,15 +57,15 @@ func (r *SqlxQuery) Gets(ctx context.Context, dest any, sql string, params ...an
if v != "" {
switch v {
case "string":
return GetToMap(db, dest.(*map[string]string), sql, params...)
return GetToMap(ctx, db, dest.(*map[string]string), sql, params...)
}
}
return db.Get(dest, sql, params...)
return db.GetContext(ctx, dest, sql, params...)
}
func Scanner[T any](db *sqlx.DB, v T, s string, params ...any) func(func(T)) error {
func Scanner[T any](ctx context.Context, db *sqlx.DB, v T, s string, params ...any) func(func(T)) error {
return func(fn func(T)) error {
rows, err := db.Queryx(s, params...)
rows, err := db.QueryxContext(ctx, s, params...)
if err != nil {
return err
}
@ -80,8 +80,8 @@ func Scanner[T any](db *sqlx.DB, v T, s string, params ...any) func(func(T)) err
}
}
func ToMapSlice[V any](db *sqlx.DB, dest *[]map[string]V, sql string, params ...any) (err error) {
rows, err := db.Query(sql, params...)
func ToMapSlice[V any](ctx context.Context, db *sqlx.DB, dest *[]map[string]V, sql string, params ...any) (err error) {
rows, err := db.QueryContext(ctx, sql, params...)
if err != nil {
return err
}
@ -113,8 +113,8 @@ func ToMapSlice[V any](db *sqlx.DB, dest *[]map[string]V, sql string, params ...
return
}
func GetToMap[V any](db *sqlx.DB, dest *map[string]V, sql string, params ...any) (err error) {
rows := db.QueryRowx(sql, params...)
func GetToMap[V any](ctx context.Context, db *sqlx.DB, dest *map[string]V, sql string, params ...any) (err error) {
rows := db.QueryRowxContext(ctx, sql, params...)
columns, err := rows.Columns()
if err != nil {
return err

View File

@ -9,51 +9,69 @@ import (
)
type MultipleFileTemplate struct {
Template map[string]*template.Template
FuncMap template.FuncMap
Template maps
}
type MultipleFsTemplate struct {
MultipleFileTemplate
Fs embed.FS
}
type TemplateMaps map[string]*template.Template
func (m TemplateMaps) Load(name string) (*template.Template, bool) {
v, ok := m[name]
return v, ok
}
func (m TemplateMaps) Store(name string, v *template.Template) {
m[name] = v
}
type maps interface {
Load(name string) (*template.Template, bool)
Store(name string, v *template.Template)
}
func (t *MultipleFileTemplate) AppendTemplate(name string, templates ...string) *MultipleFileTemplate {
tmpl, ok := t.Template[name]
tmpl, ok := t.Template.Load(name)
if ok {
t.Template[name] = template.Must(tmpl.ParseFiles(templates...))
t.Template.Store(name, template.Must(tmpl.ParseFiles(templates...)))
}
return t
}
func (t *MultipleFsTemplate) AppendTemplate(name string, templates ...string) *MultipleFsTemplate {
tmpl, ok := t.Template[name]
tmpl, ok := t.Template.Load(name)
if ok {
t.Template[name] = template.Must(tmpl.ParseFS(t.Fs, templates...))
t.Template.Store(name, template.Must(tmpl.ParseFS(t.Fs, templates...)))
}
return t
}
func NewFileTemplate() *MultipleFileTemplate {
func NewFileTemplates(m maps) *MultipleFileTemplate {
return &MultipleFileTemplate{
Template: make(map[string]*template.Template),
FuncMap: make(template.FuncMap),
Template: m,
}
}
func NewFsTemplate(f embed.FS) *MultipleFsTemplate {
return &MultipleFsTemplate{
MultipleFileTemplate: MultipleFileTemplate{
Template: make(map[string]*template.Template),
FuncMap: make(template.FuncMap),
Template: TemplateMaps(make(map[string]*template.Template)),
},
Fs: f,
}
}
func NewFsTemplates(f embed.FS, m maps) *MultipleFsTemplate {
return &MultipleFsTemplate{
MultipleFileTemplate: MultipleFileTemplate{
Template: m,
},
Fs: f,
}
}
func (t *MultipleFileTemplate) SetTemplate(name string, templ *template.Template) *MultipleFileTemplate {
if _, ok := t.Template[name]; ok {
panic("exists same template " + name)
}
t.Template[name] = templ
t.Template.Store(name, templ)
return t
}
@ -65,14 +83,15 @@ func (t *MultipleFileTemplate) AddTemplate(mainTemplatePattern string, fnMap tem
for _, mainTemplate := range mainTemplates {
file := filepath.Base(mainTemplate)
pattern := append([]string{mainTemplate}, layoutTemplatePattern...)
t.Template[mainTemplate] = template.Must(template.New(file).Funcs(fnMap).ParseFiles(pattern...))
t.Template.Store(mainTemplate, template.Must(template.New(file).Funcs(fnMap).ParseFiles(pattern...)))
}
return t
}
func (t *MultipleFileTemplate) Instance(name string, data any) render.Render {
v, _ := t.Template.Load(name)
return render.HTML{
Template: t.Template[name],
Template: v,
Data: data,
}
}
@ -83,12 +102,9 @@ func (t *MultipleFsTemplate) AddTemplate(mainTemplatePattern string, fnMap templ
panic(err)
}
for _, mainTemplate := range mainTemplates {
if _, ok := t.Template[mainTemplate]; ok {
panic("exists same Template " + mainTemplate)
}
file := filepath.Base(mainTemplate)
pattern := append([]string{mainTemplate}, layoutTemplatePattern...)
t.Template[mainTemplate] = template.Must(template.New(file).Funcs(fnMap).ParseFS(t.Fs, pattern...))
t.Template.Store(mainTemplate, template.Must(template.New(file).Funcs(fnMap).ParseFS(t.Fs, pattern...)))
}
return t
}

View File

@ -3,12 +3,28 @@ package digest
import (
"github.com/fthvgb1/wp-go/helper/html"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"regexp"
"strings"
"unicode/utf8"
)
var quto = regexp.MustCompile(`&quot; *|&amp; *|&lt; *|&gt; ?|&nbsp; *`)
var quto = regexp.MustCompile(`&quot;*|&amp;*|&lt;*|&gt;*|&nbsp;*|&#91;*|&#93;*|&emsp;*`)
func SetQutos(reg string) {
quto = regexp.MustCompile(reg)
}
type SpecialSolveConf struct {
Num int
ChuckOvered bool
EscapeCharacter map[rune]SpecialSolve
Tags map[string]SpecialSolve
}
type SpecialSolve struct {
Num int
ChuckOvered bool
}
func StripTags(content, allowTag string) string {
content = strings.Trim(content, " \t\n\r\000\x0B")
@ -24,6 +40,14 @@ func Html(content string, limit int) (string, string) {
return content, ""
}
index := quto.FindAllStringIndex(content, -1)
var runeIndex [][]int
if len(index) > 0 {
runeIndex = slice.Map(index, func(t []int) []int {
return slice.Map(t, func(i int) int {
return utf8.RuneCountInString(content[:i])
})
})
}
end := 0
ru := []rune(content)
tagIn := false
@ -32,18 +56,14 @@ func Html(content string, limit int) (string, string) {
i := -1
for {
i++
for len(index) > 0 {
ints := slice.Map(index[0], func(t int) int {
return utf8.RuneCountInString(content[:t])
})
if ints[0] <= i {
i = i + i - ints[0] + ints[1] - ints[0]
index = index[1:]
end++
continue
} else {
if end >= limit || i >= total {
break
}
for len(runeIndex) > 0 && i == runeIndex[0][0] {
i = runeIndex[0][1]
runeIndex = runeIndex[1:]
end++
continue
}
if end >= limit || i >= total {
@ -67,3 +87,120 @@ func Html(content string, limit int) (string, string) {
closeTag = html.CloseTag(content)
return content, closeTag
}
func CustomizeHtml(content string, limit int, m map[string]SpecialSolveConf) (string, string) {
closeTag := ""
length := utf8.RuneCountInString(content) + 1
if length <= limit {
return content, ""
}
index := quto.FindAllStringIndex(content, -1)
var runeIndex [][]int
if len(index) > 0 {
runeIndex = slice.Map(index, func(t []int) []int {
return slice.Map(t, func(i int) int {
return utf8.RuneCountInString(content[:i])
})
})
}
count := 0
runeContent := []rune(content)
tagIn := false
runeTotal := len(runeContent)
l, r := '<', '>'
i := -1
selfCloseTags := html.GetSelfCloseTags()
var currentTag, parentTag string
var allTags = []string{"<top>"}
var tag []rune
var tagLocal = 0
for {
i++
if count >= limit || i >= runeTotal {
break
}
for len(runeIndex) > 0 && i == runeIndex[0][0] {
i = runeIndex[0][1]
runeIndex = runeIndex[1:]
count++
continue
}
if count >= limit || i >= runeTotal {
break
}
if runeContent[i] == l {
tagLocal = i
tagIn = true
continue
}
if tagIn && runeContent[i] == r {
tagIn = false
tags := str.Join("<", string(tag), ">")
if strings.Contains(tags, " ") {
tags = str.Join("<", strings.Split(string(tag), " ")[0], ">")
}
currentTag = tags
rawTag := strings.ReplaceAll(strings.Trim(tags, "<>"), "/", "")
_, ok := selfCloseTags[rawTag]
if !ok {
if '/' == tags[1] {
parentTag = allTags[len(allTags)-2]
allTags = allTags[:len(allTags)-1]
} else {
parentTag = allTags[len(allTags)-1]
allTags = append(allTags, currentTag)
}
} else {
parentTag = allTags[len(allTags)-1]
}
tag = tag[:0]
if len(m) > 0 {
nn, ok := m[parentTag]
if ok {
if n, ok := nn.Tags[tags]; ok {
if (count+n.Num) > limit && n.ChuckOvered {
i = tagLocal
break
}
count += n.Num
continue
}
}
if n, ok := m[tags]; ok {
if (count+n.Num) > limit && n.ChuckOvered {
i = tagLocal
break
}
count += n.Num
}
}
continue
}
if tagIn {
tag = append(tag, runeContent[i])
continue
}
currentTags := allTags[len(allTags)-1]
mm, ok := m[currentTags]
if !ok || len(mm.EscapeCharacter) < 1 {
count++
continue
}
if n, ok := mm.EscapeCharacter[runeContent[i]]; ok {
if (count+n.Num) > limit && n.ChuckOvered {
break
}
count += n.Num
} else {
count++
}
}
if i > runeTotal {
i = runeTotal
}
content = string(runeContent[:i])
closeTag = html.CloseTag(content)
return content, closeTag
}

View File

@ -13,7 +13,6 @@ type Render interface {
Dots() string
Middle(page int, url string) string
Urls(u url.URL, page int, isTLS bool) string
Step() int
}
type parser struct {
@ -28,10 +27,6 @@ type parser struct {
}
func Paginate(e Render, totalRaw int, pageSize int, currentPage, step int, u url.URL, isTLS bool) string {
st := e.Step()
if st > 0 {
step = st
}
return parser{
Render: e,
TotalPage: number.DivideCeil(totalRaw, pageSize),

View File

@ -20,6 +20,12 @@ A WordPress front-end written in Go, with relatively simple functions, only the
- kill -SIGUSR1 PID update configuration and clear cache
- kill -SIGUSR2 PID clear cache
#### start up
```
go run app/cmd/main.go [-c configpath] [-p port]
```
#### The data show the degree of support
| page table | Support |
@ -71,6 +77,10 @@ It is divided into plug-ins that modify the data of list pages and plug-ins that
| digest Automatically generate a digest of the specified length | Enlighter code highlighting (enlighterjs plug-in needs to be installed in the background) |
| | hiddenLogin hidden login entry |
#### other
#### Others
The gin framework and sqlx used encapsulate the layer query method outside.
#### Thanks
<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>

71
safety/rwmap.go Normal file
View File

@ -0,0 +1,71 @@
package safety
import "sync"
type RWMap[K comparable, V any] struct {
m map[K]V
mux sync.RWMutex
}
func NewRWMap[K comparable, V any](val ...map[K]V) *RWMap[K, V] {
var m map[K]V
if len(val) < 1 {
m = make(map[K]V)
} else {
m = val[0]
}
return &RWMap[K, V]{m: m, mux: sync.RWMutex{}}
}
func (v *RWMap[K, V]) Store(key K, val V) {
v.mux.Lock()
defer v.mux.Unlock()
v.m[key] = val
}
func (v *RWMap[K, V]) Load(key K) (V, bool) {
v.mux.RLock()
defer v.mux.RUnlock()
val, ok := v.m[key]
return val, ok
}
func (v *RWMap[K, V]) Delete(keys ...K) {
v.mux.Lock()
defer v.mux.Unlock()
for _, key := range keys {
delete(v.m, key)
}
}
func (v *RWMap[K, V]) Copy() map[K]V {
v.mux.RLock()
defer v.mux.RUnlock()
var m = make(map[K]V)
for k, val := range v.m {
m[k] = val
}
return m
}
func (v *RWMap[K, V]) Len() int {
v.mux.RLock()
defer v.mux.RUnlock()
return len(v.m)
}
func (v *RWMap[K, V]) Range(fn func(K, V) bool) {
v.mux.RLock()
defer v.mux.RUnlock()
for key, val := range v.m {
if !fn(key, val) {
break
}
}
}
func (v *RWMap[K, V]) Set(m map[K]V) {
v.mux.Lock()
defer v.mux.Unlock()
v.m = m
}

View File

@ -3,21 +3,37 @@ package safety
import "sync"
type Slice[T any] struct {
*Var[[]T]
mu sync.Mutex
Var []T
mu sync.RWMutex
}
func NewSlice[T any](a []T) *Slice[T] {
func NewSlice[T any](a ...[]T) *Slice[T] {
var s []T
if len(a) > 0 {
s = a[0]
}
return &Slice[T]{
NewVar(a),
sync.Mutex{},
s,
sync.RWMutex{},
}
}
func (r *Slice[T]) Append(t ...T) {
r.mu.Lock()
defer r.mu.Unlock()
ts := append(r.Load(), t...)
r.Store(ts)
r.Var = append(r.Var, t...)
}
func (r *Slice[T]) Store(a []T) {
r.mu.Lock()
defer r.mu.Unlock()
r.Var = a
}
func (r *Slice[T]) Load() (a []T) {
r.mu.RLock()
defer r.mu.RUnlock()
a = make([]T, len(r.Var))
copy(a, r.Var)
return
}

View File

@ -19,7 +19,7 @@ func TestSlice_Append(t *testing.T) {
tests := []testCase[int]{
{
name: "t1",
r: *NewSlice([]int{}),
r: *NewSlice[int](),
args: args[int]{number.Range(1, 10, 1)},
},
}

View File

@ -10,8 +10,12 @@ type Var[T any] struct {
p unsafe.Pointer
}
func NewVar[T any](val T) *Var[T] {
return &Var[T]{val: val, p: unsafe.Pointer(&val)}
func NewVar[T any](vals ...T) *Var[T] {
var v T
if len(vals) > 0 {
v = vals[0]
}
return &Var[T]{val: v, p: unsafe.Pointer(&v)}
}
func (r *Var[T]) Load() T {

135
signs/signs.go Normal file
View File

@ -0,0 +1,135 @@
package signs
import (
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
"os"
"os/signal"
"sync"
)
type Call func() bool
type HookFn func(mockmap.Item[string, Call]) (os.Signal, mockmap.Item[string, Call], bool)
var queues = map[os.Signal]mockmap.Map[string, Call]{}
var ch = make(chan os.Signal, 1)
var stopCh = make(chan struct{}, 1)
var hooks = map[os.Signal][]HookFn{}
var mux = sync.Mutex{}
func GetChannel() chan os.Signal {
return ch
}
func Cancel(sings ...os.Signal) {
if len(sings) < 1 {
return
}
mux.Lock()
defer mux.Unlock()
for _, sing := range sings {
delete(queues, sing)
}
signal.Reset(sings...)
}
func Hook(sign os.Signal, fn HookFn) {
mux.Lock()
defer mux.Unlock()
hooks[sign] = append(hooks[sign], fn)
}
func hook(item []mockmap.Item[string, Call], sign os.Signal) []mockmap.Item[string, Call] {
mux.Lock()
defer mux.Unlock()
if len(item) < 1 {
delete(hooks, sign)
return item
}
hooksFn, ok := hooks[sign]
if !ok {
return item
}
for _, fn := range hooksFn {
item = slice.FilterAndMap(item, func(t mockmap.Item[string, Call]) (mockmap.Item[string, Call], bool) {
s, c, ok := fn(t)
if sign != s {
install(s, t.Value, t.Name, t.Order)
return t, false
}
return c, ok
})
}
delete(hooks, sign)
slice.SimpleSort(item, slice.DESC, func(t mockmap.Item[string, Call]) float64 {
return t.Order
})
return item
}
func Install(sign os.Signal, fn Call, a ...any) {
mux.Lock()
defer mux.Unlock()
arr := helper.ParseArgs([]os.Signal{}, a...)
if len(arr) > 0 {
for _, o := range arr {
install(o, fn, a...)
}
}
install(sign, fn, a...)
}
func install(sign os.Signal, fn Call, a ...any) {
m, ok := queues[sign]
if !ok {
queues[sign] = make(mockmap.Map[string, Call], 0)
signal.Notify(ch, sign)
}
m.Set(helper.ParseArgs("", a...), fn, helper.ParseArgs[float64](0, a...))
queues[sign] = m
}
func del(queue mockmap.Map[string, Call], sign os.Signal, i int) {
mux.Lock()
defer mux.Unlock()
queue.DelByIndex(i)
queues[sign] = queue
}
func Stop() {
stopCh <- struct{}{}
}
func Wait() {
for {
select {
case <-stopCh:
break
case sign := <-ch:
queue, ok := queues[sign]
if !ok {
break
}
queue = hook(queue, sign)
queues[sign] = queue
if len(queue) < 1 {
signal.Reset(sign)
continue
}
for i, item := range queue {
if !item.Value() {
del(queue, sign, i)
}
}
if len(queues[sign]) < 1 {
signal.Reset(sign)
}
}
}
}

View File

@ -7,8 +7,7 @@ import (
)
func ParallelFilterAndMap[R, T any](a Stream[T], fn func(T) (R, bool), c int) Stream[R] {
var x []R
rr := safety.NewSlice(x)
rr := safety.NewSlice[R]()
a.ParallelForEach(func(t T) {
y, ok := fn(t)
if ok {
@ -84,7 +83,7 @@ func (r Stream[T]) ParallelForEach(fn func(T), c int) {
}
func (r Stream[T]) ParallelFilterAndMap(fn func(T) (T, bool), c int) Stream[T] {
rr := safety.NewSlice([]T{})
rr := safety.NewSlice[T]()
r.ParallelForEach(func(t T) {
v, ok := fn(t)
if ok {