Files
asepharyana 8c184ccae1 feat: add Citations and WatchlistChat components, integrate with API
- Implemented Citations component to display citation data.
- Created WatchlistDrawer and ChatSidebar components for managing watchlists and AI chat functionality.
- Integrated API calls for watchlist management and chat interactions.
- Updated index.tsx to include new components in the main application layout.
- Added API client in lib/api.ts for structured API interactions.
- Developed Alerts, Dashboard, Portfolio, Routines, Screener, and Report pages with relevant data fetching and UI components.
- Introduced styles in tokens.css for consistent theming across the application.
- Configured TypeScript and Vite for project setup and development.
2026-09-15 12:36:48 +07:00

85 lines
1.8 KiB
Go

package store
import (
"context"
"encoding/json"
"sync"
"time"
"github.com/redis/go-redis/v9"
)
// Cache wraps Redis with an in-memory TTL fallback so the demo runs without
// REDIS_URL. Reference data (registry/taxonomy/tags) uses 24h TTL.
type Cache struct {
rdb *redis.Client
mu sync.Mutex
mem map[string]memEntry
}
type memEntry struct {
val string
exp time.Time
}
// NewCache builds a cache; empty redisURL selects the in-memory fallback.
func NewCache(redisURL string) *Cache {
c := &Cache{mem: map[string]memEntry{}}
if redisURL != "" {
opt, err := redis.ParseURL(redisURL)
if err == nil {
c.rdb = redis.NewClient(opt)
}
}
return c
}
// HasRedis reports whether a live Redis backs this cache.
func (c *Cache) HasRedis() bool { return c.rdb != nil }
// Get returns the cached value or "" on miss/expiry.
func (c *Cache) Get(ctx context.Context, key string) string {
if c.rdb != nil {
if v, err := c.rdb.Get(ctx, key).Result(); err == nil {
return v
}
return ""
}
c.mu.Lock()
defer c.mu.Unlock()
e, ok := c.mem[key]
if !ok || time.Now().After(e.exp) {
return ""
}
return e.val
}
// Set stores a value with TTL.
func (c *Cache) Set(ctx context.Context, key, val string, ttl time.Duration) {
if c.rdb != nil {
_ = c.rdb.Set(ctx, key, val, ttl).Err()
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.mem[key] = memEntry{val: val, exp: time.Now().Add(ttl)}
}
// GetJSON unmarshals a cached JSON value; ok=false on miss.
func (c *Cache) GetJSON(ctx context.Context, key string, v any) bool {
raw := c.Get(ctx, key)
if raw == "" {
return false
}
return json.Unmarshal([]byte(raw), v) == nil
}
// SetJSON marshals and stores a value with TTL.
func (c *Cache) SetJSON(ctx context.Context, key string, v any, ttl time.Duration) {
b, err := json.Marshal(v)
if err != nil {
return
}
c.Set(ctx, key, string(b), ttl)
}