""" Alternative auth endpoint (tg/max/sms) without touching existing flow. /api/v1/auth2/login: - platform=tg|max|sms - Validates init_data for TG/MAX and calls n8n webhook - For SMS: verifies code, calls n8n contact webhook, creates session - Returns greeting message """ import logging from typing import Optional, Literal, Any, Dict from fastapi import APIRouter, HTTPException from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from ..services.sms_service import sms_service from ..services.telegram_auth import extract_telegram_user, TelegramAuthError from ..services.max_auth import extract_max_user, MaxAuthError from ..config import settings from . import n8n_proxy from . import session as session_api logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/auth2", tags=["auth2"]) class Auth2LoginRequest(BaseModel): platform: Literal["tg", "max", "sms"] init_data: Optional[str] = None phone: Optional[str] = None code: Optional[str] = None session_token: Optional[str] = None form_id: str = "ticket_form" class Auth2LoginResponse(BaseModel): success: bool greeting: str session_token: Optional[str] = None unified_id: Optional[str] = None contact_id: Optional[str] = None phone: Optional[str] = None has_drafts: Optional[bool] = None need_contact: Optional[bool] = None avatar_url: Optional[str] = None def _generate_session_token() -> str: import uuid return f"sess-{uuid.uuid4()}" @router.post("/login", response_model=Auth2LoginResponse) async def login(request: Auth2LoginRequest): platform = request.platform logger.info("[AUTH2] login: platform=%s", platform) if platform == "tg": if not request.init_data: raise HTTPException(status_code=400, detail="init_data обязателен для tg") try: tg_user = extract_telegram_user(request.init_data) except TelegramAuthError as e: raise HTTPException(status_code=400, detail=str(e)) session_token = request.session_token or _generate_session_token() n8n_payload = { "telegram_user_id": tg_user["telegram_user_id"], "username": tg_user.get("username"), "first_name": tg_user.get("first_name"), "last_name": tg_user.get("last_name"), "session_token": session_token, "form_id": request.form_id, "init_data": request.init_data, } class _DummyRequest: def __init__(self, payload: Dict[str, Any]): self._payload = payload async def json(self): return self._payload n8n_response = await n8n_proxy.proxy_telegram_auth(_DummyRequest(n8n_payload)) # type: ignore[arg-type] n8n_data = jsonable_encoder(n8n_response) _result = n8n_data.get("result") _result_dict = _result if isinstance(_result, dict) else {} _raw_nc = n8n_data.get("need_contact") or _result_dict.get("need_contact") or n8n_data.get("needContact") or _result_dict.get("needContact") need_contact = _raw_nc is True or _raw_nc == 1 or (isinstance(_raw_nc, str) and str(_raw_nc).strip().lower() in ("true", "1")) if need_contact: logger.info("[AUTH2] TG: n8n need_contact — возвращаем need_contact=true") return Auth2LoginResponse(success=False, greeting="Привет!", need_contact=True) unified_id = n8n_data.get("unified_id") or _result_dict.get("unified_id") or n8n_data.get("unifiedId") contact_id = n8n_data.get("contact_id") or _result_dict.get("contact_id") or n8n_data.get("contactId") phone = n8n_data.get("phone") or _result_dict.get("phone") has_drafts = n8n_data.get("has_drafts") if not unified_id: logger.info("[AUTH2] TG: n8n не вернул unified_id — возвращаем need_contact=true") return Auth2LoginResponse(success=False, greeting="Привет!", need_contact=True) await session_api.create_session(session_api.SessionCreateRequest( session_token=session_token, unified_id=unified_id, phone=phone or "", contact_id=contact_id or "", ttl_hours=24, chat_id=str(tg_user["telegram_user_id"]) if tg_user.get("telegram_user_id") is not None else None, )) first_name = tg_user.get("first_name") or "" greeting = f"Привет, {first_name}!" if first_name else "Привет!" return Auth2LoginResponse( success=True, greeting=greeting, session_token=session_token, unified_id=unified_id, contact_id=contact_id, phone=phone, has_drafts=has_drafts, avatar_url=tg_user.get("photo_url") or None, ) if platform == "max": if not request.init_data: raise HTTPException(status_code=400, detail="init_data обязателен для max") try: max_user = extract_max_user(request.init_data) except MaxAuthError as e: raise HTTPException(status_code=400, detail=str(e)) session_token = request.session_token or _generate_session_token() n8n_payload = { "max_user_id": max_user["max_user_id"], "username": max_user.get("username"), "first_name": max_user.get("first_name"), "last_name": max_user.get("last_name"), "session_token": session_token, "form_id": request.form_id, "init_data": request.init_data, } class _DummyRequest: def __init__(self, payload: Dict[str, Any]): self._payload = payload async def json(self): return self._payload n8n_response = await n8n_proxy.proxy_max_auth(_DummyRequest(n8n_payload)) # type: ignore[arg-type] n8n_data = jsonable_encoder(n8n_response) _result = n8n_data.get("result") _result_dict = _result if isinstance(_result, dict) else {} _raw_nc = n8n_data.get("need_contact") or _result_dict.get("need_contact") or n8n_data.get("needContact") or _result_dict.get("needContact") need_contact = _raw_nc is True or _raw_nc == 1 or (isinstance(_raw_nc, str) and str(_raw_nc).strip().lower() in ("true", "1")) if need_contact: logger.info("[AUTH2] MAX: n8n need_contact — возвращаем need_contact=true") return Auth2LoginResponse(success=False, greeting="Привет!", need_contact=True) unified_id = n8n_data.get("unified_id") or _result_dict.get("unified_id") or n8n_data.get("unifiedId") contact_id = n8n_data.get("contact_id") or _result_dict.get("contact_id") or n8n_data.get("contactId") phone = n8n_data.get("phone") or _result_dict.get("phone") has_drafts = n8n_data.get("has_drafts") if not unified_id: logger.info("[AUTH2] MAX: n8n не вернул unified_id — возвращаем need_contact=true") return Auth2LoginResponse(success=False, greeting="Привет!", need_contact=True) await session_api.create_session(session_api.SessionCreateRequest( session_token=session_token, unified_id=unified_id, phone=phone or "", contact_id=contact_id or "", ttl_hours=24, chat_id=str(max_user["max_user_id"]) if max_user.get("max_user_id") is not None else None, )) first_name = max_user.get("first_name") or "" greeting = f"Привет, {first_name}!" if first_name else "Привет!" return Auth2LoginResponse( success=True, greeting=greeting, session_token=session_token, unified_id=unified_id, contact_id=contact_id, phone=phone, has_drafts=has_drafts, avatar_url=max_user.get("photo_url") or None, ) if platform == "sms": phone = (request.phone or "").strip() code = (request.code or "").strip() if not phone or not code: raise HTTPException(status_code=400, detail="phone и code обязательны для sms") is_valid = await sms_service.verify_code(phone, code) if not is_valid: raise HTTPException(status_code=400, detail="Неверный код или код истек") class _DummyRequest: def __init__(self, payload: Dict[str, Any]): self._payload = payload async def json(self): return self._payload n8n_payload = { "phone": phone, "session_id": request.session_token or "", "form_id": request.form_id, } n8n_response = await n8n_proxy.proxy_create_contact(_DummyRequest(n8n_payload)) # type: ignore[arg-type] n8n_data = jsonable_encoder(n8n_response) if isinstance(n8n_data, list) and n8n_data: n8n_data = n8n_data[0] if not n8n_data or not isinstance(n8n_data, dict) or not n8n_data.get("success"): raise HTTPException(status_code=500, detail="Ошибка создания контакта в n8n") result = n8n_data.get("result") or n8n_data unified_id = result.get("unified_id") contact_id = result.get("contact_id") phone_res = result.get("phone") or phone has_drafts = result.get("has_drafts") session_token = result.get("session") or request.session_token or _generate_session_token() if not unified_id: raise HTTPException(status_code=500, detail="n8n не вернул unified_id") await session_api.create_session(session_api.SessionCreateRequest( session_token=session_token, unified_id=unified_id, phone=phone_res or "", contact_id=contact_id or "", ttl_hours=24, )) return Auth2LoginResponse( success=True, greeting="Привет!", session_token=session_token, unified_id=unified_id, contact_id=contact_id, phone=phone_res, has_drafts=has_drafts, avatar_url=None, ) raise HTTPException(status_code=400, detail="Неподдерживаемая платформа")