Files
clinicpro/docs/DEPLOY.md
T

7.6 KiB

راهنمای دیپلوی ClinicPro (Coolify + Docker Compose)

این راهنما برای دیپلوی بک‌اند ClinicPro روی Coolify با استفاده از docker-compose.yml نوشته شده.

توجه: این docker-compose.yml فقط برای production است. محیط لوکال از compose.yaml خودِ ddev استفاده می‌کند — این دو را با هم اشتباه نگیر.


معماری استک

docker-compose.yml پنج سرویس بالا می‌آورد:

سرویس نقش نکته
app PHP-FPM + Nginx (وب) تنها سرویسی که RUN_INIT=1 دارد؛ مهاجرت DB و تولید کلید JWT را اجرا می‌کند. دامنه‌ها را به این سرویس (پورت 80) وصل کن.
worker-async مصرف‌کننده صف async (SMS و کارهای async) messenger:consume async
worker-scheduler زمان‌بند هر ۱ دقیقه نوبت‌های پرداخت‌نشده را منقضی می‌کند
mariadb پایگاه‌داده MariaDB 11.8 healthcheck دارد؛ بقیه منتظرش می‌مانند
redis صف Messenger + کش appendonly yes (ماندگار)

ولوم‌های ماندگار (داده‌ها در ری‌دیپلوی حفظ می‌شوند):

  • jwt_keys → کلیدهای JWT
  • uploads_public و uploads_var → فایل‌های آپلودی
  • mariadb_data → داده DB
  • redis_data → داده Redis

پیش‌نیازها

  • نمونه‌ی Coolify در حال اجرا با Traefik (پیش‌فرض Coolify).
  • ریپوی Git متصل به Coolify.
  • رکوردهای DNS برای دامنه‌ی API و همه‌ی دامنه‌های فرانت‌اند که به سرور اشاره کنند.

مرحله ۱ — ساخت منبع (Resource) در Coolify

  1. New Resource → Docker Compose (Build Pack: Docker Compose).
  2. ریپو و برنچ را انتخاب کن.
  3. فیلد Compose file را روی docker-compose.yml بگذار.
  4. networks: سفارشی تعریف نکن — شبکه را Coolify مدیریت می‌کند؛ شبکه‌ی سفارشی روتینگ Traefik را می‌شکند.

مرحله ۲ — دامنه‌ها

همه‌ی دامنه‌های سرو شونده را به سرویس app (پورت 80) اختصاص بده — هم دامنه‌ی API و هم همه‌ی دامنه‌های فرانت‌اند. Coolify لیست دامنه‌ی جدا‌شده با کاما را روی یک سرویس قبول می‌کند و TLS را خودش صادر می‌کند.

اجازه‌دادن CORS و host فرانت‌اندها از طریق متغیرهای CORS_ALLOW_ORIGIN / ALLOWED_FRONTEND_HOSTS کنترل می‌شود، نه دامنه‌ی Coolify.


مرحله ۳ — متغیرهای محیطی

از .env.coolify.example کپی کن و در تب Environment Variables منبع Coolify بگذار.

فقط متغیرهایی که در docker-compose.yml به‌صورت ${...} ارجاع شده‌اند لازم‌اند. DATABASE_URL / MESSENGER_TRANSPORT_DSN / REDIS_URL داخل خود compose از روی نام سرویس‌ها ساخته می‌شوند.

اسرار (الزامی — قبل از اولین دیپلوی)

APP_SECRET=        # php -r "echo bin2hex(random_bytes(32));"
JWT_PASSPHRASE=    # openssl rand -hex 32   (باید قبل از اولین استارت موجود باشد؛ کلید JWT با همین ساخته می‌شود)
DB_PASSWORD=       # رمز کاربر DB اپلیکیشن
DB_ROOT_PASSWORD=  # رمز root مریادی‌بی

⚠️ JWT_PASSPHRASE را بعد از اولین دیپلوی عوض نکن — کلید JWT یک‌بار با همین passphrase تولید و روی ولوم jwt_keys ماندگار می‌شود. تغییرش همه‌ی توکن‌ها را می‌شکند.

در Coolify می‌توانی به‌جای هاردکد از magic var استفاده کنی:

DB_PASSWORD=${SERVICE_PASSWORD_DB}
APP_SECRET=${SERVICE_HEX_APPSECRET}

دامنه‌ها و CORS

APP_BASE_URL=https://api.nobat724.com   # دامنه‌ی خودِ بک‌اند (برای callback پرداخت و URLهای مطلق)

ALLOWED_FRONTEND_HOSTS و CORS_ALLOW_ORIGIN از docker/frontend-domains.json تولید می‌شوند. برای اضافه/حذف دامنه‌ی شهر:

# آن فایل را ویرایش کن، سپس:
php docker/gen-cors-env.php      # روی سرور
# یا لوکال:
ddev exec php docker/gen-cors-env.php

خروجی را در Coolify جایگزین کن.

ریورس‌پراکسی

TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1

تا Symfony به هدرهای X-Forwarded-* ترافیک اعتماد کند.

api.ir (استعلام هویت — Shahkar / IbanMatch)

API_IR_BASE_URL=https://s.api.ir
API_IR_TOKEN=    # خالی => fail-closed (تأیید نماینده رد می‌شود)

چیزهایی که env لازم ندارند

  • کلیدهای SMS (kavenegar/rangineh) و درگاه پرداخت (mellat/sep) از DB ("تنظیمات سایت") خوانده می‌شوند، نه env.
  • REFRESH_TOKEN_TTL / OTP_TTL / MAX_FILE_SIZE_BYTES در خود compose ثابت‌اند.

مرحله ۴ — دیپلوی

روی Deploy بزن. در اولین استارت به‌صورت خودکار این‌ها اتفاق می‌افتد (entrypoint.sh + RUN_INIT=1 روی سرویس app):

  1. مالکیت var, public/uploads, config/jwt به www-data داده می‌شود.
  2. کلید JWT اگر روی ولوم نباشد ساخته می‌شود (--skip-if-exists).
  3. کش prod پاک و warmup می‌شود.
  4. مهاجرت‌های DB با --all-or-nothing اعمال می‌شوند (ترنزکشن).

ورکرها RUN_INIT=0 دارند تا مهاجرت/تولید کلید با هم تداخل نکنند.


مرحله ۵ — پس از اولین دیپلوی

ساخت اولین ادمین

# داخل کانتینر سرویس app
php bin/console app:create-admin

بررسی سلامت

  • healthcheck سرویس app: php fsockopen 127.0.0.1:80.
  • Swagger: https://<APP_BASE_URL>/api/doc
  • پنل ادمین: https://<APP_BASE_URL>/admin

دیپلوی‌های بعدی

push روی برنچ متصل (یا Deploy دستی). در هر ری‌دیپلوی:

  • ایمیج دوباره build می‌شود (vendor + اسمبل فرانت‌اند multi-stage).
  • مهاجرت‌های جدید روی استارت app اعمال می‌شوند.
  • ولوم‌ها حفظ می‌شوند (DB، آپلودها، کلیدهای JWT، Redis سالم می‌مانند).

عیب‌یابی

نشانه علت محتمل
ارورهای CORS در فرانت CORS_ALLOW_ORIGIN با دامنه نمی‌خواند؛ از gen-cors-env.php بازتولید کن
app بالا نمی‌آید، منتظر DB می‌ماند healthcheck mariadb رد نشده؛ لاگ mariadb را ببین
۴۰۱/توکن نامعتبر بعد از ری‌دیپلوی JWT_PASSPHRASE تغییر کرده یا ولوم jwt_keys پاک شده
IPها/HTTPS اشتباه پشت پراکسی TRUSTED_PROXIES ست نشده
مهاجرت اجرا نشد فقط app با RUN_INIT=1 اجرا می‌کند؛ مطمئن شو override نشده
تأیید نماینده رد می‌شود API_IR_TOKEN خالی است (fail-closed)