fix: six small fixes — rate limiter leak, DST SLA, probe sort, TUI cleanup
CI / test (pull_request) Successful in 1m55s
CI / lint (pull_request) Successful in 1m27s
CI / vulncheck (pull_request) Successful in 56s

1. Rate limiter cleanup goroutine now stoppable via Stop() channel
   instead of looping forever. Prevents goroutine leak in tests.

2. Dead WindowSizeMsg branch in handleFormMsg removed — top-level
   Update handles resize before forms see it.

3. Probe results sorted by node ID — map iteration no longer
   reorders rows every render.

4. fmtAlertConfig takes models.AlertConfig directly instead of an
   anonymous struct the caller builds inline.

5. Backspace no longer aliases delete — d is the documented key.
   Prevents accidental delete-confirm on habitual backspace.

6. SLA daily buckets use time.Date day arithmetic instead of
   Add(-i*24h) — lands on midnight correctly across DST transitions.
This commit was merged in pull request #117.
This commit is contained in:
2026-06-12 09:18:52 -04:00
parent edfe6122b1
commit 9115ab720c
6 changed files with 34 additions and 34 deletions
+19 -7
View File
@@ -25,6 +25,7 @@ type RateLimiter struct {
rate float64
burst float64
trusted []*net.IPNet
stop chan struct{}
}
func NewRateLimiter(requestsPerMinute int, trusted []*net.IPNet) *RateLimiter {
@@ -33,11 +34,16 @@ func NewRateLimiter(requestsPerMinute int, trusted []*net.IPNet) *RateLimiter {
rate: float64(requestsPerMinute) / 60.0,
burst: float64(requestsPerMinute),
trusted: trusted,
stop: make(chan struct{}),
}
go rl.cleanup()
return rl
}
func (rl *RateLimiter) Stop() {
close(rl.stop)
}
func (rl *RateLimiter) Allow(ip string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
@@ -84,16 +90,22 @@ func (rl *RateLimiter) evictOldest() {
}
func (rl *RateLimiter) cleanup() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
time.Sleep(5 * time.Minute)
rl.mu.Lock()
cutoff := time.Now().Add(-10 * time.Minute)
for ip, v := range rl.visitors {
if v.lastSeen.Before(cutoff) {
delete(rl.visitors, ip)
select {
case <-ticker.C:
rl.mu.Lock()
cutoff := time.Now().Add(-10 * time.Minute)
for ip, v := range rl.visitors {
if v.lastSeen.Before(cutoff) {
delete(rl.visitors, ip)
}
}
rl.mu.Unlock()
case <-rl.stop:
return
}
rl.mu.Unlock()
}
}