make the API docs page answer a consumer's first questions, and fix a missing endpoint

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>
This commit is contained in:
2026-09-23 01:28:32 +03:30
parent 5492788bf8
commit ae0375c26c
10 changed files with 143 additions and 76 deletions

View File

@@ -19,6 +19,11 @@ from drf_spectacular.views import (
SpectacularSwaggerView,
)
@extend_schema(exclude=True)
class HealthViewSlashAlias(HealthView):
"""نسخه‌ی اسلش‌دارِ `/api/health`، فقط برای پایشِ بیرونی — بیرون از اسکیما."""
logger = logging.getLogger(__name__)
@@ -52,37 +57,32 @@ urlpatterns = [
# هر `git pull` بعدی روی همین فایل گیر می‌کرد. یک مسیرِ اضافه ارزانش
# است در برابرِ یک فایلِ دست‌کاری‌شده روی پروداکشن.
path('api/health', HealthView.as_view(), name='health'),
# همان ویو، فقط برای پایشی که با اسلش صدا می‌زند. از اسکیما بیرون است تا دو
# مسیرِ یکسان operationIdهای health_retrieve و health_retrieve_2 نسازند.
path('api/health/', extend_schema(exclude=True)(HealthView).as_view(),
name='health-slash'),
# همان ویو، فقط برای پایشی که با اسلش صدا می‌زند. از اسکیما بیرون است تا
# دو مسیرِ یکسان operationIdهای health_retrieve و health_retrieve_2 نسازند.
#
# ⚠️ زیرکلاس لازم است، نه exclude روی خودِ HealthView. extend_schema روی
# یک کلاس همان کلاس را تغییر می‌دهد، و چون هر دو مسیر به یک کلاس اشاره
# می‌کردند، `/api/health` هم با خودش از اسکیما بیرون می‌افتاد — یعنی
# اندپوینتِ سلامت اصلاً در Swagger دیده نمی‌شد.
path('api/health/', HealthViewSlashAlias.as_view(), name='health-slash'),
# OpenAPI schema + Swagger UI + Redoc
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
# اپ‌های فاز ۱
# همه زیرِ /api/ جمع می‌شوند؛ هر اپ مسیرهای خودش را نگه می‌دارد، پس
# ترتیبِ این فهرست اهمیتی ندارد و تداخلی بینشان نیست.
path('api/', include('accounts.urls')),
path('api/', include('content.urls')),
path('api/', include('progress.urls')),
path('api/', include('ai_tutor.urls')),
path('api/', include('rag.urls')),
# اپ‌های فاز ۲ / بخش ۱ (subscription, payment, discount)
path('api/', include('subscription.urls')),
path('api/', include('payment.urls')),
path('api/', include('discount.urls')),
# اپ‌های فاز ۲ / بخش ۲ (notifications)
path('api/', include('notifications.urls')),
# اپ‌های فاز ۲ / بخش ۳ (affiliate)
path('api/', include('affiliate.urls')),
# اپ‌های فاز ۲ / بخش ۴ (support)
path('api/', include('support.urls')),
# اپ‌های فاز ۲ / بخش ۵ (family)
path('api/', include('family.urls')),
]