feat(auth): integrate Captcha validation in PasswordAuthenticator

- Added CaptchaGuard dependency to PasswordAuthenticator.
- Implemented Captcha validation in the authenticate method to enhance security.
- Updated the login modal in home.html.twig to redirect to the admin panel instead of opening a modal.
- Enhanced the Altcha widget with localized strings for better user experience.
- Removed the login modal implementation from home.html.twig to streamline the login process.
- Updated manifest.json and AST cache files to reflect changes in the codebase.
This commit is contained in:
hamed
2026-07-10 11:05:59 +03:30
parent aded526718
commit 8b6d431f61
12 changed files with 733 additions and 736 deletions
+12 -1
View File
@@ -10,12 +10,23 @@ interface AltchaProps {
challengeUrl?: string;
}
// برچسب‌های فارسی widget (ترجمه‌ی رسمی locale fa).
const FA_STRINGS = JSON.stringify({
label: 'من ربات نیستم',
verifying: 'در حال بررسی...',
verified: 'تأیید شد',
waitAlert: 'در حال بررسی... لطفاً منتظر بمانید.',
error: 'احراز هویت ناموفق بود. کمی بعد دوباره تلاش کنید.',
expired: 'احراز هویت منقضی شد. دوباره تلاش کنید.',
});
// altcha-widget یک custom element است؛ به JSX معرفی می‌شود (React 19: namespace زیر React.JSX).
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'altcha-widget': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
challengeurl?: string;
strings?: string;
};
}
}
@@ -39,5 +50,5 @@ export default function Altcha({ onVerified, challengeUrl = '/api/v1/altcha/chal
return () => el.removeEventListener('statechange', onStateChange);
}, [onVerified]);
return <altcha-widget ref={ref as React.Ref<HTMLElement>} challengeurl={challengeUrl} />;
return <altcha-widget ref={ref as React.Ref<HTMLElement>} challengeurl={challengeUrl} strings={FA_STRINGS} />;
}
+14 -4
View File
@@ -40,6 +40,8 @@ export default function LoginPage() {
const [cooldown, setCooldown] = useState(0);
// آخرین payload حل‌شده‌ی ALTCHA برای مرحله‌ی جاری (یک‌بارمصرف؛ بین مراحل ریست می‌شود).
const [altcha, setAltcha] = useState('');
// با تغییر key، widget رمزِ فرمِ ورود پس از هر تلاش ناموفق دوباره challenge تازه می‌گیرد.
const [pwCaptchaKey, setPwCaptchaKey] = useState(0);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => () => { if (timerRef.current) clearInterval(timerRef.current); }, []);
@@ -72,13 +74,20 @@ export default function LoginPage() {
const res = await fetch('/api/v1/user/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mobile_number: pwMobile, password: pwPass }),
body: JSON.stringify({ mobile_number: pwMobile, password: pwPass, altcha }),
});
const json = await res.json();
if (!res.ok) { toast.error(json?.errors?.[0]?.message ?? 'خطا در ورود'); return; }
if (!res.ok) {
toast.error(json?.errors?.[0]?.message ?? 'خطا در ورود');
setAltcha(''); setPwCaptchaKey((k) => k + 1); // challenge یک‌بارمصرف؛ تازه بگیر
return;
}
login(json.access_token, json.refresh_token);
toast.success('خوش آمدید');
} catch { toast.error('خطا در اتصال به سرور'); }
} catch {
toast.error('خطا در اتصال به سرور');
setAltcha(''); setPwCaptchaKey((k) => k + 1);
}
finally { setPwLoading(false); }
};
@@ -248,8 +257,9 @@ export default function LoginPage() {
</button>
</div>
</div>
<div style={{ marginTop: 12 }}><Altcha key={`pw-login-${pwCaptchaKey}`} onVerified={setAltcha} /></div>
<button type="submit" className="btn primary block" disabled={pwLoading}
style={{ marginTop: 8, height: 46, fontSize: 15 }}>
style={{ marginTop: 12, height: 46, fontSize: 15 }}>
{pwLoading ? 'در حال ورود...' : 'ورود به سیستم'}
</button>
<div style={{ textAlign: 'center', marginTop: 16 }}>
+3
View File
@@ -45,6 +45,9 @@ endpointهایی که وقتی `ALTCHA_ENABLED=true` است فیلد `altcha` ر
| `/api/v1/user/otp-login` | POST |
| `/api/v1/user/reset-password` | POST |
| `/api/v1/pre-registration` | POST |
| `/api/v1/user/login` | POST |
> ورود با رمز عبور (`/api/v1/user/login`) توسط `PasswordAuthenticator` قبل از controller intercept می‌شود؛ کپچا داخل `authenticate()` (بعد از rate-limit) با `CaptchaGuard::assertValid()` بررسی می‌شود.
> endpointهای امتیاز/نظر (`POST /api/v1/rate`، `POST /api/v1/comment`) پشت JWT هستند (کاربر لاگین‌شده)، بنابراین کپچا نمی‌گیرند — بات برای رسیدن به آن‌ها باید توکن معتبر داشته باشد که خودش از مسیر OTP (کپچا‌دار) عبور می‌کند.
+11 -1
View File
@@ -711,9 +711,12 @@
"709": "Community 709",
"710": "Community 710",
"711": "Community 711",
"712": "Community 712",
"713": "Community 713",
"714": "Community 714",
"715": "Community 715",
"716": "Community 716",
"717": "Community 717",
"718": "Community 718",
"719": "Community 719",
"720": "Community 720",
@@ -721,12 +724,19 @@
"722": "Community 722",
"723": "Community 723",
"724": "Community 724",
"725": "Community 725",
"726": "Community 726",
"727": "Community 727",
"728": "Community 728",
"729": "Community 729",
"730": "Community 730",
"731": "Community 731",
"732": "Community 732",
"733": "Community 733",
"734": "Community 734",
"735": "Community 735",
"736": "Community 736",
"737": "Community 737",
"738": "Community 738"
"738": "Community 738",
"739": "Community 739"
}
+178 -116
View File
@@ -1,16 +1,16 @@
# Graph Report - clinicpro (2026-07-10)
## Corpus Check
- 736 files · ~541,308 words
- 736 files · ~541,454 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 9268 nodes · 12823 edges · 730 communities (576 shown, 154 thin omitted)
- 9269 nodes · 12824 edges · 740 communities (589 shown, 151 thin omitted)
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 279 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `10b0743d`
- Built from commit: `aded5267`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -713,9 +713,12 @@
- [[_COMMUNITY_Community 709|Community 709]]
- [[_COMMUNITY_Community 710|Community 710]]
- [[_COMMUNITY_Community 711|Community 711]]
- [[_COMMUNITY_Community 712|Community 712]]
- [[_COMMUNITY_Community 713|Community 713]]
- [[_COMMUNITY_Community 714|Community 714]]
- [[_COMMUNITY_Community 715|Community 715]]
- [[_COMMUNITY_Community 716|Community 716]]
- [[_COMMUNITY_Community 717|Community 717]]
- [[_COMMUNITY_Community 718|Community 718]]
- [[_COMMUNITY_Community 719|Community 719]]
- [[_COMMUNITY_Community 720|Community 720]]
@@ -723,14 +726,21 @@
- [[_COMMUNITY_Community 722|Community 722]]
- [[_COMMUNITY_Community 723|Community 723]]
- [[_COMMUNITY_Community 724|Community 724]]
- [[_COMMUNITY_Community 725|Community 725]]
- [[_COMMUNITY_Community 726|Community 726]]
- [[_COMMUNITY_Community 727|Community 727]]
- [[_COMMUNITY_Community 728|Community 728]]
- [[_COMMUNITY_Community 729|Community 729]]
- [[_COMMUNITY_Community 730|Community 730]]
- [[_COMMUNITY_Community 731|Community 731]]
- [[_COMMUNITY_Community 732|Community 732]]
- [[_COMMUNITY_Community 733|Community 733]]
- [[_COMMUNITY_Community 734|Community 734]]
- [[_COMMUNITY_Community 735|Community 735]]
- [[_COMMUNITY_Community 736|Community 736]]
- [[_COMMUNITY_Community 737|Community 737]]
- [[_COMMUNITY_Community 738|Community 738]]
- [[_COMMUNITY_Community 739|Community 739]]
## God Nodes (most connected - your core abstractions)
1. `BaseController` - 80 edges
@@ -745,25 +755,25 @@
10. `formatDate()` - 39 edges
## Surprising Connections (you probably didn't know these)
- `ServiceTariffModal()` --calls--> `formatNumber()` [EXTRACTED]
assets/admin/components/ServiceTariffModal.tsx → assets/admin/lib/utils.ts
- `Pagination()` --calls--> `formatNumber()` [EXTRACTED]
assets/admin/components/ui/Pagination.tsx → assets/admin/lib/utils.ts
- `toForm()` --calls--> `rialToToman()` [EXTRACTED]
assets/admin/pages/SettingsPage.tsx → assets/admin/lib/utils.ts
- `SettlementsPage()` --calls--> `formatRial()` [EXTRACTED]
assets/admin/pages/SettlementsPage.tsx → assets/admin/lib/utils.ts
- `NewAppointmentModal()` --calls--> `useAuthStore` [EXTRACTED]
assets/admin/pages/AppointmentsPage.tsx → assets/admin/stores/authStore.ts
- `LogoUploadField()` --calls--> `useAuthStore` [EXTRACTED]
assets/admin/pages/CategoriesPage.tsx → assets/admin/stores/authStore.ts
- `LogsPage()` --calls--> `formatDateTime()` [EXTRACTED]
assets/admin/pages/LogsPage.tsx → assets/admin/lib/utils.ts
- `SmsPage()` --calls--> `formatDateTime()` [EXTRACTED]
assets/admin/pages/SmsPage.tsx → assets/admin/lib/utils.ts
## Import Cycles
- None detected.
## Communities (730 total, 154 thin omitted)
## Communities (740 total, 151 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.05
Nodes (41): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, DEGREE_OPTIONS, DoctorFormPage() (+33 more)
Cohesion: 0.04
Nodes (48): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, useSubscription(), DEGREE_OPTIONS (+40 more)
### Community 1 - "Community 1"
Cohesion: 0.03
@@ -778,24 +788,24 @@ Cohesion: 0.10
Nodes (20): `RepresentationActionController` — welcome به‌صورت inline (بدون تمپلت), `SmsMessageTemplate::DEFAULTS` (فاقد نام تمپلت و نگاشت token), `SmsService::sendNow` — انتخاب بین lookup و متن‌آزاد, الگوی فعلی همه‌ی call-siteها (به‌جز OTP) — متن‌آزاد، بدون `templateCode`, تبدیل همه‌ی پیامک‌های سیستمی به VerifyLookup کاوه‌نگار (تمپلت نام‌دار), تنها جای درست (OTP) — که باید الگوی بقیه شود, زمینه, فایل‌های مرتبط (+12 more)
### Community 4 - "Community 4"
Cohesion: 0.05
Nodes (11): DoctorServiceController, DoctorService, Doctor, DoctorServiceRepository, Collection, self, User, WeeklySchedule (+3 more)
Cohesion: 0.06
Nodes (8): DoctorService, Doctor, DoctorServiceRepository, Collection, self, User, WeeklySchedule, ManagerRegistry
### Community 5 - "Community 5"
Cohesion: 0.07
Nodes (3): UserProfile, self, User
### Community 6 - "Community 6"
Cohesion: 0.17
Nodes (8): SettlementController, SettlementRepository, Settlement, JsonResponse, Request, User, ManagerRegistry, User
Cohesion: 0.12
Nodes (12): SettlementController, SettlementRepository, WalletTransactionRepository, Settlement, JsonResponse, Request, User, ManagerRegistry (+4 more)
### Community 7 - "Community 7"
Cohesion: 0.07
Nodes (5): Clinic, Collection, Doctor, self, User
### Community 8 - "Community 8"
Cohesion: 0.08
Nodes (27): useSubscription(), AdminLayout(), avatarBg(), buildSections(), HUES, Props, ROLE_LABELS, Section (+19 more)
Cohesion: 0.16
Nodes (15): AdminLayout(), avatarBg(), buildSections(), HUES, Props, ROLE_LABELS, Section, SectionItem (+7 more)
### Community 9 - "Community 9"
Cohesion: 0.04
@@ -814,8 +824,8 @@ Cohesion: 0.05
Nodes (44): Clinic Doctor Invitation API, DELETE `/api/v1/admin/clinic/invitation/{invUuid}`, Errors, Errors, Errors, Errors, Errors, Errors (+36 more)
### Community 13 - "Community 13"
Cohesion: 0.05
Nodes (36): get, PaymentConfig, PaymentGatewayInfo, usePaymentConfig(), api, ApiError, ApiResponse, downloadFile() (+28 more)
Cohesion: 0.04
Nodes (46): get, PaymentConfig, PaymentGatewayInfo, usePaymentConfig(), api, ApiError, ApiResponse, downloadFile() (+38 more)
### Community 14 - "Community 14"
Cohesion: 0.10
@@ -839,7 +849,7 @@ Nodes (38): API, API, API, API, API, Route, Route, Route (+30 more)
### Community 19 - "Community 19"
Cohesion: 0.03
Nodes (56): CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema, LogoUploadField(), ProvinceForm, provinceSchema (+48 more)
Nodes (91): PaginatedResponse, CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema, LogoUploadField(), ProvinceForm (+83 more)
### Community 20 - "Community 20"
Cohesion: 0.13
@@ -847,11 +857,11 @@ Nodes (4): AdminApiController, JsonResponse, Request, StreamedResponse
### Community 21 - "Community 21"
Cohesion: 0.03
Nodes (48): formatNumber(), ClinicAddress, ClinicDetailPage(), ClinicDoctorItem, ClinicInvitation, EditForm, editSchema, HUES_LIST (+40 more)
Nodes (50): ServiceTariffModal(), formatNumber(), ClinicAddress, ClinicDetailPage(), ClinicDoctorItem, ClinicInvitation, EditForm, editSchema (+42 more)
### Community 22 - "Community 22"
Cohesion: 0.12
Nodes (10): RatingController, Like, CommentListNPlusOneTest, LikeRepository, JsonResponse, Request, User, Comment (+2 more)
Cohesion: 0.15
Nodes (9): RatingController, Like, LikeRepository, JsonResponse, Request, User, Comment, ManagerRegistry (+1 more)
### Community 23 - "Community 23"
Cohesion: 0.05
@@ -870,8 +880,8 @@ Cohesion: 0.24
Nodes (4): InsuranceController, JsonResponse, Request, User
### Community 27 - "Community 27"
Cohesion: 0.05
Nodes (40): 55. 🟢 `GET` all tag, 56. 🟢 `GET` supplementary_insurance, 57. 🟢 `GET` categories list, 58. 🟢 `GET` all state, 59. 🟢 `GET` all city, 60. 🟢 `GET` all specially doctor, 61. 🔵 `POST` post, 62. 🟡 `PATCH` patch (+32 more)
Cohesion: 0.06
Nodes (35): 55. 🟢 `GET` all tag, 56. 🟢 `GET` supplementary_insurance, 57. 🟢 `GET` categories list, 58. 🟢 `GET` all state, 59. 🟢 `GET` all city, 60. 🟢 `GET` all specially doctor, 61. 🔵 `POST` post, 63. 🔴 `DELETE` DELETE (+27 more)
### Community 28 - "Community 28"
Cohesion: 0.08
@@ -894,8 +904,8 @@ Cohesion: 0.06
Nodes (32): 10. Modal / Dialog, 11. Toast Notifications, 12. Empty States & Loading, 13. Page Header (هر صفحه), 14. تکنولوژی Stack, 15. Responsive Breakpoints, 16. Dark Mode (اختیاری — فاز دوم), 17. نمونه رنگ‌بندی صفحه داشبورد (+24 more)
### Community 33 - "Community 33"
Cohesion: 0.07
Nodes (25): CoverageRow, Draft, KIND, TenantInsurance, ServiceTariffModal(), TariffResponse, TariffRow, EMPTY_ITEMS (+17 more)
Cohesion: 0.08
Nodes (21): CoverageRow, Draft, KIND, TenantInsurance, TariffResponse, TariffRow, EMPTY_ITEMS, EMPTY_SECTIONS (+13 more)
### Community 34 - "Community 34"
Cohesion: 0.06
@@ -914,8 +924,8 @@ Cohesion: 0.06
Nodes (31): API endpoint موجود برای آدرس دکتر:, `DELETE /api/v1/clinic/{clinicUuid}/address/{addressUuid}` — حذف, `docs/api/appointment-settings.md`:, `docs/api/clinic.md`:, Endpoint موجود (workaround که حذف می‌شود):, Entity `DoctorAddress`:, Frontend موجود (`DoctorDetailPage.tsx`):, `GET /api/v1/clinic/{clinicUuid}/addresses` — لیست آدرس‌ها (+23 more)
### Community 38 - "Community 38"
Cohesion: 0.05
Nodes (44): Authentication API, DELETE `/api/v1/notification-mobile/{target}`, Error Codes, Errors, Errors, Errors, Errors, Errors (+36 more)
Cohesion: 0.25
Nodes (8): Authentication API, DELETE `/api/v1/notification-mobile/{target}`, Errors, Notification Mobile (OTP), POST `/oauth/logout`, Request Body, Response `200`, Response `200`
### Community 39 - "Community 39"
Cohesion: 0.12
@@ -958,8 +968,8 @@ Cohesion: 0.11
Nodes (4): Payment, Appointment, self, User
### Community 49 - "Community 49"
Cohesion: 0.08
Nodes (19): AppointmentsPage(), BookingSlot, CancelledBadge(), DateNavigator(), EMPTY_ARR, getPersianWeekDay(), navBtnSx, NewAppointmentModal() (+11 more)
Cohesion: 0.06
Nodes (28): AppointmentsPage(), BookingSlot, CancelledBadge(), DateNavigator(), EMPTY_ARR, getPersianWeekDay(), navBtnSx, NewAppointmentModal() (+20 more)
### Community 50 - "Community 50"
Cohesion: 0.07
@@ -975,7 +985,7 @@ Nodes (26): الزامات UI, باگ‌فیکس صفحه نوبت‌ها, با
### Community 53 - "Community 53"
Cohesion: 0.10
Nodes (13): AppLogRepository, ClaimItemRepository, PreRegistrationRepository, SessionServiceRepository, TagRepository, ServiceEntityRepository, ManagerRegistry, ManagerRegistry (+5 more)
Nodes (13): AppLogRepository, ClaimItemRepository, PreRegistrationRepository, SessionServiceRepository, SmsSettingsRepository, ServiceEntityRepository, SmsSettings, ManagerRegistry (+5 more)
### Community 54 - "Community 54"
Cohesion: 0.10
@@ -1014,12 +1024,12 @@ Cohesion: 0.08
Nodes (25): Bulk import / export, DELETE `/api/v1/admin/specialty/{id}`, Errors, Errors, Errors, Errors, GET `/api/v1/admin/specialties`, GET `/api/v1/specialties` (+17 more)
### Community 63 - "Community 63"
Cohesion: 0.06
Nodes (41): FreeVisitPrice(), Pricing, cn(), formatRial(), iranMobileOptionalSchema, iranMobileSchema, isValidIranMobile(), maskMobile() (+33 more)
Cohesion: 0.05
Nodes (51): FreeVisitPrice(), Pricing, Contract, InsuranceOption, KIND_LABEL, cn(), formatDate(), formatDateTime() (+43 more)
### Community 64 - "Community 64"
Cohesion: 0.19
Nodes (7): SmsWalletController, SmsSettingsRepository, SmsSettings, JsonResponse, Request, User, ManagerRegistry
Cohesion: 0.29
Nodes (4): SmsWalletController, JsonResponse, Request, User
### Community 65 - "Community 65"
Cohesion: 0.08
@@ -1042,12 +1052,12 @@ Cohesion: 0.15
Nodes (12): رفع بهم‌ریختگی کامل پنل ادمین روی iPhone 8 (Safari/Chrome iOS), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 71 - "Community 71"
Cohesion: 0.08
Nodes (22): Contract, InsuranceOption, KIND_LABEL, ChargeForm, chargeSchema, EMPTY_LOGS, POST_VISIT_VARS, REMINDER_HOUR_OPTIONS (+14 more)
Cohesion: 0.13
Nodes (12): AdminUserDetail, AVATAR_COLORS, EditForm, editSchema, GENDER_LABELS, getPrimaryRole(), MARITAL_LABELS, MEDICAL_SECTIONS (+4 more)
### Community 72 - "Community 72"
Cohesion: 0.09
Nodes (3): Claim, Collection, self
Nodes (4): ClaimItem, Claim, Collection, self
### Community 74 - "Community 74"
Cohesion: 0.09
@@ -1077,6 +1087,10 @@ Nodes (3): SubscriptionPlan, Collection, self
Cohesion: 0.09
Nodes (23): dependencies, @ckeditor/ckeditor5-build-classic, @ckeditor/ckeditor5-react, @fontsource/vazirmatn, @heroicons/react, @hookform/resolvers, jalaali-js, leaflet (+15 more)
### Community 82 - "Community 82"
Cohesion: 0.07
Nodes (8): CategoryImportTest, Connection, RepositoryClassMappingTest, KernelTestCase, DbLogger, CategoryImporter, DbLoggerTest, Stringable
### Community 83 - "Community 83"
Cohesion: 0.09
Nodes (21): Backend, CSS / UI, Frontend, روند اجرای هر قابلیت, قبل از شروع — تحلیل پرامپت و ساخت Todo, قوانین اجرا (اجباری — هیچ استثنایی ندارد), قوانین خاص این پروژه, مثال اجرا (+13 more)
@@ -1090,8 +1104,8 @@ Cohesion: 0.07
Nodes (30): devDependencies, @babel/core, @babel/preset-env, @babel/preset-react, @babel/preset-typescript, core-js, @csstools/postcss-oklab-function, @hotwired/stimulus (+22 more)
### Community 86 - "Community 86"
Cohesion: 0.06
Nodes (15): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, CaptchaFlowTest, ServiceItemStaffOwnershipTest, EntityManagerInterface, KernelBrowser (+7 more)
Cohesion: 0.05
Nodes (16): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListNPlusOneTest, ClaimsListPaginationTest, CaptchaFlowTest, ServiceItemStaffOwnershipTest, EntityManagerInterface (+8 more)
### Community 87 - "Community 87"
Cohesion: 0.10
@@ -1114,7 +1128,7 @@ Cohesion: 0.10
Nodes (20): DELETE `/api/v1/admin/users/{uuid}`, Errors, Errors, GET `/api/v1/admin/users`, GET `/api/v1/admin/users/stats`, GET `/api/v1/admin/users/{uuid}`, POST `/api/v1/admin/users/{uuid}/status`, PUT `/api/v1/admin/users/{uuid}` (+12 more)
### Community 92 - "Community 92"
Cohesion: 0.09
Cohesion: 0.10
Nodes (21): Bulk import / export, DELETE `/api/v1/admin/doctor-service/{id}`, Doctor Service API, Errors, Errors, Errors, Errors, GET `/api/v1/admin/doctor-services` (+13 more)
### Community 93 - "Community 93"
@@ -1162,8 +1176,8 @@ Cohesion: 0.11
Nodes (18): `AdminApiController::paymentDetail` — الان فقط GET, `MellatGateway.php` — الگوی موجود REST/SOAP (پس از کار sandbox), `PaymentGatewayInterface.php`, `PaymentManager.php` — الگوی log و transaction, برگشت/استرداد وجه ملت از پنل ادمین (bpReversalRequest / bpRefundRequest), زمینه, فایل‌های مرتبط, نکات مهم (+10 more)
### Community 104 - "Community 104"
Cohesion: 0.35
Nodes (3): TagController, JsonResponse, Request
Cohesion: 0.18
Nodes (6): TagController, TagRepository, JsonResponse, Request, ManagerRegistry, Tag
### Community 105 - "Community 105"
Cohesion: 0.11
@@ -1178,8 +1192,8 @@ Cohesion: 0.33
Nodes (6): AppointmentController, Appointment, Doctor, JsonResponse, Request, User
### Community 108 - "Community 108"
Cohesion: 0.10
Nodes (13): CaptchaController, BaseController, CategoryController, SiteConfigController, SiteContextController, JsonResponse, JsonResponse, Request (+5 more)
Cohesion: 0.14
Nodes (9): CaptchaController, BaseController, CategoryController, SiteContextController, JsonResponse, JsonResponse, Request, JsonResponse (+1 more)
### Community 109 - "Community 109"
Cohesion: 0.29
@@ -1374,8 +1388,8 @@ Cohesion: 0.12
Nodes (15): Endpoint ها, GET /api/v1/payment/{uuid}, POST /api/v1/payment, POST /api/v1/payment/callback/mellat, Strategy Pattern برای درگاه‌ها, Subscription Payment — POST /api/v1/subscription-payment, ⚠ امنیت: IP Whitelist برای Callback, ⚠ امنیت: جلوگیری از Open Redirect (+7 more)
### Community 160 - "Community 160"
Cohesion: 0.13
Nodes (15): Date Overrides, DELETE `/api/v1/appointment-settings/date-override/{uuid}`, Errors, Errors, GET `/api/v1/appointment-settings/date-override/list/{doctorUuid}`, GET `/api/v1/appointment-settings/date-override/{uuid}`, PATCH `/api/v1/appointment-settings/date-override/{uuid}`, POST `/api/v1/appointment-settings/date-override` (+7 more)
Cohesion: 0.09
Nodes (21): Appointment Settings API, Available Locations, Date Overrides, DELETE `/api/v1/appointment-settings/date-override/{uuid}`, Errors, Errors, Errors, `GET /api/v1/appointment-settings/available-locations/{doctorUuid}` (+13 more)
### Community 161 - "Community 161"
Cohesion: 0.24
@@ -1410,8 +1424,8 @@ Cohesion: 0.10
Nodes (20): api.ir (استعلام هویت — Shahkar / IbanMatch), اتصال به دیتابیس‌های مستقل (الزامی), اسرار (الزامی — قبل از اولین دیپلوی), امنیت و منابع, بررسی سلامت, دامنه‌ها و CORS, دیپلوی‌های بعدی, راهنمای دیپلوی ClinicPro (Coolify + Docker Compose) (+12 more)
### Community 169 - "Community 169"
Cohesion: 0.22
Nodes (4): EntityInsurancePricing, TenantInsuranceCleanupTest, EntityInsurancePricingRepository, ManagerRegistry
Cohesion: 0.15
Nodes (5): EntityInsurancePricing, TenantInsuranceCleanupTest, EntityInsurancePricingRepository, TenantInsuranceCleanupService, ManagerRegistry
### Community 170 - "Community 170"
Cohesion: 0.13
@@ -1438,8 +1452,8 @@ Cohesion: 0.13
Nodes (14): Seeder داده تست حجیم (شبیه Production) — نمایندگان، پزشکان، کلینیک‌ها، نوبت‌ها، کمیسیون, زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, ۰. تحلیل پیش از کد (الزامی) (+6 more)
### Community 176 - "Community 176"
Cohesion: 0.17
Nodes (7): PaymentLog, PaymentLogRepository, PaymentManager, ManagerRegistry, Payment, PaymentInitResult, PaymentRefundResult
Cohesion: 0.25
Nodes (4): PaymentManager, Payment, PaymentInitResult, PaymentRefundResult
### Community 177 - "Community 177"
Cohesion: 0.15
@@ -1530,8 +1544,8 @@ Cohesion: 0.14
Nodes (13): Endpoint ها, PATCH /api/v1/secretary/{uuid}, POST /api/v1/secretary, تسک ۱۴: ماژول منشی, توضیح, زمان تخمینی, ساختار JSON, سیستم مجوزها — Resource-Based Permissions (مقیاس‌پذیر) (+5 more)
### Community 201 - "Community 201"
Cohesion: 0.12
Nodes (16): Admin API, Clinic Invitation Management, Dashboard, GET `/api/v1/admin/dashboard/charts`, GET `/api/v1/admin/dashboard/recent`, GET `/api/v1/admin/dashboard/stats`, GET `/api/v1/admin/pre-registrations`, GET /api/v1/admin/settings (+8 more)
Cohesion: 0.17
Nodes (12): Admin API, Clinic Invitation Management, Dashboard, GET `/api/v1/admin/dashboard/charts`, GET `/api/v1/admin/dashboard/recent`, GET `/api/v1/admin/dashboard/stats`, GET /api/v1/admin/settings, PATCH /api/v1/admin/settings (+4 more)
### Community 202 - "Community 202"
Cohesion: 0.15
@@ -1542,8 +1556,8 @@ Cohesion: 0.15
Nodes (12): Clinic Services API, DELETE /api/v1/service-item/{uuid}, DELETE /api/v1/service-section/{uuid}, GET /api/v1/service-items/{sectionUuid}, GET /api/v1/service-items/{uuid}/tariffs, GET /api/v1/service-sections, PATCH /api/v1/service-item/{uuid}, PATCH /api/v1/service-section/{uuid} (+4 more)
### Community 204 - "Community 204"
Cohesion: 0.21
Nodes (6): AuthenticationException, ExceptionSubscriber, SecurityHeadersSubscriber, EventSubscriberInterface, ExceptionEvent, ResponseEvent
Cohesion: 0.13
Nodes (12): AbstractAuthenticator, AuthenticationException, ExceptionSubscriber, SecurityHeadersSubscriber, EventSubscriberInterface, ExceptionEvent, Passport, ResponseEvent (+4 more)
### Community 205 - "Community 205"
Cohesion: 0.07
@@ -1610,8 +1624,8 @@ Cohesion: 0.23
Nodes (5): PaymentRepository, Appointment, ManagerRegistry, Payment, User
### Community 225 - "Community 225"
Cohesion: 0.05
Nodes (36): formatDate(), formatDateTime(), toDate(), ALL_STATUSES, AppointmentDetailPage(), timeOf(), Claim, ClaimItem (+28 more)
Cohesion: 0.07
Nodes (20): emptyFeatures(), FEATURE_KEYS, FEATURE_LABELS, PeriodForm, periodSchema, PLAN_DISPLAY, PlanForm, planSchema (+12 more)
### Community 226 - "Community 226"
Cohesion: 0.15
@@ -1685,6 +1699,10 @@ Nodes (4): PatientRecordRepository, ManagerRegistry, PatientRecord, User
Cohesion: 0.28
Nodes (4): SubscriptionService, ClinicSubscription, Payment, SubscriptionPlan
### Community 245 - "Community 245"
Cohesion: 0.17
Nodes (8): FormValues, schema, SectionDef, SectionId, SECTIONS, Settings, TaxHistoryRow, toForm()
### Community 246 - "Community 246"
Cohesion: 0.17
Nodes (11): Endpoint ها (واقعی از Drupal), PATCH /api/v1/doctor/{uuid} — فیلدهای قابل ویرایش, آپلود تصویر دکتر — POST /file/upload/clinic_pro/doctor/field_image, تسک ۰۵: ماژول دکتر, توضیح, زمان تخمینی, فیلترهای GET /api/v1/doctors, نمونه Response لیست دکترها (+3 more)
@@ -1794,7 +1812,7 @@ Cohesion: 0.18
Nodes (10): Endpoint های موجود که تغییر می‌کنند, GET /api/v1/admin/dashboard/charts?from=UNIX&to=UNIX, GET /api/v1/dashboard/clinic, GET /api/v1/dashboard/doctor, تسک ۱۶: داشبورد هوشمند — چارت + فیلتر زمانی, توضیح, زمان تخمینی, فیلتر بازه زمانی (+2 more)
### Community 274 - "Community 274"
Cohesion: 0.16
Cohesion: 0.15
Nodes (5): Authentication, ClinicPro — API Documentation Index, Error Code Reference, Modules, Standard Response Envelope
### Community 275 - "Community 275"
@@ -1870,8 +1888,8 @@ Cohesion: 0.20
Nodes (9): AdminApiController — dashboardCharts با بازه زمانی, DashboardController — اضافه کردن from/to, date range selector component:, تغییر Backend, تغییر Frontend — DashboardPage.tsx, فایل‌هایی که تغییر می‌کنند, معماری — تسک ۱۶: داشبورد هوشمند, نصب dependency: (+1 more)
### Community 294 - "Community 294"
Cohesion: 0.24
Nodes (10): gridItemStyle, JALALI_MONTHS, jalaliFirstWeekday(), jalaliToGregorian(), navBtnStyle, PersianCalendar(), pf, Props (+2 more)
Cohesion: 0.36
Nodes (3): DoctorServiceController, JsonResponse, Request
### Community 295 - "Community 295"
Cohesion: 0.22
@@ -1898,8 +1916,8 @@ Cohesion: 0.42
Nodes (3): PreRegistrationController, JsonResponse, Request
### Community 301 - "Community 301"
Cohesion: 0.05
Nodes (41): PaginatedResponse, STATUS_FILTERS, AddForm, addSchema, ClinicsPage(), HUES_LIST, FILTERS, Breakdown (+33 more)
Cohesion: 0.09
Nodes (20): AddForm, addSchema, ClinicsPage(), HUES_LIST, EMPTY, PreRegistration, STATUS_META, STATUS_TABS (+12 more)
### Community 302 - "Community 302"
Cohesion: 0.12
@@ -1910,8 +1928,8 @@ Cohesion: 0.12
Nodes (16): آماده‌سازی پروژه ClinicPro برای دیپلوی روی Coolify با Docker Compose, زمینه, فایل‌های مرتبط, نکات مهم (محدودیت‌ها و edge caseها), هدف, وظایف, ۱. ساخت `Dockerfile` چندمرحله‌ای, ۱۰. ساخت راهنمای `docs/deploy/coolify.md` (+8 more)
### Community 304 - "Community 304"
Cohesion: 0.05
Nodes (43): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 32. 🔵 `POST` post, 33. 🔵 `POST` image_clinic, 34. 🔵 `POST` image logo, 35. 🟡 `PATCH` patch, 36. 🟢 `GET` get my rate, 38. 🟢 `GET` Unapproved comments (+35 more)
Cohesion: 0.22
Nodes (9): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 33. 🔵 `POST` image_clinic, 6. کلینیک (Clinic), هدرهای اضافی, پارامترهای Query, پاسخ‌ها, پاسخ‌ها (+1 more)
### Community 306 - "Community 306"
Cohesion: 0.07
@@ -1921,6 +1939,10 @@ Nodes (27): دیپلوی‌های بعدی, راهنمای دیپلوی ClinicPr
Cohesion: 0.28
Nodes (3): UserActiveContext, self, User
### Community 308 - "Community 308"
Cohesion: 0.36
Nodes (4): SiteConfigController, JsonResponse, Request, User
### Community 309 - "Community 309"
Cohesion: 0.22
Nodes (8): Definition of Done (هر اپیک), PRD — ClinicPro Phase 2, اولویت‌بندی پیاده‌سازی, خلاصه اجرایی, زیرساخت موجود قابل استفاده, معماری مشترک, نقشه Entity‌های جدید, وضعیت فعلی پروژه
@@ -2006,8 +2028,8 @@ Cohesion: 0.22
Nodes (8): Query های جدید, بیماران منحصربه‌فرد در بازه, درآمد بر اساس روز (از patient_sessions), فروش اشتراک بر اساس پنل (admin), نوبت‌ها بر اساس روز (admin chart), نکات مهم, هیچ migration لازم نیست, پایگاه داده — تسک ۱۶: داشبورد هوشمند
### Community 333 - "Community 333"
Cohesion: 0.25
Nodes (7): DELETE `/api/v1/clinic-pro/doctor-address/{id}`, Doctor API, Errors, GET `/api/v1/doctors`, Query Parameters, Response `200`, Response `200`
Cohesion: 0.06
Nodes (35): DELETE `/api/v1/clinic-pro/doctor-address/{id}`, DELETE `/api/v1/doctor/{uuid}`, Doctor API, Errors, Errors, Errors, Errors, Errors (+27 more)
### Community 334 - "Community 334"
Cohesion: 0.25
@@ -2150,8 +2172,8 @@ Cohesion: 0.29
Nodes (7): Authentication, Authorization, Input Validation, Logging Security, Rate Limiting, Secrets Management, ۷. تحلیل امنیت
### Community 371 - "Community 371"
Cohesion: 0.19
Nodes (5): UniqueConstraintsTest, FinancialBreakdown, FinancialBreakdownRepository, ManagerRegistry, Payment
Cohesion: 0.15
Nodes (6): UniqueConstraintsTest, FinancialBreakdown, FinancialBreakdownRepository, FinancialBreakdownIntegrityTest, ManagerRegistry, Payment
### Community 372 - "Community 372"
Cohesion: 0.11
@@ -2246,13 +2268,17 @@ Cohesion: 0.33
Nodes (6): Refactoring Plan, فاز ۰ — مستندسازی (۳ تا ۵ روز، قبل از هر کدنویسی), فاز ۱ — زیرساخت پایه (task-01), فاز ۲ — پیاده‌سازی ماژول‌ها (به ترتیب dependency), فاز ۳ — بهینه‌سازی (بعد از پیاده‌سازی), فاز ۴ — آماده‌سازی تولید
### Community 397 - "Community 397"
Cohesion: 0.21
Nodes (6): ClaimAmountBoundsTest, ClaimsListNPlusOneTest, ClaimItem, Claim, Doctor, User
Cohesion: 0.36
Nodes (4): ClaimAmountBoundsTest, Claim, Doctor, User
### Community 398 - "Community 398"
Cohesion: 0.22
Nodes (7): initiate(), refund(), reverse(), verify(), PaymentInitResult, PaymentRefundResult, PaymentVerifyResult
### Community 399 - "Community 399"
Cohesion: 0.23
Nodes (5): AbstractMigration, Schema, Version20260609130407, Schema, Version20260614183527
### Community 401 - "Community 401"
Cohesion: 0.12
Nodes (15): `lib/utils.ts`, `SettingsPage.tsx` (ورودی‌ها ریال ذخیره می‌شوند), زمینه, فایل‌های مرتبط, مشکل / هدف, نمونهٔ نمایش (Subscription), نکات مهم, واحد پول = تومان در پنل ادمین (نمایش ÷۱۰ / ورودی ×۱۰) — ذخیره و درگاه ریال می‌ماند (+7 more)
@@ -2261,17 +2287,9 @@ Nodes (15): `lib/utils.ts`, `SettingsPage.tsx` (ورودی‌ها ریال ذخ
Cohesion: 0.11
Nodes (17): بازطراحی معماری پرداخت — سرویس‌محور، امن، توسعه‌پذیر (Backend), تست دستی (ddev، در حالت `payment_test_mode=1`), خروجی نهایی (طبق spec — در گزارش اجرا ارائه شود), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (+9 more)
### Community 406 - "Community 406"
Cohesion: 0.23
Nodes (5): AbstractMigration, Schema, Version20260609133546, Schema, Version20260628165710
### Community 407 - "Community 407"
Cohesion: 0.26
Nodes (7): MessageBusInterface, MockObject, SmsServiceLookupOnlyTest, SmsLogRepository, SmsMessageTemplateRepository, SmsTextResolver, KavehNegarProvider
### Community 414 - "Community 414"
Cohesion: 0.24
Nodes (5): WalletTransactionRepository, WalletTransactionsPaginationTest, ManagerRegistry, User, WalletTransaction
Cohesion: 0.27
Nodes (8): MessageBusInterface, MockObject, SmsServiceLookupOnlyTest, SmsLogRepository, SmsMessageTemplateRepository, SmsService, SmsTextResolver, KavehNegarProvider
### Community 418 - "Community 418"
Cohesion: 0.17
@@ -2314,8 +2332,8 @@ Cohesion: 0.47
Nodes (6): formatPersianDate(), gToJ(), jFirstDayOfWeek(), PersianDateInput(), todayGregorian(), toPersianNums()
### Community 459 - "Community 459"
Cohesion: 0.40
Nodes (5): API موجود (نیاز به تغییر ندارند), اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندی‌های کارکردی
Cohesion: 0.33
Nodes (6): API موجود (نیاز به تغییر ندارند), اپیک‌ها, اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندی‌های کارکردی
### Community 460 - "Community 460"
Cohesion: 0.33
@@ -2418,8 +2436,8 @@ Cohesion: 0.40
Nodes (5): Admin Endpoints, GET /api/v1/admin/sms/settings/review, GET /api/v1/admin/sms/wallet-report, POST /api/v1/admin/sms/settings/{id}/approve, POST /api/v1/admin/sms/settings/{id}/reject
### Community 490 - "Community 490"
Cohesion: 0.15
Nodes (11): emptyFeatures(), FEATURE_KEYS, FEATURE_LABELS, PeriodForm, periodSchema, PLAN_DISPLAY, PlanForm, planSchema (+3 more)
Cohesion: 0.53
Nodes (3): PaymentLog, PaymentLogRepository, ManagerRegistry
### Community 491 - "Community 491"
Cohesion: 0.12
@@ -2450,8 +2468,8 @@ Cohesion: 0.40
Nodes (5): addMinutes(), calcSlotCount(), hasOverlap(), parseMinutes(), SessionEditor()
### Community 500 - "Community 500"
Cohesion: 0.33
Nodes (6): API موجود (نیاز به endpoint جدید ندارد), اپیک‌ها, اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندی‌های کارکردی
Cohesion: 0.40
Nodes (5): API موجود (نیاز به endpoint جدید ندارد), اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندی‌های کارکردی
### Community 501 - "Community 501"
Cohesion: 0.40
@@ -2542,8 +2560,8 @@ Cohesion: 0.50
Nodes (4): GET /api/v1/appointment-settings/slots — دریافت اسلات‌های خالی, POST /api/v1/appointment-settings/holidays — ثبت تعطیلی (فقط ادمین), POST /api/v1/appointment-settings/overrides — ثبت Override توسط دکتر, Task-09: API تنظیمات نوبت
### Community 527 - "Community 527"
Cohesion: 0.32
Nodes (3): SmsService, SendSmsMessage, SmsProviderInterface
Cohesion: 0.16
Nodes (4): RanginehProvider, SmsService, SendSmsMessage, SmsProviderInterface
### Community 528 - "Community 528"
Cohesion: 0.50
@@ -2554,8 +2572,8 @@ Cohesion: 0.50
Nodes (4): Errors, POST `/oauth/token/refresh`, Request Body, Response `200`
### Community 530 - "Community 530"
Cohesion: 0.29
Nodes (6): Appointment Settings API, Available Locations, Errors, `GET /api/v1/appointment-settings/available-locations/{doctorUuid}`, Response `200`, Slot Calculation Logic (Reference)
Cohesion: 0.40
Nodes (5): Errors, Notes, POST `/api/v1/notification-mobile/request-otp`, Request Body, Response `200`
### Community 531 - "Community 531"
Cohesion: 0.50
@@ -2590,8 +2608,8 @@ Cohesion: 0.48
Nodes (3): DoctorInsuranceRepository, DoctorInsurance, ManagerRegistry
### Community 543 - "Community 543"
Cohesion: 0.30
Nodes (6): AbstractAuthenticator, Passport, PasswordAuthenticator, Request, Response, TokenInterface
Cohesion: 0.40
Nodes (5): 32. 🔵 `POST` post, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 544 - "Community 544"
Cohesion: 0.15
@@ -2738,9 +2756,13 @@ Cohesion: 0.50
Nodes (3): Entity: Payment, ساختار فایل‌ها, معماری — تسک ۱۵: ماژول پرداخت
### Community 595 - "Community 595"
Cohesion: 0.53
Cohesion: 0.43
Nodes (3): SmsLogRepository, SmsLog, ManagerRegistry
### Community 596 - "Community 596"
Cohesion: 0.40
Nodes (5): 35. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 597 - "Community 597"
Cohesion: 0.29
Nodes (7): Configuration, Errors, GET `/api/v1/admin/sms/templates`, GET `/api/v1/sms/template/{uuid}`, Response `200`, Response `200`, SMS API
@@ -2770,8 +2792,8 @@ Cohesion: 0.67
Nodes (3): بک‌اند, فرانت‌اند, وضعیت فعلی کد (مهم — قبل از تغییر بخوان)
### Community 618 - "Community 618"
Cohesion: 0.12
Nodes (6): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantServiceCoverageRepository, TenantInsuranceCleanupService, ManagerRegistry, TenantServiceCoverage
Cohesion: 0.16
Nodes (5): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantServiceCoverageRepository, ManagerRegistry, TenantServiceCoverage
### Community 625 - "Community 625"
Cohesion: 0.40
@@ -2782,8 +2804,8 @@ Cohesion: 0.43
Nodes (3): SmsMessageController, JsonResponse, Request
### Community 631 - "Community 631"
Cohesion: 0.38
Nodes (3): RepositoryClassMappingTest, KernelTestCase, DbLoggerTest
Cohesion: 0.40
Nodes (5): 62. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 633 - "Community 633"
Cohesion: 0.40
@@ -2859,7 +2881,7 @@ Nodes (10): Application Logs, DELETE `/api/v1/admin/logs`, GET `/api/v1/admin/lo
### Community 667 - "Community 667"
Cohesion: 0.40
Nodes (5): Errors, PATCH `/api/v1/clinic-pro/doctor-address/{id}`, Path Parameters, Request Body, Response `200`
Nodes (4): Altcha(), AltchaProps, FA_STRINGS, IntrinsicElements
### Community 672 - "Community 672"
Cohesion: 0.17
@@ -2871,7 +2893,7 @@ Nodes (4): GET `/api/v1/admin/secretaries`, Query Parameters, Response `200`, Se
### Community 676 - "Community 676"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/doctor`, Request Body (`application/json`), Response `201`
Nodes (4): GET `/api/v1/admin/pre-registrations`, POST `/api/v1/admin/pre-registrations/{uuid}/approve`, POST `/api/v1/admin/pre-registrations/{uuid}/reject`, Pre-Registration Management
### Community 677 - "Community 677"
Cohesion: 0.40
@@ -2887,7 +2909,7 @@ Nodes (4): Errors, POST `/api/v1/auth/switch-context`, Request Body, Response `2
### Community 684 - "Community 684"
Cohesion: 0.50
Nodes (4): Errors, POST `/file/upload/clinic_pro/doctor/field_image`, Request, Response `200`
Nodes (4): Error Codes, POST `/api/v1/user/otp-login`, Request Body, Response `200`
### Community 686 - "Community 686"
Cohesion: 0.40
@@ -2907,7 +2929,7 @@ Nodes (4): GET `/api/v1/admin/representations`, Query Parameters, Representation
### Community 693 - "Community 693"
Cohesion: 0.50
Nodes (4): DELETE `/api/v1/doctor/{uuid}`, Errors, Path Parameters, Response `200`
Nodes (4): Errors, POST `/api/v1/user/send-code`, Request Body, Response `200`
### Community 694 - "Community 694"
Cohesion: 0.50
@@ -2939,16 +2961,20 @@ Nodes (5): DELETE `/api/v1/billing/tenant-insurances/{uuid}`, GET `/api/v1/billi
### Community 707 - "Community 707"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/clinic-pro/doctor-address`, Request Body, Response `201`
Nodes (4): Errors, POST `/api/v1/user/register`, Request Body, Response `201`
### Community 708 - "Community 708"
Cohesion: 0.50
Nodes (4): Errors, Path Parameters, POST `/api/v1/clinic-pro/doctor-address/from-clinic/{clinicUuid}`, Response `201`
Nodes (4): Errors, POST `/api/v1/user/login`, Request Body, Response `200`
### Community 710 - "Community 710"
Cohesion: 0.67
Nodes (3): GET `/api/v1/representation/{uuid}/dashboard/yearly`, Query Parameters, Response `200`
### Community 712 - "Community 712"
Cohesion: 0.50
Nodes (4): Errors, POST `/oauth/token`, Request Body, Response `200`
### Community 713 - "Community 713"
Cohesion: 0.50
Nodes (4): 39. 🟡 `PATCH` Comment confirmation, مثال Request, هدرهای اضافی, پاسخ‌ها
@@ -2957,6 +2983,14 @@ Nodes (4): 39. 🟡 `PATCH` Comment confirmation, مثال Request, هدرهای
Cohesion: 0.50
Nodes (4): 45. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 716 - "Community 716"
Cohesion: 0.50
Nodes (4): Errors, GET `/oauth/userinfo`, Headers, Response `200`
### Community 717 - "Community 717"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/user/verify-code`, Request Body, Response `200`
### Community 718 - "Community 718"
Cohesion: 0.50
Nodes (4): Errors, GET `/api/v1/admin/insurances`, Query Parameters, Response `200`
@@ -2978,8 +3012,16 @@ Cohesion: 0.50
Nodes (4): PUT `/api/v1/insurance-pricing`, Request Body, Response `200`, خطاها
### Community 724 - "Community 724"
Cohesion: 0.67
Nodes (3): GET `/api/v1/clinic-pro/doctor-addresses/{doctorId}`, Path Parameters, Response `200`
Cohesion: 0.50
Nodes (4): 38. 🟢 `GET` Unapproved comments, هدرهای اضافی, پارامترهای Query, پاسخ‌ها
### Community 725 - "Community 725"
Cohesion: 0.50
Nodes (4): 40. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 726 - "Community 726"
Cohesion: 0.50
Nodes (4): 44. 🟢 `GET` list comment, هدرهای اضافی, پارامترهای Query, پاسخ‌ها
### Community 727 - "Community 727"
Cohesion: 0.67
@@ -2989,6 +3031,22 @@ Nodes (3): POST `/api/v1/admin/sms/template/{uuid}/approve`, Request Body (`appl
Cohesion: 0.50
Nodes (4): 46. 🟡 `PATCH` patch, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 730 - "Community 730"
Cohesion: 0.67
Nodes (3): Errors, GET `/api/v1/notification-mobile/{target}`, Response `200`
### Community 731 - "Community 731"
Cohesion: 0.67
Nodes (3): 34. 🔵 `POST` image logo, هدرهای اضافی, پاسخ‌ها
### Community 732 - "Community 732"
Cohesion: 0.67
Nodes (3): 36. 🟢 `GET` get my rate, هدرهای اضافی, پاسخ‌ها
### Community 733 - "Community 733"
Cohesion: 0.67
Nodes (3): 42. 🔴 `DELETE` delete, هدرهای اضافی, پاسخ‌ها
### Community 734 - "Community 734"
Cohesion: 0.67
Nodes (3): GET `/api/v1/billing/tenant-insurances/{uuid}/service-coverage`, PUT `/api/v1/billing/tenant-insurances/{uuid}/service-coverage`, TenantServiceCoverage — پوشش خدمت تحت یک قرارداد بیمه (فاز ۲)
@@ -3009,24 +3067,28 @@ Nodes (3): POST `/api/v1/admin/insurance/{id}/upload-logo`, Request, Response `2
Cohesion: 0.50
Nodes (4): Errors, GET `/api/v1/representation/{uuid}`, Path Parameters, Response `200`
### Community 739 - "Community 739"
Cohesion: 0.67
Nodes (3): 43. 🟢 `GET` get, هدرهای اضافی, پاسخ‌ها
## Knowledge Gaps
- **4030 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps`, `TenantInsurance`, `CoverageRow` (+4025 more)
- **4031 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps`, `TenantInsurance`, `CoverageRow` (+4026 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **154 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **151 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `Altcha` connect `Community 367` to `Community 0`, `Community 485`?**
- **Why does `Altcha` connect `Community 367` to `Community 667`, `Community 485`?**
_High betweenness centrality (0.068) - this node is a cross-community bridge._
- **Why does `BaseController` connect `Community 108` to `Community 4`, `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 552`, `Community 424`, `Community 300`, `Community 433`, `Community 58`, `Community 59`, `Community 318`, `Community 64`, `Community 75`, `Community 77`, `Community 607`, `Community 480`, `Community 230`, `Community 104`, `Community 107`, `Community 109`, `Community 626`, `Community 380`, `Community 121`, `Community 122`, `Community 252`?**
_High betweenness centrality (0.033) - this node is a cross-community bridge._
- **Why does `ApiTestCase` connect `Community 86` to `Community 397`, `Community 22`, `Community 535`, `Community 534`, `Community 541`, `Community 414`, `Community 169`, `Community 690`, `Community 562`, `Community 565`, `Community 573`, `Community 574`, `Community 575`, `Community 594`, `Community 82`, `Community 609`, `Community 484`, `Community 618`, `Community 497`, `Community 371`, `Community 632`?**
_High betweenness centrality (0.025) - this node is a cross-community bridge._
- **Why does `BaseController` connect `Community 108` to `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 294`, `Community 295`, `Community 552`, `Community 424`, `Community 300`, `Community 433`, `Community 308`, `Community 58`, `Community 59`, `Community 318`, `Community 64`, `Community 75`, `Community 77`, `Community 607`, `Community 480`, `Community 230`, `Community 104`, `Community 107`, `Community 109`, `Community 626`, `Community 380`, `Community 121`, `Community 122`, `Community 252`?**
_High betweenness centrality (0.037) - this node is a cross-community bridge._
- **Why does `ApiTestCase` connect `Community 86` to `Community 397`, `Community 534`, `Community 535`, `Community 541`, `Community 169`, `Community 690`, `Community 562`, `Community 565`, `Community 573`, `Community 574`, `Community 575`, `Community 594`, `Community 82`, `Community 729`, `Community 609`, `Community 484`, `Community 618`, `Community 497`, `Community 371`, `Community 632`?**
_High betweenness centrality (0.026) - this node is a cross-community bridge._
- **What connects `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps` to the rest of the system?**
_4030 weakly-connected nodes found - possible documentation gaps or missing edges._
_4031 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.048087431693989074 - nodes in this community are weakly interconnected._
_Cohesion score 0.04390451832907076 - nodes in this community are weakly interconnected._
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.028985507246376812 - nodes in this community are weakly interconnected._
- **Should `Community 2` be split into smaller, more focused modules?**
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_docs_api_captcha_md", "label": "captcha.md", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L1"}, {"id": "api_captcha_captcha_api_altcha", "label": "Captcha API (ALTCHA)", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L1"}, {"id": "api_captcha_get_api_v1_altcha_challenge", "label": "GET `/api/v1/altcha/challenge`", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L10"}, {"id": "api_captcha_response_200", "label": "Response `200`", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L14"}, {"id": "api_captcha_\u0627\u0639\u0645\u0627\u0644_\u06a9\u067e\u0686\u0627_\u0631\u0648\u06cc_endpoint\u0647\u0627\u06cc_\u0645\u062d\u0627\u0641\u0638\u062a_\u0634\u062f\u0647", "label": "\u0627\u0639\u0645\u0627\u0644 \u06a9\u067e\u0686\u0627 \u0631\u0648\u06cc endpoint\u0647\u0627\u06cc \u0645\u062d\u0627\u0641\u0638\u062a\u200c\u0634\u062f\u0647", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L31"}, {"id": "api_captcha_\u062e\u0637\u0627\u06cc_\u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc_\u06a9\u067e\u0686\u0627_422", "label": "\u062e\u0637\u0627\u06cc \u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc \u06a9\u067e\u0686\u0627 `422`", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L54"}, {"id": "api_captcha_\u0627\u0645\u0646\u06cc\u062a", "label": "\u0627\u0645\u0646\u06cc\u062a", "file_type": "document", "source_file": "docs/api/captcha.md", "source_location": "L67"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_docs_api_captcha_md", "target": "api_captcha_captcha_api_altcha", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L1", "weight": 1.0}, {"source": "api_captcha_captcha_api_altcha", "target": "api_captcha_get_api_v1_altcha_challenge", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L10", "weight": 1.0}, {"source": "api_captcha_get_api_v1_altcha_challenge", "target": "api_captcha_response_200", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L14", "weight": 1.0}, {"source": "api_captcha_captcha_api_altcha", "target": "api_captcha_\u0627\u0639\u0645\u0627\u0644_\u06a9\u067e\u0686\u0627_\u0631\u0648\u06cc_endpoint\u0647\u0627\u06cc_\u0645\u062d\u0627\u0641\u0638\u062a_\u0634\u062f\u0647", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L31", "weight": 1.0}, {"source": "api_captcha_\u0627\u0639\u0645\u0627\u0644_\u06a9\u067e\u0686\u0627_\u0631\u0648\u06cc_endpoint\u0647\u0627\u06cc_\u0645\u062d\u0627\u0641\u0638\u062a_\u0634\u062f\u0647", "target": "api_captcha_\u062e\u0637\u0627\u06cc_\u0627\u0639\u062a\u0628\u0627\u0631\u0633\u0646\u062c\u06cc_\u06a9\u067e\u0686\u0627_422", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L54", "weight": 1.0}, {"source": "api_captcha_captcha_api_altcha", "target": "api_captcha_\u0627\u0645\u0646\u06cc\u062a", "relation": "contains", "confidence": "EXTRACTED", "source_file": "docs/api/captcha.md", "source_location": "L67", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}
+1 -1
View File
File diff suppressed because one or more lines are too long
+497 -477
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -260,8 +260,8 @@
"semantic_hash": ""
},
"assets/admin/pages/LoginPage.tsx": {
"mtime": 1783667366.139762,
"ast_hash": "d518496ce1aa2a3d3c1509106ff36e77",
"mtime": 1783668734.8468878,
"ast_hash": "7f97461eb12e31df27e16fcc46efda9b",
"semantic_hash": ""
},
"assets/admin/pages/MyClinicPage.tsx": {
@@ -955,8 +955,8 @@
"semantic_hash": ""
},
"src/Auth/Security/PasswordAuthenticator.php": {
"mtime": 1781946748.8785093,
"ast_hash": "2fbb197825e081dad86bebd660ef9abd",
"mtime": 1783668692.4381845,
"ast_hash": "e4107ad152265492e7427334d4534b10",
"semantic_hash": ""
},
"src/Auth/Service/OtpService.php": {
@@ -3805,8 +3805,8 @@
"semantic_hash": ""
},
"assets/admin/components/ui/Altcha.tsx": {
"mtime": 1783666097.1123717,
"ast_hash": "76bfa4d9d7c539585eb8b3836c81f3cb",
"mtime": 1783668418.3771482,
"ast_hash": "121352e982ca664204d71d9f779984f0",
"semantic_hash": ""
},
"src/Shared/Captcha/AltchaService.php": {
@@ -3840,8 +3840,8 @@
"semantic_hash": ""
},
"docs/api/captcha.md": {
"mtime": 1783666775.6831665,
"ast_hash": "4796a4acacfaba72c1e4b741b234c96f",
"mtime": 1783668751.744952,
"ast_hash": "977820fe357db7b73b2dc2de861eaae9",
"semantic_hash": ""
}
}
@@ -3,6 +3,7 @@
namespace App\Auth\Security;
use App\Auth\Repository\UserRepository;
use App\Shared\Captcha\CaptchaGuard;
use App\Shared\Constant\ErrorCodes;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Psr\Log\LoggerInterface;
@@ -27,6 +28,7 @@ class PasswordAuthenticator extends AbstractAuthenticator
private readonly CacheInterface $cache,
private readonly LoggerInterface $logger,
private readonly RateLimiterFactory $loginLimiter,
private readonly CaptchaGuard $captcha,
private readonly int $refreshTokenTtl = 2592000,
) {}
@@ -43,6 +45,9 @@ class PasswordAuthenticator extends AbstractAuthenticator
throw new TooManyRequestsHttpException(60, 'تعداد تلاش‌های ورود از حد مجاز گذشت');
}
// AppException را ExceptionSubscriber به پاسخ 422 با ERR_CAPTCHA_001 تبدیل می‌کند.
$this->captcha->assertValid($request);
$data = json_decode($request->getContent(), true) ?? [];
$mobile = trim($data['mobile_number'] ?? '');
$pass = $data['password'] ?? '';
+2 -128
View File
@@ -89,7 +89,7 @@
<svg viewbox="0 0 24 24" fill="none"><path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
<button class="btn btn-ghost" id="openRegModal" style="font-size:13px;padding:8px 20px">ثبت نام</button>
<button class="btn btn-blue" id="openLoginModal" style="font-size:13px;padding:8px 20px">ورود به پنل</button>
<a href="/admin" class="btn btn-blue" style="font-size:13px;padding:8px 20px;text-decoration:none">ورود به پنل</a>
</div>
</div>
</header>
@@ -558,7 +558,7 @@
</div>
<!-- ALTCHA captcha — proof-of-work در پس‌زمینه (بدون تعامل کاربر) -->
<altcha-widget id="regAltcha" challengeurl="/api/v1/altcha/challenge" auto="onload" style="display:block;margin-top:16px"></altcha-widget>
<altcha-widget id="regAltcha" challengeurl="/api/v1/altcha/challenge" auto="onload" style="display:block;margin-top:16px" strings='{"label":"من ربات نیستم","verifying":"در حال بررسی...","verified":"تأیید شد","waitAlert":"در حال بررسی... لطفاً منتظر بمانید.","error":"احراز هویت ناموفق بود. کمی بعد دوباره تلاش کنید.","expired":"احراز هویت منقضی شد. دوباره تلاش کنید."}'></altcha-widget>
<!-- Error / Success -->
<div id="regMsg" style="display:none;margin-top:14px;padding:12px 16px;border-radius:10px;font-size:13px"></div>
@@ -731,132 +731,6 @@ msg.style.background = type === 'success' ? '#dcfce7' : '#fef2f2';
msg.style.color = type === 'success' ? '#16a34a' : '#ef4444';
msg.style.border = type === 'success' ? '1px solid #bbf7d0' : '1px solid #fecaca';
}
})();
</script>
<!-- ===================== Panel Login Modal ===================== -->
<div id="loginOverlay" style="display:none;position:fixed;inset:0;background:oklch(0.2 0.05 285 / 0.55);z-index:900;backdrop-filter:blur(4px);overflow-y:auto;padding:24px 16px" onclick="if(event.target===this)closeLoginModal()">
<div style="background:#fff;border-radius:20px;max-width:420px;margin:auto;padding:32px 28px;position:relative;box-shadow:0 24px 60px oklch(0.3 0.1 285 / 0.22)">
<button onclick="closeLoginModal()" aria-label="بستن" style="position:absolute;top:16px;left:20px;background:none;border:none;cursor:pointer;font-size:22px;color:var(--text-2);line-height:1">×</button>
<h2 style="margin:0 0 6px;font-size:20px;font-weight:800;color:var(--ink)">ورود به پنل</h2>
<p style="margin:0 0 24px;font-size:13px;color:var(--text-2)">با شماره موبایل و رمز عبور وارد شوید</p>
<div style="display:flex;flex-direction:column;gap:14px">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:5px;color:var(--ink)">شماره موبایل</label>
<input id="loginMobile" type="tel" placeholder="09xxxxxxxxx" dir="ltr" autocomplete="username" style="width:100%;border:1.5px solid var(--border);border-radius:10px;padding:10px 14px;font-size:14px;font-family:inherit;outline:none;box-sizing:border-box;color:var(--ink);text-align:right"/>
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:5px;color:var(--ink)">رمز عبور</label>
<input id="loginPass" type="password" placeholder="••••••••" autocomplete="current-password" style="width:100%;border:1.5px solid var(--border);border-radius:10px;padding:10px 14px;font-size:14px;font-family:inherit;outline:none;box-sizing:border-box;color:var(--ink)"/>
</div>
</div>
<div id="loginMsg" style="display:none;margin-top:14px;padding:12px 16px;border-radius:10px;font-size:13px"></div>
<button id="loginSubmitBtn" onclick="submitLogin()" style="margin-top:20px;width:100%;background:var(--blue);color:#fff;border:none;border-radius:12px;padding:13px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit;transition:opacity .2s">
ورود به سیستم
</button>
<p style="text-align:center;font-size:12px;color:var(--text-2);margin:12px 0 0">حساب ندارید؟ از دکمه‌ی «ثبت نام» استفاده کنید</p>
</div>
</div>
<script>
(function () {
var loginOverlay = document.getElementById('loginOverlay');
var loginBtn = document.getElementById('loginSubmitBtn');
var openBtn = document.getElementById('openLoginModal');
if (openBtn)
openBtn.addEventListener('click', function () {
loginOverlay.style.display = 'block';
document.body.style.overflow = 'hidden';
});
window.closeLoginModal = function () {
loginOverlay.style.display = 'none';
document.body.style.overflow = '';
};
function loginMsg(text, type) {
var m = document.getElementById('loginMsg');
m.textContent = text;
m.style.display = 'block';
m.style.background = type === 'success' ? '#dcfce7' : '#fef2f2';
m.style.color = type === 'success' ? '#16a34a' : '#ef4444';
m.style.border = type === 'success' ? '1px solid #bbf7d0' : '1px solid #fecaca';
}
// state را دقیقاً به شکل store ادمین (کلید clinicpro-auth) می‌نویسیم تا SPA پس از ری‌دایرکت لاگین بماند.
function persistAuth(tokens, info) {
var d = (info && info.data) ? info.data : {};
var state = {
token: tokens.access_token,
refreshToken: tokens.refresh_token || null,
isAuthenticated: true,
userUuid: d.uuid || null,
userName: d.realName || null,
primaryRole: d.primary_role || null,
dbUuid: d.db_uuid || null,
dbKey: d.db_key || null,
doctorUuid: d.doctor_uuid || null,
context: d.context || null,
availableContexts: d.available_contexts || []
};
localStorage.setItem('clinicpro-auth', JSON.stringify({ state: state, version: 0 }));
}
window.submitLogin = function () {
var mobile = document.getElementById('loginMobile').value.trim();
var pass = document.getElementById('loginPass').value;
if (! /^09[0-9]{9}$/.test(mobile)) {
loginMsg('شماره موبایل معتبر نیست', 'error');
return;
}
if (! pass) {
loginMsg('رمز عبور را وارد کنید', 'error');
return;
}
loginBtn.disabled = true;
loginBtn.textContent = 'در حال ورود...';
fetch('/api/v1/user/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mobile_number: mobile, password: pass })
}).then(function (r) {
return r.json().then(function (j) { return { ok: r.ok, body: j }; });
}).then(function (res) {
if (! res.ok || ! res.body.access_token) {
var err = (res.body.errors && res.body.errors[0]) ? res.body.errors[0].message : 'نام کاربری یا رمز عبور اشتباه است';
loginMsg(err, 'error');
loginBtn.disabled = false;
loginBtn.textContent = 'ورود به سیستم';
return;
}
var tokens = res.body;
// اطلاعات کاربر را می‌گیریم تا state کامل ذخیره شود؛ اگر نشد، با state حداقلی ادامه بده.
fetch('/oauth/userinfo', { headers: { 'Authorization': 'Bearer ' + tokens.access_token } })
.then(function (r) { return r.json(); })
.catch(function () { return null; })
.then(function (info) {
persistAuth(tokens, info);
loginMsg('خوش آمدید، در حال انتقال به پنل...', 'success');
window.location.href = '/admin';
});
}).catch(function () {
loginMsg('خطا در اتصال به سرور', 'error');
loginBtn.disabled = false;
loginBtn.textContent = 'ورود به سیستم';
});
};
document.getElementById('loginPass').addEventListener('keydown', function (e) {
if (e.key === 'Enter') submitLogin();
});
})();
</script>