You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.5 KiB
132 lines
4.5 KiB
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)
|