feat(ui): add maintenance page for task management
Some checks failed
CI / build-and-test (push) Failing after -31m53s
Some checks failed
CI / build-and-test (push) Failing after -31m53s
* Implement maintenance page with task and log display * Add backfill and metadata retry functionality * Integrate grid component for project display in thoughts page * Update types for maintenance tasks and logs * Enhance sidebar and shell for new maintenance navigation
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -16,12 +18,19 @@ type AccessSnapshot struct {
|
||||
}
|
||||
|
||||
type AccessTracker struct {
|
||||
mu sync.RWMutex
|
||||
entries map[string]AccessSnapshot
|
||||
mu sync.RWMutex
|
||||
entries map[string]AccessSnapshot
|
||||
ipCounts map[string]int
|
||||
agentCounts map[string]int
|
||||
totalRequests int
|
||||
}
|
||||
|
||||
func NewAccessTracker() *AccessTracker {
|
||||
return &AccessTracker{entries: make(map[string]AccessSnapshot)}
|
||||
return &AccessTracker{
|
||||
entries: make(map[string]AccessSnapshot),
|
||||
ipCounts: make(map[string]int),
|
||||
agentCounts: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *AccessTracker) Record(keyID, path, remoteAddr, userAgent string, now time.Time) {
|
||||
@@ -32,14 +41,36 @@ func (t *AccessTracker) Record(keyID, path, remoteAddr, userAgent string, now ti
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
normalizedRemoteAddr := normalizeRemoteAddr(remoteAddr)
|
||||
|
||||
entry := t.entries[keyID]
|
||||
entry.KeyID = keyID
|
||||
entry.LastPath = path
|
||||
entry.RemoteAddr = remoteAddr
|
||||
entry.RemoteAddr = normalizedRemoteAddr
|
||||
entry.UserAgent = userAgent
|
||||
entry.LastAccessedAt = now.UTC()
|
||||
entry.RequestCount++
|
||||
t.entries[keyID] = entry
|
||||
t.totalRequests++
|
||||
|
||||
if normalizedRemoteAddr != "" {
|
||||
t.ipCounts[normalizedRemoteAddr]++
|
||||
}
|
||||
if userAgent != "" {
|
||||
t.agentCounts[userAgent]++
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeRemoteAddr(value string) string {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
host, _, err := net.SplitHostPort(trimmed)
|
||||
if err == nil {
|
||||
return host
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func (t *AccessTracker) Snapshot() []AccessSnapshot {
|
||||
@@ -79,3 +110,55 @@ func (t *AccessTracker) ConnectedCount(now time.Time, window time.Duration) int
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
type RequestAggregate struct {
|
||||
Key string `json:"key"`
|
||||
RequestCount int `json:"request_count"`
|
||||
}
|
||||
|
||||
type AccessMetrics struct {
|
||||
TotalRequests int `json:"total_requests"`
|
||||
UniquePrincipals int `json:"unique_principals"`
|
||||
UniqueIPs int `json:"unique_ips"`
|
||||
UniqueAgents int `json:"unique_agents"`
|
||||
TopIPs []RequestAggregate `json:"top_ips"`
|
||||
TopAgents []RequestAggregate `json:"top_agents"`
|
||||
}
|
||||
|
||||
func (t *AccessTracker) Metrics(topN int) AccessMetrics {
|
||||
if t == nil {
|
||||
return AccessMetrics{}
|
||||
}
|
||||
if topN <= 0 {
|
||||
topN = 10
|
||||
}
|
||||
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
return AccessMetrics{
|
||||
TotalRequests: t.totalRequests,
|
||||
UniquePrincipals: len(t.entries),
|
||||
UniqueIPs: len(t.ipCounts),
|
||||
UniqueAgents: len(t.agentCounts),
|
||||
TopIPs: topAggregates(t.ipCounts, topN),
|
||||
TopAgents: topAggregates(t.agentCounts, topN),
|
||||
}
|
||||
}
|
||||
|
||||
func topAggregates(items map[string]int, topN int) []RequestAggregate {
|
||||
out := make([]RequestAggregate, 0, len(items))
|
||||
for key, count := range items {
|
||||
out = append(out, RequestAggregate{Key: key, RequestCount: count})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].RequestCount == out[j].RequestCount {
|
||||
return out[i].Key < out[j].Key
|
||||
}
|
||||
return out[i].RequestCount > out[j].RequestCount
|
||||
})
|
||||
if len(out) > topN {
|
||||
out = out[:topN]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user