auth-yes/tasks/complete/2026-0826.07.gem.refactor.backend.attendee-relational-decoupling.2359.md

68 lines
2.9 KiB
Markdown

# Phase 7: Attendee Relational Decoupling & Immutable Identity
## 1. Context & Rationale
Currently, guest attendee sessions are bound to their originating event via
brittle string matching on `users.username` (e.g.,
`WHERE username LIKE 'guest_' || ep.slug || '_%'`). This creates two major
architectural flaws:
1. **Rotation Breakage:** If an event host rotates the event's ingress PIN/slug
(e.g., `deno-lab-1a2b` to `deno-lab-9x8z`), the backend loses track of
previously joined guests because their usernames retain the old slug suffix.
2. **Unwieldy Data:** Guest usernames inherit the full event slug, leading to
massive, unreadable usernames in logs and the database (e.g.,
`guest_annual-cyber-security-training-workshop-4f8x_12`).
## 2. Objective
Implement a robust **"A+B Architecture"** to permanently decouple guest session
lifecycle from transient URL slugs:
- **Option A (Relational Integrity):** Link guests directly to the Event UUID
via a strict Foreign Key (`event_pass_id`).
- **Option B (Immutable Identity):** Mint short, deterministic, and immutable
usernames using the Event UUID prefix.
## 3. Scope & Acceptance Criteria
### 3.1 Database Migration (`server/db.ts`)
- [ ] Add column:
`ALTER TABLE users ADD COLUMN IF NOT EXISTS event_pass_id UUID REFERENCES event_passes(id) ON DELETE CASCADE;`
- [ ] Ensure backward compatibility with existing standalone users (column
should be nullable, as admins/owners won't have an `event_pass_id`).
### 3.2 Immutable Username Minting (`server/routes/events.ts`)
- [ ] In `POST /api/join`: Change guest username generation from
`guest_${event.slug}_${seat}` to
`guest_${event.id.split('-')[0]}_${seat}`.
- [ ] In `POST /api/join`: Insert `event.id` into the new `event_pass_id` column
for the newly minted user.
### 3.3 Query Refactoring (Zero String Parsing)
- [ ] **`GET /api/events/:id/attendees`**: Refactor the query to use
`WHERE u.event_pass_id = ${eventId}`.
- [ ] **`POST /api/events/:id/extend`**: Refactor the query to target sessions
belonging to `u.event_pass_id = ${eventId}`.
- [ ] **`POST /api/events/:id/end`**: Refactor the revocation query to target
`u.event_pass_id = ${eventId}`.
- [ ] **`DELETE /api/sessions/:id`**: Refactor the ownership check to verify
`u.event_pass_id = ${eventId}` instead of string parsing.
### 3.4 Quality Gates
- [ ] Run `deno fmt`, `deno task lint`, `deno task check`.
- [ ] All 67+ backend tests must pass. (Update `server/tests/events.test.ts` to
mock/handle the new `event_pass_id` column and short username generation).
## 4. Anti-Patterns to Avoid
- **No `LIKE 'guest_%'` queries** anywhere in the backend logic.
- **No updating usernames on slug rotation.** The
`POST /api/events/:id/rotate-pin` endpoint should ONLY touch
`event_passes.slug` and `event_passes.pin_code`, leaving attendees completely
unaffected.