| | package cache |
| |
|
| | import ( |
| | "crypto/sha256" |
| | "encoding/hex" |
| | "sort" |
| | "sync" |
| | "time" |
| | ) |
| |
|
| | |
| | type SignatureEntry struct { |
| | Signature string |
| | Timestamp time.Time |
| | } |
| |
|
| | const ( |
| | |
| | SignatureCacheTTL = 1 * time.Hour |
| |
|
| | |
| | MaxEntriesPerSession = 100 |
| |
|
| | |
| | SignatureTextHashLen = 16 |
| |
|
| | |
| | MinValidSignatureLen = 50 |
| | ) |
| |
|
| | |
| | var signatureCache sync.Map |
| |
|
| | |
| | type sessionCache struct { |
| | mu sync.RWMutex |
| | entries map[string]SignatureEntry |
| | } |
| |
|
| | |
| | func hashText(text string) string { |
| | h := sha256.Sum256([]byte(text)) |
| | return hex.EncodeToString(h[:])[:SignatureTextHashLen] |
| | } |
| |
|
| | |
| | func getOrCreateSession(sessionID string) *sessionCache { |
| | if val, ok := signatureCache.Load(sessionID); ok { |
| | return val.(*sessionCache) |
| | } |
| | sc := &sessionCache{entries: make(map[string]SignatureEntry)} |
| | actual, _ := signatureCache.LoadOrStore(sessionID, sc) |
| | return actual.(*sessionCache) |
| | } |
| |
|
| | |
| | |
| | func CacheSignature(sessionID, text, signature string) { |
| | if sessionID == "" || text == "" || signature == "" { |
| | return |
| | } |
| | if len(signature) < MinValidSignatureLen { |
| | return |
| | } |
| |
|
| | sc := getOrCreateSession(sessionID) |
| | textHash := hashText(text) |
| |
|
| | sc.mu.Lock() |
| | defer sc.mu.Unlock() |
| |
|
| | |
| | if len(sc.entries) >= MaxEntriesPerSession { |
| | now := time.Now() |
| | for key, entry := range sc.entries { |
| | if now.Sub(entry.Timestamp) > SignatureCacheTTL { |
| | delete(sc.entries, key) |
| | } |
| | } |
| | |
| | if len(sc.entries) >= MaxEntriesPerSession { |
| | |
| | oldest := make([]struct { |
| | key string |
| | ts time.Time |
| | }, 0, len(sc.entries)) |
| | for key, entry := range sc.entries { |
| | oldest = append(oldest, struct { |
| | key string |
| | ts time.Time |
| | }{key, entry.Timestamp}) |
| | } |
| | |
| | sort.Slice(oldest, func(i, j int) bool { |
| | return oldest[i].ts.Before(oldest[j].ts) |
| | }) |
| |
|
| | toRemove := len(oldest) / 4 |
| | if toRemove < 1 { |
| | toRemove = 1 |
| | } |
| |
|
| | for i := 0; i < toRemove; i++ { |
| | delete(sc.entries, oldest[i].key) |
| | } |
| | } |
| | } |
| |
|
| | sc.entries[textHash] = SignatureEntry{ |
| | Signature: signature, |
| | Timestamp: time.Now(), |
| | } |
| | } |
| |
|
| | |
| | |
| | func GetCachedSignature(sessionID, text string) string { |
| | if sessionID == "" || text == "" { |
| | return "" |
| | } |
| |
|
| | val, ok := signatureCache.Load(sessionID) |
| | if !ok { |
| | return "" |
| | } |
| | sc := val.(*sessionCache) |
| |
|
| | textHash := hashText(text) |
| |
|
| | sc.mu.RLock() |
| | entry, exists := sc.entries[textHash] |
| | sc.mu.RUnlock() |
| |
|
| | if !exists { |
| | return "" |
| | } |
| |
|
| | |
| | if time.Since(entry.Timestamp) > SignatureCacheTTL { |
| | sc.mu.Lock() |
| | delete(sc.entries, textHash) |
| | sc.mu.Unlock() |
| | return "" |
| | } |
| |
|
| | return entry.Signature |
| | } |
| |
|
| | |
| | func ClearSignatureCache(sessionID string) { |
| | if sessionID != "" { |
| | signatureCache.Delete(sessionID) |
| | } else { |
| | signatureCache.Range(func(key, _ any) bool { |
| | signatureCache.Delete(key) |
| | return true |
| | }) |
| | } |
| | } |
| |
|
| | |
| | func HasValidSignature(signature string) bool { |
| | return signature != "" && len(signature) >= MinValidSignatureLen |
| | } |
| |
|