Plan.kind (pack/subscription) collided with the lesson kind (quiz/video); drf-spectacular named one KindEd8Enum and warned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tadrisino API
بکاندِ اپلیکیشنِ آموزشیِ تدریسینو — یک REST API روی Django که برنامهی درسی، آزمونها، پیشرفتِ کاربر، دستیارِ هوشِ مصنوعی، اشتراک و پرداخت را سرو میکند.
فرانتاند ریپوی جداگانهای است (MathApp_frontend).
قراردادِ بینِ دو طرف، اسکیمای OpenAPI همین ریپوست — نه حافظهی کسی، نه یک
فایلِ دستی. هر تغییری در API باید از آنجا دیده شود.
| وضعیت | روی پروداکشن، در حالِ سرویسدهی |
| آدرسِ زنده | https://mathapi.tadrisino2.ir · سلامت |
| استک | Python 3.11+ · Django 5.2 · DRF · SQLite · Qdrant (RAG) |
| API | ۶۱ اندپوینت · مستنداتِ زنده در /api/docs/ |
| تست | ۶۸۵ تست، همه سبز (python manage.py test) |
| محتوا | ۸ پایه · ۵۸ فصل · ۲۶۳ نود · ۲٬۶۱۷ درس · ۲٬۵۴۵ سوال |
شروعِ سریع
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # برای توسعهی محلی: DJANGO_DEBUG=1 را ست کنید
python manage.py migrate
python manage.py seed_content # برنامهی درسی و سوالهای واقعی از content/seed/
python manage.py runserver
سرویس روی http://127.0.0.1:8000 بالا میآید. .env خودکار خوانده میشود —
لازم نیست چیزی را export کنید.
| Swagger UI | /api/docs/ |
| Redoc | /api/redoc/ |
| اسکیمای OpenAPI | /api/schema/ |
| پنلِ ادمین | /admin/ |
| سلامتِ سرویس | /api/health |
اولین درخواست در Swagger: POST /api/auth/session با
{"type":"guest","deviceId":"d_test1"} بزنید، token را بردارید، روی
Authorize کلیک کنید و همان رشته را (بدونِ کلمهی Bearer) وارد کنید.
دستورهای پرکاربردِ دیگر:
python manage.py test # کلِ تستسوییت
python manage.py createsuperuser # دسترسی به /admin
python manage.py seed_test_data # دادهی ساختگی برای توسعه (فقط محلی)
معماری
هر اپ یک مسئولیت دارد و مدلهای خودش را نگه میدارد:
| اپ | مسئولیت |
|---|---|
accounts |
هویت، session، ورود با OTP |
content |
برنامهی درسی، درس، سوال، پیشرفت روی نقشه |
progress |
آمار، لیدربرد، streak، شخصیتهای بازی |
ai_tutor · rag |
دستیارِ هوشِ مصنوعی: بازیابی از Qdrant + تولید با GPT |
subscription · payment · discount |
پلن، سفارش، تراکنش، کدِ تخفیف |
notifications · affiliate · support · campaigns |
امکاناتِ جانبی |
statuslog |
لاگِ عمومیِ تغییرِ وضعیت، مستقل از بقیه |
config |
تنظیمات، روتهای اصلی، middleware |
سه تصمیمِ معماری که غیرِبدیهیاند و دانستنشان لازم است:
- احرازِ هویت، سیستمِ خودمان است نه جنگو. توکنِ bearerِ امضاشده و stateless
با
django.core.signing(بدونِ کتابخانهی JWT)، پشتیبانیشده با مدلِaccounts.Session.accounts.UserعمداًAUTH_USER_MODELنیست — یک مدلِ بیزنسیِ مستقل است. هدرِ نامعتبر یعنی درخواستِ ناشناس، نه خطا؛ هر ویو خودش تصمیم میگیرد که آیا ورود لازم است یا نه (accounts/authentication.py). - اپها با قرارداد از هم جدا شدهاند، نه با ForeignKey. ارجاعِ بینِ اپها با
user_idوobject_id+labelانجام میشود تا هر اپ بعداً قابلِ جدا شدن باشد. FK بینِ اپها اضافه نکنید. - محدودیتِ نرخ (throttle) متمرکز است. هر اندپوینتِ حساس یک
throttle_scopeدارد و نرخش درREST_FRAMEWORK['DEFAULT_THROTTLE_RATES']داخلِconfig/settings.pyتعریف میشود. اندپوینتِ جدید یعنی هر دو.
جزئیاتِ بیشتر: docs/architecture.md.
دیپلوی
کلِ انتشار یک خط است:
ssh -t administrator@78.38.35.249 \
'cd ~/mathapp-v2 && ./deploy.sh; sudo systemctl restart zareian-mathapp'
deploy.sh کد را میگیرد، وابستگیها را نصب میکند، مهاجرت میزند، محتوا را
seed میکند، فایلهای استاتیک را جمع میکند و در پایان ثابت میکند که سرویس
واقعاً ریاستارت شده — نه اینکه فقط فرض کند. ریاستارت جداست چون sudo
داخلِ اسکریپت نمیتواند رمز بپرسد.
پس از هر انتشار، از بیرون و بدونِ SSH تأیید کنید:
curl https://mathapi.tadrisino2.ir/api/health
200 یعنی سالم، 503 یعنی چیزی واقعاً خراب است. هیچ سکرتی برنمیگرداند.
رویهی کامل — متغیرهای محیطی، ورودِ توسعهدهندهها، بکاپ، عیبیابی:
docs/operations.md.
مستندات
| سند | موضوع |
|---|---|
docs/operations.md |
دیپلوی، متغیرهای محیطی، سلامت، بکاپ، ورودِ توسعهدهندهها |
docs/technical-handbook.md · PDF |
مستندِ فنیِ کامل و تحویلِ پروژه: معماری، مدلِ سناریو و رابطهای، API، Qdrant، گیمیفیکیشن، امنیت، استقرار، مقیاس |
docs/security-before-production.md |
پیش از کاربرِ واقعی: هرچه برای دمو عمداً باز مانده، خطرش و راهِ بستنش |
docs/content-authoring.md |
افزودن و ویرایشِ محتوا از پنلِ ادمین، ساختِ انبوهِ سوال |
docs/architecture.md |
تصمیمهای معماری و دلیلشان |
docs/frontend/README.md |
راهنمای اتصالِ فرانتاند + کلاینتِ تایپدار |
docs/docker.md |
استکِ لاگِ محلی (Loki + Alloy + Grafana) |
docs/extraction-playbook.md |
استخراجِ محتوا از کتاب با مدلِ ویژن و هزینههایش |
محدودیتهای شناختهشده
اینها عمداً نوشته شدهاند تا کسی دوباره کشفشان نکند:
- دیتابیس هنوز SQLite است، نه Postgres. برای حجمِ فعلی کافیست، ولی تکنویسنده بودنش اولین گلوگاهِ مقیاس است.
- بکاپِ خودکار وجود ندارد.
db.sqlite3در گیت نیست و تنها نسخهی دادهی کاربران است. روشِ بکاپِ دستی درdocs/operations.mdآمده؛ خودکار کردنش اولین کارِ ارزشمندِ بعدی است. CACHE_URLروی سرور ست نشده، پس محدودیتِ نرخ بینِ workerها مشترک نیست. Redis روی همان ماشین بالاست./api/healthاین را هشدار میدهد.KAVENEGAR_API_KEYتنظیم نشده، پسPOST /api/auth/otp/requestبرای شمارههای واقعی503میدهد. راهِ ورودِ تیمِ توسعه درdocs/operations.mdتوضیح داده شده.- پوششِ محتوا یکدست نیست. پایههای هشتم تا دوازدهم کاملند (هر نود چهار سوال)؛ در پایههای سوم/ششم/هفتم ۳۹ نود از ۱۴۵ کمتر از چهار سوال دارند — هیچکدام خالی نیست، پس روی نقشه نودِ غیرقابلِ بازی وجود ندارد.
- محتوای ویدیویی وجود ندارد، هرچند
Lesson.kindوvideo_urlمیتوانند بیانش کنند. DEV_MASTER_OTP(کد0000) باید پشتِDEBUGبماند. روشن بودنش یعنی هر کسی میتواند بهجای هر کاربری وارد شود./api/healthهشدارش میدهد.