feat: add middleware.cache

This commit is contained in:
loveuer
2024-07-25 17:31:57 +08:00
parent cee0bfb43d
commit 53f124c9ec
5 changed files with 159 additions and 4 deletions

View File

@ -23,6 +23,11 @@ func handleValue(value any) ([]byte, error) {
err error
)
switch value.(type) {
case []byte:
return value.([]byte), nil
}
if imp, ok := value.(encoded_value); ok {
bs, err = imp.MarshalBinary()
} else {

129
internal/middleware/cache/cache.go vendored Normal file
View File

@ -0,0 +1,129 @@
package cache
import (
"bytes"
"encoding/json"
"errors"
"github.com/loveuer/nf"
"github.com/loveuer/nf/nft/log"
"io"
"net/http"
"strconv"
"time"
"ultone/internal/database/cache"
"ultone/internal/model"
)
var (
defaultKeyFn = func(c *nf.Ctx) string {
return c.Request.URL.String()
}
defaultTimeout = 3600
defaultPrefix = "midd:cache"
)
type Config struct {
// if return "" (won't cache)
KeyFn func(c *nf.Ctx) string
// cache timeout(seconds)
Timeout int
Prefix string
Refresh bool
}
type store struct {
Body []byte `json:"body"`
Header http.Header `json:"header"`
When int64 `json:"when"`
}
func New(cfgs ...Config) nf.HandlerFunc {
if cache.Client == nil {
log.Panic("[middleware.cache] database cache client is nil")
}
var cfg Config
if len(cfgs) > 0 {
cfg = cfgs[0]
}
if cfg.KeyFn == nil {
cfg.KeyFn = defaultKeyFn
}
if cfg.Timeout <= 0 {
cfg.Timeout = defaultTimeout
}
if cfg.Prefix == "" {
cfg.Prefix = defaultPrefix
}
return func(c *nf.Ctx) error {
var (
key string
err error
bs []byte
res = new(store)
)
if key = cfg.KeyFn(c); key == "" {
return c.Next()
}
key = cfg.Prefix + ":" + key
duration := time.Duration(cfg.Timeout) * time.Second
if cfg.Refresh {
if bs, err = cache.Client.GetEx(c.Context(), key, duration); err != nil {
if !errors.Is(err, cache.ErrorKeyNotFound) {
log.Warn("[middleware.cache] cache get err: %s", err.Error())
}
goto FromNext
}
} else {
if bs, err = cache.Client.Get(c.Context(), key); err != nil {
if !errors.Is(err, cache.ErrorKeyNotFound) {
log.Warn("[middleware.cache] cache get err: %s", err.Error())
}
goto FromNext
}
}
if err = json.Unmarshal(bs, res); err != nil {
log.Warn("[middleware.cache] cache data unamrshal err: %s", err.Error())
goto FromNext
}
for key := range res.Header {
for idx := range res.Header[key] {
c.SetHeader(key, res.Header[key][idx])
}
}
c.SetHeader("X-Nf-Cache-At", strconv.Itoa(int(res.When)))
_, err = c.Write(res.Body)
return err
FromNext:
blw := &model.CustomResponseWriter{Body: bytes.NewBuffer(make([]byte, 0, 256)), ResponseWriter: c.Writer}
c.Writer = blw
rerr := c.Next()
resp, _ := io.ReadAll(blw.Body)
data := &store{Body: resp, Header: blw.Header().Clone(), When: time.Now().UnixMilli()}
cbs, _ := json.Marshal(data)
if err = cache.Client.SetEx(c.Context(), key, cbs, duration); err != nil {
log.Warn("[middleware.cache] cache client setex err: %s", err.Error())
}
return rerr
}
}

21
internal/model/writer.go Normal file
View File

@ -0,0 +1,21 @@
package model
import (
"bytes"
"github.com/loveuer/nf"
)
type CustomResponseWriter struct {
nf.ResponseWriter
Body *bytes.Buffer
}
func (w CustomResponseWriter) Write(b []byte) (int, error) {
w.Body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w CustomResponseWriter) WriteString(s string) (int, error) {
w.Body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}