82 lines
1.4 KiB
Go
82 lines
1.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type AccessSnapshot struct {
|
|
KeyID string
|
|
LastPath string
|
|
RemoteAddr string
|
|
UserAgent string
|
|
RequestCount int
|
|
LastAccessedAt time.Time
|
|
}
|
|
|
|
type AccessTracker struct {
|
|
mu sync.RWMutex
|
|
entries map[string]AccessSnapshot
|
|
}
|
|
|
|
func NewAccessTracker() *AccessTracker {
|
|
return &AccessTracker{entries: make(map[string]AccessSnapshot)}
|
|
}
|
|
|
|
func (t *AccessTracker) Record(keyID, path, remoteAddr, userAgent string, now time.Time) {
|
|
if t == nil || keyID == "" {
|
|
return
|
|
}
|
|
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
entry := t.entries[keyID]
|
|
entry.KeyID = keyID
|
|
entry.LastPath = path
|
|
entry.RemoteAddr = remoteAddr
|
|
entry.UserAgent = userAgent
|
|
entry.LastAccessedAt = now.UTC()
|
|
entry.RequestCount++
|
|
t.entries[keyID] = entry
|
|
}
|
|
|
|
func (t *AccessTracker) Snapshot() []AccessSnapshot {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
|
|
items := make([]AccessSnapshot, 0, len(t.entries))
|
|
for _, entry := range t.entries {
|
|
items = append(items, entry)
|
|
}
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].LastAccessedAt.After(items[j].LastAccessedAt)
|
|
})
|
|
|
|
return items
|
|
}
|
|
|
|
func (t *AccessTracker) ConnectedCount(now time.Time, window time.Duration) int {
|
|
if t == nil {
|
|
return 0
|
|
}
|
|
|
|
cutoff := now.UTC().Add(-window)
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
|
|
count := 0
|
|
for _, entry := range t.entries {
|
|
if !entry.LastAccessedAt.Before(cutoff) {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|