Files
1Panel/core/utils/captcha/store.go

77 lines
1.5 KiB
Go

package captcha
import (
"container/list"
"errors"
"sync"
"time"
)
type challenge struct {
id string
answer string
expiresAt time.Time
}
type memoryStore struct {
mu sync.Mutex
entries map[string]*list.Element
order *list.List
limit int
expiration time.Duration
now func() time.Time
}
func newMemoryStore(limit int, expiration time.Duration) *memoryStore {
return &memoryStore{
entries: make(map[string]*list.Element), order: list.New(),
limit: limit, expiration: expiration, now: time.Now,
}
}
func (s *memoryStore) put(id, answer string) error {
s.mu.Lock()
defer s.mu.Unlock()
now := s.now()
s.removeExpired(now)
if len(s.entries) >= s.limit {
return errors.New("captcha store is full")
}
if _, exists := s.entries[id]; exists {
return errors.New("captcha ID already exists")
}
s.entries[id] = s.order.PushBack(challenge{id: id, answer: answer, expiresAt: now.Add(s.expiration)})
return nil
}
func (s *memoryStore) consume(id string) string {
s.mu.Lock()
defer s.mu.Unlock()
now := s.now()
s.removeExpired(now)
e, ok := s.entries[id]
if !ok {
return ""
}
value := e.Value.(challenge)
s.remove(e)
if !now.Before(value.expiresAt) {
return ""
}
return value.answer
}
func (s *memoryStore) removeExpired(now time.Time) {
for e := s.order.Front(); e != nil; e = s.order.Front() {
if now.Before(e.Value.(challenge).expiresAt) {
return
}
s.remove(e)
}
}
func (s *memoryStore) remove(e *list.Element) {
delete(s.entries, e.Value.(challenge).id)
s.order.Remove(e)
}