import logging from django.contrib.auth import get_user_model from drf_spectacular.utils import extend_schema, OpenApiResponse from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.generics import GenericAPIView from rest_framework.permissions import AllowAny from rest_framework.response import Response from apps.account.serializers import ExchangeTokenSerializer from utils import absolute_url from utils.redis import OnlineClassTokenManager from django.utils import timezone from utils.ip_helper import get_client_ip logger = logging.getLogger(__name__) UserModel = get_user_model() def detect_device_os(user_agent): if not user_agent: return None ua = user_agent.lower() if 'android' in ua: return 'android' if 'iphone' in ua or 'ipad' in ua or 'ios' in ua: return 'apple' if any(k in ua for k in ['mozilla', 'chrome', 'safari', 'windows', 'macintosh', 'linux']): return 'web' return None class ExchangeTokenAPIView(GenericAPIView): """ تبدیل temporary token به اطلاعات کاربر برای ورود از اپ موبایل """ permission_classes = [AllowAny] serializer_class = ExchangeTokenSerializer @extend_schema( summary="Exchange temporary token for user authentication", description="Exchange a one-time temporary token (issued by mobile app) for a real auth token and user info.", request=ExchangeTokenSerializer, responses={ 200: OpenApiResponse(description="Token exchanged successfully. Returns auth token and user details."), 400: OpenApiResponse(description="Invalid request — token not provided or malformed."), 404: OpenApiResponse(description="Token not found, expired, or user does not exist."), } ) def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) temp_token = serializer.validated_data['temp_token'] # دریافت اطلاعات از Redis/Cache manager = OnlineClassTokenManager() try: token_data = manager.get_payload(temp_token) except Exception: return Response({ 'success': False, 'message': 'توکن نامعتبر یا منقضی شده است' }, status=status.HTTP_404_NOT_FOUND) user_id = token_data.get('user_id') if not user_id: return Response({ 'success': False, 'message': 'توکن نامعتبر است' }, status=status.HTTP_400_BAD_REQUEST) # دریافت کاربر try: user = UserModel.objects.get(id=user_id) except UserModel.DoesNotExist: return Response({ 'success': False, 'message': 'کاربر یافت نشد' }, status=status.HTTP_404_NOT_FOUND) # حذف توکن موقت (one-time use) manager.delete_token(temp_token) # دریافت یا تولید Token واقعی کاربر auth_token, _ = Token.objects.get_or_create(user=user) client_ip = get_client_ip(request) user_agent = request.META.get('HTTP_USER_AGENT', '') device_os = token_data.get('device_os') if not device_os and user_agent: device_os = detect_device_os(user_agent) user.last_login = timezone.now() if client_ip: user.client_ip = client_ip if user_agent: user.user_agent = user_agent if device_os: user.device_os = device_os user.save() user.login_history.create( ip=client_ip, user_agent=user_agent, device_os=user.device_os or device_os, ) # دریافت avatar URL avatar_url = None if hasattr(user, 'avatar') and user.avatar: try: avatar_url = absolute_url(user.avatar.url) except Exception: avatar_url = None # برگرداندن اطلاعات کاربر با token واقعی return Response({ 'success': True, 'message': 'ورود موفق', 'token': auth_token.key, 'user': { 'id': user.id, 'fullname': user.get_full_name() or user.username or '', 'email': user.email or '', 'avatar': avatar_url } }, status=status.HTTP_200_OK)