Some checks failed
CI / Build & Test (push) Failing after 1m31s
- Add user management endpoints (list/create/get/update/delete) with pagination and search - Add dashboard stats endpoint with tenant/user/online counts and growth metrics - Add tenant resolver middleware for multi-tenant request scoping - Add i18n kernel with zh/en message files and AcceptLanguage middleware - Add WebSocket hub/handler for real-time communication - Add job scheduler kernel with cron support - Add plugin sandbox for isolated execution - Add storage kernel (local filesystem) - Add event bus kernel for pub/sub - Add cache kernel abstraction - Add database migration runner and version upgrade checker - Add rate limiting middleware with Redis backend - Add SQL migrations for rbac, audit_logs, settings, notifications, examples - Extend user repository with list/delete/count operations - Register all module routes with tenant resolver
102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
type Store struct {
|
|
rdb *redis.Client
|
|
prefix string
|
|
}
|
|
|
|
func NewStore(rdb *redis.Client, prefix string) *Store {
|
|
return &Store{rdb: rdb, prefix: prefix}
|
|
}
|
|
|
|
func (s *Store) key(tenantID, key string) string {
|
|
if tenantID == "" {
|
|
return fmt.Sprintf("%s:%s", s.prefix, key)
|
|
}
|
|
return fmt.Sprintf("%s:%s:%s", s.prefix, tenantID, key)
|
|
}
|
|
|
|
func (s *Store) Get(ctx context.Context, tenantID, key string, dest interface{}) error {
|
|
val, err := s.rdb.Get(ctx, s.key(tenantID, key)).Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal([]byte(val), dest)
|
|
}
|
|
|
|
func (s *Store) Set(ctx context.Context, tenantID, key string, value interface{}, ttl time.Duration) error {
|
|
data, err := json.Marshal(value)
|
|
if err != nil {
|
|
return fmt.Errorf("cache marshal: %w", err)
|
|
}
|
|
return s.rdb.Set(ctx, s.key(tenantID, key), data, ttl).Err()
|
|
}
|
|
|
|
func (s *Store) Delete(ctx context.Context, tenantID, key string) error {
|
|
return s.rdb.Del(ctx, s.key(tenantID, key)).Err()
|
|
}
|
|
|
|
func (s *Store) DeletePattern(ctx context.Context, tenantID, pattern string) error {
|
|
fullPattern := s.key(tenantID, pattern)
|
|
keys, err := s.rdb.Keys(ctx, fullPattern).Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(keys) == 0 {
|
|
return nil
|
|
}
|
|
return s.rdb.Del(ctx, keys...).Err()
|
|
}
|
|
|
|
func (s *Store) GetOrSet(ctx context.Context, tenantID, key string, dest interface{}, ttl time.Duration, fetch func() (interface{}, error)) error {
|
|
err := s.Get(ctx, tenantID, key, dest)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if err != redis.Nil {
|
|
return err
|
|
}
|
|
|
|
val, err := fetch()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := json.Marshal(val)
|
|
if err != nil {
|
|
return fmt.Errorf("cache marshal: %w", err)
|
|
}
|
|
|
|
if err := s.rdb.Set(ctx, s.key(tenantID, key), data, ttl).Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return json.Unmarshal(data, dest)
|
|
}
|
|
|
|
func (s *Store) Exists(ctx context.Context, tenantID, key string) (bool, error) {
|
|
n, err := s.rdb.Exists(ctx, s.key(tenantID, key)).Result()
|
|
return n > 0, err
|
|
}
|
|
|
|
func (s *Store) Expire(ctx context.Context, tenantID, key string, ttl time.Duration) error {
|
|
return s.rdb.Expire(ctx, s.key(tenantID, key), ttl).Err()
|
|
}
|
|
|
|
func (s *Store) Incr(ctx context.Context, tenantID, key string) (int64, error) {
|
|
return s.rdb.Incr(ctx, s.key(tenantID, key)).Result()
|
|
}
|
|
|
|
func (s *Store) Client() *redis.Client {
|
|
return s.rdb
|
|
}
|