feat: update cache; fix: user logout
This commit is contained in:
parent
534b9586f2
commit
32b30ae183
@ -24,4 +24,4 @@
|
||||
"username": "admin",
|
||||
"password": "password"
|
||||
}
|
||||
}
|
||||
}
|
6
go.mod
6
go.mod
@ -1,8 +1,6 @@
|
||||
module ultone
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.23.4
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
gitea.com/taozitaozi/gredis v0.0.0-20241226104049-af698e5ad477
|
||||
@ -18,7 +16,7 @@ require (
|
||||
github.com/jackc/pgtype v1.12.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9
|
||||
github.com/loveuer/esgo2dump v0.3.3
|
||||
github.com/loveuer/nf v0.2.12
|
||||
github.com/loveuer/nf v0.3.1
|
||||
github.com/loveuer/ngorm/v2 v2.1.1
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/rabbitmq/amqp091-go v1.10.0
|
||||
|
18
go.sum
18
go.sum
@ -69,11 +69,8 @@ github.com/facebook/fbthrift v0.31.1-0.20211129061412-801ed7f9f295/go.mod h1:2tn
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
@ -112,7 +109,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@ -184,12 +180,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@ -197,8 +191,8 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/loveuer/esgo2dump v0.3.3 h1:/AidoaFV7bDRyT1ycyBKs4XGmyVs2ShaUKrpEBiUWkM=
|
||||
github.com/loveuer/esgo2dump v0.3.3/go.mod h1:thZvfsO0kd7Ck3TA0jc9rRc4CuIa4Iuiq6tF3tCqXEY=
|
||||
github.com/loveuer/nf v0.2.12 h1:1Og+ORHsOWKFmy9kKJhjvXDkdbaurH82HjIxuGA3nNM=
|
||||
github.com/loveuer/nf v0.2.12/go.mod h1:M6reF17/kJBis30H4DxR5hrtgo/oJL4AV4cBe4HzJLw=
|
||||
github.com/loveuer/nf v0.3.1 h1:FTmyAC9LQF06BVGeGwrwaYfbC6MIQMqr+GoZUQQPvXU=
|
||||
github.com/loveuer/nf v0.3.1/go.mod h1:aApO+2cSP0ULczkfS4OVw8zfWM3rY8gQrzc5PnVV7lY=
|
||||
github.com/loveuer/ngorm/v2 v2.1.1 h1:v+ut5BjeSBFU87o800pI8Q3fXEOUAkvk5+btMw2oOEc=
|
||||
github.com/loveuer/ngorm/v2 v2.1.1/go.mod h1:BVhFGQsRMdcf08MtmwwRihwCR/x7wDd0Fzy8Xj+edM0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
@ -216,13 +210,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
|
||||
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -240,7 +231,6 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
@ -282,7 +272,6 @@ github.com/tdewolff/parse/v2 v2.7.11 h1:v+W45LnzmjndVlfqPCT5gGjAAZKd1GJGOPJveTIk
|
||||
github.com/tdewolff/parse/v2 v2.7.11/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/vesoft-inc/nebula-go/v3 v3.5.0 h1:2ZSkoBxtIfs15AXJXqrAPDPd0Z9HrzKR7YKXPqlJcR0=
|
||||
github.com/vesoft-inc/nebula-go/v3 v3.5.0/go.mod h1:+sXv05jYQBARdTbTcIEsWVXCnF/6ttOlDK35xQ6m54s=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
@ -299,7 +288,6 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
@ -456,10 +444,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -7,6 +7,12 @@ Content-Type: application/json
|
||||
"password": "123456"
|
||||
}
|
||||
|
||||
### logout
|
||||
POST http://127.0.0.1:8080/api/user/auth/logout
|
||||
Content-Type: application/json
|
||||
|
||||
{}
|
||||
|
||||
### verify login state
|
||||
GET http://127.0.0.1:8080/api/user/auth/login
|
||||
|
||||
|
@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"ultone/internal/api"
|
||||
"ultone/internal/controller"
|
||||
"ultone/internal/database/cache"
|
||||
@ -12,19 +13,17 @@ import (
|
||||
"ultone/internal/tool"
|
||||
)
|
||||
|
||||
var (
|
||||
filename string
|
||||
)
|
||||
var filename string
|
||||
|
||||
func execute(ctx context.Context) error {
|
||||
tool.Must(opt.Init(filename))
|
||||
tool.Must(db.Init(ctx, opt.Cfg.DB.Uri))
|
||||
tool.Must(cache.Init())
|
||||
tool.Must(cache.Init(ctx, opt.Cfg.Cache.Uri))
|
||||
|
||||
// todo: if elastic search required
|
||||
//tool.Must(es.Init(ctx, opt.Cfg.ES.Uri))
|
||||
// tool.Must(es.Init(ctx, opt.Cfg.ES.Uri))
|
||||
// 或者使用 https://github.com/olivere/elastic
|
||||
//tool.Must(elastic.Init(ctx, opt.Cfg.ES.Uri))
|
||||
// tool.Must(elastic.Init(ctx, opt.Cfg.ES.Uri))
|
||||
|
||||
// todo: if nebula required
|
||||
// tool.Must(nebula.Init(ctx, opt.Cfg.Nebula))
|
||||
@ -34,7 +33,7 @@ func execute(ctx context.Context) error {
|
||||
tool.Must(api.Start(ctx))
|
||||
|
||||
// todo: if need some cli operation, should start local unix rpc svc
|
||||
//tool.Must(unix.Start(ctx))
|
||||
// tool.Must(unix.Start(ctx))
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
|
63
internal/database/cache/cache.go
vendored
Normal file
63
internal/database/cache/cache.go
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache interface {
|
||||
Get(ctx context.Context, key string) ([]byte, error)
|
||||
Gets(ctx context.Context, keys ...string) ([][]byte, error)
|
||||
GetScan(ctx context.Context, key string) Scanner
|
||||
GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error)
|
||||
GetExScan(ctx context.Context, key string, duration time.Duration) Scanner
|
||||
// Set value 会被序列化, 优先使用 MarshalBinary 方法, 没有则执行 json.Marshal
|
||||
Set(ctx context.Context, key string, value any) error
|
||||
Sets(ctx context.Context, vm map[string]any) error
|
||||
// SetEx value 会被序列化, 优先使用 MarshalBinary 方法, 没有则执行 json.Marshal
|
||||
SetEx(ctx context.Context, key string, value any, duration time.Duration) error
|
||||
Del(ctx context.Context, keys ...string) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
var Client Cache
|
||||
|
||||
type Scanner interface {
|
||||
Scan(model any) error
|
||||
}
|
||||
|
||||
type encoded_value interface {
|
||||
MarshalBinary() ([]byte, error)
|
||||
}
|
||||
|
||||
type decoded_value interface {
|
||||
UnmarshalBinary(bs []byte) error
|
||||
}
|
||||
|
||||
const (
|
||||
Prefix = "upp:"
|
||||
)
|
||||
|
||||
var ErrorKeyNotFound = errors.New("key not found")
|
||||
|
||||
func handleValue(value any) ([]byte, error) {
|
||||
var (
|
||||
bs []byte
|
||||
err error
|
||||
)
|
||||
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return value.([]byte), nil
|
||||
}
|
||||
|
||||
if imp, ok := value.(encoded_value); ok {
|
||||
bs, err = imp.MarshalBinary()
|
||||
} else {
|
||||
bs, err = json.Marshal(value)
|
||||
}
|
||||
|
||||
return bs, err
|
||||
}
|
@ -2,13 +2,13 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/golang-lru/v2/expirable"
|
||||
_ "github.com/hashicorp/golang-lru/v2/expirable"
|
||||
"time"
|
||||
"ultone/internal/interfaces"
|
||||
)
|
||||
|
||||
var _ interfaces.Cacher = (*_lru)(nil)
|
||||
var _ Cache = (*_lru)(nil)
|
||||
|
||||
type _lru struct {
|
||||
client *expirable.LRU[string, *_lru_value]
|
||||
@ -38,6 +38,24 @@ func (l *_lru) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
return v.bs, nil
|
||||
}
|
||||
|
||||
func (l *_lru) Gets(ctx context.Context, keys ...string) ([][]byte, error) {
|
||||
bss := make([][]byte, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
bs, err := l.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bss = append(bss, bs)
|
||||
}
|
||||
|
||||
return bss, nil
|
||||
}
|
||||
|
||||
func (l *_lru) GetScan(ctx context.Context, key string) Scanner {
|
||||
return newScanner(l.Get(ctx, key))
|
||||
}
|
||||
|
||||
func (l *_lru) GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error) {
|
||||
v, ok := l.client.Get(key)
|
||||
if !ok {
|
||||
@ -64,6 +82,10 @@ func (l *_lru) GetEx(ctx context.Context, key string, duration time.Duration) ([
|
||||
return v.bs, nil
|
||||
}
|
||||
|
||||
func (l *_lru) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner {
|
||||
return newScanner(l.GetEx(ctx, key, duration))
|
||||
}
|
||||
|
||||
func (l *_lru) Set(ctx context.Context, key string, value any) error {
|
||||
bs, err := handleValue(value)
|
||||
if err != nil {
|
||||
@ -94,6 +116,16 @@ func (l *_lru) SetEx(ctx context.Context, key string, value any, duration time.D
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *_lru) Sets(ctx context.Context, m map[string]any) error {
|
||||
for k, v := range m {
|
||||
if err := l.Set(ctx, k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *_lru) Del(ctx context.Context, keys ...string) error {
|
||||
for _, key := range keys {
|
||||
l.client.Remove(key)
|
||||
@ -102,7 +134,12 @@ func (l *_lru) Del(ctx context.Context, keys ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newLRUCache() (interfaces.Cacher, error) {
|
||||
func (l *_lru) Close() error {
|
||||
l.client = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func newLRUCache() (Cache, error) {
|
||||
client := expirable.NewLRU[string, *_lru_value](1024*1024, nil, 0)
|
||||
|
||||
return &_lru{client: client}, nil
|
@ -5,17 +5,24 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
"ultone/internal/interfaces"
|
||||
|
||||
"gitea.com/taozitaozi/gredis"
|
||||
)
|
||||
|
||||
var _ interfaces.Cacher = (*_mem)(nil)
|
||||
var _ Cache = (*_mem)(nil)
|
||||
|
||||
type _mem struct {
|
||||
client *gredis.Gredis
|
||||
}
|
||||
|
||||
func (m *_mem) GetScan(ctx context.Context, key string) Scanner {
|
||||
return newScanner(m.Get(ctx, key))
|
||||
}
|
||||
|
||||
func (m *_mem) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner {
|
||||
return newScanner(m.GetEx(ctx, key, duration))
|
||||
}
|
||||
|
||||
func (m *_mem) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
v, err := m.client.Get(key)
|
||||
if err != nil {
|
||||
@ -34,6 +41,20 @@ func (m *_mem) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func (m *_mem) Gets(ctx context.Context, keys ...string) ([][]byte, error) {
|
||||
bss := make([][]byte, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
bs, err := m.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bss = append(bss, bs)
|
||||
}
|
||||
|
||||
return bss, nil
|
||||
}
|
||||
|
||||
func (m *_mem) GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error) {
|
||||
v, err := m.client.GetEx(key, duration)
|
||||
if err != nil {
|
||||
@ -60,6 +81,16 @@ func (m *_mem) Set(ctx context.Context, key string, value any) error {
|
||||
return m.client.Set(key, bs)
|
||||
}
|
||||
|
||||
func (m *_mem) Sets(ctx context.Context, vm map[string]any) error {
|
||||
for k, v := range vm {
|
||||
if err := m.Set(ctx, k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *_mem) SetEx(ctx context.Context, key string, value any, duration time.Duration) error {
|
||||
bs, err := handleValue(value)
|
||||
if err != nil {
|
||||
@ -72,3 +103,9 @@ func (m *_mem) Del(ctx context.Context, keys ...string) error {
|
||||
m.client.Delete(keys...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *_mem) Close() error {
|
||||
m.client = nil
|
||||
|
||||
return nil
|
||||
}
|
@ -3,8 +3,11 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type _redis struct {
|
||||
@ -24,6 +27,28 @@ func (r *_redis) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
return []byte(result), nil
|
||||
}
|
||||
|
||||
func (r *_redis) Gets(ctx context.Context, keys ...string) ([][]byte, error) {
|
||||
result, err := r.client.MGet(ctx, keys...).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, ErrorKeyNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lo.Map(
|
||||
result,
|
||||
func(item any, index int) []byte {
|
||||
return []byte(cast.ToString(item))
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (r *_redis) GetScan(ctx context.Context, key string) Scanner {
|
||||
return newScanner(r.Get(ctx, key))
|
||||
}
|
||||
|
||||
func (r *_redis) GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error) {
|
||||
result, err := r.client.GetEx(ctx, key, duration).Result()
|
||||
if err != nil {
|
||||
@ -37,6 +62,10 @@ func (r *_redis) GetEx(ctx context.Context, key string, duration time.Duration)
|
||||
return []byte(result), nil
|
||||
}
|
||||
|
||||
func (r *_redis) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner {
|
||||
return newScanner(r.GetEx(ctx, key, duration))
|
||||
}
|
||||
|
||||
func (r *_redis) Set(ctx context.Context, key string, value any) error {
|
||||
bs, err := handleValue(value)
|
||||
if err != nil {
|
||||
@ -47,6 +76,20 @@ func (r *_redis) Set(ctx context.Context, key string, value any) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *_redis) Sets(ctx context.Context, values map[string]any) error {
|
||||
vm := make(map[string]any)
|
||||
for k, v := range values {
|
||||
bs, err := handleValue(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm[k] = bs
|
||||
}
|
||||
|
||||
return r.client.MSet(ctx, vm).Err()
|
||||
}
|
||||
|
||||
func (r *_redis) SetEx(ctx context.Context, key string, value any, duration time.Duration) error {
|
||||
bs, err := handleValue(value)
|
||||
if err != nil {
|
||||
@ -61,3 +104,7 @@ func (r *_redis) SetEx(ctx context.Context, key string, value any, duration time
|
||||
func (r *_redis) Del(ctx context.Context, keys ...string) error {
|
||||
return r.client.Del(ctx, keys...).Err()
|
||||
}
|
||||
|
||||
func (r *_redis) Close() error {
|
||||
return r.client.Close()
|
||||
}
|
38
internal/database/cache/client.go
vendored
38
internal/database/cache/client.go
vendored
@ -1,38 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"ultone/internal/interfaces"
|
||||
)
|
||||
|
||||
var (
|
||||
Client interfaces.Cacher
|
||||
)
|
||||
|
||||
type encoded_value interface {
|
||||
MarshalBinary() ([]byte, error)
|
||||
}
|
||||
|
||||
type decoded_value interface {
|
||||
UnmarshalBinary(bs []byte) error
|
||||
}
|
||||
|
||||
func handleValue(value any) ([]byte, error) {
|
||||
var (
|
||||
bs []byte
|
||||
err error
|
||||
)
|
||||
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return value.([]byte), nil
|
||||
}
|
||||
|
||||
if imp, ok := value.(encoded_value); ok {
|
||||
bs, err = imp.MarshalBinary()
|
||||
} else {
|
||||
bs, err = json.Marshal(value)
|
||||
}
|
||||
|
||||
return bs, err
|
||||
}
|
7
internal/database/cache/error.go
vendored
7
internal/database/cache/error.go
vendored
@ -1,7 +0,0 @@
|
||||
package cache
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrorKeyNotFound = errors.New("key not found")
|
||||
)
|
@ -1,30 +1,31 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.com/taozitaozi/gredis"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"net/url"
|
||||
"strings"
|
||||
"ultone/internal/opt"
|
||||
|
||||
"ultone/internal/tool"
|
||||
|
||||
"gitea.com/taozitaozi/gredis"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
|
||||
func New(ctx context.Context, uri string) (Cache, error) {
|
||||
var (
|
||||
err error
|
||||
err error
|
||||
newClient Cache
|
||||
strs = strings.Split(uri, "::")
|
||||
)
|
||||
|
||||
strs := strings.Split(opt.Cfg.Cache.Uri, "::")
|
||||
|
||||
switch strs[0] {
|
||||
case "memory":
|
||||
gc := gredis.NewGredis(1024 * 1024)
|
||||
Client = &_mem{client: gc}
|
||||
newClient = &_mem{client: gc}
|
||||
case "lru":
|
||||
if Client, err = newLRUCache(); err != nil {
|
||||
return err
|
||||
if newClient, err = newLRUCache(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "redis":
|
||||
var (
|
||||
@ -33,7 +34,7 @@ func Init() error {
|
||||
)
|
||||
|
||||
if len(strs) != 2 {
|
||||
return fmt.Errorf("cache.Init: invalid cache uri: %s", opt.Cfg.Cache.Uri)
|
||||
return nil, fmt.Errorf("cache.Init: invalid cache uri: %s", uri)
|
||||
}
|
||||
|
||||
uri := strs[1]
|
||||
@ -43,7 +44,7 @@ func Init() error {
|
||||
}
|
||||
|
||||
if ins, err = url.Parse(uri); err != nil {
|
||||
return fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", opt.Cfg.Cache.Uri, err.Error())
|
||||
return nil, fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", uri, err.Error())
|
||||
}
|
||||
|
||||
addr := ins.Host
|
||||
@ -58,13 +59,18 @@ func Init() error {
|
||||
})
|
||||
|
||||
if err = rc.Ping(tool.Timeout(5)).Err(); err != nil {
|
||||
return fmt.Errorf("cache.Init: redis ping err: %s", err.Error())
|
||||
return nil, fmt.Errorf("cache.Init: redis ping err: %s", err.Error())
|
||||
}
|
||||
|
||||
Client = &_redis{client: rc}
|
||||
newClient = &_redis{client: rc}
|
||||
default:
|
||||
return fmt.Errorf("cache type %s not support", strs[0])
|
||||
return nil, fmt.Errorf("cache type %s not support", strs[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
return newClient, nil
|
||||
}
|
||||
|
||||
func Init(ctx context.Context, uri string) (err error) {
|
||||
Client, err = New(ctx, uri)
|
||||
return
|
||||
}
|
20
internal/database/cache/scan.go
vendored
Normal file
20
internal/database/cache/scan.go
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package cache
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type scanner struct {
|
||||
err error
|
||||
bs []byte
|
||||
}
|
||||
|
||||
func (s *scanner) Scan(model any) error {
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
|
||||
return json.Unmarshal(s.bs, model)
|
||||
}
|
||||
|
||||
func newScanner(bs []byte, err error) *scanner {
|
||||
return &scanner{bs: bs, err: err}
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
"ultone/internal/controller"
|
||||
"ultone/internal/database/cache"
|
||||
"ultone/internal/database/db"
|
||||
"ultone/internal/log"
|
||||
"ultone/internal/middleware/oplog"
|
||||
"ultone/internal/model"
|
||||
"ultone/internal/opt"
|
||||
@ -75,26 +76,27 @@ func AuthLogin(c *nf.Ctx) error {
|
||||
|
||||
if !opt.MultiLogin {
|
||||
var (
|
||||
last = fmt.Sprintf("%s:user:last_token:%d", opt.CachePrefix, target.Id)
|
||||
bs []byte
|
||||
lastKey = fmt.Sprintf("%s:user:last_token:%d", opt.CachePrefix, target.Id)
|
||||
lastToken string
|
||||
)
|
||||
|
||||
// 获取之前的 token
|
||||
if bs, err = cache.Client.Get(tool.Timeout(3), last); err == nil {
|
||||
key := fmt.Sprintf("%s:user:token:%s", opt.CachePrefix, string(bs))
|
||||
_ = cache.Client.Del(tool.Timeout(3), key)
|
||||
if err = cache.Client.GetScan(tool.Timeout(3), lastKey).Scan(&lastToken); err != nil {
|
||||
if !errors.Is(err, cache.ErrorKeyNotFound) {
|
||||
log.Warn(c.Context(), "handler.AuthLogin: get last token err = %v", err)
|
||||
goto HandleMultiEnd
|
||||
}
|
||||
}
|
||||
|
||||
// 删掉之前的 token
|
||||
if len(bs) > 0 {
|
||||
_ = controller.UserController.RmToken(c.Context(), string(bs))
|
||||
}
|
||||
controller.UserController.RmToken(c.Context(), lastToken)
|
||||
|
||||
// 将当前的 token 存入 last_token
|
||||
if err = cache.Client.Set(tool.Timeout(3), last, token); err != nil {
|
||||
if err = cache.Client.Set(tool.Timeout(3), lastKey, token); err != nil {
|
||||
return resp.Resp500(c, err.Error())
|
||||
}
|
||||
}
|
||||
HandleMultiEnd:
|
||||
|
||||
c.Set("Set-Cookie", fmt.Sprintf("%s=%s; Path=/", opt.CookieName, token))
|
||||
c.Locals("user", target)
|
||||
@ -121,12 +123,28 @@ func AuthVerify(c *nf.Ctx) error {
|
||||
}
|
||||
|
||||
func AuthLogout(c *nf.Ctx) error {
|
||||
defer func() {
|
||||
c.Set("Set-Cookie", fmt.Sprintf("%s=; Path=/; Max-Age=0", opt.CookieName))
|
||||
}()
|
||||
|
||||
op, ok := c.Locals("user").(*model.User)
|
||||
if !ok {
|
||||
return resp.Resp401(c, nil)
|
||||
}
|
||||
|
||||
_ = controller.UserController.RmUserCache(c.Context(), op.Id)
|
||||
token, ok := c.Locals("token").(string)
|
||||
if !ok {
|
||||
return resp.Resp401(c, nil)
|
||||
}
|
||||
|
||||
if !opt.MultiLogin {
|
||||
_ = controller.UserController.RmUserCache(c.Context(), op.Id)
|
||||
lastKey := fmt.Sprintf("%s:user:last_token:%d", opt.CachePrefix, op.Id)
|
||||
cache.Client.Del(c.Context(), lastKey)
|
||||
}
|
||||
|
||||
// 删掉之前的 token
|
||||
controller.UserController.RmToken(c.Context(), token)
|
||||
|
||||
c.Locals(opt.OpLogLocalKey, &oplog.OpLog{
|
||||
Type: model.OpLogTypeLogout,
|
||||
|
@ -1,16 +0,0 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cacher interface {
|
||||
Get(ctx context.Context, key string) ([]byte, error)
|
||||
GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error)
|
||||
// Set value 会被序列化, 优先使用 MarshalBinary 方法, 没有则执行 json.Marshal
|
||||
Set(ctx context.Context, key string, value any) error
|
||||
// SetEx value 会被序列化, 优先使用 MarshalBinary 方法, 没有则执行 json.Marshal
|
||||
SetEx(ctx context.Context, key string, value any, duration time.Duration) error
|
||||
Del(ctx context.Context, keys ...string) error
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package tool
|
||||
|
||||
import "cmp"
|
||||
|
||||
func Min[T cmp.Ordered](a, b T) T {
|
||||
func Min[T ~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64 | ~float32 | ~float64](a, b T) T {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
@ -10,7 +8,7 @@ func Min[T cmp.Ordered](a, b T) T {
|
||||
return b
|
||||
}
|
||||
|
||||
func Max[T cmp.Ordered](a, b T) T {
|
||||
func Max[T ~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64 | ~float32 | ~float64](a, b T) T {
|
||||
if a >= b {
|
||||
return a
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user