throttle hot read endpoints and cap unbounded list responses
progress/views.py
- GuestAwareReadThrottle (same guest-by-IP pattern as the other apps);
throttle_scope='read' on UserSummaryView, StreakCalendarView,
LeaderboardView.
notifications/views.py
- GuestAwareReadThrottle + throttle_scope='read' on NotificationListView.
- list capped at NOTIFICATIONS_MAX=100 newest (Meta.ordering already
newest-first); unreadCount still counted over the full set.
notifications/tests.py
- 150 rows -> 100 returned newest-first, unreadCount = 75.
rag/views.py
- ChunkListView: explicit order_by('-id') before the existing [:200] cap.
ai_tutor/views.py
- ConversationMessagesView: return the newest 200 messages in
chronological order instead of the whole conversation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,25 @@ from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import ScopedRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .models import LeaderboardEntry, StreakRecord, UserHearts, UserStats
|
||||
|
||||
|
||||
class GuestAwareReadThrottle(ScopedRateThrottle):
|
||||
"""همون الگوی GuestAwareContentThrottle: کاربرِ مهمان (هر deviceId یک pk
|
||||
تازه) با IP کلید میخوره تا چرخشِ حساب سقفِ read رو دور نزنه."""
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
user = request.user
|
||||
if user and user.is_authenticated and getattr(user, 'is_guest', False):
|
||||
ident = f'guest-ip:{self.get_ident(request)}'
|
||||
elif user and user.is_authenticated:
|
||||
ident = user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
return self.cache_format % {'scope': self.scope, 'ident': ident}
|
||||
from .serializers import (
|
||||
LeaderboardResponseSerializer,
|
||||
StreakCalendarResponseSerializer,
|
||||
@@ -17,6 +33,8 @@ class UserSummaryView(APIView):
|
||||
"""GET /api/user/summary — پروفایل + آمار + استریک (ادغامشده)"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
throttle_scope = 'read'
|
||||
throttle_classes = [GuestAwareReadThrottle]
|
||||
|
||||
@extend_schema(responses=UserSummaryResponseSerializer, tags=['Progress & Gamification (progress)'])
|
||||
def get(self, request):
|
||||
@@ -45,6 +63,8 @@ class StreakCalendarView(APIView):
|
||||
"""GET /api/user/streak/calendar — تقویم کامل روزهای فعال"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
throttle_scope = 'read'
|
||||
throttle_classes = [GuestAwareReadThrottle]
|
||||
|
||||
@extend_schema(responses=StreakCalendarResponseSerializer, tags=['Progress & Gamification (progress)'])
|
||||
def get(self, request):
|
||||
@@ -62,6 +82,9 @@ class StreakCalendarView(APIView):
|
||||
class LeaderboardView(APIView):
|
||||
"""GET /api/leaderboard?period= — رتبهبندی هفتگی/ماهانه"""
|
||||
|
||||
throttle_scope = 'read'
|
||||
throttle_classes = [GuestAwareReadThrottle]
|
||||
|
||||
@extend_schema(
|
||||
parameters=[OpenApiParameter('period', OpenApiTypes.STR, required=False,
|
||||
enum=['weekly', 'all_time'])],
|
||||
|
||||
Reference in New Issue
Block a user