add a typed API layer for the Tadrisino backend
اپ تا الان هیچ درخواستی به سرور نمیزد و همهی دادهها داخلِ خودِ کامپوننتها
هاردکد بود. این کامیت لایهی ارتباط را اضافه میکند، بدونِ اینکه هیچ صفحهای
را تغییر دهد.
عمداً فقط فایلِ جدید اضافه شده و App.tsx دست نخورده است، چون کارِ در جریانِ
برنچِ updateApp روی همان فایل است و نباید با هم تصادف کنند. وصل کردنِ
صفحهها به این لایه کارِ بعدیست.
هیچ پکیجِ جدیدی اضافه نشد — فقط fetch استاندارد.
چیزهایی که در client.ts هست برای حالتِ خراباند، نه حالتِ خوب:
• timeout روی هر درخواست — وگرنه بکاندِ کند سوکتها را باز نگه میدارد تا
مرورگر به سقفِ ۶ درخواستِ همزمان بخورد و اپ قفل شود.
• backoff با jitter — بدونِ jitter همهی کلاینتها بعد از یک خطا همزمان
دوباره میزنند و موجِ دوم از موجِ اول بدتر است.
• تکرارِ خودکار فقط روی GET؛ POST فقط وقتی Idempotency-Key دارد (ثبتِ پاسخ،
سفارش) تا دابلکلیکِ کاربر دو بار XP یا دو سفارش نسازد.
• refresh تکنسخهای — ۲۰ درخواستِ همزمانِ ۴۰۱ نباید ۲۰ بار refresh بزنند.
• ادغامِ GETهای یکسانِ در جریان.
• احترام به هدرِ Retry-After که بکاند موقعِ throttle میفرستد.
credentials: 'omit' عمدیست: احرازِ هویت توکنِ Bearer در هدر است و سمتِ سرور
CORS_ALLOW_CREDENTIALS=False تنظیم شده.
.env هم به .gitignore اضافه شد؛ قبلاً فقط node_modules داخلش بود و آدرس/
تنظیماتِ محلی میتوانست ناخواسته کامیت شود.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
91
src/api/README.md
Normal file
91
src/api/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# اتصال به بکاند
|
||||
|
||||
کلِ ارتباط با سرور از همین پوشه رد میشود. **هیچجای اپ مستقیم `fetch` نزنید** —
|
||||
مدیریتِ توکن، تکرارِ درخواست و رفتارِ درستِ خطا اینجا یکجا حل شدهاند.
|
||||
|
||||
## راهاندازی
|
||||
|
||||
**هیچ تنظیماتی لازم نیست.** بهطورِ پیشفرض به بکاندِ مشترک
|
||||
(`https://api.math.tadrisino2.ir/api`) وصل میشود:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
فقط اگر بکاند را روی ماشینِ خودتان بالا آوردهاید:
|
||||
|
||||
```bash
|
||||
cp .env.example .env # و خطِ VITE_API_BASE_URL را باز کنید
|
||||
```
|
||||
|
||||
برای اینکه ببینید اتصال برقرار است، یک خط کافیست (بعداً پاکش کنید):
|
||||
|
||||
```tsx
|
||||
import { ConnectionCheck } from '@/api/ConnectionCheck';
|
||||
// داخلِ خروجیِ App:
|
||||
<ConnectionCheck />
|
||||
```
|
||||
|
||||
گوشهی صفحه سبز میشود و فهرستِ پایهها را از سرور نشان میدهد، یا قرمز
|
||||
میشود و میگوید دقیقاً کجای کار میلنگد.
|
||||
|
||||
## استفاده
|
||||
|
||||
```tsx
|
||||
import { api, ApiError } from '@/api';
|
||||
|
||||
const grades = await api.grades();
|
||||
const home = await api.home(); // پایهی خودِ کاربر
|
||||
const intro = await api.lessons.intro(lessonId);
|
||||
const answer = await api.ai.ask('این رو نفهمیدم', { lessonId });
|
||||
```
|
||||
|
||||
### ورود
|
||||
|
||||
```tsx
|
||||
await api.auth.guest(deviceId); // «شروع سریع»، بدونِ شماره
|
||||
// یا
|
||||
const { devOtp } = await api.auth.requestOtp('09120000000');
|
||||
await api.auth.login('09120000000', otp); // نامِ فیلد در سرور otp است
|
||||
```
|
||||
|
||||
توکن خودش در `localStorage` ذخیره میشود و به هر درخواست اضافه میگردد.
|
||||
|
||||
### خطاها
|
||||
|
||||
```tsx
|
||||
try {
|
||||
await api.ai.ask(text, { lessonId });
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) {
|
||||
if (error.isRateLimited) { /* ۴۲۹ — خیلی سریع پرسیده */ }
|
||||
if (error.isProviderDown) { /* ۵۰۳ — سرویسِ AI موقتاً قطع */ }
|
||||
if (error.isNetwork) { /* اصلاً نرسید یا timeout */ }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## چرا اینقدر ملاحظه در `client.ts`
|
||||
|
||||
هر تکهاش برای حالتیست که فقط زیرِ فشار پیش میآید:
|
||||
|
||||
| مسئله | اگر نباشد چه میشود |
|
||||
|---|---|
|
||||
| `timeout` روی هر درخواست | یک بکاندِ کند سوکتها را باز نگه میدارد؛ مرورگر به سقفِ ۶ درخواستِ همزمان میخورد و کلِ اپ قفل میشود |
|
||||
| backoff با **jitter** | هزار کلاینت بعد از یک خطا *همزمان* دوباره میزنند و موجِ دوم سرور را میخواباند |
|
||||
| تکرارِ خودکار فقط روی `GET` | تکرارِ `POST` یعنی سفارش یا پاسخِ تکراری |
|
||||
| `Idempotency-Key` روی ثبتِ پاسخ و سفارش | دابلکلیکِ کاربر دو بار XP یا دو سفارش میسازد |
|
||||
| `refresh` تکنسخهای | ۲۰ درخواستِ همزمانِ ۴۰۱ ⇒ ۲۰ بار refresh ⇒ سرور همه را throttle میکند |
|
||||
| ادغامِ `GET`های یکسان | دو کامپوننت که با هم یک چیز میخواهند، دو برابر بار میسازند |
|
||||
| احترام به هدرِ `Retry-After` | بکاند صریح میگوید چقدر صبر کن؛ نادیده گرفتنش throttle را طولانیتر میکند |
|
||||
|
||||
`credentials: 'omit'` هم عمدیست: احرازِ هویت توکنِ Bearer در هدر است و سمتِ
|
||||
سرور `CORS_ALLOW_CREDENTIALS=False` تنظیم شده. عوضش نکنید.
|
||||
|
||||
## چیزی که هنوز نیست
|
||||
|
||||
- صفحهی ورود در اپ وجود ندارد؛ `api.auth` آماده است ولی UI ندارد.
|
||||
- `api.home()` ساختارِ واقعیِ درسها را میدهد، ولی وصل کردنش به صفحهها
|
||||
هنوز انجام نشده — این PR عمداً `App.tsx` را دست نزده تا با کارِ در جریان
|
||||
تصادف نکند.
|
||||
Reference in New Issue
Block a user