Files
aerbim-ht-monitor/backend/api/auth/views.py
Timofey Syrokvashko 65a63235e5 feat / AEB-26 login page
2025-09-01 11:34:30 +03:00

258 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from rest_framework import status
from datetime import datetime
import traceback
from django.conf import settings
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.views import APIView
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample
from drf_spectacular.types import OpenApiTypes
from django.contrib.auth.models import User as DjangoUser
from .serializers import (
UserResponseSerializer,
LoginRequestSerializer,
LoginResponseSerializer,
LogoutResponseSerializer,
RefreshTokenRequestSerializer,
RefreshTokenResponseSerializer
)
from api.utils.cookies import AuthBaseViewSet
@extend_schema(tags=['Логин'])
class LoginViewSet(AuthBaseViewSet):
"""ViewSet для авторизации пользователей"""
serializer_class = LoginRequestSerializer
@extend_schema(
summary="Авторизация пользователя",
description="Эндпоинт для авторизации пользователя по логину и паролю",
responses={
200: OpenApiResponse(
response=LoginResponseSerializer,
description="Успешная авторизация",
examples=[
OpenApiExample(
'Успешный ответ',
value={
"message": "Успешная авторизация",
"access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"user": {
"id": 1,
"email": "user@example.com",
"account_type": "engieneer",
"name": "Иван",
"surname": "Иванов",
"imageURL": "https://example.com/avatar.jpg",
"uuid": "abc123"
}
}
)
]
),
400: OpenApiResponse(
description="Неверные параметры запроса",
response=OpenApiTypes.OBJECT,
examples=[
OpenApiExample(
'Отсутствуют обязательные поля',
value={"error": "Логин и пароль обязательны"}
)
]
),
403: OpenApiResponse(
description="Неверный пароль",
response=OpenApiTypes.OBJECT,
examples=[
OpenApiExample(
'Неверный пароль',
value={"error": "Неверный пароль"}
)
]
),
404: OpenApiResponse(
description="Пользователь не найден",
response=OpenApiTypes.OBJECT,
examples=[
OpenApiExample(
'Пользователь не найден',
value={"error": "Пользователь не найден"}
)
]
),
500: OpenApiResponse(
description="Внутренняя ошибка сервера",
response=OpenApiTypes.OBJECT,
examples=[
OpenApiExample(
'Ошибка сервера',
value={"error": "Ошибка авторизации"}
)
]
)
}
)
@action(detail=False, methods=['post'], url_path="login")
def login_client(self, request):
try:
login = request.data.get("login")
password = request.data.get("password")
if not login or not password:
return Response(
{"error": "Логин и пароль обязательны"},
status=status.HTTP_400_BAD_REQUEST
)
try:
user = DjangoUser.objects.get(username=login)
except DjangoUser.DoesNotExist:
return Response(
{"error": "Пользователь не найден"},
status=status.HTTP_404_NOT_FOUND
)
if not user.check_password(password):
return Response(
{"error": "Неверный пароль"},
status=status.HTTP_403_FORBIDDEN
)
refresh = RefreshToken.for_user(user)
user_data = UserResponseSerializer(user).data
response = Response({
"message": "Успешная авторизация",
"access": str(refresh.access_token),
"refresh": str(refresh),
"user": user_data
}, status=status.HTTP_200_OK)
return self._set_auth_cookies(response, refresh)
except Exception as e:
return Response(
{"error": "Ошибка авторизации"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@extend_schema(tags=['Логин'])
class RefreshTokenView(APIView):
"""View для обновления JWT токенов"""
serializer_class = RefreshTokenRequestSerializer
@extend_schema(
summary="Обновление токенов",
description="Эндпоинт для обновления JWT токенов. Принимает refresh token и возвращает новую пару токенов.",
request=RefreshTokenRequestSerializer,
responses={
200: OpenApiResponse(
response=RefreshTokenResponseSerializer,
description="Токены успешно обновлены",
examples=[
OpenApiExample(
'Успешное обновление',
value={
"access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"expires_at": 1679831642.0
}
)
]
),
400: OpenApiResponse(
description="Ошибка обновления токена",
response=OpenApiTypes.OBJECT,
examples=[
OpenApiExample(
'Отсутствует refresh token',
value={"error": "Refresh token is required"}
),
OpenApiExample(
'Невалидный refresh token',
value={"error": "Invalid refresh token: Token is invalid or expired"}
)
]
)
}
)
def post(self, request):
try:
refresh_token = request.data.get('refresh')
if not refresh_token:
return Response(
{'error': 'Требуется refresh token'},
status=status.HTTP_400_BAD_REQUEST
)
try:
token = RefreshToken(refresh_token)
# точное время истечения токена
expires_at = datetime.now() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
response_data = {
'access': str(token.access_token),
'refresh': str(token),
'expires_at': datetime.timestamp(expires_at)
}
return Response(response_data)
except Exception as e:
# логируем ошибки
print(f"Token refresh error: {str(e)}")
print(traceback.format_exc())
return Response(
{'error': f'Невалидный refresh token: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return Response(
{'error': f'Ошибка обновления токена: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
@extend_schema(tags=['Логаут'])
class LogoutView(APIView):
"""View для выхода из системы"""
serializer_class = LogoutResponseSerializer
@extend_schema(
summary="Выход из системы",
description="Эндпоинт для выхода из системы. Очищает JWT токены и сессионные куки.",
responses={
200: OpenApiResponse(
response=LogoutResponseSerializer,
description="Успешный выход из системы",
examples=[
OpenApiExample(
'Успешный выход',
value={"message": "Успешный выход из системы"}
)
]
)
}
)
def post(self, request):
"""Выход из системы с очисткой всех токенов и куки"""
response = Response(
{'message': 'Успешный выход из системы'},
status=status.HTTP_200_OK
)
response.delete_cookie('access_token')
response.delete_cookie('refresh_token')
response.delete_cookie('sessionid')
return response