6.6 KiB
دسترسی صاحب کلینیک به پروفایل پزشکان عضو کلینیک
زمینه
وقتی صاحب کلینیک (ROLE_CLINIC) در صفحه کلینیک روی آیکون چشم کنار نام یک پزشک کلیک میکند، به جای مشاهده پروفایل پزشک، به داشبورد redirect میشود. دلیل: route doctors/:uuid در App.tsx فقط ['admin', 'doctor'] را قبول دارد و RoleRoute صاحب کلینیک را به /admin/dashboard میفرستد.
مشکل
دو لایه مشکل وجود دارد:
۱. Frontend — Route permission:
// assets/admin/App.tsx — خط 141
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />
RoleRoute فقط primaryRole را چک میکند — اگر clinic بود، redirect به dashboard میکند.
۲. Backend — API permission:
// src/Doctor/Controller/DoctorController.php — خط 140-141
#[Route('/api/v1/doctor/{uuid}', methods: ['GET'])]
public function show(string $uuid): JsonResponse // بدون IsGranted — PUBLIC است
GET /api/v1/doctor/{uuid} عمومی است، اما برای اطمینان از امنیت باید یک endpoint اختصاصی برای کلینیک وجود داشته باشد که تأیید کند پزشک واقعاً عضو کلینیک مربوطه است.
فایلهای مرتبط
| فایل | نقش |
|---|---|
assets/admin/App.tsx |
تعریف route و RoleRoute — جای اصلی مشکل frontend |
assets/admin/pages/DoctorDetailPage.tsx |
صفحه پروفایل پزشک |
src/Doctor/Controller/DoctorController.php |
GET /api/v1/doctor/{uuid} |
src/Clinic/Repository/ClinicRepository.php |
دارای findByDoctor() و findByUser() |
وضعیت فعلی
// App.tsx خط 141
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />
// RoleRoute خط 70-74
function RoleRoute({ roles, children }: { roles: string[]; children: React.ReactNode }) {
const primaryRole = useAuthStore((s) => s.primaryRole);
if (!primaryRole) return <div>در حال بارگذاری...</div>;
if (!roles.includes(primaryRole)) return <Navigate to="/admin/dashboard" replace />;
return <>{children}</>;
}
// DoctorController.php خط 140-163 — PUBLIC, no auth check
#[Route('/api/v1/doctor/{uuid}', methods: ['GET'])]
public function show(string $uuid): JsonResponse
{
$doctor = $this->doctorRepo->findByUuid($uuid);
if ($doctor === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
}
// ... بدون چک مالکیت یا عضویت در کلینیک
return $this->success(['data' => array_merge($doctor->toDetailArray(), ['clinics' => $clinicData])]);
}
وظایف
۱. Frontend — اضافه کردن clinic به RoleRoute مسیر پزشک
// App.tsx — خط 141 را تغییر بده
<Route
path="doctors/:uuid"
element={<RoleRoute roles={['admin', 'doctor', 'clinic']}><DoctorDetailPage /></RoleRoute>}
/>
۲. Backend — endpoint اختصاصی برای کلینیک
یک endpoint جدید در DoctorController اضافه کن که صاحب کلینیک بتواند پروفایل پزشکان عضو کلینیکش را ببیند، با تأیید عضویت:
// src/Doctor/Controller/DoctorController.php
#[Route('/api/v1/clinic/my-doctor/{doctorUuid}', methods: ['GET'])]
#[IsGranted('ROLE_CLINIC')]
public function showForClinic(string $doctorUuid, #[CurrentUser] User $user): JsonResponse
{
$clinic = $this->clinicRepo->findByUser($user);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کلینیک یافت نشد', 404);
}
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
if ($doctor === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
}
// فقط پزشکانی که عضو این کلینیک هستند قابل مشاهدهاند
if (!$clinic->getDoctors()->contains($doctor)) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'این پزشک عضو کلینیک شما نیست', 403);
}
$clinicData = [[
'id' => (string) $clinic->getId(),
'uuid' => $clinic->getUuid(),
'name' => $clinic->getName(),
'address' => $clinic->getAddress(),
'telephone' => $clinic->getTelephone(),
]];
return $this->success(['data' => array_merge($doctor->toDetailArray(), ['clinics' => $clinicData])]);
}
وابستگیهای constructor: ClinicRepository $clinicRepo باید به DoctorController اضافه شود (بررسی کن آیا از قبل موجود است).
۳. Frontend — DoctorDetailPage: fetch با endpoint مناسب
در DoctorDetailPage.tsx، وقتی primaryRole === 'clinic'، از endpoint جدید استفاده کن:
// در DoctorDetailPage.tsx — در useQuery که doctor را fetch میکند
const { data: doctorData } = useQuery({
queryKey: ['doctor', uuid, primaryRole],
queryFn: () =>
primaryRole === 'clinic'
? api.get(`/api/v1/clinic/my-doctor/${uuid}`)
: api.get(`/api/v1/doctor/${uuid}`),
enabled: !!uuid,
});
۴. Frontend — DoctorDetailPage: محدود کردن قابلیتهای ویرایش برای clinic
وقتی primaryRole === 'clinic':
- دکمه ویرایش پروفایل نمایش داده نشود (فقط مشاهده)
- دکمه حذف نمایش داده نشود
- بخش دعوتنامهها (
ClinicInvitationsSection) نمایش داده نشود
بررسی کن چه بخشهایی با isOwnProfile یا primaryRole === 'admin' guard شدهاند و clinic را به عنوان read-only viewer handle کن.
نکات مهم
ClinicRepository::findByUser(User $user)وجود دارد — از آن برای پیدا کردن کلینیک صاحب استفاده کنClinic::getDoctors()یکCollectionاست — از->contains($doctor)برای چک عضویت استفاده کنGET /api/v1/doctor/{uuid}را تغییر نده — این endpoint عمومی است و frontend برای admin/doctor از آن استفاده میکند- migration لازم نیست — هیچ entity تغییر نمیکند
- بعد از تغییر route باید
ddev exec php bin/console cache:clearاجرا شود - مستندات:
docs/api/doctor.mdرا با endpoint جدید بهروزرسانی کن