package responses import ( "sync" "time" "ds2api/internal/auth" ) type storedResponse struct { Owner string Value map[string]any ExpiresAt time.Time } type responseStore struct { mu sync.Mutex ttl time.Duration items map[string]storedResponse } func newResponseStore(ttl time.Duration) *responseStore { if ttl <= 0 { ttl = 15 * time.Minute } return &responseStore{ ttl: ttl, items: make(map[string]storedResponse), } } func responseStoreKey(owner, id string) string { return owner + "\x00" + id } func responseStoreOwner(a *auth.RequestAuth) string { if a == nil { return "" } return a.CallerID } func (s *responseStore) put(owner, id string, value map[string]any) { if s == nil || owner == "" || id == "" || value == nil { return } now := time.Now() s.mu.Lock() defer s.mu.Unlock() s.sweepLocked(now) s.items[responseStoreKey(owner, id)] = storedResponse{ Owner: owner, Value: cloneAnyMap(value), ExpiresAt: now.Add(s.ttl), } } func (s *responseStore) get(owner, id string) (map[string]any, bool) { if s == nil || owner == "" || id == "" { return nil, false } now := time.Now() s.mu.Lock() defer s.mu.Unlock() s.sweepLocked(now) item, ok := s.items[responseStoreKey(owner, id)] if !ok { return nil, false } if item.Owner != owner { return nil, false } return cloneAnyMap(item.Value), true } func (s *responseStore) sweepLocked(now time.Time) { for k, v := range s.items { if now.After(v.ExpiresAt) { delete(s.items, k) } } } func cloneAnyMap(in map[string]any) map[string]any { if in == nil { return nil } out := make(map[string]any, len(in)) for k, v := range in { out[k] = v } return out } func (h *Handler) getResponseStore() *responseStore { if h == nil { return nil } h.responsesMu.Lock() defer h.responsesMu.Unlock() if h.responses == nil { ttl := 15 * time.Minute if h.Store != nil { ttl = time.Duration(h.Store.ResponsesStoreTTLSeconds()) * time.Second } h.responses = newResponseStore(ttl) } return h.responses }