Features: 1) Add rate-limiting decorators for multiple API methods across core viewsets; 2) Add translation support to messaging documentation.

Fixes: 1) Fix missing import for `send_message` moved to method scope in Telegram message handler; 2) Correct Swagger UI socket connection setting to `False`.

Extra: 1) Minor code cleanup and reformatting in viewsets and settings.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-11-12 11:50:51 +03:00
parent 554769d48e
commit de0cb836fc
4 changed files with 17 additions and 2 deletions

View file

@ -266,6 +266,7 @@ class CategoryViewSet(EvibesViewSet):
AllowAny,
],
)
@method_decorator(ratelimit(key="ip", rate="4/s" if not settings.DEBUG else "44/s"))
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
category = self.get_object()
@ -506,6 +507,7 @@ class ProductViewSet(EvibesViewSet):
# noinspection PyUnusedLocal
@action(detail=True, methods=["get"], url_path="feedbacks")
@method_decorator(ratelimit(key="ip", rate="2/s" if not settings.DEBUG else "44/s"))
def feedbacks(self, request: Request, *args, **kwargs) -> Response:
product = self.get_object()
qs = Feedback.objects.filter(order_product__product=product)
@ -522,6 +524,7 @@ class ProductViewSet(EvibesViewSet):
AllowAny,
],
)
@method_decorator(ratelimit(key="ip", rate="4/s" if not settings.DEBUG else "44/s"))
def seo_meta(self, request: Request, *args, **kwargs) -> Response:
p = self.get_object()
images = list(p.images.all()[:6])
@ -561,6 +564,10 @@ class ProductViewSet(EvibesViewSet):
}
return Response(SeoSnapshotSerializer(payload).data)
@method_decorator(ratelimit(key="ip", rate="4/s" if not settings.DEBUG else "44/s"))
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@extend_schema_view(**VENDOR_SCHEMA)
class VendorViewSet(EvibesViewSet):
@ -665,6 +672,7 @@ class OrderViewSet(EvibesViewSet):
return obj
@action(detail=False, methods=["get"], url_path="current")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def current(self, request: Request, *args, **kwargs) -> Response:
if not request.user.is_authenticated:
raise PermissionDenied(permission_denied_message)
@ -678,6 +686,7 @@ class OrderViewSet(EvibesViewSet):
)
@action(detail=True, methods=["post"], url_path="buy")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def buy(self, request: Request, *args, **kwargs) -> Response:
serializer = BuyOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -731,6 +740,7 @@ class OrderViewSet(EvibesViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(e)})
@action(detail=True, methods=["post"], url_path="add_order_product")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def add_order_product(self, request: Request, *args, **kwargs) -> Response:
serializer = AddOrderProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -750,6 +760,7 @@ class OrderViewSet(EvibesViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
@action(detail=True, methods=["post"], url_path="remove_order_product")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def remove_order_product(self, request: Request, *args, **kwargs) -> Response:
serializer = RemoveOrderProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -769,6 +780,7 @@ class OrderViewSet(EvibesViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
@action(detail=True, methods=["post"], url_path="bulk_add_order_products")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def bulk_add_order_products(self, request: Request, *args, **kwargs) -> Response:
serializer = BulkAddOrderProductsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -788,6 +800,7 @@ class OrderViewSet(EvibesViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
@action(detail=True, methods=["post"], url_path="bulk_remove_order_products")
@method_decorator(ratelimit(key="ip", rate="1/s" if not settings.DEBUG else "44/s"))
def bulk_remove_order_products(self, request: Request, *args, **kwargs) -> Response:
serializer = BulkRemoveOrderProductsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

View file

@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
from drf_spectacular.utils import OpenApiParameter
from engine.vibes_auth.messaging.serializers import (

View file

@ -14,7 +14,6 @@ from django.contrib.auth import get_user_model
from django.db.models import Q
from engine.vibes_auth.choices import SenderType
from engine.vibes_auth.messaging.services import send_message as svc_send_message
from engine.vibes_auth.models import ChatThread
logger = logging.getLogger(__name__)
@ -97,6 +96,8 @@ def build_router() -> Optional["Router"]:
@router.message()
async def any_message(message: types.Message): # type: ignore[valid-type]
from engine.vibes_auth.messaging.services import send_message as svc_send_message
if not message.from_user or not message.text:
return
tid = message.from_user.id

View file

@ -113,7 +113,7 @@ SPECTACULAR_SETTINGS = {
"ENABLE_DJANGO_DEPLOY_CHECK": not DEBUG, # noqa: F405
"SWAGGER_UI_FAVICON_HREF": r"/static/favicon.png",
"SWAGGER_UI_SETTINGS": {
"connectSocket": True,
"connectSocket": False,
"socketMaxMessages": 8,
"socketMessagesInitialOpened": False,
},