diff --git a/.env.example b/.env.example index bb78548..1bfaf97 100644 --- a/.env.example +++ b/.env.example @@ -133,3 +133,52 @@ DB_CONN_MAX_AGE=60 AUTH_USER_CACHE_TTL=0 # سقفِ ارزانِ خواندن روی endpointهای پرترافیکِ GET (home/leaderboard/summary/...). READ_THROTTLE_RATE=120/min + +# ⚠️ تعدادِ پروکسی‌هایی که جلوی اپ نشسته‌اند (nginx = ۱). محدودیتِ نرخِ +# کاربرِ مهمان بر اساسِ IP است و IP از X-Forwarded-For خوانده می‌شود؛ اگر این +# عدد از تعدادِ واقعیِ پروکسی‌ها بیشتر باشد، کلاینت می‌تواند هدر را جعل کند و +# محدودیت را دور بزند. کمتر باشد، همه پشتِ IP پروکسی یکی شمرده می‌شوند. +NUM_PROXIES=1 + +# ----------------------------------------------------------------------- +# بقیه — همه پیش‌فرضِ کارا دارند و معمولاً لازم نیست دست بخورند +# ----------------------------------------------------------------------- +# پیامک: بدونِ SENDER هم کاوه‌نگار با خطِ پیش‌فرضِ حساب می‌فرستد، ولی اگر خطِ +# اختصاصی دارید اینجا بگذارید. قالبِ متنِ پیامک هم قابلِ تغییر است ({code} لازم). +KAVENEGAR_SENDER= +# OTP_SMS_TEMPLATE=کد ورود شما به تدریسینو: {code} + +# کلیدِ endpointهای داخلی (ingest در اپ rag) و امضای وبهوکِ پرداخت. با +# DEBUG=1 مقدارِ توسعه دارند؛ با DEBUG=0 خالی‌اند و آن مسیرها بسته می‌مانند +# تا وقتی مقدارِ واقعی بگذارید. +INTERNAL_SERVICE_KEY= +PAYMENT_WEBHOOK_SECRET= + +# پرداختِ درون‌برنامه‌ایِ کافه‌بازار. بدونِ این چهار تا، مسیرِ بازار +# fail-closed می‌ماند (payment/bazaar.py). REFRESH_TOKEN یک‌بار و دستی از +# پنلِ devapi گرفته می‌شود. +BAZAAR_CLIENT_ID= +BAZAAR_CLIENT_SECRET= +BAZAAR_REFRESH_TOKEN= +# BAZAAR_PACKAGE_NAME=ir.tadrisino.app +# BAZAAR_API_BASE=https://pardakht.cafebazaar.ir/devapi/v2 + +# آدرس‌های زرین‌پال از روی ZARINPAL_SANDBOX بالا انتخاب می‌شوند؛ فقط اگر +# زرین‌پال دامنه‌اش را عوض کرد این سه را دستی بدهید. +# ZARINPAL_API_BASE= +# ZARINPAL_STARTPAY_BASE= +# ZARINPAL_CALLBACK_URL= + +# نوشتنِ لاگ در فایل علاوه بر stdout (داخلِ Docker ست می‌شود). خالی = فقط stdout. +LOG_DIR= +# سقفِ گیرنده‌ی اکشنِ «ارسالِ پیامک به انتخاب‌شده‌ها» در پنلِ ادمین. این اکشن +# داخلِ همان request اجرا می‌شود، پس سقف جلوی تایم‌اوت را می‌گیرد. +ADMIN_SMS_MAX_RECIPIENTS=200 +# سقفِ نرخِ /api/health برای پایشِ بیرونی. +HEALTH_THROTTLE_RATE=60/min +# تایم‌اوت و پارامترهای RAG. EMBEDDING_DIM باید با مدلِ embedding بالا بخواند. +OPENAI_TIMEOUT_SECONDS=60 +QDRANT_TIMEOUT_SECONDS=30 +OPENAI_EMBEDDING_DIM=1536 +# کشِ پاسخِ دستیار (ثانیه) — سوالِ تکراری دوباره به مدل نمی‌رود. +RAG_ANSWER_CACHE_TTL=1800 diff --git a/CLAUDE.md b/CLAUDE.md index a463735..ea53e9b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -353,20 +353,44 @@ preferred setup, because there is then no key to leak. **Never let the server ru `django-insecure-…` fallback from `config/settings.py`: that string is committed here, so anyone who can read this repo can forge a token for any user.** -## وضعیت فعلی (۲۰۲۶-۰۹-۲۲) — این را اول بخوان +## وضعیت فعلی (۲۰۲۶-۰۹-۲۳) — این را اول بخوان -سرور روی `api.math.tadrisino2.ir` زنده است و **کدِ فعلی را سرو می‌کند** (سرویس واقعاً -ری‌استارت شد، نه فقط فایل‌ها روی دیسک). `/api/health` می‌دهد: -`grades: 8, lessons: 2617, questions: 2463` و هیچ مهاجرتِ اعمال‌نشده‌ای ندارد. +پروژه در این تاریخ **بسته و تحویل داده شد**. سرور روی `api.math.tadrisino2.ir` زنده +است و کامیتِ `5003b8a` را سرو می‌کند (سرویس واقعاً ری‌استارت شد، نه فقط فایل‌ها روی +دیسک). `/api/health` می‌دهد: `grades: 8, lessons: 2617, questions: 2463` و هیچ +مهاجرتِ اعمال‌نشده‌ای ندارد. - **هر نودِ پایه‌های هشتم تا دوازدهم آزمونِ کاملِ چهارسوالی دارد.** پایه‌های سوم/ششم/هفتم کتابِ تیمِ دیگرند: ۳۹ نود از ۱۴۵ کمتر از چهار سوال دارند، ولی هیچ‌کدام خالی نیست، پس روی نقشه جزیره‌ی مرده وجود ندارد. -- ۴۵۸ تست سبز، `check` و `check --deploy` و `makemigrations --check` تمیز، و - `spectacular` بدونِ خطا و هشدار (قرارداد کامل است). +- ۴۸۳ تست سبز، `check` و `makemigrations --check` تمیز، و `spectacular` بدونِ خطا و + هشدار. ۵۶ اندپوینت. - تستِ هم‌زمانیِ campaigns که مدت‌ها flaky بود **دیگر flaky نیست** — علتش یک باگِ واقعی بود، نه طبیعتِ تست؛ پایینِ همین فایل توضیح داده شده. +**مستندات در ۲۰۲۶-۰۹-۲۳ بازسازی شد.** `README.md` حالا درِ ورودی است نه دفترچه‌ی +راهنما؛ رویه‌ها رفتند به `docs/operations.md` (دیپلوی، متغیرهای محیطی، بکاپ و +بازیابی، ورودِ توسعه‌دهنده‌ها، قواعدِ سرور)، `docs/content-authoring.md`، +`docs/architecture.md` و `docs/docker.md`. **قبل از نوشتنِ هر سندِ تازه آنجا را نگاه +کن**، وگرنه همان حرف در سه جا تکرار می‌شود. + +سه چیزِ کوچک که در همان روز اضافه شد و دانستنشان لازم است: + +- **`TEST_LOGIN_PHONES`** — فهرستِ «شماره:کد» در `.env` که ورود بدونِ کاوه‌نگار را + ممکن می‌کند. روی سرور **عمداً خالی است**؛ انتخابِ شماره و کد یک تصمیمِ اعتبارنامه‌ای + است و مالِ صاحبِ پروژه. جزئیات در `docs/operations.md`. +- **حسابِ مهمانِ تستِ فرانت** (`deviceId = frontend-test-device`، کاربرِ ۶) با اعلان و + لیدربردِ دستی‌نوشته روی دیتابیسِ پروداکشن. `purge_demo_data` نمی‌گیردش و باید پیش + از آمدنِ کاربرِ واقعی پاک شود. +- **صفحه‌های `/api/docs/` و `/api/redoc/` از قالبِ خودمان رد می‌شوند** + (`templates/tadrisino/`) تا متنِ فارسی راست‌به‌چپ رندر شود. قالبِ drf-spectacular + دست‌نخورده است و با `{{ block.super }}` گسترش داده شده. + +**بکاپ هنوز خودکار نیست.** `deploy.sh` پیش از `migrate` بکاپ نمی‌گیرد؛ روشِ دستی در +`docs/operations.md` است و در سه دیپلویِ آن روز دستی گرفته شد. خودکار کردنش +ارزشمندترین کارِ بعدی‌ست. `sqlite3` روی سرور نصب نیست — با `venv/bin/python` و +`sqlite3.Connection.backup()` بکاپ بگیر، نه با `cp`. + بخش‌های تاریخیِ زیر به‌ترتیبِ زمان نگه داشته شده‌اند چون درس‌هایشان هنوز معتبر است؛ **اعدادِ محتوایی‌شان کهنه‌اند و ملاک همین بخش است.**