hamed af0ae51987 feat: add clinic doctor invitation feature
- Implemented ClinicInvitationController to handle doctor invitations.
- Created ClinicDoctorInvitation entity and repository for managing invitations.
- Added ClinicInvitationService for business logic related to invitations.
- Introduced endpoints for inviting, listing, resending, changing status, and deleting invitations.
- Updated security configuration to allow public access to invitation endpoints.
- Added migration for clinic_doctor_invitations table.
- Enhanced DoctorRepository with a method to find doctors by mobile number.
- Updated ClinicDetailPage to include invitation management UI.
2026-06-10 22:13:39 +03:30

ClinicPro — مستندات کامل پروژه

تاریخ آخرین به‌روزرسانی: ۱۴۰۵/۰۳/۲۰ | نسخه Symfony: 7.4 | PHP: ≥ 8.2


فهرست مطالب

  1. معرفی پروژه
  2. تکنولوژی Stack
  3. ساختار دایرکتوری
  4. راه‌اندازی محیط توسعه
  5. متغیرهای محیطی
  6. پایگاه داده — موجودیت‌ها
  7. API Endpoints
  8. امنیت و احراز هویت
  9. پنل ادمین React
  10. Frontend Build
  11. دستورات Console
  12. Migrations
  13. سرویس‌های خارجی

۱. معرفی پروژه

ClinicPro یک سیستم جامع مدیریت کلینیک و نوبت‌دهی پزشکی است که شامل:

  • API Backend: Symfony 7.4 (REST API + JWT Auth)
  • Admin Panel: React 19 + TypeScript (SPA داخل Symfony)
  • ویژگی‌های اصلی:
    • مدیریت پزشکان، کلینیک‌ها و کاربران
    • سیستم نوبت‌دهی با مدیریت اسلات زمانی
    • درگاه پرداخت (ملت و سپ)
    • سیستم کیف پول و تسویه‌حساب
    • نمایندگان (Representation) با کمیسیون
    • احراز هویت OTP + رمز عبور
    • مدیریت منشی با دسترسی‌های دانه‌ای
    • ارسال پیامک (KaveNegar / Rangineh)
    • امتیاز و نظرات پزشکان
    • بلاگ
    • مستندات API تعاملی (Swagger UI) — محافظت‌شده با HTTP Basic Auth

۲. تکنولوژی Stack

Backend

لایه تکنولوژی
Framework Symfony 7.4
PHP >= 8.2
ORM Doctrine ORM 3.6
Auth JWT (lexik/jwt-authentication-bundle)
Queue Symfony Messenger
Cache/Session Redis
Database MariaDB 11.8
API Docs NelmioApiDocBundle v5 + swagger-php v6
Rate Limiting Symfony Rate Limiter

Frontend (Admin)

لایه تکنولوژی
Framework React 19 + TypeScript
Routing React Router DOM v7
Styling Tailwind CSS v4
State (server) TanStack Query v5
State (client) Zustand v5
Forms React Hook Form + Zod
Icons Heroicons v2
Toast Sonner
Tables TanStack Table v8
Build Webpack Encore (Symfony UX React)

۳. ساختار دایرکتوری

clinic-pro-symfony/
├── assets/
│   ├── admin/                    ← React Admin SPA
│   │   ├── index.tsx             ← Entry point
│   │   ├── App.tsx               ← Router + PrivateRoute
│   │   ├── styles.css            ← Tailwind CSS v4
│   │   ├── components/
│   │   │   └── layout/
│   │   │       ├── AdminLayout.tsx
│   │   │       ├── Sidebar.tsx
│   │   │       └── Topbar.tsx
│   │   ├── pages/                ← ۲۲ صفحه پیاده‌سازی‌شده
│   │   ├── stores/
│   │   │   ├── authStore.ts      ← Zustand JWT store
│   │   │   └── uiStore.ts        ← Sidebar state
│   │   ├── hooks/
│   │   └── lib/
│   ├── app.js                    ← Symfony main entry
│   ├── styles/app.css
│   └── react/controllers/        ← UX React controllers
│
├── config/
│   ├── packages/
│   │   ├── security.yaml         ← Firewalls + ACL
│   │   ├── doctrine.yaml
│   │   ├── lexik_jwt_authentication.yaml
│   │   ├── messenger.yaml
│   │   ├── nelmio_api_doc.yaml   ← Swagger config
│   │   ├── rate_limiter.yaml
│   │   └── webpack_encore.yaml
│   └── routes/
│       ├── security.yaml
│       └── nelmio_api_doc.yaml
│
├── migrations/                   ← 17 Doctrine migrations
│
├── public/
│   ├── index.php
│   ├── build/                    ← Webpack output
│   └── bundles/nelmioapidoc/     ← Swagger UI assets (local)
│
├── src/
│   ├── Admin/Controller/         ← AdminApiController + SPA catch-all
│   ├── Appointment/              ← نوبت‌دهی
│   ├── Auth/                     ← احراز هویت
│   ├── Blog/                     ← بلاگ
│   ├── Category/                 ← دسته‌بندی‌ها
│   ├── Clinic/                   ← کلینیک‌ها
│   ├── Doctor/                   ← پزشکان
│   ├── Insurance/                ← بیمه
│   ├── Payment/                  ← پرداخت
│   ├── Rating/                   ← امتیاز و نظرات
│   ├── Representation/           ← نمایندگان
│   ├── Secretary/                ← منشی‌ها
│   ├── Settlement/               ← تسویه‌حساب
│   ├── Shared/                   ← زیرساخت مشترک
│   ├── Sms/                      ← پیامک
│   ├── UserProfile/              ← پروفایل کاربر
│   └── Kernel.php
│
├── tests/
├── webpack.config.js
├── tsconfig.json
├── postcss.config.js
├── composer.json
├── package.json
└── .env

۴. راه‌اندازی محیط توسعه

پیش‌نیازها

  • ddev نصب شده
  • Docker Desktop

مراحل اجرا

# ۱. کلون پروژه
git clone <repo-url>
cd clinic-pro-symfony

# ۲. راه‌اندازی ddev
ddev start

# ۳. نصب PHP packages
ddev composer install

# ۴. ساخت JWT keys
ddev exec php bin/console lexik:jwt:generate-keypair

# ۵. اجرای migrations
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

# ۶. نصب npm packages
npm install --legacy-peer-deps

# ۷. Build frontend (dev)
npm run dev

# ۸. یا watch mode
npm run watch

دسترسی

سرویس آدرس توضیح
Admin Panel https://clinic-pro.ddev.site/admin JWT auth
API https://clinic-pro.ddev.site/api/v1 REST API
Swagger UI https://clinic-pro.ddev.site/api/doc HTTP Basic Auth
Health Check https://clinic-pro.ddev.site/health عمومی

اطلاعات ادمین پیش‌فرض

فیلد مقدار
موبایل 09120671713
رمز عبور admin1234
نقش ROLE_ADMIN

اطلاعات ورود به Swagger UI

فیلد مقدار پیش‌فرض
Username admin
Password clinic-pro-docs

برای تغییر رمز: ddev exec php -r "echo password_hash('رمز-جدید', PASSWORD_BCRYPT) . PHP_EOL;" — hash را در .env در API_DOC_PASSWORD قرار دهید.


۵. متغیرهای محیطی

فایل .env — کلیدها (مقادیر حساس در .env.local تنظیم می‌شوند):

APP_ENV=dev|prod
APP_SECRET=                      # کلید امنیتی Symfony
DEFAULT_URI=                     # آدرس پایه سایت (مثلاً https://clinic-pro.ddev.site)

DATABASE_URL=                    # DSN پایگاه داده MariaDB

JWT_SECRET_KEY=                  # مسیر کلید خصوصی JWT
JWT_PUBLIC_KEY=                  # مسیر کلید عمومی JWT
JWT_PASSPHRASE=                  # رمز کلید JWT

CORS_ALLOW_ORIGIN=               # آدرس‌های مجاز CORS (regex)

MESSENGER_TRANSPORT_DSN=         # DSN صف پیام (Redis)
REDIS_URL=                       # آدرس Redis

REFRESH_TOKEN_TTL=2592000        # عمر refresh token (ثانیه) = ۳۰ روز
OTP_TTL=1200                     # عمر کد OTP (ثانیه) = ۲۰ دقیقه

# SMS
KAVENEGAR_API_KEY=
KAVENEGAR_SENDER=
RANGINEH_API_KEY=
RANGINEH_SENDER=
SMS_PROVIDER=kavenegar|rangineh  # ارائه‌دهنده فعال

# File Upload
MAX_FILE_SIZE_BYTES=5242880      # حداکثر حجم فایل (۵ مگابایت)
UPLOAD_DIR=                      # مسیر ذخیره فایل‌ها

# Payment
ALLOWED_FRONTEND_HOSTS=          # هاست‌های مجاز برای redirect پرداخت
MELLAT_TERMINAL_ID=
MELLAT_USERNAME=
MELLAT_PASSWORD=
SEP_TERMINAL_ID=
APP_BASE_URL=                    # آدرس پایه برای callback پرداخت

# API Documentation (Swagger UI)
API_DOC_USERNAME=admin           # نام کاربری ورود به /api/doc
API_DOC_PASSWORD=                # bcrypt hash رمز عبور

۶. پایگاه داده — موجودیت‌ها

جداول پایگاه داده

جدول Entity توضیح
users Auth\Entity\User کاربران سیستم
doctors Doctor\Entity\Doctor پروفایل پزشکان
doctor_addresses Doctor\Entity\DoctorAddress آدرس مطب
clinics Clinic\Entity\Clinic کلینیک‌ها
categories Category\Entity\Category دسته‌بندی‌ها
appointments Appointment\Entity\Appointment نوبت‌ها
weekly_schedules Appointment\Entity\WeeklySchedule برنامه هفتگی
date_overrides Appointment\Entity\DateOverride تغییر برنامه خاص
holidays Appointment\Entity\Holiday تعطیلات
payments Payment\Entity\Payment پرداخت‌ها
settlements Settlement\Entity\Settlement درخواست تسویه
wallet_transactions Settlement\Entity\WalletTransaction تراکنش کیف پول
representations Representation\Entity\Representation نمایندگان
doctor_secretaries Secretary\Entity\DoctorSecretary منشی‌های پزشک
comments Rating\Entity\Comment نظرات
rates Rating\Entity\Rate امتیازها
likes Rating\Entity\Like لایک نظرات
blogs Blog\Entity\Blog مقالات بلاگ
sms_templates Sms\Entity\SmsTemplate قالب‌های پیامک
sms_logs Sms\Entity\SmsLog لاگ ارسال پیامک
profiles UserProfile\Entity\UserProfile پروفایل پزشکی کاربر
doctor_insurances Insurance\Entity\DoctorInsurance بیمه‌های پزشک

جداول Join (ManyToMany)

جدول رابطه
doctor_specialties Doctor ↔ Category (specialty)
doctor_expertise Doctor ↔ Category (expertise)
doctor_states Doctor ↔ Category (state)
doctor_cities Doctor ↔ Category (city)
clinic_doctors Clinic ↔ Doctor
clinic_specialties Clinic ↔ Category
clinic_services Clinic ↔ Category
clinic_insurances Clinic ↔ Category

User (users)

id              INT PK AUTO_INCREMENT
uuid            VARCHAR(36) UNIQUE
mobile_number   VARCHAR(20) UNIQUE             ← شناسه ورود
password_hash   VARCHAR(255) nullable
email           VARCHAR(100) nullable
real_name       VARCHAR(100) nullable
roles           JSON                           ← ['ROLE_USER', 'ROLE_ADMIN', ...]
status          SMALLINT default:1             ← 1=active, 0=inactive
created_at      INT (unix timestamp)
updated_at      INT (unix timestamp)

نقش‌های موجود: ROLE_USER | ROLE_ADMIN | ROLE_DOCTOR | ROLE_CLINIC | ROLE_REPRESENTATION | ROLE_SECRETARY


Doctor (doctors)

id                          INT PK
uuid                        VARCHAR(36) UNIQUE
user_id                     INT FK → users
name                        VARCHAR(255)
gender                      VARCHAR(10) nullable
medical_system_code         VARCHAR(25) nullable
mobile_number               VARCHAR(15) nullable
activity_time               INT nullable          ← مدت ویزیت (دقیقه)
degree                      VARCHAR(30) nullable  ← general/specialist/subspecialist
info                        TEXT nullable
images                      JSON nullable
doctor_rate                 FLOAT default:3.5
doctor_rate_percentage      FLOAT default:60.0   ← سهم پزشک از پرداخت (%)
active_doctor_appointment   BOOL default:true
representation_id           INT nullable
created_at / updated_at     INT

Clinic (clinics)

id                INT PK
uuid              VARCHAR(36) UNIQUE
user_id           INT FK → users
name              VARCHAR(255) nullable
info              TEXT nullable
address           TEXT nullable
telephone         VARCHAR(50) nullable
is_24_7           BOOL default:false
working_days      VARCHAR(255) nullable
latitude/longitude FLOAT nullable
city_id           INT nullable → categories (bundle='city')
state_id          INT nullable → categories (bundle='state')
representation_id INT nullable
images_clinic     JSON nullable
clinic_logo       JSON nullable

Representation (representations)

id              INT PK
uuid            VARCHAR(36) UNIQUE
full_name       VARCHAR(255)
mobile_number   VARCHAR(20)
city_id         INT nullable → categories (bundle='city')   ← ID شهر (نه نام)
wallet_balance  INT default:0
commission_rate FLOAT default:10.0
is_active       BOOL default:true
created_at      INT (unix timestamp)
updated_at      INT (unix timestamp)

توجه: city_id یک FK عددی به جدول categories (bundle='city') است. نام شهر از طریق JOIN در API برگردانده می‌شود.


Appointment (appointments)

id          INT PK
uuid        VARCHAR(36) UNIQUE
doctor_id   INT FK → doctors
user_id     INT FK → users
slot_start  INT (unix timestamp)
slot_end    INT (unix timestamp)
status      VARCHAR(30)
note        VARCHAR(255) nullable
version     INT (optimistic locking)

وضعیت‌های نوبت:

waiting_for_payment  → در انتظار پرداخت
reserved             → رزرو شده
checked_in           → ورود به مطب
waiting              → در صف انتظار
in_progress          → در حال ویزیت
visited              → ویزیت شده
completed            → تکمیل شده
cancelled_by_doctor  → لغو توسط پزشک
cancelled_by_user    → لغو توسط کاربر
auto_cancel_unpaid   → لغو خودکار (پرداخت‌نشده)
no_show              → غیبت

Payment (payments)

id              INT PK
uuid            VARCHAR(36) UNIQUE
order_id        VARCHAR(64) UNIQUE
user_id         INT FK
appointment_id  INT FK nullable
amount_rials    INT
status          VARCHAR(30)   ← pending|success|failed|refunded
gateway         VARCHAR(20)   ← mellat|sep
type            VARCHAR(30)   ← appointment|subscription
gateway_token   VARCHAR(255) nullable
reference_id    VARCHAR(255) nullable
frontend_address VARCHAR(500) nullable
callback_ip     VARCHAR(45) nullable

Category (categories)

id                INT PK
uuid              VARCHAR(36) UNIQUE
bundle            VARCHAR(32)    ← نوع دسته‌بندی
label             VARCHAR(255) nullable
status            SMALLINT default:1
parent_id         INT nullable   ← برای city → state
weight            INT default:0
representation_id INT nullable

نوع‌های bundle:

state                   ← استان‌ها
city                    ← شهرها (parent_id = state.id)
specially_doctor        ← تخصص پزشک
doctor_services         ← خدمات پزشک
insurance_type          ← نوع بیمه پایه
supplementary_insurance ← بیمه تکمیلی
tag                     ← تگ بلاگ

Settlement + Wallet

-- settlements
uuid, user_id, amount_rials, status(pending|approved|rejected|paid),
bank_account(JSON), admin_note, reviewed_by, reviewed_at

-- wallet_transactions
user_id, amount, type(credit|debit), balance_after, description

DoctorSecretary

uuid
doctor_id    FK → doctors
secretary_id FK → users
permission   JSON:
  {
    "appointments": { "view": true, "create": true, "cancel": false, "update_status": false },
    "addresses":    { "view": true, "create": false, "update": false, "delete": false },
    "clinic_info":  { "view": true, "update": false },
    "insurances":   { "view": true, "create": false, "update": false, "delete": false }
  }
active       BOOL

۷. API Endpoints

Base URL: https://clinic-pro.ddev.site

مستندات کامل تعاملی: https://clinic-pro.ddev.site/api/doc (نیاز به HTTP Basic Auth)


🔐 Auth (/api/v1/user/ & /oauth/)

Method Path Auth توضیح
POST /api/v1/user/login Public ورود با موبایل و رمز → JWT + refresh
POST /api/v1/user/send-code Public ارسال کد OTP به موبایل
POST /api/v1/user/verify-code Public تأیید کد OTP
POST /api/v1/user/register Public ثبت‌نام کاربر جدید
POST /oauth/token Public دریافت JWT (mobile grant)
POST /oauth/token/refresh Public تمدید JWT با refresh token
GET /oauth/userinfo اطلاعات کاربر جاری
POST /oauth/logout خروج و ابطال refresh token
GET /session/token Public CSRF session token

درخواست Login:

POST /api/v1/user/login
{ "mobile_number": "09120671713", "password": "admin1234" }

پاسخ موفق:

{
  "access_token": "eyJ...",
  "refresh_token": "8e75...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token_expires_in": 2592000
}

🩺 Doctors

Method Path Auth توضیح
GET /api/v1/doctors Public لیست پزشکان (فیلتر + pagination)
GET /api/v1/doctor/{uuid} Public جزئیات پزشک
POST /api/v1/doctor ایجاد پروفایل پزشک
PATCH /api/v1/doctor/{uuid} به‌روزرسانی پروفایل
DELETE /api/v1/doctor/{uuid} ADMIN حذف پزشک
POST /file/upload/clinic_pro/doctor/field_image آپلود تصویر
GET /api/v1/clinic-pro/doctor-addresses/{doctorId} Public لیست آدرس‌ها
POST /api/v1/clinic-pro/doctor-address افزودن آدرس
PATCH /api/v1/clinic-pro/doctor-address/{id} ویرایش آدرس
DELETE /api/v1/clinic-pro/doctor-address/{id} حذف آدرس

🏥 Clinics

Method Path Auth توضیح
GET /api/v1/clinics Public لیست کلینیک‌ها
GET /api/v1/clinic/{uuid} Public جزئیات کلینیک
POST /api/v1/clinic ایجاد کلینیک
PATCH /api/v1/clinic/{uuid} ویرایش کلینیک
GET /api/v1/clinic/doctor-list/{clinicUuid} Public پزشکان کلینیک
POST /file/upload/clinic_pro/clinic/field_image_clinic آپلود تصویر
POST /file/upload/clinic_pro/clinic/field_clinic_logo آپلود لوگو

📅 Appointments

Method Path Auth توضیح
GET /api/v1/appointment-slots Public اسلات‌های خالی پزشک
POST /api/v1/appointment رزرو نوبت
GET /api/v1/appointment/{uuid} جزئیات نوبت
PATCH /api/v1/appointment/{uuid}/status تغییر وضعیت نوبت
GET /api/v1/appointments/doctor/{doctorUuid} نوبت‌های پزشک
GET /api/v1/appointments/user نوبت‌های کاربر

تنظیمات نوبت‌دهی:

Method Path توضیح
POST /api/v1/appointment-settings/weekly-schedule برنامه هفتگی
POST /api/v1/appointment-settings/date-override تغییر برنامه خاص
POST /api/v1/appointment-settings/holidays ثبت تعطیلات

💳 Payments

Method Path Auth توضیح
POST /api/v1/payment/appointment شروع پرداخت نوبت
GET/POST /api/v1/payment/callback/{gateway} Public callback درگاه
GET /api/v1/payment/{uuid} وضعیت پرداخت
POST /api/v1/subscription-payment پرداخت اشتراک

پارامتر {gateway}: mellat یا sep


🏦 Settlement & Wallet

Method Path Auth توضیح
GET /api/v1/wallet/balance موجودی کیف پول
GET /api/v1/wallet/transactions تاریخچه تراکنش‌ها
POST /api/v1/settlement درخواست تسویه
GET /api/v1/settlement لیست درخواست‌ها
GET /api/v1/settlement/{uuid} جزئیات درخواست
POST /api/v1/settlement/{uuid}/approve ADMIN تأیید تسویه
POST /api/v1/settlement/{uuid}/reject ADMIN رد تسویه

Ratings & Comments

Method Path Auth توضیح
POST /api/v1/rate امتیاز به پزشک (۱-۵)
GET /api/v1/rate/{doctorUuid} Public میانگین امتیاز پزشک
POST /api/v1/comment ثبت نظر
GET /api/v1/comments/{doctorUuid} Public نظرات تأییدشده
DELETE /api/v1/comment/{uuid} حذف نظر
POST /api/v1/like/{commentUuid} لایک/آنلایک نظر

🤝 Representations (نمایندگان)

Method Path Auth توضیح
POST /api/v1/representation ADMIN ایجاد نماینده
GET /api/v1/representation/{uuid} جزئیات نماینده
PATCH /api/v1/representation/{uuid} ویرایش
DELETE /api/v1/representation/{uuid} ADMIN حذف
GET /api/v1/representation/{uuid}/dashboard/monthly آمار ماهانه (شمسی)
GET /api/v1/representation/{uuid}/dashboard/yearly آمار سالانه

🔑 Secretaries (منشی‌ها)

Method Path Auth توضیح
POST /api/v1/secretary ایجاد منشی
GET /api/v1/secretary/{uuid} جزئیات
PATCH /api/v1/secretary/{uuid} ویرایش دسترسی‌ها
DELETE /api/v1/secretary/{uuid} حذف
GET /api/v1/secretaries/{doctorUuid} لیست منشی‌های پزشک

📱 SMS

Method Path Auth توضیح
GET /api/v1/admin/sms/templates ADMIN لیست قالب‌ها
POST /api/v1/sms/template ADMIN ایجاد قالب
PATCH /api/v1/sms/template/{uuid} ADMIN ویرایش قالب
POST /api/v1/sms/template/{uuid}/submit ADMIN ارسال برای تأیید
POST /api/v1/admin/sms/template/{uuid}/approve ADMIN تأیید قالب
POST /api/v1/admin/sms/template/{uuid}/reject ADMIN رد قالب
POST /api/v1/sms/send ADMIN ارسال پیامک مستقیم
POST /api/v1/sms/send-template ADMIN ارسال با قالب

🗂 Categories

Method Path Auth توضیح
GET /api/v1/categorys/{bundle} Public لیست دسته‌بندی (?state_id=X)
POST /api/v1/category ADMIN ایجاد دسته‌بندی
PATCH /api/v1/category/{id} ADMIN ویرایش
DELETE /api/v1/category/{id} ADMIN حذف

مقادیر {bundle}: state | city | specially_doctor | doctor_services | insurance_type | supplementary_insurance | tag


📝 Blog

Method Path Auth توضیح
GET /api/v1/blogs Public لیست مقالات منتشرشده
GET /api/v1/blog/{slug} Public مقاله با slug یا uuid
POST /api/v1/blog ADMIN ایجاد مقاله
PATCH /api/v1/blog/{uuid} ADMIN ویرایش
DELETE /api/v1/blog/{uuid} ADMIN حذف
POST /file/upload/clinic_pro/blog/field_image ADMIN آپلود تصویر مقاله

🖥 Admin API (/api/v1/admin/)

همه endpoint‌های ادمین نیاز به ROLE_ADMIN دارند:

Method Path توضیح
GET /api/v1/admin/users لیست کاربران (?search=)
GET /api/v1/admin/appointments لیست نوبت‌ها
GET /api/v1/admin/payments لیست پرداخت‌ها
GET /api/v1/admin/representations لیست نمایندگان (?city_id=)
GET /api/v1/admin/secretaries لیست منشی‌ها
GET /api/v1/admin/rates لیست امتیازها
GET /api/v1/admin/comments لیست نظرات
GET /api/v1/admin/sms/logs لاگ پیامک‌ها
GET /api/v1/admin/settlements لیست تسویه‌حساب‌ها
GET /api/v1/admin/sms/sample-templates قالب‌های نمونه پیامک
GET /api/v1/admin/dashboard/stats آمار کلی داشبورد
GET /api/v1/admin/dashboard/recent فعالیت‌های اخیر

❤️ Health

Method Path توضیح
GET /health بررسی وضعیت DB و Redis

۸. امنیت و احراز هویت

جریان احراز هویت

[Client] → POST /api/v1/user/login
           { mobile_number, password }

           ↓ PasswordAuthenticator

[Symfony] → Rate Limit check (IP)
          → findByMobile()
          → verify password_hash
          → check isStaff()

           ↓ موفق

[Response] → {
               access_token: "eyJ...",   // JWT — عمر: ۱ ساعت
               refresh_token: "8e75...", // در DB کش — عمر: ۳۰ روز
               expires_in: 3600
             }

JWT Token Payload

{
  "iat": 1781031215,
  "exp": 1781034815,
  "roles": ["ROLE_ADMIN", "ROLE_USER"],
  "username": "09120671713"
}

استفاده از Token

Authorization: Bearer eyJ...

Firewalls

dev              → ^/(_profiler|_wdt|assets|build)/ — no security
health           → ^/health$                        — no security
api_doc          → ^/api/doc                        — HTTP Basic Auth (InMemoryUser)
public_endpoints → مسیرهای عمومی API               — no security
payment_callback → callback درگاه پرداخت            — no security
api              → ^/(api|oauth)/                   — JWT authenticator

محافظت از Swagger UI

مسیر /api/doc از طریق یک firewall جداگانه با HTTP Basic Authentication محافظت می‌شود:

# config/packages/security.yaml
api_doc:
    pattern: ^/api/doc
    http_basic:
        realm: "ClinicPro API Documentation"
    provider: api_doc_provider

اطلاعات ورود از متغیرهای محیطی API_DOC_USERNAME و API_DOC_PASSWORD (bcrypt hash) خوانده می‌شود.

نقش‌ها و دسترسی‌ها

نقش دسترسی
ROLE_USER کاربر عادی — رزرو نوبت، پرداخت
ROLE_DOCTOR پزشک — مدیریت پروفایل و نوبت‌ها
ROLE_CLINIC مدیر کلینیک
ROLE_REPRESENTATION نماینده — آمار و کیف پول
ROLE_SECRETARY منشی — بر اساس permissions JSON
ROLE_ADMIN ادمین کامل — تمام endpoint‌ها

Rate Limiting

# config/packages/rate_limiter.yaml
login:      5 تلاش در دقیقه (per IP)
send_code:  3 درخواست در 5 دقیقه (per IP)

۹. پنل ادمین React

آدرس: https://clinic-pro.ddev.site/admin

نحوه کار

[Browser] GET /admin/**
    ↓
[Symfony] AdminController::index()
    ↓
[Twig] templates/admin/index.html.twig
    ↓ (HTML shell + Webpack assets)
[React] mounts در #admin-root
    ↓
[React Router] مسیریابی client-side

مسیرهای React (پیاده‌سازی‌شده)

مسیر صفحه توضیح
/admin/login LoginPage ورود با JWT
/admin/dashboard DashboardPage آمار واقعی از API
/admin/users UsersPage لیست کاربران
/admin/users/:uuid UserDetailPage جزئیات کاربر
/admin/doctors DoctorsPage لیست پزشکان
/admin/doctors/:uuid DoctorDetailPage جزئیات پزشک
/admin/clinics ClinicsPage لیست کلینیک‌ها
/admin/clinics/:uuid ClinicDetailPage جزئیات کلینیک
/admin/appointments AppointmentsPage لیست نوبت‌ها
/admin/appointments/:uuid AppointmentDetailPage جزئیات نوبت
/admin/payments PaymentsPage لیست پرداخت‌ها
/admin/payments/:uuid PaymentDetailPage جزئیات پرداخت
/admin/settlements SettlementsPage لیست تسویه‌حساب‌ها
/admin/representations RepresentationsPage لیست نمایندگان (فیلتر شهر)
/admin/representations/:uuid RepresentationDetailPage جزئیات نماینده
/admin/comments CommentsPage مدیریت نظرات
/admin/ratings RatingsPage لیست امتیازها
/admin/sms SmsPage مدیریت پیامک
/admin/categories CategoriesPage مدیریت دسته‌بندی‌ها
/admin/blogs BlogsPage لیست مقالات
/admin/blogs/new BlogFormPage ایجاد مقاله
/admin/blogs/:uuid/edit BlogFormPage ویرایش مقاله
/admin/secretaries SecretariesPage مدیریت منشی‌ها

Auth Guard

// اگر کاربر لاگین نباشد → redirect به /admin/login
// اگر کاربر لاگین باشد و /admin/login باز کند → redirect به /admin/dashboard

JWT ذخیره‌سازی

// Zustand + localStorage (با persist middleware)
// کلید: 'clinicpro-auth'
{
  token: string | null,         // access_token
  refreshToken: string | null,
  isAuthenticated: boolean
}

طراحی (Design System)

/* رنگ اصلی */
--color-primary-500: #8b5cf6;  /* بنفش */
--color-bg-sidebar: #0f172a;   /* dark navy */
--color-bg-body: #f1f5f9;      /* خاکستری روشن */

/* فونت */
font-family: "Vazirmatn", "Inter", sans-serif;
direction: rtl;

۱۰. Frontend Build

دستورات npm

ddev exec yarn dev        # build یک‌بار (dev)
ddev exec yarn watch      # build + watch
ddev exec yarn build      # build production (minified + hashed)

توجه: خطای lightningcss.linux-arm64-gnu.node در محیط ddev از پیش وجود دارد و JS/TS compilation را مسدود نمی‌کند.

فایل‌های خروجی

public/build/
├── runtime.js          ← webpack runtime
├── admin.js            ← React Admin bundle
├── admin.css           ← Tailwind CSS
├── app.js              ← Symfony main bundle
├── vendors-*.js        ← کتابخانه‌های مشترک
└── manifest.json       ← نقشه فایل‌ها

تنظیمات TypeScript (tsconfig.json)

{
  "compilerOptions": {
    "target": "ES2020",
    "jsx": "react-jsx",
    "strict": true,
    "baseUrl": ".",
    "paths": { "@/*": ["assets/admin/*"] }
  }
}

۱۱. دستورات Console

دستورات کاربردی

# لغو خودکار نوبت‌های منقضی‌شده
ddev exec php bin/console app:cancel-expired-appointments

# مشاهده همه route‌ها
ddev exec php bin/console debug:router | grep api

# پاک کردن کش
ddev exec php bin/console cache:clear

# اجرای migrations
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

# ساخت migration بعد از تغییر entity
ddev exec php bin/console doctrine:migrations:diff --no-interaction

# هش کردن پسورد
ddev exec php bin/console security:hash-password "رمز-جدید"

# تولید JWT key
ddev exec php bin/console lexik:jwt:generate-keypair

# بررسی صف پیام
ddev exec php bin/console messenger:consume async

# خروجی OpenAPI/Swagger
ddev exec php bin/console nelmio:apidoc:dump

۱۲. Migrations

تعداد: ۱۷ migration

# مشاهده وضعیت
ddev exec php bin/console doctrine:migrations:status

# اجرای migrations جدید
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

# ساخت migration جدید بعد از تغییر entity
ddev exec php bin/console doctrine:migrations:diff

۱۳. سرویس‌های خارجی

درگاه‌های پرداخت

درگاه Provider متغیرها
بانک ملت MellatGateway MELLAT_TERMINAL_ID, MELLAT_USERNAME, MELLAT_PASSWORD
سامان (SEP) SepGateway SEP_TERMINAL_ID

SMS Providers

ارائه‌دهنده Provider Class انتخاب
کاوه‌نگار KavehNegarProvider SMS_PROVIDER=kavenegar
رنگینه RanginehProvider SMS_PROVIDER=rangineh

Redis

  • صف پیام (Symfony Messenger)
  • کش کدهای OTP
  • کش Refresh Tokens
  • Rate limiter storage

JWT Keys

config/jwt/private.pem   ← کلید خصوصی (در .gitignore)
config/jwt/public.pem    ← کلید عمومی (در .gitignore)

خلاصه آماری

معیار تعداد
Domain modules ۱۵
PHP Controllers ۱۷
API Endpoints ۹۷ (مستندسازی‌شده در Swagger)
Doctrine Entities ۲۲
Database Tables ۳۰
Migrations ۱۷
React Pages ۲۳ مسیر (کاملاً پیاده‌سازی‌شده)
OpenAPI Tags ۱۱ گروه
npm packages ۳۴
composer packages ۲۸
S
Description
No description provided
Readme MIT
30 MiB
Languages
PHP 59.8%
TypeScript 37.5%
Twig 0.9%
CSS 0.9%
JavaScript 0.8%