Zareian a367685b2e expose per-node lessonIds in GET /api/home
The skill-map response gave units -> nodes with id/title/status but no way
to map a node to its lessons, and no other endpoint fills that gap
(/api/progress/nodes doesn't either). Any client rendering the map needs
this to know which lesson to open when a node is tapped.

The ids are already computed in the cached _grade_tree; this just surfaces
them. Additive and backward-compatible; content tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 11:04:47 +03:30

Tadrisino API

بک‌اند اپلیکیشن آموزشی Tadrisino — Django + Django REST Framework.

احراز هویت با OTP، محتوای درسی و پیشرفت کاربر، دستیار هوش مصنوعی، اشتراک و پرداخت، تخفیف، اعلان‌ها، همکاری در فروش، پشتیبانی و حساب خانواده.

اپ مسئولیت
accounts احراز هویت، session، پروفایل کاربر
content پایه‌ها، مباحث، درس‌ها، سوال‌ها، پیشرفت
progress آمار، لیدربرد، streak
ai_tutor مکالمات دستیار هوش مصنوعی
rag لایه‌ی RAG: بازیابی از Qdrant + تولید با GPT، پشتِ POST /api/ai/ask
subscription / payment / discount پلن، سفارش، تراکنش، کد تخفیف
notifications, affiliate, support, family امکانات جانبی
statuslog لاگ عمومی تغییر status، مستقل از بقیه‌ی اپ‌ها

۱. راه‌اندازی محیط توسعه

پیش‌نیازها

  • پایتون ۳.۱۲
  • pip

تنظیم محیط

python3 -m venv venv
source venv/bin/activate        # ویندوز: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env

.env.example (تمام متغیرهایی که واقعاً توسط اپ خونده می‌شن):

DJANGO_DEBUG=1
DJANGO_SECRET_KEY=
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
CACHE_URL=
KAVENEGAR_API_KEY=
GRAFANA_ADMIN_PASSWORD=admin

مقادیر .env رو به شل خودتون export کنید (یا از direnv/python-dotenv استفاده کنید):

export DJANGO_DEBUG=1            # ویندوز (PowerShell): $env:DJANGO_DEBUG=1

DEBUG دیفالتش False است. برای فعال‌کردنِ کمک‌کننده‌های dev-only در Swagger (DEV_MASTER_OTP و فیلدِ devOtp توی پاسخ‌ها)، محلی DJANGO_DEBUG=1 رو ست کنید.

سریع‌ترین راه اجرای پروژه

python manage.py migrate
python manage.py runserver

اپ روی http://127.0.0.1:8000 بالا میاد.

مایگریشن‌ها

python manage.py migrate

دیتای تستی (Seed)

یک دیتاست کوچک و idempotent می‌سازه که همه‌ی اپ‌ها رو پوشش می‌ده (کاربر، محتوا، پیشرفت، اشتراک، پرداخت، تخفیف، دستیار هوش مصنوعی، اعلان، افیلیت، پشتیبانی، خانواده). اجرای مکرر امن است — با get_or_create هیچ رکورد تکراری‌ای نمی‌سازه. پیش‌فرض فقط وقتی DEBUG=True اجرا می‌شه، مگر با --force.

python manage.py seed_test_data              # لوکال (DEBUG=True)
python manage.py seed_test_data --force      # staging (DEBUG=False)
python manage.py seed_test_data --reset      # پاک‌کردنِ دیتای قبلیِ seed و ساختِ دوباره

مستندات API

Swagger UI http://127.0.0.1:8000/api/docs/
Redoc http://127.0.0.1:8000/api/redoc/
OpenAPI schema http://127.0.0.1:8000/api/schema/
پنل ادمین http://127.0.0.1:8000/admin/

شروع سریع در Swagger:

  1. POST /api/auth/session با {"type":"guest","deviceId":"d_test1"} → یک token بگیرید.
  2. روی دکمه‌ی Authorize بزنید و فقط خودِ token رو (بدون کلمه‌ی Bearer) وارد کنید.
  3. برای تست لاگین با شماره: POST /api/auth/otp/request بزنید، یا در محیط توسعه کد 0000 رو با هر شماره‌ای در POST /api/auth/login امتحان کنید (dev-only، نیاز به DEBUG=True).

دستورهای پرکاربرد

python manage.py createsuperuser   # اختیاری، برای /admin
python manage.py test              # اجرای کل تست‌سوییت

دستیار هوش مصنوعی (RAG)

POST /api/ai/ask پاسخ را از روی محتوای واقعیِ کتاب می‌سازد: embedding سوال → جستجوی Qdrant محدود به همان درس/فصل → تولید با GPT. تیکه‌های استفاده‌شده در usedChunks برمی‌گردند.

  • نیازمندِ OPENAI_API_KEY و QDRANT_API_KEY در .env (اپ rag؛ تنظیماتِ OPENAI_* / RAG_* در config/settings.py). بدونِ کلید، همان پاسخِ stub قبلی.
  • تست در Swagger: POST /api/ai/ask با {"userQuestion":"...","subject":"ریاضی","grade":"ششم","lessonNumber":2}.
  • "stream": true پاسخ را به‌صورت SSE توکن‌به‌توکن می‌فرستد (قراردادِ JSON بدونِ این فلگ دست‌نخورده).
  • python manage.py rag_catalog فهرستِ زنده‌ی collectionهای Qdrant را نشان می‌دهد (فقط‌خواندنی).
  • افزودنِ محتوای خودمان: POST /api/rag/ingest/text و /api/rag/ingest/pdf (داخلی، هدرِ X-Internal-Key).

۲. راهنمای دیپلوی پروداکشن

متغیرهای محیطی موردنیاز

متغیر لازم در پروداکشن توضیح
DJANGO_DEBUG باید ست‌نشده یا 0 باشه دیفالت‌های امنیتیِ پروداکشن رو فعال می‌کنه
DJANGO_SECRET_KEY بله بدونش وقتی DEBUG=False است اپ اصلاً بالا نمیاد
DJANGO_ALLOWED_HOSTS بله لیستِ host ها، کاما-جدا
CACHE_URL بله آدرسِ Redis، مثلاً redis://host:6379/0 — برای rate limitِ مشترک وقتی DEBUG=False لازمه
KAVENEGAR_API_KEY بله ارسال واقعیِ OTP از طریق کاوه‌نگار
GRAFANA_ADMIN_PASSWORD فقط اگه از استکِ Grafana استفاده می‌کنید بخشِ Docker پایین رو ببینید

تنظیمات پروداکشن

  • DEBUG باید ست نشه (یا 0 باشه). با DEBUG=False:
    • SECRET_KEY اجباریه — هیچ fallback ناامنی وجود نداره.
    • CACHE_URL (Redis) اجباریه — LocMemCache فقط برای dev هست.
    • DEV_MASTER_OTP و فیلدِ devOtp توی پاسخ‌ها غیرفعال می‌شن.
  • برای ترافیک واقعی به‌جای SQLیت پیش‌فرض، از یک دیتابیسِ پروداکشن‌گرید استفاده کنید (پروژه با همون SQLite شیپ می‌شه؛ عوض‌کردنِ engine یک تصمیمِ زیرساختیه، خارج از این ریپو).
  • از طریق یک سرورِ WSGI/ASGI (مثلاً gunicorn) جلوی اپ سرو کنید — تنظیماتِ Docker موجود همچنان runserver اجرا می‌کنه و فقط برای dev/local مستند شده (پایین رو ببینید).

تنظیمات Docker/دیپلوی

docker-compose.yml موجود، اپ رو به‌همراه یک استکِ لاگِ متمرکز (Loki + Grafana Alloy + Grafana) اجرا می‌کنه. این یک تنظیمِ dev/local هست، نه دیپلویِ پروداکشن (سرویسِ django همچنان runserver اجرا می‌کنه، نه یک سرورِ پروداکشن).

cp .env.example .env      # اختیاری — بدونش compose از دیفالت‌های dev استفاده می‌کنه
docker compose up --build
سرویس آدرس توضیح
اپ Django http://localhost:8000
Grafana http://localhost:3000 دسترسیِ anonymous admin، فقط محیطِ محلی
Loki http://localhost:3100 ذخیره‌سازیِ لاگ، در معرضِ کاربر نیست
Alloy http://localhost:12345 رابطِ دیباگِ اختیاری

ترتیبِ لازمِ بالا آمدن

Compose خودش این رو مدیریت می‌کنه:

  1. اول loki بالا میاد.
  2. alloy و grafana بعد از loki بالا میان (depends_on).
  3. django هیچ وابستگی‌ای به استکِ لاگ نداره — خودش migrate بعد runserver رو اجرا می‌کنه و منتظرِ Loki/Alloy/Grafana نمی‌مونه.

دستورهای manage.py داخلِ Docker

اگه فقط از طریقِ Docker اجرا می‌کنید (بدونِ venv محلی)، python manage.py ... رو مستقیم روی سیستم خودتون اجرا نکنید — باید داخلِ کانتینرِ django اجرا بشه:

docker compose exec django python manage.py createsuperuser
docker compose exec django python manage.py seed_test_data
docker compose exec django python manage.py test

(migrate نیازی به این نیست — طبقِ بالا، خودکار قبل از runserver اجرا می‌شه.)

مایگریشن‌ها

python manage.py migrate --noinput

در Docker، این به‌صورت خودکار به‌عنوان بخشی از دستورِ سرویسِ django، قبل از runserver اجرا می‌شه.

راهنمای seed برای staging/تست

  • هرگز seed_test_data رو روی یک دیتابیسِ پروداکشنِ واقعی اجرا نکنید.
  • روی staging (DEBUG=False)، صریحاً با --force اجرا کنید:
    python manage.py seed_test_data --force
    
  • برای پاک‌کردن و ساختِ دوباره‌ی دیتای seed بین چرخه‌های تست، از --reset استفاده کنید. همه‌ی رکوردهای seed‌شده در فیلدهای متنیِ قابل‌مشاهده با تگِ [SEED] مشخص شدن، برای شناساییِ راحت‌تر و پاک‌سازی.

چطور API دیپلوی‌شده رو verify کنیم

curl -i https://<host>/api/schema/          # باید 200 برگردونه (OpenAPI schema)
curl -i https://<host>/api/docs/            # باید 200 برگردونه (Swagger UI)
curl -i -X POST https://<host>/api/auth/session \
  -H "Content-Type: application/json" \
  -d '{"type":"guest","deviceId":"verify-1"}'   # باید یک token برگردونه

نکات مهمِ پروداکشن

  • DEV_MASTER_OTP (کد 0000) و فیلدِ devOtp توی پاسخ‌ها فقط وقتی DEBUG=True وجود دارن — قبل از رفتن روی محیطِ واقعی مطمئن بشید DEBUG خاموشه.
  • احراز هویت با Bearer token امضاشده و stateless (django.core.signing)، هماهنگ با مدل Session — بدون کتابخانه‌ی JWT خارجی.
  • accounts.User یک مدل بیزنسیِ مستقله، نه AUTH_USER_MODEL جنگو.
  • همه‌ی لاگ‌های ساختاریافته‌ی درخواست (method/path/status/duration/user، بدونِ بدنه/هدر/سکرت) روی stdout و — وقتی LOG_DIR ست باشه (مثلِ داخلِ Docker) — در /var/log/django/app.log نوشته می‌شن؛ قابلِ مشاهده در Grafana از طریقِ استکِ بالا.
  • POST /api/ai/ask با تنظیمِ OPENAI_API_KEY/QDRANT_API_KEY پاسخِ واقعیِ RAG می‌دهد (اپ rag)؛ بدونِ کلید همان stub. جزئیات: بخشِ «دستیار هوش مصنوعی (RAG)» بالاتر.
  • ارسالِ OTP از طریقِ کاوه‌نگار (KAVENEGAR_API_KEY) انجام می‌شه؛ شکستِ ارسال به‌جای موفقیتِ ساختگی، خطای صریحِ 503 برمی‌گردونه.
  • هر خطای پیش‌بینی‌نشده (نه فقط خطاهای شناخته‌شده‌ی API) همیشه پاسخِ JSON برمی‌گردونه، نه صفحه‌ی HTML پیش‌فرضِ جنگو، و کامل با traceback لاگ می‌شه (config/exceptions.py).
Description
No description provided
Readme 67 MiB
Languages
Python 96.8%
Shell 2.2%
HTML 0.9%