Реализация кэширования
This commit is contained in:
parent
d39b98bc0f
commit
2243da0360
|
@ -165,6 +165,11 @@ func generateModelBase() {
|
|||
}
|
||||
}
|
||||
|
||||
// Создаем файл с функциями кэширования пользователей из JWT для их работы
|
||||
if err := templ.PrepareTmplFile("tmpl/backend/db/user-cache.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "db", "user-cache.go")); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// middleware
|
||||
if err := templ.PrepareTmplFile("tmpl/backend/middleware/middleware.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "middleware", "middleware.go")); err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -16,9 +16,12 @@ import (
|
|||
"gorm.io/gorm"
|
||||
{{ if .DB.SQLite }}"gorm.io/driver/sqlite"{{ end }}
|
||||
"gorm.io/gorm/logger"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
var RedisDB *redis.Client
|
||||
var UsersCache *UserCache
|
||||
|
||||
func DBConnect() {
|
||||
var err error
|
||||
|
@ -111,6 +114,15 @@ func DBConnect() {
|
|||
|
||||
}
|
||||
|
||||
if lib.AppConfig.RedisAddr != "" {
|
||||
RedisDB = redis.NewClient(&redis.Options{
|
||||
Addr: lib.AppConfig.RedisAddr,
|
||||
Password: lib.AppConfig.RedisPwd,
|
||||
DB: lib.AppConfig.RedisDB,
|
||||
Protocol: lib.AppConfig.RedisProtocol,
|
||||
})
|
||||
}
|
||||
UsersCache = NewUserCache(time.Second*time.Duration(lib.AppConfig.CacheTTL), RedisDB)
|
||||
}
|
||||
|
||||
// Получить роль по имени
|
||||
|
|
|
@ -105,8 +105,16 @@ func (user *User) BeforeCreate(scope *gorm.DB) (err error) {
|
|||
}
|
||||
|
||||
func (user *User) GetUserByID(id uuid.UUID, scope *gorm.DB) (usr User, err error) {
|
||||
if res := scope.Preload("Role").First(&usr, id); res.RowsAffected == 0 {
|
||||
if res := scope.Preload("UserRole.Role").First(&usr, id); res.RowsAffected == 0 {
|
||||
err = res.Error
|
||||
}
|
||||
if len(usr.UserRole) > 0 {
|
||||
usr.Roles = &[]Role{}
|
||||
var roles []Role
|
||||
for i := range usr.UserRole {
|
||||
roles = append(roles, usr.UserRole[i].Role)
|
||||
}
|
||||
usr.Roles = &roles
|
||||
}
|
||||
return
|
||||
}
|
112
lib/templ/tmpl/backend/db/user-cache.tmpl
Normal file
112
lib/templ/tmpl/backend/db/user-cache.tmpl
Normal file
|
@ -0,0 +1,112 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
"{{ .Name }}/db/model"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type uCache struct {
|
||||
User *model.User
|
||||
DT time.Time
|
||||
}
|
||||
|
||||
type UserCache struct {
|
||||
cache map[uuid.UUID]uCache
|
||||
cacheMutex sync.Mutex
|
||||
ttl time.Duration
|
||||
redisClient *redis.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewUserCache(ttl time.Duration, redisClient *redis.Client) *UserCache {
|
||||
uc := &UserCache{
|
||||
ttl: ttl,
|
||||
redisClient: redisClient,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
if uc.redisClient == nil {
|
||||
uc.cache = make(map[uuid.UUID]uCache)
|
||||
go uc.cleaner()
|
||||
}
|
||||
return uc
|
||||
}
|
||||
|
||||
func (uc *UserCache) cleaner() {
|
||||
for {
|
||||
func() {
|
||||
now := time.Now()
|
||||
uc.cacheMutex.Lock()
|
||||
defer uc.cacheMutex.Unlock()
|
||||
for k, v := range uc.cache {
|
||||
if v.DT.Add(uc.ttl).Before(now) {
|
||||
delete(uc.cache, k)
|
||||
}
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}
|
||||
|
||||
func (uc *UserCache) Get(id uuid.UUID, tx *gorm.DB) (user *model.User, err error) {
|
||||
if uc.redisClient == nil {
|
||||
uc.cacheMutex.Lock()
|
||||
defer uc.cacheMutex.Unlock()
|
||||
if val, ok := uc.cache[id]; ok {
|
||||
return val.User, nil
|
||||
}
|
||||
} else {
|
||||
var tmp string
|
||||
if tmp, err = uc.redisClient.Get(uc.ctx, id.String()).Result(); err == nil {
|
||||
var u model.User
|
||||
if err = json.Unmarshal([]byte(tmp), &u); err != nil {
|
||||
return
|
||||
}
|
||||
user = &u
|
||||
return
|
||||
}
|
||||
}
|
||||
if tx == nil {
|
||||
tx = BeginTransation()
|
||||
defer func() {
|
||||
EndTransaction(tx, err)
|
||||
}()
|
||||
}
|
||||
var u model.User
|
||||
u, err = u.GetUserByID(id, tx)
|
||||
user = &u
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if uc.redisClient != nil {
|
||||
var tmp []byte
|
||||
if tmp, err = json.Marshal(u); err != nil {
|
||||
return
|
||||
}
|
||||
uc.redisClient.Set(uc.ctx, user.ID.String(), string(tmp), uc.ttl)
|
||||
} else {
|
||||
uc.cache[user.ID] = uCache{
|
||||
User: user,
|
||||
DT: time.Now(),
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (uc *UserCache) Unset(id uuid.UUID) {
|
||||
if uc.redisClient == nil {
|
||||
uc.cacheMutex.Lock()
|
||||
defer uc.cacheMutex.Unlock()
|
||||
if _, ok := uc.cache[id]; ok {
|
||||
delete(uc.cache, id)
|
||||
}
|
||||
} else {
|
||||
uc.redisClient.Del(uc.ctx, id.String())
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ gorm.io/driver/mysql
|
|||
gorm.io/driver/postgres
|
||||
gorm.io/gorm/logger
|
||||
gorm.io/datatypes
|
||||
github.com/redis/go-redis/v9
|
||||
github.com/golang-jwt/jwt
|
||||
github.com/alexflint/go-arg
|
||||
github.com/labstack/echo/v4
|
||||
|
|
|
@ -16,6 +16,13 @@ var AppConfig struct {
|
|||
Host string `arg:"--host,env:HOST" default:"example.com" help:"Host name for display and other processes"`
|
||||
Port string `arg:"--port,env:PORT" default:"3000" help:"Open port for incoming connections"`
|
||||
|
||||
RedisAddr string `arg:"--redis-addr,env:REDIS_ADDR" help:"Address for Redis server for caching data. If not set - not use"`
|
||||
RedisPwd string `arg:"--redis-password,env:REDIS_PASSWORD" help:"Password for connect to Redis"`
|
||||
RedisDB int `arg:"--redis-db,env:REDIS_DB" default:"0" help:"Number DB in Redis"`
|
||||
RedisProtocol int `arg:"--redis-protocol,env:REDIS_PROTOCOL" default:"3" help:"Specify the version of the RESP protocol. Default: 3"`
|
||||
|
||||
CacheTTL int `arg:"--cache-ttl,env:CACHE_TTL" default:"604800" help:"Cache time to live, after this itemcan be remove from cache"`
|
||||
|
||||
LdapURL string `arg:"--ldap-url,env:LDAP_URL" help:"Ldap url for server"`
|
||||
LdapBind string `arg:"--ldap-bind,env:LDAP_BIND" help:"Ldap bind for credential"`
|
||||
LdapPassword string `arg:"--ldap-pwd,env:LDAP_PWD" help:"Ldap password for credential"`
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
|
||||
func InRole(idUser uuid.UUID, roleName string) bool {
|
||||
var err error
|
||||
tx := db.BeginTransation()
|
||||
/*tx := db.BeginTransation()
|
||||
|
||||
defer func() {
|
||||
db.EndTransaction(tx, err)
|
||||
}()
|
||||
}()*/
|
||||
|
||||
var userRoles []model.UserRole
|
||||
/*var userRoles []model.UserRole
|
||||
if res := tx.Joins("Role").Find(&userRoles, "id_user = ?", idUser); res.RowsAffected == 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -26,6 +26,17 @@ func InRole(idUser uuid.UUID, roleName string) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
var user *model.User
|
||||
if user, err = db.UsersCache.Get(idUser, nil); err == nil && user != nil {
|
||||
if len(*user.Roles) > 0 {
|
||||
for _, item := range *user.Roles {
|
||||
if item.Name == roleName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -33,12 +44,12 @@ func InRole(idUser uuid.UUID, roleName string) bool {
|
|||
|
||||
func InsRole(idUser uuid.UUID, roleNames []string) bool {
|
||||
|
||||
if len(roleNames) == 0{
|
||||
if len(roleNames) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var err error
|
||||
tx := db.BeginTransation()
|
||||
/*tx := db.BeginTransation()
|
||||
|
||||
defer func() {
|
||||
db.EndTransaction(tx, err)
|
||||
|
@ -47,17 +58,29 @@ func InsRole(idUser uuid.UUID, roleNames []string) bool {
|
|||
var userRoles []model.UserRole
|
||||
if res := tx.Joins("Role").Find(&userRoles, "id_user = ?", idUser); res.RowsAffected == 0 {
|
||||
return false
|
||||
}
|
||||
}*/
|
||||
|
||||
if len(userRoles) > 0 {
|
||||
/*if len(userRoles) > 0 {
|
||||
for _, item := range userRoles {
|
||||
for _,item2:=range roleNames{
|
||||
for _, item2 := range roleNames {
|
||||
if item.Role.Name == item2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
var user *model.User
|
||||
if user, err = db.UsersCache.Get(idUser, nil); err == nil && user != nil {
|
||||
if len(*user.Roles) > 0 {
|
||||
for _, item := range *user.Roles {
|
||||
for _, item2 := range roleNames {
|
||||
if item.Name == item2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"{{ .Name }}/route/api/user/role"
|
||||
"{{ .Name }}/middleware"
|
||||
"{{ .Name }}/structs"
|
||||
"{{ .Name }}/db"
|
||||
|
||||
"net/http"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
@ -141,7 +142,9 @@ func put(c echo.Context) error {
|
|||
Message: &[]string{"Отказано в доступе"}[0],
|
||||
})
|
||||
}
|
||||
return restPut(c)
|
||||
res := restPut(c)
|
||||
db.UsersCache.Unset(uuid.FromStringOrNil(c.Param("id")))
|
||||
return res
|
||||
}
|
||||
|
||||
// LockUser lockUser
|
||||
|
@ -170,7 +173,9 @@ func lock(c echo.Context) error {
|
|||
Message: &[]string{"Отказано в доступе"}[0],
|
||||
})
|
||||
}
|
||||
return restLock(c)
|
||||
res := restLock(c)
|
||||
db.UsersCache.Unset(uuid.FromStringOrNil(c.Param("id")))
|
||||
return res
|
||||
}
|
||||
|
||||
// UnlockUser unlockUser
|
||||
|
@ -199,7 +204,9 @@ func unlock(c echo.Context) error {
|
|||
Message: &[]string{"Отказано в доступе"}[0],
|
||||
})
|
||||
}
|
||||
return restUnlock(c)
|
||||
res := restUnlock(c)
|
||||
db.UsersCache.Unset(uuid.FromStringOrNil(c.Param("id")))
|
||||
return res
|
||||
}
|
||||
|
||||
// DeleteUser deleteUser
|
||||
|
@ -228,7 +235,9 @@ func delete(c echo.Context) error {
|
|||
Message: &[]string{"Отказано в доступе"}[0],
|
||||
})
|
||||
}
|
||||
return restDelete(c)
|
||||
res := restDelete(c)
|
||||
db.UsersCache.Unset(uuid.FromStringOrNil(c.Param("id")))
|
||||
return res
|
||||
}
|
||||
|
||||
// LdapSearch ldapSearch
|
||||
|
|
Loading…
Reference in New Issue
Block a user