Zareian 7d6abab15c document the twenty environment variables the code reads but never mentioned
A mechanical cross-check of os.environ.get() in settings against .env.example
found twenty variables the app reads that were written down nowhere. Most have
harmless defaults, but three would have cost someone real time:

- NUM_PROXIES decides how many X-Forwarded-For hops are trusted, and guest rate
  limiting is keyed on the resulting IP. Set too high, a client can forge the
  header and walk past the limit; too low and everyone collapses onto the proxy's
  own IP. CLAUDE.md warns about it; the file an operator actually edits did not.
- KAVENEGAR_SENDER is read alongside the API key, so someone configuring SMS from
  the README alone would have set only half of it.
- INTERNAL_SERVICE_KEY and PAYMENT_WEBHOOK_SECRET are empty under DEBUG=0 by
  design, which quietly keeps the ingest and webhook routes shut. That is the
  right behaviour and exactly the kind of thing that reads as a bug when it is
  not written down.

The whole Cafe Bazaar block was undocumented too.

Three variables in .env.example are not read by settings and were left alone:
GRAFANA_ADMIN_PASSWORD is consumed by docker-compose, TADRISINO_SERVICE by
deploy.sh, and OPENAI_API_KEY_OPENAI_DIRECT is a documented parking spot.

CLAUDE.md's current-state section was a day stale (458 tests, now 483) and said
nothing about the documentation restructure, the RTL docs shell, the test-login
allowlist or the frontend test account, so anyone picking this up would have
rediscovered all four. It now also records that backups are still manual and
that sqlite3 is not installed on the server.

Also verified mechanically, with no findings: no docstring references an
endpoint absent from the OpenAPI schema, no docstring names a setting or
constant that does not exist, and every internal documentation link resolves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 02:11:29 +03:30

Tadrisino API

بک‌اندِ اپلیکیشنِ آموزشیِ تدریسینو — یک REST API روی Django که برنامه‌ی درسی، آزمون‌ها، پیشرفتِ کاربر، دستیارِ هوشِ مصنوعی، اشتراک و پرداخت را سرو می‌کند.

فرانت‌اند ریپوی جداگانه‌ای است (MathApp_frontend). قراردادِ بینِ دو طرف، اسکیمای OpenAPI همین ریپوست — نه حافظه‌ی کسی، نه یک فایلِ دستی. هر تغییری در API باید از آنجا دیده شود.

وضعیت روی پروداکشن، در حالِ سرویس‌دهی
آدرسِ زنده https://api.math.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 · 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.


دیپلوی

کلِ انتشار یک خط است:

ssh -t administrator@78.38.35.249 \
  'cd ~/mathapp-v2 && ./deploy.sh; sudo systemctl restart zareian-mathapp'

deploy.sh کد را می‌گیرد، وابستگی‌ها را نصب می‌کند، مهاجرت می‌زند، محتوا را seed می‌کند، فایل‌های استاتیک را جمع می‌کند و در پایان ثابت می‌کند که سرویس واقعاً ری‌استارت شده — نه اینکه فقط فرض کند. ری‌استارت جداست چون sudo داخلِ اسکریپت نمی‌تواند رمز بپرسد.

پس از هر انتشار، از بیرون و بدونِ SSH تأیید کنید:

curl https://api.math.tadrisino2.ir/api/health

200 یعنی سالم، 503 یعنی چیزی واقعاً خراب است. هیچ سکرتی برنمی‌گرداند.

رویه‌ی کامل — متغیرهای محیطی، ورودِ توسعه‌دهنده‌ها، بکاپ، عیب‌یابی: docs/operations.md.


مستندات

سند موضوع
docs/operations.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 هشدارش می‌دهد.
Description
No description provided
Readme 67 MiB
Languages
Python 96.8%
Shell 2.2%
HTML 0.9%