9.3 KiB
تطبیق سایت عمومی با تغییرات API برنچ backend-audit
✅ حلشده (Path A) — هیچ تغییری در
nobat724_frontلازم نیست. مشکل rotation با نرمکردن backend حل شد: درclinicpro(commit رویbackend-audit) چرخش/باطلسازی single-use از/oauth/token/refreshبرداشته شد و توکن refresh دوباره قابلاستفاده شد (فقط چک کاربر معلقstatus!=1باقی ماند). پسlib/serverToken.jsبدون تغییر کار میکند. تنها نکتهی باقیمانده اختیاری است: ۵۰-cap نظرات (وظیفه ۲ پایین) — اگر ۵۰ نظر کافی است، کاری لازم نیست.
پروژه
nobat724_front (سایت عمومی). پرامپت همتا برای Admin SPA: clinicpro/.claude/prompt/admin-spa-adapt-backend-audit.md. منبع تغییرات: برنچ backend-audit در clinicpro (۳۳ commit).
زمینه
ممیزی بکاند چند endpoint را تغییر داد. مقدارهای wire کدهای خطا حفظ شدهاند، ولی یک تغییر رفتارِ شکننده برای سایت عمومی وجود دارد (rotation توکن refresh) و یک تغییر کماهمیت (صفحهبندی نظرات). این پرامپت مصرفکنندههای nobat724_front را اصلاح میکند.
جدول تغییرات API مرتبط با سایت عمومی
| Endpoint | تغییر | مصرف در nobat724 | شدت |
|---|---|---|---|
POST /oauth/token/refresh |
refresh_token اکنون یکبارمصرف است و در هر فراخوانی چرخش میکند؛ کاربر معلق 401 |
lib/serverToken.js |
🔴 شکننده |
GET /api/v1/comments/{uuid} |
data.meta افزوده شد، پیشفرض ۵۰ نظر (قبلاً همه) |
app/doctor/[slug]/page.js, services/response.js getDoctorComments |
🟡 کم |
GET /api/v1/insurance/{id}, GET /clinic-pro/doctor-address/{id}, appointment-settings/{...} |
حالا owner/admin (403) |
مصرف نمیشود | ⚪ بدون اثر |
| کدهای خطای legacy (M21) | مقدار wire بدون تغییر | — | ⚪ بدون اثر |
🔴 مشکل اصلی: rotation توکن refresh (POST /oauth/token/refresh)
رفتار جدید backend
هر فراخوانی /oauth/token/refresh:
- توکن ارائهشده را باطل میکند (single-use)،
- یک جفت
access_token+refresh_tokenجدید صادر میکند، - کاربر با
status != 1→401.
پاسخ:
{ "access_token": "…", "refresh_token": "<توکن جدید — با قبلی فرق دارد>", "token_type": "Bearer", "expires_in": 900, "refresh_token_expires_in": 2592000 }
وضعیت فعلی nobat724 (کد واقعی — lib/serverToken.js)
export async function getServerAccessToken() {
const cookieStore = await cookies();
const refreshToken = cookieStore.get("refresh_token")?.value;
if (!refreshToken) return null;
try {
const res = await axiosInstance.post(
`${process.env.NEXT_PUBLIC_API_URL}/oauth/token/refresh`,
{ refresh_token: refreshToken },
{ headers: { "Content-Type": "application/json", Authorization: "" } }
);
return res.data?.access_token ?? null; // ⚠️ فقط access_token خوانده میشود
} catch { return null; }
}
چرا میشکند
- این تابع در هر بار رندر صفحه صدا زده میشود (مثلاً
app/dashboard/page.js). refresh_tokenچرخشیافتهی جدید را ذخیره نمیکند و توکن قدیمیِ کوکی پس از اولین refresh باطل شده است.- نتیجه: refresh اول OK → ناوبری بعدی همان کوکی باطل را میفرستد →
401→null→ redirect به login (خروج عملی کاربر). - محدودیت Next.js:
getServerAccessToken()از یک Server Component صدا زده میشود؛ Server Componentها نمیتوانند کوکی ست کنند (فقط Server Action / Route Handler / middleware میتوانند). پس ذخیرهی توکن چرخشیافته در همین تابع ممکن نیست.
وظیفه ۱ — حل rotation (یکی از دو مسیر؛ مسیر A توصیه میشود)
این یک تصمیم cross-repo است. قبل از پیادهسازی، با تیم بکاند هماهنگ کن.
مسیر A (توصیهشده — تغییر در backend):
چون سایت در هر رندر refresh میزند، single-use rotation با این معماری ناسازگار است. بهترین کار: در clinicpro AuthController::refreshToken بخشِ چرخش/باطلسازی را بردار و فقط چک status (کاربر معلق 401) را نگهدار. در این حالت serverToken.js نیازی به تغییر ندارد. (یک پرامپت backend جدا برای این کار بساز.)
مسیر B (تغییر در nobat724 — اگر rotation باید بماند): refresh را به جایی منتقل کن که اجازهی ستکردن کوکی دارد:
- یک Route Handler (
app/api/refresh/route.js) یا middleware که/oauth/token/refreshرا صدا بزند وres.data.refresh_tokenوaccess_tokenجدید را در کوکی بنویسد، سپس صفحات بهجای فراخوانی مستقیم، از این مسیر استفاده کنند. - نمونه (Route Handler):
// app/api/refresh/route.js
import { cookies } from "next/headers";
export async function POST() {
const jar = await cookies();
const rt = jar.get("refresh_token")?.value;
if (!rt) return Response.json({ ok: false }, { status: 401 });
const r = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/oauth/token/refresh`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: rt }),
});
if (!r.ok) return Response.json({ ok: false }, { status: 401 });
const d = await r.json();
jar.set("access_token", d.access_token, { httpOnly: true, secure: true, sameSite: "lax", maxAge: d.expires_in });
jar.set("refresh_token", d.refresh_token, { httpOnly: true, secure: true, sameSite: "lax", maxAge: d.refresh_token_expires_in }); // ← توکن چرخشیافته
return Response.json({ ok: true, access_token: d.access_token });
}
- بعد
getServerAccessToken(در Server Component، read-only) فقط کوکیaccess_tokenمعتبر فعلی را بخواند؛ تجدید توسط middleware/route قبل از رندر انجام شود.
مهم: هر کجای دیگر
nobat724که توکن refresh ذخیره/استفاده میشود را هم بررسی کن (مثلاً جریان login که کوکیها را ست میکند) تا توکن چرخشیافتهی جدید همیشه جایگزین قدیمی شود.
🟡 وظیفه ۲ — صفحهبندی نظرات (GET /api/v1/comments/{uuid})
وضعیت فعلی (app/doctor/[slug]/page.js)
const resComments = await fetch(`${API_URL}/api/v1/comments/${doctor.uuid}`, { cache: "no-store" });
const jsonComments = resComments.ok ? await resComments.json() : null;
comments = jsonComments?.data?.data; // آرایه — هنوز کار میکند
تغییر
پاسخ حالا { data: { data: [...], meta: { totalRecords, totalPages, currentPage } } } است و پیشفرض ۵۰ نظر برمیگرداند (قبلاً همه). data.data (آرایه) دستنخورده → کد فعلی نمیشکند، فقط حداکثر ۵۰ نظر نشان میدهد.
وظیفه
- اگر برای صفحهی پزشک ۵۰ نظر کافی است (بهعلاوهی schema/Review)، هیچ تغییری لازم نیست (فقط آگاه باش).
- اگر همهی نظرات لازم است: یا
?limit=100بفرست، یا «نمایش بیشتر»/صفحهبندی باdata.meta.totalPagesاضافه کن.getDoctorCommentsدرservices/response.jsرا هم در صورت نیاز با پارامترpage/limitتطبیق بده. - اگر برای SEO/
Reviewschema از تعداد کل نظر استفاده میکنی، آن را ازdata.meta.totalRecordsبخوان (نهlengthآرایهی ۵۰تایی).
نکات مهم
lib/serverToken.jsدر Server Component اجرا میشود → نمیتواند کوکی ست کند؛ ستکردن کوکی فقط در Route Handler/Server Action/middleware. (مسیر A این مشکل را کلاً حذف میکند.)clinic-pro-tauriتحت تأثیر rotation نیست چون از/oauth/token(grant_type=refresh_token، OAuth bundle) استفاده میکند نه/oauth/token/refresh.- کدهای خطا (M21) مقدار wire ثابت دارند؛ هر منطقی که روی رشتهی
codeswitch میکند سالم است. - endpointهای owner-only جدید (
insurance/{id},doctor-address/{id},appointment-settings/*) توسط سایت عمومی مصرف نمیشوند → بدون اثر. - بعد از تغییر:
npm run lintوnpm run build.