Address code review findings across API, web and deployment
release gate / check (push) Failing after 6m43s
release gate / check (push) Failing after 6m43s
High severity: - wire TRUSTED_PROXY through Compose/deploy/docs and warn at web startup - bucket public rate limits on effective client IP, add per-bookmark budget, and forward the signed browser identity from the share page - disable first-run bootstrap in server deployments and document seeding - serialize last-admin demotion in one advisory-locked transaction Medium/low: - cap and batch share recipient inserts - add a shared outbound budget for preview/CardDAV/IMAP/signal work - pin CardDAV dials to 443 and bound IMAP logout - fix rightward kanban column drags and normalize cards on column delete - classify backend outages in auth guards, add request timeouts and an error boundary, remove the dead login route, surface throttling - enforce pinned/unpinned page reorder grouping server-side - avoid preview work for stale bookmark versions; drop duplicate dialog fetch - sequence-guard collection/tag/calendar/quick-find loads - validate property config and fix MULTI_SELECT/DATE filter inputs - document index maintenance; apply same-origin check to sign-out - trim signup names, unique shared dates and kanban keys (migration 010), reset reference cache on identity changes, hide OIDC issuer, return 413 for oversized direct bodies, escape Markdown link destinations - remove dead template files and duplicate test helper Verified with go build/vet, full PostgreSQL race suite, db convergence, tsc, both ESLint configs and a Next production build.
This commit is contained in:
+10
-49
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/andrew/go-computer/internal/netguard"
|
||||
"github.com/andrew/go-computer/internal/outbound"
|
||||
"golang.org/x/net/html"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -72,60 +72,21 @@ var previewClient = &http.Client{
|
||||
// previews are decorative, and a bookmark save must never be coupled to
|
||||
// preview availability (the row persists without og metadata).
|
||||
const (
|
||||
keyLimit = 20
|
||||
keyWindow = time.Minute
|
||||
keyLimit = 20
|
||||
keyWindow = time.Minute
|
||||
// maxConcurrent mirrors outbound's global cap; kept for the budget tests
|
||||
// and documentation.
|
||||
maxConcurrent = 8
|
||||
)
|
||||
|
||||
type rateWindow struct {
|
||||
count int
|
||||
reset time.Time
|
||||
}
|
||||
|
||||
type budgetStore struct {
|
||||
mu sync.Mutex
|
||||
inFlight int
|
||||
keys map[string]*rateWindow
|
||||
}
|
||||
|
||||
var sharedBudget = &budgetStore{keys: map[string]*rateWindow{}}
|
||||
|
||||
// Reserve claims one preview-work slot for key (the requesting identity).
|
||||
// The returned release function MUST be called when the fetch finishes.
|
||||
// ok is false when either budget layer is exhausted.
|
||||
// ok is false when either budget layer is exhausted. Preview shares the
|
||||
// process-wide outbound budget with CardDAV/IMAP/signal checks (review M-2),
|
||||
// while keeping its own more generous per-identity rate.
|
||||
func Reserve(key string) (release func(), ok bool) {
|
||||
if key == "" {
|
||||
key = "anonymous"
|
||||
}
|
||||
sharedBudget.mu.Lock()
|
||||
defer sharedBudget.mu.Unlock()
|
||||
if sharedBudget.inFlight >= maxConcurrent {
|
||||
return nil, false
|
||||
}
|
||||
now := time.Now()
|
||||
// Opportunistic prune keeps the map bounded by active identities.
|
||||
if len(sharedBudget.keys) > 4096 {
|
||||
for k, w := range sharedBudget.keys {
|
||||
if now.After(w.reset) {
|
||||
delete(sharedBudget.keys, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
w := sharedBudget.keys[key]
|
||||
if w == nil || now.After(w.reset) {
|
||||
w = &rateWindow{reset: now.Add(keyWindow)}
|
||||
sharedBudget.keys[key] = w
|
||||
}
|
||||
if w.count >= keyLimit {
|
||||
return nil, false
|
||||
}
|
||||
w.count++
|
||||
sharedBudget.inFlight++
|
||||
return func() {
|
||||
sharedBudget.mu.Lock()
|
||||
sharedBudget.inFlight--
|
||||
sharedBudget.mu.Unlock()
|
||||
}, true
|
||||
release, _, ok = outbound.Reserve(key, keyLimit, keyWindow)
|
||||
return release, ok
|
||||
}
|
||||
|
||||
// FetchPreview resolves metadata for raw on behalf of key (the requesting
|
||||
|
||||
Reference in New Issue
Block a user