mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-29 09:15:33 +00:00
175 lines
3.3 KiB
Go
175 lines
3.3 KiB
Go
package collections
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
|
|
)
|
|
|
|
type cacheEntry struct {
|
|
user dbmodels.FixedUser
|
|
session dbmodels.FixedSession
|
|
}
|
|
|
|
type UserSessionCache struct {
|
|
mu sync.RWMutex
|
|
capacity int
|
|
cache sync.Map
|
|
approximateSize int
|
|
cleanupInterval time.Duration
|
|
stopCleanupSignal chan struct{}
|
|
}
|
|
|
|
func NewUserSessionCache(capacity int, cleanupInterval time.Duration) *UserSessionCache {
|
|
if capacity <= 0 {
|
|
capacity = 1000
|
|
}
|
|
if cleanupInterval <= 0 {
|
|
cleanupInterval = 5 * time.Minute
|
|
}
|
|
|
|
cache := &UserSessionCache{
|
|
capacity: capacity,
|
|
cache: sync.Map{},
|
|
cleanupInterval: cleanupInterval,
|
|
stopCleanupSignal: make(chan struct{}),
|
|
}
|
|
go cache.startCleanupRoutine()
|
|
return cache
|
|
}
|
|
|
|
func (c *UserSessionCache) Set(user *dbmodels.User, session *dbmodels.Session) (*dbmodels.FixedUser, *dbmodels.FixedSession) {
|
|
if user == nil || session == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
newEntry := &cacheEntry{
|
|
user: user.Fixed(),
|
|
session: session.Fixed(),
|
|
}
|
|
|
|
_, loaded := c.cache.LoadOrStore(session.Token(), newEntry)
|
|
if !loaded {
|
|
c.cache.Store(session.Token(), newEntry)
|
|
c.mu.Lock()
|
|
c.approximateSize++
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
return &newEntry.user, &newEntry.session
|
|
}
|
|
|
|
func (c *UserSessionCache) Get(sessionTokenClear string) (*dbmodels.FixedUser, *dbmodels.FixedSession, bool) {
|
|
if sessionTokenClear == "" {
|
|
return nil, nil, false
|
|
}
|
|
|
|
value, ok := c.cache.Load(sessionTokenClear)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
|
|
entry, ok := value.(*cacheEntry)
|
|
if !ok {
|
|
c.cache.Delete(sessionTokenClear)
|
|
return nil, nil, false
|
|
}
|
|
|
|
if time.Now().After(entry.session.Expires.Time()) {
|
|
c.cache.Delete(sessionTokenClear)
|
|
c.mu.Lock()
|
|
c.approximateSize--
|
|
c.mu.Unlock()
|
|
return nil, nil, false
|
|
}
|
|
|
|
return &entry.user, &entry.session, true
|
|
}
|
|
|
|
func (c *UserSessionCache) Delete(sessionTokenClear string) {
|
|
if sessionTokenClear == "" {
|
|
return
|
|
}
|
|
_, loaded := c.cache.LoadAndDelete(sessionTokenClear)
|
|
if loaded {
|
|
c.mu.Lock()
|
|
c.approximateSize--
|
|
c.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (c *UserSessionCache) DeleteSessionByUserID(uid string) {
|
|
if uid == "" {
|
|
return
|
|
}
|
|
|
|
c.cache.Range(func(key, value any) bool {
|
|
entry, ok := value.(*cacheEntry)
|
|
if !ok {
|
|
c.cache.Delete(key)
|
|
return true
|
|
}
|
|
|
|
if entry.user.Id == uid {
|
|
c.cache.Delete(key)
|
|
c.mu.Lock()
|
|
c.approximateSize--
|
|
c.mu.Unlock()
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (c *UserSessionCache) Clear() {
|
|
c.cache.Clear()
|
|
c.mu.Lock()
|
|
c.approximateSize = 0
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *UserSessionCache) startCleanupRoutine() {
|
|
ticker := time.NewTicker(c.cleanupInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
c.cleanupExpiredItems()
|
|
case <-c.stopCleanupSignal:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *UserSessionCache) cleanupExpiredItems() {
|
|
now := time.Now()
|
|
var newSize int
|
|
c.cache.Range(func(key, value any) bool {
|
|
entry, ok := value.(*cacheEntry)
|
|
if !ok {
|
|
c.cache.Delete(key)
|
|
return true
|
|
}
|
|
|
|
if now.After(entry.session.Expires.Time()) {
|
|
c.cache.Delete(key)
|
|
} else {
|
|
newSize++
|
|
}
|
|
return true
|
|
})
|
|
|
|
c.mu.Lock()
|
|
c.approximateSize = newSize
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *UserSessionCache) StopCleanup() {
|
|
select {
|
|
case <-c.stopCleanupSignal:
|
|
default:
|
|
close(c.stopCleanupSignal)
|
|
}
|
|
}
|