Рефакторинг инфраструктуры и конфигурации проекта
- Перенесена структура Django-конфига в src/core и src/settings - Унифицирована Docker-сборка и docker-compose для dev/prod - Добавлены startup-checks (DB/Redis) и обновлены env-шаблоны - Расширена OpenAPI-документация и ответы API - Удалены устаревшие deploy/requirements/служебные скрипты - Обновлены CI/CD, README и тесты
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
from django.urls import path
|
||||
from rest_framework_simplejwt.views import TokenVerifyView
|
||||
|
||||
from . import views
|
||||
|
||||
@@ -11,7 +10,7 @@ urlpatterns = [
|
||||
path("login/", views.LoginView.as_view(), name="login"),
|
||||
path("logout/", views.LogoutView.as_view(), name="logout"),
|
||||
path("token/refresh/", views.TokenRefreshView.as_view(), name="token_refresh"),
|
||||
path("token/verify/", TokenVerifyView.as_view(), name="token_verify"),
|
||||
path("token/verify/", views.TokenVerifySwaggerView.as_view(), name="token_verify"),
|
||||
# Пользовательские данные
|
||||
path("me/", views.CurrentUserView.as_view(), name="current_user"),
|
||||
path("me/update/", views.UserUpdateView.as_view(), name="user_update"),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from apps.core.openapi import CommonResponses, ErrorResponses, swagger_tag
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from drf_yasg import openapi
|
||||
@@ -8,6 +9,7 @@ from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework_simplejwt.views import TokenVerifyView as SimpleJWTTokenVerifyView
|
||||
|
||||
from .serializers import (
|
||||
LoginSerializer,
|
||||
@@ -21,8 +23,8 @@ from .serializers import (
|
||||
from .services import ProfileService, UserService
|
||||
|
||||
# Swagger теги для группировки
|
||||
AUTH_TAG = "Аутентификация"
|
||||
USER_TAG = "Пользователь"
|
||||
AUTH_TAG = swagger_tag("Аутентификация", "authentication")
|
||||
USER_TAG = swagger_tag("Пользователь", "user")
|
||||
|
||||
|
||||
class RegisterView(APIView):
|
||||
@@ -39,7 +41,11 @@ class RegisterView(APIView):
|
||||
operation_summary="Регистрация",
|
||||
operation_description="Создание новой учётной записи пользователя.",
|
||||
request_body=UserRegistrationSerializer,
|
||||
responses={201: UserSerializer},
|
||||
responses={
|
||||
201: UserSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
**ErrorResponses.PUBLIC,
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
serializer = UserRegistrationSerializer(data=request.data)
|
||||
@@ -74,7 +80,12 @@ class LoginView(APIView):
|
||||
operation_summary="Вход",
|
||||
operation_description="Аутентификация по email и паролю. Возвращает JWT токены.",
|
||||
request_body=LoginSerializer,
|
||||
responses={200: TokenSerializer},
|
||||
responses={
|
||||
200: TokenSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
401: CommonResponses.UNAUTHORIZED,
|
||||
**ErrorResponses.PUBLIC,
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
serializer = LoginSerializer(data=request.data)
|
||||
@@ -108,7 +119,10 @@ class LogoutView(APIView):
|
||||
tags=[AUTH_TAG],
|
||||
operation_summary="Выход",
|
||||
operation_description="Выход из системы (удаление токенов на клиенте).",
|
||||
responses={200: "Успешный выход"},
|
||||
responses={
|
||||
200: "Успешный выход",
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
# Для JWT логаут означает удаление токенов на клиенте.
|
||||
@@ -125,7 +139,10 @@ class CurrentUserView(APIView):
|
||||
tags=[USER_TAG],
|
||||
operation_summary="Текущий пользователь",
|
||||
operation_description="Возвращает данные авторизованного пользователя.",
|
||||
responses={200: UserSerializer},
|
||||
responses={
|
||||
200: UserSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
serializer = UserSerializer(request.user)
|
||||
@@ -142,7 +159,10 @@ class UserUpdateView(APIView):
|
||||
operation_summary="Обновить данные",
|
||||
operation_description="Частичное обновление данных пользователя.",
|
||||
request_body=UserUpdateSerializer,
|
||||
responses={200: UserSerializer},
|
||||
responses={
|
||||
200: UserSerializer,
|
||||
**ErrorResponses.AUTHENTICATED_VALIDATION,
|
||||
},
|
||||
)
|
||||
def patch(self, request):
|
||||
serializer = UserUpdateSerializer(request.user, data=request.data, partial=True)
|
||||
@@ -158,6 +178,7 @@ class ProfileDetailView(generics.RetrieveUpdateAPIView):
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = ProfileUpdateSerializer
|
||||
http_method_names = ["get", "patch", "head", "options"]
|
||||
|
||||
def get_object(self):
|
||||
profile = ProfileService.get_profile_by_user_id_or_none(self.request.user.id)
|
||||
@@ -172,6 +193,10 @@ class ProfileDetailView(generics.RetrieveUpdateAPIView):
|
||||
tags=[USER_TAG],
|
||||
operation_summary="Получить профиль",
|
||||
operation_description="Возвращает профиль текущего пользователя.",
|
||||
responses={
|
||||
200: ProfileUpdateSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
def get(self, request, *args, **kwargs):
|
||||
profile = self.get_object()
|
||||
@@ -183,6 +208,10 @@ class ProfileDetailView(generics.RetrieveUpdateAPIView):
|
||||
operation_summary="Обновить профиль",
|
||||
operation_description="Частичное обновление профиля пользователя.",
|
||||
request_body=ProfileUpdateSerializer,
|
||||
responses={
|
||||
200: ProfileUpdateSerializer,
|
||||
**ErrorResponses.AUTHENTICATED_VALIDATION,
|
||||
},
|
||||
)
|
||||
def patch(self, request, *args, **kwargs):
|
||||
profile = self.get_object()
|
||||
@@ -205,7 +234,10 @@ class PasswordChangeView(APIView):
|
||||
operation_summary="Сменить пароль",
|
||||
operation_description="Смена пароля. Требуется текущий пароль для подтверждения.",
|
||||
request_body=PasswordChangeSerializer,
|
||||
responses={200: "Пароль успешно изменен"},
|
||||
responses={
|
||||
200: "Пароль успешно изменен",
|
||||
**ErrorResponses.AUTHENTICATED_VALIDATION,
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
serializer = PasswordChangeSerializer(data=request.data)
|
||||
@@ -234,6 +266,10 @@ class PasswordChangeView(APIView):
|
||||
tags=[USER_TAG],
|
||||
operation_summary="Полный профиль",
|
||||
operation_description="Расширенная информация о пользователе и профиле.",
|
||||
responses={
|
||||
200: ProfileUpdateSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
@api_view(["GET"])
|
||||
@permission_classes([IsAuthenticated])
|
||||
@@ -261,7 +297,12 @@ class TokenRefreshView(APIView):
|
||||
},
|
||||
required=["refresh"],
|
||||
),
|
||||
responses={200: TokenSerializer},
|
||||
responses={
|
||||
200: TokenSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
401: CommonResponses.UNAUTHORIZED,
|
||||
**ErrorResponses.PUBLIC,
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
refresh_token = request.data.get("refresh")
|
||||
@@ -280,3 +321,21 @@ class TokenRefreshView(APIView):
|
||||
return Response(
|
||||
{"error": "Неверный refresh token"}, status=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
|
||||
|
||||
class TokenVerifySwaggerView(SimpleJWTTokenVerifyView):
|
||||
"""Проверка валидности access токена."""
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=[AUTH_TAG],
|
||||
operation_summary="Проверить токен",
|
||||
operation_description="Проверяет валидность JWT токена.",
|
||||
responses={
|
||||
200: "Токен валиден",
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
401: CommonResponses.UNAUTHORIZED,
|
||||
**ErrorResponses.PUBLIC,
|
||||
},
|
||||
)
|
||||
def post(self, request, *args, **kwargs):
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user