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:
Kwaku Danso
2026-05-19 22:15:11 +01:00
parent b873012191
commit 3629ab8c79
6 changed files with 520 additions and 401 deletions
+17
View File
@@ -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)})
}