import json import logging from urllib.parse import parse_qsl from fastapi import Depends, Header, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings from app.core.database import get_db from app.core.security import validate_init_data from app.models.user import User logger = logging.getLogger(__name__) def _log_auth_failure(reason: str, init_data: str | None) -> None: parsed = dict(parse_qsl(init_data or "", keep_blank_values=True)) logger.warning( "telegram auth failed: reason=%s init_len=%s has_hash=%s has_user=%s auth_date=%s bot_token_set=%s", reason, len(init_data or ""), bool(parsed.get("hash")), bool(parsed.get("user")), parsed.get("auth_date", ""), bool(settings.bot_token), ) async def get_current_user( x_telegram_init_data: str | None = Header(None, alias="X-Telegram-Init-Data"), db: AsyncSession = Depends(get_db), ) -> User: if not x_telegram_init_data: _log_auth_failure("missing_header", x_telegram_init_data) raise HTTPException(status_code=401, detail="Open this page from the Telegram bot Mini App") data = validate_init_data(x_telegram_init_data) if data is None: _log_auth_failure("invalid_init_data", x_telegram_init_data) raise HTTPException(status_code=401, detail="Invalid Telegram auth data") user_json = data.get("user", "{}") try: user_data = json.loads(user_json) except (json.JSONDecodeError, TypeError): _log_auth_failure("invalid_user_json", x_telegram_init_data) raise HTTPException(status_code=401, detail="Invalid user data") user_id = user_data.get("id") if not user_id: _log_auth_failure("missing_user_id", x_telegram_init_data) raise HTTPException(status_code=401, detail="Missing user id") result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if user is None: user = User( id=user_id, username=user_data.get("username"), first_name=user_data.get("first_name", ""), last_name=user_data.get("last_name"), ) db.add(user) await db.commit() await db.refresh(user) else: user.username = user_data.get("username") user.first_name = user_data.get("first_name") or user.first_name user.last_name = user_data.get("last_name") await db.commit() return user