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:
@@ -109,6 +109,20 @@ PaymentRequest را میزند، `Order.gateway_authority` را ذخیره م
|
||||
میکند؛ قبلاً retrieval رد میشد و جواب عمومی بود. `lessonId` اگر بیاید همچنان
|
||||
دقیقتر است، چون فصل را هم معلوم میکند.
|
||||
|
||||
## استریک و جدولِ رتبهبندی، زنده از فعالیت
|
||||
|
||||
تا ۲۰۲۶-۰۹-۲۴ هیچجای اپ `StreakRecord` نمینوشت و `GET /api/leaderboard` فقط
|
||||
جدولی را میخواند که با اجرای دستیِ `rebuild_leaderboard` پر میشد؛ یعنی برای
|
||||
کاربرِ واقعی «روز پیاپی» همیشه صفر بود و XPاش هیچوقت در جدول دیده نمیشد.
|
||||
|
||||
- **استریک** (`progress/services.py`): پاسخ به سوال، پایانِ درس و پایانِ جزیره
|
||||
امروز را (به وقتِ تهران) روزِ فعال ثبت میکنند. نمایش با
|
||||
`effective_current_streak` است: اگر آخرین روزِ فعال نه امروز است نه دیروز،
|
||||
صفر نشان داده میشود. خطای استریک هرگز پاسخ یا تکمیل را خراب نمیکند.
|
||||
- **رتبهبندی** (`progress/leaderboard.py`): زنده حساب و ۶۰ ثانیه کش میشود.
|
||||
`all_time` از `UserStats.xp`، `weekly` از XPی درسها و جزیرههای ۷ روزِ اخیر.
|
||||
`rebuild_leaderboard` هم از همین تابع میخواند و فقط جدولِ ادمین را پر میکند.
|
||||
|
||||
## دیتابیس
|
||||
|
||||
هنوز SQLite است (`OPTIONS.timeout=20` تا قفلهای همزمانیِ نوشتن را تحمل کند).
|
||||
|
||||
@@ -106,6 +106,7 @@ curl https://api.math.tadrisino2.ir/api/health
|
||||
| `CACHE_URL` | سرویس بالا میآید ولی نرخ بینِ workerها مشترک نیست؛ `/api/health` هشدار میدهد |
|
||||
| `KAVENEGAR_API_KEY` | `POST /api/auth/otp/request` خطای صریحِ `503` میدهد، نه موفقیتِ ساختگی |
|
||||
| `ZARINPAL_MERCHANT_ID` | مسیرِ پرداخت fail-closed میماند (`503`) |
|
||||
| `gunicorn.conf.py` (در ریپو، نه env) | gunicorn خودش از پوشهی کاری میخواندش: workerهای `gthread` با ۸ نخ و timeoutِ ۱۲۰. بدونِ آن، سه استریمِ همزمانِ دستیار کلِ API را قفل میکرد |
|
||||
| `FRONTEND_URL` · `ZARINPAL_REDIRECT_URL` | بعد از پرداخت کاربر به `https://math.tadrisino2.ir/?orderId=…&status=paid\|failed` برمیگردد. `ZARINPAL_REDIRECT_URL=-` یعنی همان پاسخِ JSONِ قدیمی |
|
||||
| `OPENAI_API_KEY` · `QDRANT_API_KEY` | `POST /api/ai/ask` همان پاسخِ stub را میدهد |
|
||||
| `TEST_LOGIN_PHONES` | هیچ حسابِ تستی باز نیست (بخشِ بعد) |
|
||||
|
||||
Reference in New Issue
Block a user