Files
Thorsten f37dc9fa76 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>
2026-08-31 18:43:08 +02:00

69 lines
2.1 KiB
Python

import uuid
from collections.abc import AsyncGenerator
from fastapi import Depends, Request
from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin
from fastapi_users.authentication import (
AuthenticationBackend,
BearerTransport,
JWTStrategy,
)
from fastapi_users.db import SQLAlchemyUserDatabase
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth.email import send_email
from app.config import settings
from app.db import get_async_session
from app.models.user import User
JWT_LIFETIME_SECONDS = 60 * 60 * 24 * 7 # 7 days
async def get_user_db(
session: AsyncSession = Depends(get_async_session),
) -> AsyncGenerator[SQLAlchemyUserDatabase, None]:
yield SQLAlchemyUserDatabase(session, User)
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
reset_password_token_secret = settings.reset_password_secret
verification_token_secret = settings.verification_token_secret
async def on_after_forgot_password(
self, user: User, token: str, request: Request | None = None
) -> None:
reset_url = f"{settings.frontend_origin}/reset-password?token={token}"
await send_email(
to=user.email,
subject="Passwort zurücksetzen — DungeonsDragons",
body=(
f"Hallo {user.name},\n\n"
f"klicke auf den folgenden Link, um dein Passwort zurückzusetzen:\n{reset_url}\n\n"
"Falls du das nicht angefordert hast, ignoriere diese E-Mail."
),
)
async def get_user_manager(
user_db: SQLAlchemyUserDatabase = Depends(get_user_db),
) -> AsyncGenerator[UserManager, None]:
yield UserManager(user_db)
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
def get_jwt_strategy() -> JWTStrategy:
return JWTStrategy(secret=settings.jwt_secret, lifetime_seconds=JWT_LIFETIME_SECONDS)
auth_backend = AuthenticationBackend(
name="jwt",
transport=bearer_transport,
get_strategy=get_jwt_strategy,
)
fastapi_users = FastAPIUsers[User, uuid.UUID](get_user_manager, [auth_backend])
current_active_user = fastapi_users.current_user(active=True)