from typing import Annotated from fastapi import Depends, Header, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings from app.db.session import get_session from app.models.car import Car from app.models.user import User from app.services.telegram_auth import verify_webapp_init_data async def get_or_create_telegram_user( session: AsyncSession, *, telegram_id: int, username: str | None = None, first_name: str | None = None, last_name: str | None = None, locale: str | None = None, currency: str | None = None, ) -> User: result = await session.execute(select(User).where(User.telegram_id == telegram_id)) user = result.scalar_one_or_none() payload = { "telegram_id": telegram_id, "username": str(telegram_id), "first_name": first_name, "last_name": last_name, "locale": locale, "currency": currency, } if user is None: user = User(**{key: value for key, value in payload.items() if value is not None}) session.add(user) else: for field, value in payload.items(): if value is not None: setattr(user, field, value) await session.commit() await session.refresh(user) return user def require_internal_api_token(token: str | None) -> None: if not settings.internal_api_token: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Internal API token is not configured", ) if not token or token != settings.internal_api_token: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden") async def get_current_telegram_user( session: Annotated[AsyncSession, Depends(get_session)], x_telegram_init_data: Annotated[str | None, Header(alias="X-Telegram-Init-Data")] = None, x_internal_api_token: Annotated[str | None, Header(alias="X-Internal-API-Token")] = None, x_telegram_user_id: Annotated[int | None, Header(alias="X-Telegram-User-Id")] = None, x_dev_telegram_id: Annotated[int | None, Header(alias="X-Dev-Telegram-Id")] = None, ) -> User: if x_telegram_init_data: user_data = verify_webapp_init_data(x_telegram_init_data, settings.bot_token) return await get_or_create_telegram_user( session, telegram_id=int(user_data["id"]), username=user_data.get("username"), first_name=user_data.get("first_name"), last_name=user_data.get("last_name"), locale=user_data.get("language_code"), ) if x_internal_api_token and x_telegram_user_id: require_internal_api_token(x_internal_api_token) return await get_or_create_telegram_user(session, telegram_id=x_telegram_user_id) if settings.allow_dev_auth and not settings.is_production and x_dev_telegram_id: return await get_or_create_telegram_user(session, telegram_id=x_dev_telegram_id) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Telegram initData required") async def get_owned_car( car_id: int, current_user: Annotated[User, Depends(get_current_telegram_user)], session: Annotated[AsyncSession, Depends(get_session)], ) -> Car: car = await session.get(Car, car_id) if car is None: raise HTTPException(status_code=404, detail="Car not found") if car.owner_id != current_user.id: raise HTTPException(status_code=403, detail="Forbidden") return car