Zareian 42783f9ba4 tutor answers step by step in plain text, islands finish, and the audit's money and login holes are closed
Tutor (prompt tutor-v3-stepwise-plain): plain text instead of LaTeX/markdown,
with rag/services/mathtext.py converting whatever still slips through; full
step-by-step solutions with a check before the final answer; three tones by
grade; the last 6 messages of the conversation go to the model and follow-ups
are no longer served from another user's cache; book solutions the checker
disputes are kept out of the context; a questionId gives the tutor the
practice question and the student's wrong answer. /api/health reports when
the AI credit runs out. rag_model_benchmark compares chat models on verified
book problems (run on the server; credit ran out mid-run).

Map: only lessons that have questions count toward finishing an island,
lessonIds[0] / nextLessonId is always the next lesson to play, and a finished
island stays finished. The Seyar characters (sigma/delta/pi) are seeded.

Audit fixes, each reproduced before fixing:
- the plan is stored on the order, so paying a monthly order after starting
  a yearly checkout no longer grants a year
- opening checkout no longer demotes an active subscription; renewals extend
  from the current expiry; expired subscriptions are not reported active
- one discount code per order and only before the gateway; a 100% code
  completes the purchase; lost Zarinpal confirmations are re-verified
- the unused /api/payment/webhook "mark paid" endpoint is removed
- XP counts only answers recorded on the server
- 6-digit OTP, each code dies after 5 wrong guesses; POST /api/auth/logout
- a guest who signs up keeps their account and progress (test phones excluded)
- affiliate commission on the first real payment; guests excluded
- parents with several children no longer get a 500
- SQLite runs transactions IMMEDIATE, since it ignores select_for_update
- admin login is rate-limited; display names drop phone numbers and links;
  userQuestion is capped at 2000 characters
- server-side heart enforcement exists behind HEARTS_ENFORCED (off until the
  frontend handles 403 no_hearts)

Not done on purpose: rotating the server's secret key and moving to Postgres.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:24:13 +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%