make streaks and the leaderboard live, and stop three AI chats from freezing the API
Found in a deeper pass over what the frontend's flow actually touches:
- Nothing in the app ever wrote StreakRecord: "days in a row" was 0 forever
for every real user. Answers and lesson/island completions now record the
day (Tehran time); a broken chain shows 0. A streak error never fails the
answer itself.
- /api/leaderboard only changed when someone ran rebuild_leaderboard by
hand. It is now computed live (60s cache); weekly counts island XP too.
- gunicorn ran 3 sync workers with a 30s timeout, so every "ask Pi" stream
pinned a whole worker (~10s, GapGPT buffers the answer) and three at once
queued the entire API. gunicorn.conf.py (read automatically from the
working directory) switches to gthread, 8 threads, 120s timeout.
- Every /api/ response now says Cache-Control: private, no-store, so turning
on caching in the ArvanCloud panel can never serve one student another's data.
- /api/user/summary named no active plan ever ("subscription app not
implemented yet" had long stopped being true).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -89,3 +89,24 @@ class APIRequestLoggingMiddleware:
|
||||
log_func(message, request.method, request.path, status, duration_ms, _user_id_for_log(request))
|
||||
except Exception:
|
||||
logger.exception('api_request logging failed')
|
||||
|
||||
|
||||
class APINoStoreMiddleware:
|
||||
"""
|
||||
هر پاسخِ `/api/` هدرِ `Cache-Control: no-store` میگیرد، مگر خودِ view
|
||||
چیزِ دیگری گذاشته باشد (مثلاً استریمِ دستیار که `no-cache` دارد).
|
||||
|
||||
ArvanCloud جلوی سرور است و الان برای API کش نمیکند (`x-cache: BYPASS`)،
|
||||
ولی این فقط به تنظیمِ پنلِ آروان بند است. پاسخهای API به کاربر وابستهاند
|
||||
(توکنِ Bearer)؛ اگر روزی کسی کش را در پنل روشن کند، بدونِ این هدر یک
|
||||
دانشآموز میتوانست `/api/home` یا پروفایلِ دیگری را ببیند.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if request.path.startswith(_LOGGED_PATH_PREFIX) and not response.has_header('Cache-Control'):
|
||||
response['Cache-Control'] = 'private, no-store'
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user