سینیور خواست زودتر به فرانتاند وصل شویم تا از آنجا هم تست شود. اینها
سمتِ ما لازم بودند:
CORS با allowlist صریح (نه ALLOW_ALL): احرازِ هویتِ ما توکنِ Bearer در هدر
است، پس origin ناشناس نباید از مرورگرِ کاربر به API ما بزند.
CORS_ALLOW_CREDENTIALS عمداً خاموش میماند — کوکیای در کار نیست و روشن
کردنش فقط سطحِ حمله اضافه میکند. CorsMiddleware بالاتر از CommonMiddleware
گذاشته شد وگرنه ریدایرکتِ APPEND_SLASH و خطای ۴۰۰ هدرِ CORS نمیگیرند و در
مرورگر بهشکلِ خطای مبهمِ شبکه دیده میشوند.
PATCH /api/user/profile اضافه شد؛ تا پیش از این هیچ راهی برای ثبتِ نامِ
کاربر نبود. GET /api/home بدونِ پارامتر حالا پایهی ذخیرهشدهی کاربر را
برمیگرداند و POST /api/ai/ask فقط با lessonId کار میکند — هر دو برای
اینکه فرانت لازم نباشد پایه را خودش نگه دارد. questionCount به
lessons/{id}/intro اضافه شد.
docs/frontend/api.ts و README.md کلاینتِ تایپدار و راهنمای اتصالاند، از
روی OpenAPI واقعیِ همین سرویس تولید شدهاند.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
from rest_framework import serializers
|
|
|
|
|
|
# ---- Request serializers ----
|
|
|
|
class GuestSessionRequestSerializer(serializers.Serializer):
|
|
type = serializers.ChoiceField(choices=['guest'])
|
|
deviceId = serializers.CharField(max_length=64)
|
|
|
|
|
|
class LoginRequestSerializer(serializers.Serializer):
|
|
phone = serializers.CharField(max_length=15)
|
|
otp = serializers.CharField(max_length=6)
|
|
|
|
|
|
class RefreshRequestSerializer(serializers.Serializer):
|
|
refreshToken = serializers.CharField()
|
|
|
|
|
|
class SetGradeRequestSerializer(serializers.Serializer):
|
|
gradeId = serializers.IntegerField()
|
|
|
|
|
|
class UpdateProfileRequestSerializer(serializers.Serializer):
|
|
"""
|
|
ورودی PATCH /api/user/profile — همهی فیلدها اختیاریاند و فقط همانهایی
|
|
که فرستاده شوند تغییر میکنند (partial update واقعی).
|
|
|
|
`phone` و `isGuest` عمداً اینجا نیستند: شماره فقط از مسیرِ OTP عوض
|
|
میشود و مهمانبودن را کاربر نباید بتواند خودش تغییر دهد.
|
|
"""
|
|
name = serializers.CharField(max_length=100, required=False, allow_blank=True)
|
|
avatar = serializers.CharField(max_length=100, required=False, allow_blank=True)
|
|
birthYear = serializers.IntegerField(required=False, allow_null=True,
|
|
min_value=1300, max_value=1500)
|
|
parentPhone = serializers.CharField(max_length=15, required=False, allow_blank=True)
|
|
bio = serializers.CharField(max_length=200, required=False, allow_blank=True)
|
|
|
|
def validate(self, attrs):
|
|
if not attrs:
|
|
raise serializers.ValidationError('حداقل یکی از فیلدها باید ارسال شود.')
|
|
return attrs
|
|
|
|
|
|
class RequestOTPRequestSerializer(serializers.Serializer):
|
|
"""اپ کمکی برای گرفتن OTP (در سند اصلی نبود، برای تستپذیری Swagger اضافه شد)."""
|
|
phone = serializers.CharField(max_length=15)
|
|
|
|
|
|
# ---- Response serializers ----
|
|
|
|
class GuestSessionResponseSerializer(serializers.Serializer):
|
|
userId = serializers.CharField()
|
|
token = serializers.CharField()
|
|
isNewUser = serializers.BooleanField()
|
|
isGuest = serializers.BooleanField()
|
|
|
|
|
|
class LoginResponseSerializer(serializers.Serializer):
|
|
userId = serializers.CharField()
|
|
token = serializers.CharField()
|
|
refreshToken = serializers.CharField()
|
|
|
|
|
|
class RefreshResponseSerializer(serializers.Serializer):
|
|
token = serializers.CharField()
|
|
refreshToken = serializers.CharField()
|
|
expiresIn = serializers.IntegerField()
|
|
|
|
|
|
class GradeSerializer(serializers.Serializer):
|
|
id = serializers.IntegerField()
|
|
title = serializers.CharField()
|
|
icon = serializers.CharField()
|
|
color = serializers.CharField()
|
|
|
|
|
|
class GradesListResponseSerializer(serializers.Serializer):
|
|
grades = GradeSerializer(many=True)
|
|
|
|
|
|
class SetGradeResponseSerializer(serializers.Serializer):
|
|
success = serializers.BooleanField()
|
|
gradeId = serializers.IntegerField()
|
|
|
|
|
|
class RequestOTPResponseSerializer(serializers.Serializer):
|
|
success = serializers.BooleanField()
|
|
devOtp = serializers.CharField()
|
|
note = serializers.CharField()
|
|
|
|
|
|
class ProfileResponseSerializer(serializers.Serializer):
|
|
"""پاسخ GET /api/user/profile — فقط دادهی لازم برای تب Profile اپ موبایل."""
|
|
userId = serializers.CharField()
|
|
phone = serializers.CharField(allow_null=True)
|
|
name = serializers.CharField()
|
|
avatar = serializers.CharField()
|
|
isGuest = serializers.BooleanField()
|
|
gradeId = serializers.IntegerField(allow_null=True)
|
|
birthYear = serializers.IntegerField(allow_null=True)
|
|
parentPhone = serializers.CharField()
|
|
bio = serializers.CharField()
|