Files
clinicpro/.claude/prompt/deploy-liara-php.md
T

281 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# دیپلوی ClinicPro (Symfony) روی لیارا با پلتفرم PHP (بدون داکر)
## پروژه
`clinicpro` (Backend Symfony 7.4 + پنل ادمین React 19).
راهنما + تسک پیاده‌سازی برای دیپلوی روی **پلتفرم PHP لیارا** (buildpack بومی؛ لیارا خودش از سورس با `composer install` build می‌کند — بدون Dockerfile).
> منابع: مستندات لیارا — paas/php/how-tos (set-envs، use-disk، set-logs، customize-php-ini، customize-htaccess، use-queues، set-http-security-headers، use-hooks، set-cron-job) و paas/laravel (quick-start، set-envs، set-cron-job، choose-version، manage-logs).
---
## زمینه
تا الان دو مسیر دیپلوی برای این پروژه آماده شده:
- **داکر/Coolify:** `Dockerfile` + `docker-compose.yml`
- **داکر/لیارا:** `liara.json` (platform docker) + `liara-compose.yaml`
این پرامپت مسیر **سومی** را می‌سازد: **پلتفرم PHP لیارا** (نوع برنامه = `php` هنگام «ساخت برنامه‌ی جدید»). اینجا داکری در کار نیست؛ لیارا کد را می‌گیرد، `composer install` می‌زند و روی Apache + PHP-FPM سرو می‌کند.
تفاوت‌های کلیدی پلتفرم PHP که معماری را تعیین می‌کنند (از مستندات):
- **Hookها فایل‌های شل‌اند، نه JSON:** `liara_pre_build.sh` (قبل build، **بدون** دسترسی به env، مناسب apt) و `liara_pre_start.sh` (قبل start، **با** env، مناسب migration).
- **Worker با `supervisor.conf`:** فایل `supervisor.conf` در ریشه؛ هر `[program]` یک دستور دلخواه (`php bin/console messenger:consume ...`) را زنده نگه می‌دارد. `$ROOT` = ریشهٔ اپ، `user=www-data`.
- **php.ini با `liara_php.ini`** در ریشه.
- **سرو با Apache + `.htaccess`؛ documentRoot باید `public/` شود.**
- **دیسک‌ها** در `liara.json` با مسیر **absolute**.
- **نسخهٔ PHP:** 7.2 تا 8.4 (پیش‌فرض 7.4) — پروژه `php: >=8.2` می‌خواهد، پس باید 8.2+ انتخاب شود.
---
## مشکل / هدف
اجرای کامل Symfony روی پلتفرم PHP لیارا، شامل:
1. سرو front-controller سیمفونی از `public/` روی Apache.
2. اجرای migration و تولید کلید JWT در زمان استقرار.
3. اجرای دو مصرف‌کنندهٔ Messenger (`async` و `scheduler_default`) به‌صورت پایدار.
4. آپلودِ asset‌های build‌شدهٔ React (که `composer install` آن‌ها را نمی‌سازد).
5. اتصال به MariaDB و Redis مدیریت‌شدهٔ لیارا.
---
## فایل‌های مرتبط
| فایل | نقش | وضعیت |
|------|-----|------|
| `liara.json` | پیکربندی دیپلوی | **بازنویسی برای platform php** (فعلاً platform docker است) |
| `public/.htaccess` | rewrite سیمفونی برای Apache | **وجود ندارد — ساخته شود** (apache-pack نصب نیست) |
| `liara_pre_start.sh` | hook قبل start (migration/JWT) | **جدید** |
| `supervisor.conf` | اجرای دو worker | **جدید** |
| `liara_php.ini` | تنظیم php.ini | **جدید** |
| `.liaraignore` | کنترل فایل‌های آپلودی | **جدید** (تا `public/build` آپلود شود) |
| `composer.json` | اسکریپت‌های نصب | بدون تغییر (با احتیاط؛ پایین) |
> ⚠️ **تداخل liara.json:** الان `liara.json` برای platform docker است. پلتفرم PHP به محتوای متفاوتی نیاز دارد. فقط **یک** مسیر را همزمان نگه دار. اگر PHP را انتخاب کردی، محتوای docker را با نسخهٔ PHP زیر جایگزین کن (یا برنچ جدا).
---
## وضعیت فعلی (کد واقعی)
`composer.json` هنگام نصب این اسکریپت‌ها را اجرا می‌کند (روی build لیارا هم اجرا می‌شوند):
```json
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": ["@auto-scripts"]
```
`.gitignore` این‌ها را نادیده می‌گیرد (پس به‌صورت پیش‌فرض آپلود **نمی‌شوند**):
```
/public/build/ # ← خروجی yarn build؛ باید آپلود شود
/config/jwt/*.pem # ← روی دیسک ساخته می‌شود
/var/
.env
```
`public/.htaccess` وجود ندارد؛ extensionهای موردنیاز Symfony: `pdo_mysql`, `intl`, `opcache`, و برای Messenger روی Redis: `redis` (phpredis).
---
## وظایف
### ۱. ساخت `liara.json` برای پلتفرم PHP
محتوای فعلی (platform docker) را با این جایگزین کن:
```json
{
"app": "clinicpro-api",
"platform": "php",
"port": 80,
"php": {
"version": "8.2"
},
"documentRoot": "public",
"disks": [
{ "name": "jwt", "mountTo": "/var/www/config/jwt" },
{ "name": "uploads", "mountTo": "/var/www/public/uploads" },
{ "name": "var-uploads", "mountTo": "/var/www/var/uploads" }
]
}
```
نکات:
- `documentRoot: "public"` تا Apache front-controller سیمفونی را سرو کند.
- مسیر mount دیسک‌ها **absolute** است؛ ریشهٔ اپ روی پلتفرم PHP معمولاً `/var/www` است — اگر در کنسول/لاگ مسیر دیگری دیدی، اصلاح کن.
- کلید `php.version` و `documentRoot` را اگر کنسول نپذیرفت، از تنظیمات کنسول (Platform Settings) ست کن — مستند نسخه‌گزینی را از طریق کنسول هم ممکن می‌داند.
- سه دیسک `jwt` / `uploads` / `var-uploads` را پیش از دیپلوی در کنسول بساز.
### ۲. ساخت `public/.htaccess` (rewrite سیمفونی)
چون `symfony/apache-pack` نصب نیست، یا نصبش کن:
```bash
ddev exec composer require symfony/apache-pack
```
(این به‌صورت تعاملی `public/.htaccess` استاندارد سیمفونی را می‌سازد) — یا فایل را دستی بساز:
```apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
RewriteRule .* - [E=BASE:%1]
RewriteCond %{HTTP:Authorization} .+
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
RewriteRule ^ %{ENV:BASE}/index.php [L]
</IfModule>
<IfModule !mod_rewrite.c>
<IfModule mod_alias.c>
RedirectMatch 307 ^/$ /index.php/
</IfModule>
</IfModule>
```
### ۳. ساخت `supervisor.conf` (دو worker مسنجر)
روی پلتفرم PHP، خود لیارا Apache/PHP-FPM را اجرا می‌کند؛ `supervisor.conf` **فقط** workerها را اضافه می‌کند (نه وب). در ریشه بساز:
```ini
[program:worker-async]
process_name=%(program_name)s_%(process_num)02d
command=cd $ROOT && php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=1
user=www-data
redirect_stderr=true
stdout_logfile=/tmp/worker-async.log
[program:worker-scheduler]
process_name=%(program_name)s_%(process_num)02d
command=cd $ROOT && php bin/console messenger:consume scheduler_default --time-limit=3600 -v
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=1
user=www-data
redirect_stderr=true
stdout_logfile=/tmp/worker-scheduler.log
```
> جایگزینِ cron: اگر نخواهی scheduler به‌صورت worker بماند، می‌توانی همان کار را با Cron Job کنسول (`* * * * *` → دستوری که یک پیام scheduler را مصرف کند) انجام دهی. ولی worker async (SMS) حتماً باید در `supervisor.conf` بماند.
### ۴. ساخت `liara_pre_start.sh` (migration + JWT + cache)
در ریشه، با env در دسترس:
```bash
#!/bin/sh
set -e
echo "pre-start: waiting for database..."
i=0
until php bin/console doctrine:query:sql "SELECT 1" >/dev/null 2>&1; do
i=$((i + 1))
[ "$i" -ge 30 ] && { echo "DB not reachable after 60s" >&2; exit 1; }
sleep 2
done
# کلید JWT روی دیسک ماندگار jwt
php bin/console lexik:jwt:generate-keypair --skip-if-exists --no-interaction
php bin/console cache:clear --no-warmup
php bin/console cache:warmup
php bin/console doctrine:migrations:migrate --all-or-nothing --no-interaction
```
فایل را اجرایی کن: `chmod +x liara_pre_start.sh`.
### ۵. ساخت `liara_php.ini`
برای پشتیبانی از آپلود (سقف اپ ۵MB ولی هدر/چندبخشی بزرگ‌تر) و زمان اجرا:
```ini
memory_limit = 256M
upload_max_filesize = 16M
post_max_size = 24M
max_execution_time = 120
file_uploads = On
```
### ۶. ساخت `.liaraignore` تا asset‌های build آپلود شوند
`.gitignore` پوشهٔ `/public/build/` را نادیده می‌گیرد و لیارا به‌صورت پیش‌فرض از gitignore پیروی می‌کند → asset‌ها آپلود نمی‌شوند. با ساخت `.liaraignore` کنترل را به‌دست بگیر (فقط واقعاً غیرلازم‌ها را کنار بگذار، `public/build` را **نگه دار**):
```
.git
.ddev
.claude
.github
.vscode
node_modules
tests
var/cache
var/log
docs
graphify-out
*.sql
*.sql.gz
.DS_Store
```
پیش از هر دیپلوی، asset‌ها را **محلی** build کن (لیارا `yarn build` نمی‌زند):
```bash
ddev exec yarn install --frozen-lockfile
ddev exec yarn build # خروجی در public/build
```
### ۷. دیتابیس و Redis مدیریت‌شده + متغیرهای محیطی
مثل مسیر داکر:
1. در کنسول، **MariaDB 11.8** و **Redis** بساز، شبکهٔ خصوصی فعال، برنامه را به همان شبکه وصل کن.
2. envها را با `liara env set ... --app clinicpro-api` یا تب Environment ست کن (مرجع کامل: `.env.liara.example`). حداقل:
- `APP_ENV=prod`, `APP_DEBUG=0`
- `APP_SECRET`, `JWT_PASSPHRASE` (ثابت)
- `DATABASE_URL` (هاست خصوصی MariaDB، `serverVersion=mariadb-11.8.0`)
- `REDIS_URL`, `MESSENGER_TRANSPORT_DSN` (هاست خصوصی Redis)
- `JWT_SECRET_KEY`/`JWT_PUBLIC_KEY` (مسیر `config/jwt/*.pem`)
- `APP_BASE_URL`, `DEFAULT_URI`, `TRUSTED_PROXIES`
- `ALLOWED_FRONTEND_HOSTS`, `CORS_ALLOW_ORIGIN` (خروجی `php docker/gen-cors-env.php`)
- `API_IR_BASE_URL`, `API_IR_TOKEN`
3. **مهم:** `APP_ENV=prod` باید موقع **build** هم موجود باشد، چون `composer install` اسکریپت `cache:clear` را اجرا می‌کند.
### ۸. استقرار و تأیید
```bash
liara deploy # از ریشهٔ clinicpro، با liara.json platform php
```
سپس:
- `GET https://<domain>/health` → 200
- لاگ: در کنسول/`liara logs` هر دو worker (`worker-async`, `worker-scheduler`) بالا باشند
- ورود `/admin` و تست یک endpoint
---
## نکات مهم
- **ext-redis ریسک اصلی است:** اگر پلتفرم PHP لیارا افزونهٔ `redis` (phpredis) را نداشته باشد، `REDIS_URL` و `MESSENGER_TRANSPORT_DSN: redis://...` کار نمی‌کنند. اول در محیط لیارا چک کن (`php -m | grep redis`). اگر نبود:
- برای Messenger به transport مبتنی بر **Doctrine** سوییچ کن (`MESSENGER_TRANSPORT_DSN=doctrine://default`) و جدول مسنجر را با migration بساز،
- برای cache هم به adapter فایل‌سیستم/Doctrine برگرد.
- **اسکریپت‌های composer در build:** `cache:clear` در prod بدون DB معمولاً سالم است؛ اگر build به خاطر اسکریپت‌ها شکست، می‌توان نصب را بدون اسکریپت کرد و همه‌چیز را به `liara_pre_start.sh` سپرد (هماهنگ با کاری که `Dockerfile` با `--no-scripts` می‌کند).
- **ماندگاری کلید JWT:** دیسک `jwt` حتماً mount شود؛ وگرنه هر دیپلوی کلید نو می‌سازد و توکن‌ها باطل می‌شوند.
- **asset‌ها فراموش نشوند:** اگر `public/build` آپلود نشود، پنل ادمین لود نمی‌شود — قبل دیپلوی `yarn build` و `.liaraignore` درست.
- **documentRoot:** اگر روی `public` ست نشود، Apache فایل‌های ریشه را سرو می‌کند و اپ ۴۰۴/۴۰۳ می‌دهد.
- **هماهنگی نسخهٔ DB:** `serverVersion` در `DATABASE_URL` با نسخهٔ MariaDB مدیریت‌شده یکی باشد (11.8).
- این تغییر فقط زیرساخت دیپلوی است و هیچ route/endpoint را عوض نمی‌کند → نیازی به‌روزرسانی `docs/api/*` نیست.
- **یک مسیر را انتخاب کن:** Docker (`liara.json` فعلی + `liara-compose.yaml`) یا PHP platform (این پرامپت). همزمان هر دو `liara.json` نگه‌داشتنی نیست.
```