The Swagger header was a list of features. That is what the project does, not what a caller needs. It now opens with the three things anyone integrating against this API asks first: how do I get a token, what does an error look like, and when do I get rate limited -- plus the field-naming and date conventions, and a note that the schema is the contract and clients should be generated from it rather than written from memory. Swagger UI and Redoc both render Markdown, so it is structured rather than one long paragraph, which also fixes how badly the old mixed Persian/Latin line wrapped right-to-left. Two defects surfaced while checking the rendered schema: - /api/health was absent from the schema entirely. The trailing-slash alias was excluded with extend_schema(exclude=True)(HealthView), but that decorator mutates the class, and both routes pointed at the same class, so the canonical route disappeared with it. A subclass now carries the exclusion, and the endpoint is back, exactly once. Endpoint count is 56, not 55. - One endpoint was tagged 'محتوا (content)' while every other content endpoint used 'Curriculum (content)', which split the group into two sections. Tags are now declared with descriptions and ordered by the path a real user takes, rather than alphabetically. Comment pass over the codebase, keeping the substance and dropping the noise: the "⚠️ اضافه شده" prefixes dated comments against a moment no current reader shares; "فاز ۲ / بخش ۴" referenced a plan that no longer exists and only repeated the app name on the next line; "فاز اول - ۴ از ۱۱ اپ" had drifted (there are fourteen). INSTALLED_APPS is now grouped by responsibility. The TEST_LOGIN_PHONES block in settings shrank from 29 lines to 8 now that the same explanation lives in docs/operations.md and .env.example -- a comment beside the code should carry what a code reader needs, not a copy of the runbook. Checked mechanically that no comment references an identifier that no longer exists: zero. The dated measurements in rag/services/ingest.py and generate_questions_from_chunks.py were deliberately left alone -- a measurement carrying the date it was taken is the useful kind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
151 lines
8.5 KiB
Markdown
151 lines
8.5 KiB
Markdown
# Tadrisino API
|
||
|
||
بکاندِ اپلیکیشنِ آموزشیِ تدریسینو — یک REST API روی Django که برنامهی درسی،
|
||
آزمونها، پیشرفتِ کاربر، دستیارِ هوشِ مصنوعی، اشتراک و پرداخت را سرو میکند.
|
||
|
||
فرانتاند ریپوی جداگانهای است ([`MathApp_frontend`](https://gitea.tadrisino2.ir/Tadrisino/MathApp_frontend)).
|
||
**قراردادِ بینِ دو طرف، اسکیمای OpenAPI همین ریپوست** — نه حافظهی کسی، نه یک
|
||
فایلِ دستی. هر تغییری در API باید از آنجا دیده شود.
|
||
|
||
| | |
|
||
|---|---|
|
||
| **وضعیت** | روی پروداکشن، در حالِ سرویسدهی |
|
||
| **آدرسِ زنده** | `https://api.math.tadrisino2.ir` · [سلامت](https://api.math.tadrisino2.ir/api/health) |
|
||
| **استک** | Python 3.11+ · Django 5.2 · DRF · SQLite · Qdrant (RAG) |
|
||
| **API** | ۵۶ اندپوینت · مستنداتِ زنده در `/api/docs/` |
|
||
| **تست** | ۴۸۱ تست، همه سبز (`python manage.py test`) |
|
||
| **محتوا** | ۸ پایه · ۵۸ فصل · ۲۶۳ نود · ۲٬۶۱۷ درس · ۲٬۴۶۳ سوال |
|
||
|
||
---
|
||
|
||
## شروعِ سریع
|
||
|
||
```bash
|
||
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`) وارد کنید.
|
||
|
||
دستورهای پرکاربردِ دیگر:
|
||
|
||
```bash
|
||
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` · `family` · `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`](docs/architecture.md).
|
||
|
||
---
|
||
|
||
## دیپلوی
|
||
|
||
کلِ انتشار یک خط است:
|
||
|
||
```bash
|
||
ssh -t administrator@78.38.35.249 \
|
||
'cd ~/mathapp-v2 && ./deploy.sh; sudo systemctl restart zareian-mathapp'
|
||
```
|
||
|
||
`deploy.sh` کد را میگیرد، وابستگیها را نصب میکند، مهاجرت میزند، محتوا را
|
||
seed میکند، فایلهای استاتیک را جمع میکند و در پایان **ثابت میکند که سرویس
|
||
واقعاً ریاستارت شده** — نه اینکه فقط فرض کند. ریاستارت جداست چون `sudo`
|
||
داخلِ اسکریپت نمیتواند رمز بپرسد.
|
||
|
||
پس از هر انتشار، از بیرون و بدونِ SSH تأیید کنید:
|
||
|
||
```bash
|
||
curl https://api.math.tadrisino2.ir/api/health
|
||
```
|
||
|
||
`200` یعنی سالم، `503` یعنی چیزی واقعاً خراب است. هیچ سکرتی برنمیگرداند.
|
||
|
||
رویهی کامل — متغیرهای محیطی، ورودِ توسعهدهندهها، بکاپ، عیبیابی:
|
||
[`docs/operations.md`](docs/operations.md).
|
||
|
||
---
|
||
|
||
## مستندات
|
||
|
||
| سند | موضوع |
|
||
|---|---|
|
||
| [`docs/operations.md`](docs/operations.md) | دیپلوی، متغیرهای محیطی، سلامت، بکاپ، ورودِ توسعهدهندهها |
|
||
| [`docs/content-authoring.md`](docs/content-authoring.md) | افزودن و ویرایشِ محتوا از پنلِ ادمین، ساختِ انبوهِ سوال |
|
||
| [`docs/architecture.md`](docs/architecture.md) | تصمیمهای معماری و دلیلشان |
|
||
| [`docs/frontend/README.md`](docs/frontend/README.md) | راهنمای اتصالِ فرانتاند + کلاینتِ تایپدار |
|
||
| [`docs/docker.md`](docs/docker.md) | استکِ لاگِ محلی (Loki + Alloy + Grafana) |
|
||
| [`docs/extraction-playbook.md`](docs/extraction-playbook.md) | استخراجِ محتوا از کتاب با مدلِ ویژن و هزینههایش |
|
||
|
||
---
|
||
|
||
## محدودیتهای شناختهشده
|
||
|
||
اینها عمداً نوشته شدهاند تا کسی دوباره کشفشان نکند:
|
||
|
||
- **دیتابیس هنوز SQLite است**، نه Postgres. برای حجمِ فعلی کافیست، ولی
|
||
تکنویسنده بودنش اولین گلوگاهِ مقیاس است.
|
||
- **بکاپِ خودکار وجود ندارد.** `db.sqlite3` در گیت نیست و تنها نسخهی دادهی
|
||
کاربران است. روشِ بکاپِ دستی در [`docs/operations.md`](docs/operations.md)
|
||
آمده؛ خودکار کردنش اولین کارِ ارزشمندِ بعدی است.
|
||
- **`CACHE_URL` روی سرور ست نشده**، پس محدودیتِ نرخ بینِ workerها مشترک نیست.
|
||
Redis روی همان ماشین بالاست. `/api/health` این را هشدار میدهد.
|
||
- **`KAVENEGAR_API_KEY` تنظیم نشده**، پس `POST /api/auth/otp/request` برای
|
||
شمارههای واقعی `503` میدهد. راهِ ورودِ تیمِ توسعه در
|
||
[`docs/operations.md`](docs/operations.md) توضیح داده شده.
|
||
- **پوششِ محتوا یکدست نیست.** پایههای هشتم تا دوازدهم کاملند (هر نود چهار
|
||
سوال)؛ در پایههای سوم/ششم/هفتم ۳۹ نود از ۱۴۵ کمتر از چهار سوال دارند —
|
||
هیچکدام خالی نیست، پس روی نقشه نودِ غیرقابلِ بازی وجود ندارد.
|
||
- **محتوای ویدیویی وجود ندارد**، هرچند `Lesson.kind` و `video_url` میتوانند
|
||
بیانش کنند.
|
||
- **`DEV_MASTER_OTP` (کد `0000`) باید پشتِ `DEBUG` بماند.** روشن بودنش یعنی هر
|
||
کسی میتواند بهجای هر کاربری وارد شود. `/api/health` هشدارش میدهد.
|