feat: add item notes and duplicate detection across platforms
- Add per-item notes editable in the detail and edit screens, synced between desktop and Android - Guard against duplicates by external ID and by type/year/title, mirroring the desktop findDuplicate logic in MediaDao - Wrap multi-step database operations in Room transactions - Disable Android auto backup and remove the destructive Room migration fallback so schema bumps fail loudly instead of wiping data - Bound crypto envelope KDF parameters when reading untrusted headers - Handle malformed server responses in SyncClient instead of crashing - Extend settings, database, and image cache on the desktop side
This commit is contained in:
parent
85f5c6dd4e
commit
2b35139364
30 changed files with 837 additions and 124 deletions
48
server/ratelimit.go
Normal file
48
server/ratelimit.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// rateLimiter is a minimal in-memory fixed-window limiter keyed by string
|
||||
// (bearer-token subject for the TMDB proxy). It blunts runaway clients abusing
|
||||
// the shared API key; it is not a full DoS defense.
|
||||
type rateLimiter struct {
|
||||
mu sync.Mutex
|
||||
limit int
|
||||
window time.Duration
|
||||
counts map[string]rateEntry
|
||||
}
|
||||
|
||||
type rateEntry struct {
|
||||
windowStart time.Time
|
||||
count int
|
||||
}
|
||||
|
||||
func newRateLimiter(limit int, per time.Duration) *rateLimiter {
|
||||
return &rateLimiter{limit: limit, window: per, counts: map[string]rateEntry{}}
|
||||
}
|
||||
|
||||
func (r *rateLimiter) allow(key string) bool {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
e, ok := r.counts[key]
|
||||
if !ok || now.Sub(e.windowStart) >= r.window {
|
||||
// Occasionally garbage-collect stale keys so the map stays bounded.
|
||||
if len(r.counts) > 10_000 {
|
||||
for k, v := range r.counts {
|
||||
if now.Sub(v.windowStart) >= r.window {
|
||||
delete(r.counts, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
r.counts[key] = rateEntry{windowStart: now, count: 1}
|
||||
return true
|
||||
}
|
||||
e.count++
|
||||
r.counts[key] = e
|
||||
return e.count <= r.limit
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue