fix(tier2-g): rebrand Security → Gate with event-planner language
The original Block G UI was correct but felt like a firewall config panel.
The audience for GuestGuard is wedding couples, party hosts, and event
planners — not network admins. Three concrete problems:
1. "Fraud detection" / "Risk score" frames every guest as a suspect.
"Gate" is the metaphor every host already uses ("gate the guest
list", "doorman"). It's the same idea, friendlier.
2. Three threshold sliders (0–100) are abstract. Hosts can't reason
about "block at 85" without context. Replaced by four presets —
Relaxed / Balanced / Strict / Very strict — each with a sentence
that explains the kind of event it fits. The sliders survive under
an Advanced disclosure for the rare power user.
3. CIDR notation is gibberish to a layperson. "Trusted networks" is
what they're actually doing. A "Use my current network" button
detects the host's apparent IP, widens IPv4 to /24 (typical home
block), and pre-fills the form. Manual entry stays for cases
where the host knows what they're doing.
Plus two leaks fixed on the guest-facing RSVP page: removed the
"Risk score 72 · high" line from both the confirmation and the
blocked-attempt cards. Guests should never see internal scoring
detail — the blocked message now reads "Something about this
attempt looked off" instead of "suspicious access attempt".
Backend
- New GET /me/public-ip (authed) — echoes the caller's apparent IP
so the frontend's auto-detect button doesn't need a third-party
ipify call
Frontend
- New components/GateCard.vue with preset cards + advanced disclosure
+ trusted-networks UX with auto-detect
- Removed components/SecurityCard.vue
- Event tab nav: Security → Gate. Hash-link alias preserves any
/events/<id>#security bookmarks
- RSVP page: leaked risk-score lines removed; blocked-attempt
message reworded for non-technical guests
Internal storage / API names stay (FraudThresholds, fraud_v2,
/security/* endpoints) — they're never user-visible and renaming
them would be a breaking change for no end-user benefit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -30,3 +30,20 @@ func (h *meHandler) get(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
writeJSON(w, http.StatusOK, u)
|
||||
}
|
||||
|
||||
// GET /me/public-ip — returns the IP the API sees the caller from.
|
||||
// Powers the Gate tab's "Use my current network" button so a host can
|
||||
// allowlist their own home / office Wi-Fi with one click rather than
|
||||
// learning what a CIDR is. Tier 2 Block G UX rebrand.
|
||||
//
|
||||
// In production behind an ingress proxy the real public IP arrives via
|
||||
// X-Forwarded-For (handled by clientIP); in dev with Docker Desktop the
|
||||
// caller's apparent IP is the bridge gateway (~192.168.65.1), which is
|
||||
// still the right value to allowlist for local hosts.
|
||||
func (h *meHandler) publicIP(w http.ResponseWriter, r *http.Request) {
|
||||
if _, ok := UserIDFromContext(r.Context()); !ok {
|
||||
writeError(w, http.StatusUnauthorized, "unauthenticated")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"ip": clientIP(r)})
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("POST /auth/reset-password", s.authH.resetPassword)
|
||||
|
||||
mux.Handle("GET /me", authed(http.HandlerFunc(s.me.get)))
|
||||
mux.Handle("GET /me/public-ip", authed(http.HandlerFunc(s.me.publicIP)))
|
||||
mux.Handle("POST /auth/ws-ticket", authed(http.HandlerFunc(s.wsTicket.issue)))
|
||||
|
||||
// Privacy / GDPR-style endpoints — host can export their data,
|
||||
|
||||
Reference in New Issue
Block a user