package store import ( "sort" "strings" "sync" "time" "github.com/tinode/chat/server/store/types" ) const configCacheTTL = time.Minute type configCacheEntry[T any] struct { value T expiresAt time.Time } type configCache[T any] struct { mu sync.RWMutex items map[string]configCacheEntry[T] } func newConfigCache[T any]() *configCache[T] { return &configCache[T]{items: make(map[string]configCacheEntry[T])} } func (c *configCache[T]) get(key string) (T, bool) { var zero T c.mu.RLock() entry, ok := c.items[key] c.mu.RUnlock() if !ok || time.Now().After(entry.expiresAt) { return zero, false } return entry.value, true } func (c *configCache[T]) set(key string, value T) { c.mu.Lock() c.items[key] = configCacheEntry[T]{value: value, expiresAt: time.Now().Add(configCacheTTL)} c.mu.Unlock() } var ( tenantByCodeCache = newConfigCache[*types.Tenant]() tenantByIDCache = newConfigCache[*types.Tenant]() registrationSMSCache = newConfigCache[bool]() thirdPartyConfigCache = newConfigCache[map[string]string]() thirdPartyConfigAllKeys = "*" ) func cloneTenant(t *types.Tenant) *types.Tenant { if t == nil { return nil } cp := *t return &cp } func cloneStringMap(src map[string]string) map[string]string { if src == nil { return nil } dst := make(map[string]string, len(src)) for k, v := range src { dst[k] = v } return dst } func thirdPartyCacheKey(group string, keys ...string) string { group = strings.TrimSpace(group) if len(keys) == 0 { return group + ":" + thirdPartyConfigAllKeys } normalized := make([]string, 0, len(keys)) for _, key := range keys { if key = strings.TrimSpace(key); key != "" { normalized = append(normalized, key) } } sort.Strings(normalized) return group + ":" + strings.Join(normalized, ",") }