package httpapi import ( "crypto/sha256" "encoding/hex" "sync" "evobgp/internal/store" ) type apiKeyResolver struct { mu sync.RWMutex envByToken map[string]apiKeyRecord byHash map[string]apiKeyRecord } func newAPIKeyResolver(envSpec string, st store.Backend) (*apiKeyResolver, error) { r := &apiKeyResolver{ envByToken: make(map[string]apiKeyRecord), byHash: make(map[string]apiKeyRecord), } for _, rec := range parseAPIKeysSpec(envSpec) { r.envByToken[rec.token] = rec } return r, r.reloadFromStore(st) } func (r *apiKeyResolver) reloadFromStore(st store.Backend) error { rows, err := st.ListActiveAPIKeyHashes() if err != nil { return err } byHash := make(map[string]apiKeyRecord, len(rows)) for _, row := range rows { if len(row.TokenHash) != 32 { continue } byHash[hex.EncodeToString(row.TokenHash)] = apiKeyRecord{ token: "", tenantID: row.TenantID, role: row.Role, keyID: row.ID, } } r.mu.Lock() r.byHash = byHash r.mu.Unlock() return nil } func (r *apiKeyResolver) Reload(st store.Backend) error { return r.reloadFromStore(st) } func (r *apiKeyResolver) Lookup(raw string) (apiKeyRecord, bool) { r.mu.RLock() defer r.mu.RUnlock() if rec, ok := r.envByToken[raw]; ok { return rec, true } sum := sha256.Sum256([]byte(raw)) key := hex.EncodeToString(sum[:]) rec, ok := r.byHash[key] return rec, ok }