Add Phase 1 MVP: D&D text-adventure server
FastAPI + Postgres backend with fastapi-users auth, games/characters CRUD, and a WebSocket chat endpoint where an x.ai Grok DM narrates play, rolls dice, and maintains character sheets via tool calls. React + Tailwind frontend covers registration/login, game list and creation, participation-code join flow, character viewer, and a real-time chat session view. Docker Compose skeleton (Postgres + backend) included; Caddy/frontend container wiring is deferred to the deploy milestone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import secrets
|
||||
import time
|
||||
import uuid
|
||||
|
||||
TICKET_TTL_SECONDS = 30
|
||||
|
||||
_tickets: dict[str, tuple[uuid.UUID, uuid.UUID, float]] = {} # ticket -> (game_id, user_id, expires_at)
|
||||
|
||||
|
||||
def issue_ticket(game_id: uuid.UUID, user_id: uuid.UUID) -> str:
|
||||
ticket = secrets.token_urlsafe(32)
|
||||
_tickets[ticket] = (game_id, user_id, time.monotonic() + TICKET_TTL_SECONDS)
|
||||
return ticket
|
||||
|
||||
|
||||
def consume_ticket(ticket: str, game_id: uuid.UUID) -> uuid.UUID | None:
|
||||
"""Single-use: returns the user_id if the ticket is valid for this game and not expired, else None."""
|
||||
entry = _tickets.pop(ticket, None)
|
||||
if entry is None:
|
||||
return None
|
||||
ticket_game_id, user_id, expires_at = entry
|
||||
if ticket_game_id != game_id or time.monotonic() > expires_at:
|
||||
return None
|
||||
return user_id
|
||||
Reference in New Issue
Block a user