f37dc9fa76
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>
25 lines
792 B
Python
25 lines
792 B
Python
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
|