import secrets from datetime import UTC, datetime, timedelta from fastapi import APIRouter, Depends, HTTPException from config import cfg from core.deps import get_auth_context, get_service_identity from db.session import UnitOfWork, get_uow from repositories.link_codes import create_link_code, get_link_code_by_code, use_link_code from repositories.users import UserRepository from schemas.dto import AuthContext from schemas.enums import LinkCodeStatus from schemas.link_codes import LinkCodeConsume, LinkCodeResponse from schemas.user import UserInfo router = APIRouter(prefix="/link-codes") @router.post("", response_model=LinkCodeResponse, status_code=201) async def gen_link_code( ctx: AuthContext = Depends(get_auth_context), uow: UnitOfWork = Depends(get_uow) ): code = secrets.token_urlsafe(cfg.link_code_length) exp = datetime.now(UTC) + timedelta(minutes=cfg.link_code_ttl) link_code = await create_link_code( uow, code=code, user_id=ctx.user.id, status=LinkCodeStatus.ACTIVE, expires_at=exp ) await uow.commit() return LinkCodeResponse(code=link_code.code, expires_at=link_code.expires_at) @router.post("/consume", response_model=UserInfo) async def consume_link_code( payload: LinkCodeConsume, ctx: AuthContext = Depends(get_service_identity), uow: UnitOfWork = Depends(get_uow), ): link_code = await get_link_code_by_code(uow, payload.code) if not link_code: raise HTTPException(404, detail="Code not found") if link_code.status != LinkCodeStatus.ACTIVE: raise HTTPException(404, detail="Code expired or is invalid.") users_repo = UserRepository(uow) user = await users_repo.get_user_by_id(link_code.user_id) if not user: raise HTTPException(404, detail="User not found") user = await users_repo.update_telegram_id(user, payload.telegram_id) await use_link_code(uow, code=link_code) await uow.commit() return UserInfo( username=user.username, telegram_id=user.telegram_id, referal_code=user.referal_code, bonus_balance=user.balance, )