initial commit: Artifacto v0.1
Self-hosted HTML artifact publisher. Single Go binary, SQLite metadata, HTML on disk. Features: password-protected artifacts, per-artifact expiration, admin dashboard with view metrics (privacy-preserving daily-salt visitor hash, sparklines, 30-day SVG chart, top referrers), rate-limited unlock endpoint. Packaged as a Docker image (two-stage, CGO_ENABLED=0, pure-Go SQLite). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
55
internal/server/ratelimit.go
Normal file
55
internal/server/ratelimit.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Simple token bucket keyed by arbitrary string. Thread-safe.
|
||||
type bucket struct {
|
||||
tokens float64
|
||||
lastRef time.Time
|
||||
}
|
||||
|
||||
type RateLimiter struct {
|
||||
mu sync.Mutex
|
||||
buckets map[string]*bucket
|
||||
capacity float64
|
||||
refill float64 // tokens per second
|
||||
}
|
||||
|
||||
// NewRateLimiter: e.g. capacity=5, per=10min → 5 tokens, refilled at 5/600 tokens/sec.
|
||||
func NewRateLimiter(capacity int, per time.Duration) *RateLimiter {
|
||||
return &RateLimiter{
|
||||
buckets: map[string]*bucket{},
|
||||
capacity: float64(capacity),
|
||||
refill: float64(capacity) / per.Seconds(),
|
||||
}
|
||||
}
|
||||
|
||||
// Allow returns true if the key has a token to spend.
|
||||
func (l *RateLimiter) Allow(key string) bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
b, ok := l.buckets[key]
|
||||
now := time.Now()
|
||||
if !ok {
|
||||
b = &bucket{tokens: l.capacity, lastRef: now}
|
||||
l.buckets[key] = b
|
||||
}
|
||||
elapsed := now.Sub(b.lastRef).Seconds()
|
||||
b.tokens = minF(l.capacity, b.tokens+elapsed*l.refill)
|
||||
b.lastRef = now
|
||||
if b.tokens >= 1 {
|
||||
b.tokens--
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func minF(a, b float64) float64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user