Files
clinicpro/docs/deploy/coolify.md
T

10 KiB
Raw Blame History

دیپلوی ClinicPro روی Coolify (Docker Compose)

این راهنما نحوهٔ دیپلوی بک‌اند ClinicPro (Symfony 7.4 + پنل React) را روی Coolify با Build Pack از نوع Docker Compose توضیح می‌دهد.

همهٔ فایل‌های دیپلوی در ریشهٔ ریپو هستند: Dockerfile، docker-compose.yml، پوشهٔ docker/ و .env.coolify.example. فایل‌های compose.yaml، compose.override.yaml و .ddev/ مخصوص محیط لوکال (ddev) هستند و در دیپلوی نقشی ندارند.


معماری دیپلوی

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

سرویس نقش نکته
app وب (PHP-FPM + Nginx) دامنه به این سرویس اختصاص می‌یابد (پورت ۸۰). RUN_INIT=1 → migration و تولید کلید JWT
worker-async مصرف صف async (ارسال SMS) RUN_INIT=0
worker-scheduler مصرف scheduler_default (انقضای نوبت‌های پرداخت‌نشده، هر دقیقه) RUN_INIT=0
mariadb دیتابیس MariaDB 11.8 healthcheck دارد؛ سرویس‌های اپ منتظر سالم‌شدن آن می‌مانند
redis Messenger transport + کش/OTP با appendonly persist می‌شود

هر سه سرویس اپ از یک image یکسان (همان Dockerfile) ساخته می‌شوند و فقط command/RUN_INIT آن‌ها متفاوت است.


مراحل دیپلوی

۱. ساخت Resource در Coolify

  1. New Resource → Public/Private Repository و ریپوی clinicpro را انتخاب کن.
  2. Build Pack را روی Docker Compose بگذار (نه Nixpacks).
  3. در تنظیمات:
    • Branch: main
    • Base Directory: / (ریشهٔ ریپو)
    • Docker Compose File: docker-compose.yml

۲. اختصاص دامنه

  • در سرویس app، همهٔ دامنه‌هایی که باید سرویس بگیرند را وارد کن — هم دامنهٔ API بک‌اند و هم همهٔ دامنه‌های فرانت‌اند (nobat724.com و همهٔ *-nobat.ir). Coolify لیست دامنهٔ کامادار را روی یک سرویس می‌پذیرد.
  • چون کانتینر روی پورت 80 گوش می‌دهد، نیازی به افزودن پورت به دامنه نیست.
  • Coolify به‌صورت خودکار برای هر دامنه TLS را از طریق Traefik (Let's Encrypt) صادر می‌کند.

دو مفهوم را اشتباه نگیر:

  • اختصاص دامنه در UI = Traefik برای آن دامنه روت و گواهی TLS می‌سازد.
  • CORS_ALLOW_ORIGIN / ALLOWED_FRONTEND_HOSTS = سیمفونی به آن origin اجازهٔ مرورگری/پرداخت می‌دهد.

یک دامنهٔ جدید معمولاً به هر دو نیاز دارد: هم در UI کولیفای اضافه شود، هم در docker/frontend-domains.json (و سپس بازتولید env). به بخش «چند دامنه فرانت‌اند» پایین مراجعه کن.

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

محتوای .env.coolify.example را در تب Environment Variables وارد کن. الزامی‌ها پیش از اولین دیپلوی:

متغیر توضیح
APP_SECRET php -r "echo bin2hex(random_bytes(32));"
JWT_PASSPHRASE openssl rand -hex 32باید قبل از اولین استارت موجود باشد (کلید JWT با آن ساخته می‌شود)
DB_PASSWORD پسورد یوزر دیتابیس
DB_ROOT_PASSWORD پسورد root مریادی‌بی
APP_BASE_URL دامنهٔ خودِ بک‌اند (مثلاً https://api.nobat724.com) — برای callback پرداخت و URL مطلق
ALLOWED_FRONTEND_HOSTS / CORS_ALLOW_ORIGIN دامنه‌های فرانت‌اند (چندتایی) — به بخش «چند دامنه» پایین مراجعه کن
TRUSTED_PROXIES پیش‌فرض رنج شبکهٔ داخلی داکر (در فایل نمونه هست)
API_IR_TOKEN توکن استعلام هویت (خالی = استعلام رد می‌شود)

DATABASE_URL، MESSENGER_TRANSPORT_DSN و REDIS_URL در خودِ compose از نام سرویس‌ها (mariadb/redis) ساخته می‌شوند؛ در UI تعریف نکن. کلیدهای SMS و درگاه پرداخت از DB («تنظیمات سایت») خوانده می‌شوند، نه از env. می‌توانی به‌جای hardcode از magic variableهای Coolify استفاده کنی، مثلاً DB_PASSWORD=${SERVICE_PASSWORD_DB}.

چند دامنه فرانت‌اند (مهم)

این بک‌اند به ده‌ها دامنهٔ شهری سرویس می‌دهد (nobat724.com و *-nobat.ir). دو متغیر باید همهٔ این دامنه‌ها را پوشش دهند:

  • ALLOWED_FRONTEND_HOSTS — لیست host با کاما؛ در validate کردن host بازگشتِ پرداخت استفاده می‌شود (تطبیق دقیق در PaymentController::isAllowedFrontend).
  • CORS_ALLOW_ORIGIN — یک regex واحد (nelmio با origin_regex: true) که فقط https و دقیقاً همان host‌ها را می‌پذیرد.

هر دو مقدار به‌صورت خودکار از فایل docker/frontend-domains.json تولید می‌شوند. برای افزودن یا حذف یک دامنه:

# ۱) یک رکورد به آرایهٔ "domains" در docker/frontend-domains.json اضافه/حذف کن، مثلاً:
#    { "domain": "newcity-nobat.ir", "label": "شهر جدید" }
# ۲) مقادیر جدید را تولید کن:
ddev exec php docker/gen-cors-env.php       # لوکال
#   یا روی سرور داخل کانتینر app:
php docker/gen-cors-env.php
# ۳) خروجی (CORS_ALLOW_ORIGIN و ALLOWED_FRONTEND_HOSTS) را در Coolify جایگزین کن و دوباره deploy کن

فیلد label فقط برای خوانایی است و در تولید env استفاده نمی‌شود؛ فقط domain مهم است.

payment_allowed_frontend_hosts در «تنظیمات سایت» (DB) بر مقدار env اولویت دارد؛ اگر آن را در DB ست کرده‌ای، آن مرجع است.

۴. Persistent Storage (حیاتی)

این volumeها در compose تعریف شده‌اند و Coolify آن‌ها را persist می‌کند:

Volume مسیر چرا مهم است
jwt_keys /app/config/jwt مهم‌ترین. کلید JWT بین دیپلوی‌ها باید ثابت بماند؛ در غیر این صورت هر دیپلوی همهٔ توکن‌ها را باطل و همهٔ کاربران را logout می‌کند
uploads_public /app/public/uploads فایل‌های آپلودی عمومی
uploads_var /app/var/uploads فایل‌های آپلودی خصوصی
mariadb_data /var/lib/mysql دادهٔ دیتابیس
redis_data /data پایداری Redis

مطمئن شو در Coolify این volumeها به‌صورت named volume (نه ephemeral) باقی می‌مانند.

۵. اولین دیپلوی و ساخت ادمین

  1. Deploy را بزن. سرویس app هنگام استارت به‌صورت خودکار:
    • کلید JWT می‌سازد (اگر روی volume نباشد)،
    • کش prod را warm می‌کند،
    • migrationها را با --all-or-nothing اجرا می‌کند.
  2. بعد از سالم‌شدن سرویس‌ها، از Terminal سرویس app در Coolify، ادمین اولیه را بساز:
    php bin/console app:create-admin
    

نکات عملیاتی

  • Worker‌ها: اگر worker-scheduler بالا نباشد، نوبت‌های رزرو ولی پرداخت‌نشده آزاد نمی‌شوند. اگر worker-async بالا نباشد، SMS ارسال نمی‌شود. هر دو در همین compose مدیریت می‌شوند و با restart: unless-stopped خودکار بازمی‌گردند.
  • Migration در دیپلوی‌های بعدی: فقط سرویس app (با RUN_INIT=1) migration اجرا می‌کند تا بین سرویس‌ها race رخ ندهد. هر دیپلوی، migrationهای جدید را اعمال می‌کند.
  • Health check: سرویس app با یک fsockopen روی پورت 80 سالم‌بودن خود را گزارش می‌دهد.
  • Trusted Proxies: مقدار TRUSTED_PROXIES به Symfony می‌گوید به هدرهای X-Forwarded-* از Traefik اعتماد کند تا https و IP واقعی کلاینت درست تشخیص داده شوند. در محیط لوکال (ddev) این متغیر تنظیم نمی‌شود و مقدار پیش‌فرض خالی است.
  • بدون شبکهٔ سفارشی: طبق توصیهٔ Coolify، در compose هیچ networks: سفارشی تعریف نشده تا روتینگ Traefik پایدار بماند.

رفع اشکال

نشانه علت محتمل راه‌حل
همهٔ کاربران بعد از دیپلوی logout می‌شوند volume jwt_keys persist نشده بررسی named volume بودن آن
خطای اتصال به دیتابیس هنگام استارت app قبل از سالم‌شدن mariadb بالا آمده depends_on: condition: service_healthy این را پوشش می‌دهد؛ صبر کن یا لاگ mariadb را ببین
پنل admin سفید/بدون استایل دارایی‌های public/build ساخته نشده بررسی موفقیت stage assets در لاگ build (yarn build)
تولید کلید JWT شکست می‌خورد JWT_PASSPHRASE تنظیم نشده متغیر را در Coolify ست کن و دوباره deploy کن
تصاویر/فایل‌های آپلودی بعد از ری‌دیپلوی ناپدید می‌شوند volumeهای uploads persist نشده بررسی uploads_public / uploads_var