Files
gooseek/backend/pkg/cache/redis.go
home 06fe57c765 feat: Go backend, enhanced search, new widgets, Docker deploy
Major changes:
- Add Go backend (backend/) with microservices architecture
- Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler,
  proxy-manager, media-search, fastClassifier, language detection
- New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard,
  UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions
- Improved discover-svc with discover-db integration
- Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md)
- Library-svc: project_id schema migration
- Remove deprecated finance-svc and travel-svc
- Localization improvements across services

Made-with: Cursor
2026-02-27 04:15:32 +03:00

184 lines
4.3 KiB
Go

package cache
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"time"
"github.com/redis/go-redis/v9"
)
type RedisCache struct {
client *redis.Client
prefix string
}
func NewRedisCache(redisURL, prefix string) (*RedisCache, error) {
opts, err := redis.ParseURL(redisURL)
if err != nil {
return nil, err
}
client := redis.NewClient(opts)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := client.Ping(ctx).Err(); err != nil {
return nil, err
}
return &RedisCache{
client: client,
prefix: prefix,
}, nil
}
func (c *RedisCache) Close() error {
return c.client.Close()
}
func (c *RedisCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
data, err := json.Marshal(value)
if err != nil {
return err
}
return c.client.Set(ctx, c.prefix+":"+key, data, ttl).Err()
}
func (c *RedisCache) Get(ctx context.Context, key string, dest interface{}) error {
data, err := c.client.Get(ctx, c.prefix+":"+key).Bytes()
if err != nil {
return err
}
return json.Unmarshal(data, dest)
}
func (c *RedisCache) Exists(ctx context.Context, key string) (bool, error) {
n, err := c.client.Exists(ctx, c.prefix+":"+key).Result()
if err != nil {
return false, err
}
return n > 0, nil
}
func (c *RedisCache) Delete(ctx context.Context, key string) error {
return c.client.Del(ctx, c.prefix+":"+key).Err()
}
func (c *RedisCache) SetJSON(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
return c.Set(ctx, key, value, ttl)
}
func (c *RedisCache) GetJSON(ctx context.Context, key string, dest interface{}) error {
return c.Get(ctx, key, dest)
}
type CacheKey string
const (
KeySearchResults CacheKey = "search"
KeyArticleSummary CacheKey = "summary"
KeyDigest CacheKey = "digest"
KeyChatResponse CacheKey = "chat"
)
func HashKey(parts ...string) string {
combined := ""
for _, p := range parts {
combined += p + ":"
}
hash := sha256.Sum256([]byte(combined))
return hex.EncodeToString(hash[:16])
}
func (c *RedisCache) CacheSearch(ctx context.Context, query string, results interface{}, ttl time.Duration) error {
key := string(KeySearchResults) + ":" + HashKey(query)
return c.Set(ctx, key, results, ttl)
}
func (c *RedisCache) GetCachedSearch(ctx context.Context, query string, dest interface{}) error {
key := string(KeySearchResults) + ":" + HashKey(query)
return c.Get(ctx, key, dest)
}
func (c *RedisCache) CacheArticleSummary(ctx context.Context, url string, events []string, ttl time.Duration) error {
key := string(KeyArticleSummary) + ":" + HashKey(url)
return c.Set(ctx, key, events, ttl)
}
func (c *RedisCache) GetCachedArticleSummary(ctx context.Context, url string) ([]string, error) {
key := string(KeyArticleSummary) + ":" + HashKey(url)
var events []string
if err := c.Get(ctx, key, &events); err != nil {
return nil, err
}
return events, nil
}
func (c *RedisCache) CacheDigest(ctx context.Context, topic, region, title string, digest interface{}, ttl time.Duration) error {
key := string(KeyDigest) + ":" + HashKey(topic, region, title)
return c.Set(ctx, key, digest, ttl)
}
func (c *RedisCache) GetCachedDigest(ctx context.Context, topic, region, title string, dest interface{}) error {
key := string(KeyDigest) + ":" + HashKey(topic, region, title)
return c.Get(ctx, key, dest)
}
type MemoryCache struct {
data map[string]cacheEntry
}
type cacheEntry struct {
value interface{}
expiresAt time.Time
}
func NewMemoryCache() *MemoryCache {
return &MemoryCache{
data: make(map[string]cacheEntry),
}
}
func (c *MemoryCache) Set(key string, value interface{}, ttl time.Duration) {
c.data[key] = cacheEntry{
value: value,
expiresAt: time.Now().Add(ttl),
}
}
func (c *MemoryCache) Get(key string) (interface{}, bool) {
entry, ok := c.data[key]
if !ok {
return nil, false
}
if time.Now().After(entry.expiresAt) {
delete(c.data, key)
return nil, false
}
return entry.value, true
}
func (c *MemoryCache) Delete(key string) {
delete(c.data, key)
}
func (c *MemoryCache) Clear() {
c.data = make(map[string]cacheEntry)
}
func (c *MemoryCache) Cleanup() int {
count := 0
now := time.Now()
for k, v := range c.data {
if now.After(v.expiresAt) {
delete(c.data, k)
count++
}
}
return count
}