168 lines
7.6 KiB
Markdown
168 lines
7.6 KiB
Markdown
# راهنمای دیپلوی 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 از روی نام سرویسها ساخته میشوند.
|
|
|
|
### اسرار (الزامی — قبل از اولین دیپلوی)
|
|
|
|
```bash
|
|
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 استفاده کنی:
|
|
|
|
```bash
|
|
DB_PASSWORD=${SERVICE_PASSWORD_DB}
|
|
APP_SECRET=${SERVICE_HEX_APPSECRET}
|
|
```
|
|
|
|
### دامنهها و CORS
|
|
|
|
```bash
|
|
APP_BASE_URL=https://api.nobat724.com # دامنهی خودِ بکاند (برای callback پرداخت و URLهای مطلق)
|
|
```
|
|
|
|
`ALLOWED_FRONTEND_HOSTS` و `CORS_ALLOW_ORIGIN` از `docker/frontend-domains.json` **تولید** میشوند. برای اضافه/حذف دامنهی شهر:
|
|
|
|
```bash
|
|
# آن فایل را ویرایش کن، سپس:
|
|
php docker/gen-cors-env.php # روی سرور
|
|
# یا لوکال:
|
|
ddev exec php docker/gen-cors-env.php
|
|
```
|
|
|
|
خروجی را در Coolify جایگزین کن.
|
|
|
|
### ریورسپراکسی
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
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` دارند تا مهاجرت/تولید کلید با هم تداخل نکنند.
|
|
|
|
---
|
|
|
|
## مرحله ۵ — پس از اولین دیپلوی
|
|
|
|
### ساخت اولین ادمین
|
|
|
|
```bash
|
|
# داخل کانتینر سرویس 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) |
|