Files
clinicpro/.env.example
T
hamedandClaude Opus 4.8 af125572c9 feat(doctor): complete IRIMC import feature — claim flow, least-privilege importer, unique import key
- Extract import logic from AdminApiController into DoctorImportService
  (thin DoctorImportController keeps the same route/contract)
- Surrogate users get marker role ROLE_UNCLAIMED_DOCTOR (+ backfill command
  app:doctors:backfill-surrogate-role) enabling safe deletion after claim
- DB-level UNIQUE (source, medical_system_code) + concurrent-import retry
- Doctor profile claim flow (climed.md): shahkar + PersonInfo identity checks
  via existing ApiIrService, Persian name normalization (PersianText),
  pessimistic-lock race protection, DoctorClaimRequest audit table
  (national code hashed, mobile masked), doctor_claim rate limiter,
  public claim-info endpoint, welcome SMS
- Admin support tools: manual transfer endpoint + paginated doctor-claims
  audit list + owner_status filter/fields in admin doctors list
- Least privilege: system owner now gets ROLE_IMPORTER (ROLE_ADMIN stripped),
  import endpoint accepts ADMIN|IMPORTER, isStaff includes IMPORTER
- Headless crawler login: X-Service-Token header bypasses captcha only
  (rate limit + password checks intact; empty env = no bypass)
- docs: doctor-claim.md (new), doctor-import.md, admin.md, doctor.md
- tests: DoctorImportTest (6), DoctorClaimTest (11), PersianTextTest (5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 11:39:15 +03:30

69 lines
2.7 KiB
Bash

###> symfony/framework-bundle ###
APP_ENV=prod
# IMPORTANT: generate a strong random secret for production:
# php -r "echo bin2hex(random_bytes(32));"
APP_SECRET=CHANGE_ME_STRONG_RANDOM_32_CHARS
APP_SHARE_DIR=var/share
###< symfony/framework-bundle ###
###> symfony/routing ###
DEFAULT_URI=https://your-domain.com
###< symfony/routing ###
###> doctrine/doctrine-bundle ###
DATABASE_URL="mysql://user:CHANGE_ME@db:3306/clinic_pro?serverVersion=8.0&charset=utf8mb4"
###< doctrine/doctrine-bundle ###
###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
# Generate with: openssl rand -hex 32
JWT_PASSPHRASE=CHANGE_ME_STRONG_PASSPHRASE
###< lexik/jwt-authentication-bundle ###
###> nelmio/cors-bundle ###
# رجکسِ CORS در PHP از روی ALLOWED_FRONTEND_HOSTS ساخته می‌شود (CorsRegexEnvProcessor).
# CORS_ALLOW_ORIGIN دیگر استفاده نمی‌شود — ست نکن.
###< nelmio/cors-bundle ###
###> symfony/messenger ###
MESSENGER_TRANSPORT_DSN=redis://redis:6379/messages
###< symfony/messenger ###
###> Redis ###
REDIS_URL=redis://redis:6379
###< Redis ###
###> Auth ###
REFRESH_TOKEN_TTL=2592000
OTP_TTL=1200
###< Auth ###
###> SMS ###
# کلید API کاوه‌نگار فقط از همین env خوانده می‌شود (نه DB).
KAVENEGAR_API_KEY=
###< SMS ###
###> api.ir Identity Inquiry (Shahkar + IbanMatch) ###
# استعلام هویت نماینده: تطبیق کد ملی با موبایل (شاهکار) و تطبیق شبا با کد ملی.
# توکن خالی = سرویس fail-closed (استعلام انجام نمی‌شود و تأیید رد می‌شود).
API_IR_BASE_URL=https://s.api.ir
API_IR_TOKEN=CHANGE_ME
###< api.ir Identity Inquiry ###
###> File Upload ###
MAX_FILE_SIZE_BYTES=5242880
UPLOAD_DIR=var/uploads
###< File Upload ###
###> Payment ###
# میزبان‌های مجاز برای redirect بازگشت پرداخت (کاما-جدا): پنل + دامنه‌های عمومی نوبت‌دهی.
ALLOWED_FRONTEND_HOSTS=clinic-pro.ir,yasuj-nobat.ir,yazd-nobat.ir
# باید دقیقاً دامنهٔ ثبت‌شده نزد ملت/شاپرک باشد (callBackUrl از این ساخته می‌شود؛ IP مجاز نیست — کد پاسخ 62).
APP_BASE_URL=https://clinic-pro.ir
# کلیدهای درگاه (mellat/sep) از پنل «تنظیمات سایت» (DB) خوانده می‌شوند؛ env فقط fallback اختیاری است.
###< Payment ###
# لاگین سرویسی کرالر: مقدار غیرخالی، هدر X-Service-Token را برای دورزدن کپچای لاگین فعال می‌کند (فقط کپچا)
CRAWLER_SERVICE_TOKEN=