diff --git a/.claude/prompt/deploy-liara-docker.md b/.claude/prompt/deploy-liara-docker.md new file mode 100644 index 00000000..b513ac67 --- /dev/null +++ b/.claude/prompt/deploy-liara-docker.md @@ -0,0 +1,236 @@ +# دیپلوی ClinicPro (Symfony) روی لیارا با Docker + +## پروژه + +`clinicpro` (Backend Symfony 7.4 + پنل ادمین React، image داکر چندمرحله‌ای). + +این یک **راهنمای دیپلوی + تسک پیاده‌سازی** است. هدف: انتقال استک فعلی (که برای **Coolify / docker-compose** ساخته شده) به **لیارا**، جایی که docker-compose پشتیبانی نمی‌شود. + +> منبع: مستندات لیارا — quick-start، deploy-docker-compose، set-envs، use-disk، configure-supercronic. + +--- + +## زمینه + +پروژه همین الان یک استک داکر کامل دارد که برای Coolify تنظیم شده: + +- `Dockerfile` — multi-stage (vendor → assets → runtime PHP-FPM + Nginx via Supervisor)، non-root `www-data`، `EXPOSE 8080`، healthcheck روی `/health`. +- `docker-compose.yml` — **۳ سرویس**: `app` (وب) + `worker-async` + `worker-scheduler`؛ MariaDB و Redis سرویس‌های جدا (Coolify Database Resources). +- `docker/supervisord.conf` — فعلاً **فقط** `php-fpm` و `nginx` را اجرا می‌کند (workerها داخلش نیستند؛ در compose سرویس مجزا بودند). +- `docker/entrypoint.sh` — وقتی `RUN_INIT=1`: صبر برای DB → تولید کلید JWT → `cache:clear/warmup` → `doctrine:migrations:migrate`. +- `.env.coolify.example` — مرجع کامل متغیرهای محیطی. +- دیسک‌های ماندگار (volume) فعلی: `jwt_keys → /app/config/jwt`، `uploads_public → /app/public/uploads`، `uploads_var → /app/var/uploads`. + +--- + +## مشکل / هدف + +**لیارا از docker-compose مستقیم پشتیبانی نمی‌کند** («لیارا، به صورت مستقیم از Docker Compose پشتیبانی نمی‌کند»). پس ساختار ۳-سرویسی + DB/Redis جداگانه نمی‌تواند عیناً منتقل شود. باید بازچینش شود: + +| جزء فعلی (compose) | معادل در لیارا | +|---|---| +| سرویس `app` (PHP-FPM+Nginx) | یک **برنامه داکر** روی لیارا (همین Dockerfile، پورت `8080`) | +| `worker-async` + `worker-scheduler` | داخل **همان** image با Supervisor اجرا شوند (توصیه‌شده، تک‌برنامه) — یا برنامه‌های داکر مجزا | +| سرویس MariaDB جدا | **دیتابیس مدیریت‌شدهٔ MariaDB لیارا** (شبکهٔ خصوصی) | +| سرویس Redis جدا | **Redis مدیریت‌شدهٔ لیارا** (شبکهٔ خصوصی) | +| volumeها (`jwt`, `uploads`) | **دیسک‌های لیارا** که در `liara.json` mount می‌شوند | +| env tab کولیفای | `liara env set` / کنسول لیارا | + +**تصمیم معماری (پیش‌فرض توصیه‌شده): تک‌برنامهٔ داکر.** هر دو worker را به `supervisord.conf` اضافه کن تا در همان کانتینر کنار php-fpm/nginx اجرا شوند. ارزان‌ترین و نزدیک‌ترین گزینه به image فعلی. (دلیل اینکه supercronic مناسب نیست: هر دو worker پروسهٔ **بلندمدت** `messenger:consume` هستند نه job دوره‌ای cron — جای آن‌ها Supervisor است نه crontab. supercronic فقط اگر scheduler را به یک دستور one-shot تبدیل کنی به کار می‌آید؛ در «گزینه‌های جایگزین» پایین آمده.) + +--- + +## فایل‌های مرتبط + +| فایل | نقش | تغییر | +|------|-----|------| +| `Dockerfile` | image رانتایم | احتمالاً بدون تغییر (سازگار با لیارا است) | +| `docker/supervisord.conf` | پروسه‌منیجر کانتینر | **افزودن دو program برای workerها** | +| `docker/entrypoint.sh` | init و migration | بازبینی wait-for-DB (بدون compose `depends_on`) | +| `liara.json` | پیکربندی دیپلوی لیارا | **فایل جدید — ساخته شود** | +| `.env.liara.example` | مرجع env لیارا | **فایل جدید — اختیاری ولی توصیه‌شده** | +| `.dockerignore` | استثناهای build | بدون تغییر (`.env` عمداً نگه داشته می‌شود) | + +--- + +## وضعیت فعلی (کد واقعی) + +`docker/supervisord.conf` فقط دو program دارد: + +```ini +[program:php-fpm] +command=php-fpm -F +autorestart=true +priority=10 +... + +[program:nginx] +command=nginx -g 'daemon off;' +autorestart=true +priority=20 +... +``` + +`docker-compose.yml` workerها را اینطور اجرا می‌کند (همین دستورها باید به supervisord منتقل شوند): + +```yaml +worker-async: + command: php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v +worker-scheduler: + command: php bin/console messenger:consume scheduler_default --time-limit=3600 -v +``` + +--- + +## وظایف + +### ۱. افزودن workerها به Supervisor + +به انتهای `docker/supervisord.conf` این دو program را اضافه کن. مهم: فقط **یک** پروسه باید init/migration بزند؛ این کار را entrypoint با `RUN_INIT` کنترل می‌کند و چون اینجا تک‌کانتینر است مشکلی نیست (entrypoint قبل از `exec supervisord` یک‌بار اجرا می‌شود، نه per-program). + +```ini +[program:worker-async] +command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v +autorestart=true +priority=30 +# مصرف‌کنندهٔ messenger با SIGTERM پیام در حال پردازش را تمام و سپس خارج می‌شود. +stopsignal=TERM +stopwaitsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:worker-scheduler] +command=php bin/console messenger:consume scheduler_default --time-limit=3600 -v +autorestart=true +priority=30 +stopsignal=TERM +stopwaitsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +``` + +> `--time-limit=3600` باعث می‌شود پروسه هر ساعت سالم خارج شود و Supervisor با `autorestart=true` دوباره بالا بیاورد (جلوگیری از نشت حافظه / اتصال‌های بیات). دقیقاً رفتار compose. + +### ۲. ساخت `liara.json` در ریشهٔ پروژه + +```json +{ + "app": "clinicpro-api", + "platform": "docker", + "port": 8080, + "healthCheck": { + "command": "/usr/local/bin/healthcheck.sh", + "interval": 30, + "timeout": 5, + "initialDelaySeconds": 60 + }, + "disks": [ + { "name": "jwt", "mountTo": "/app/config/jwt" }, + { "name": "uploads", "mountTo": "/app/public/uploads" }, + { "name": "var-uploads", "mountTo": "/app/var/uploads" } + ] +} +``` + +نکات: +- `platform: "docker"` → لیارا از `Dockerfile` ریشه build می‌کند. +- `port: 8080` چون nginx داخل image روی 8080 (non-root) گوش می‌دهد و `EXPOSE 8080` ست شده. +- مسیر mount دیسک‌ها **absolute** و با احتساب `WORKDIR /app` نوشته شده (طبق مستند use-disk). +- پیش از دیپلوی، در کنسول لیارا این سه دیسک را با همین نام‌ها بساز: `jwt`، `uploads`، `var-uploads`. +- اگر فیلد `healthCheck` در نسخهٔ liara.json پشتیبانی نشد، حذفش کن و healthcheck را از کنسول ست کن؛ خود image هم `HEALTHCHECK` داخلی دارد. + +### ۳. ساخت دیتابیس و Redis مدیریت‌شدهٔ لیارا + +در کنسول لیارا: +1. یک دیتابیس **MariaDB 11.8** بساز (هماهنگ با `serverVersion=mariadb-11.8.0`). شبکهٔ خصوصی را فعال کن. +2. یک **Redis** بساز، شبکهٔ خصوصی فعال. +3. هاست داخلی هرکدام را از صفحهٔ سرویس بردار (روی شبکهٔ خصوصی، مثلاً `clinicpro-db` / `clinicpro-redis`). +4. برنامهٔ داکر و دو سرویس را روی **همان شبکهٔ خصوصی** قرار بده. + +### ۴. ست‌کردن متغیرهای محیطی روی لیارا + +با CLI (`liara env set KEY=VALUE --app clinicpro-api`) یا تب Environment در کنسول. حداقل متغیرهای موردنیاز (از `.env.coolify.example` گرفته شده، فقط هاست‌ها به سرویس‌های لیارا اشاره می‌کنند): + +```bash +APP_ENV=prod +APP_DEBUG=0 +APP_SECRET= +JWT_PASSPHRASE= + +# هاست داخلی = نام سرویس دیتابیس لیارا روی شبکهٔ خصوصی +DATABASE_URL=mysql://:@:3306/?serverVersion=mariadb-11.8.0&charset=utf8mb4 +REDIS_URL=redis://:6379 +MESSENGER_TRANSPORT_DSN=redis://:6379/messages + +JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem +JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem + +APP_BASE_URL=https:// +DEFAULT_URI=https:// +TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1 + +ALLOWED_FRONTEND_HOSTS=<از docker/gen-cors-env.php> +CORS_ALLOW_ORIGIN=<از docker/gen-cors-env.php> + +API_IR_BASE_URL=https://s.api.ir +API_IR_TOKEN= + +REFRESH_TOKEN_TTL=2592000 +OTP_TTL=1200 +MAX_FILE_SIZE_BYTES=5242880 +UPLOAD_DIR=var/uploads +``` + +نکات: +- در compose این مقادیر در `x-app-env` بودند؛ روی لیارا چون env-tab وجود دارد همه باید اینجا ست شوند. +- `ALLOWED_FRONTEND_HOSTS` و `CORS_ALLOW_ORIGIN` را با اجرای `php docker/gen-cors-env.php` تولید کن (از `docker/frontend-domains.json` می‌خواند). +- نیازی به ست‌کردن `RUN_INIT` نیست؛ entrypoint پیش‌فرض `1` می‌گیرد و چون تک‌برنامه است درست است. +- کلیدهای SMS و درگاه پرداخت از DB خوانده می‌شوند، نه env. + +### ۵. اولین دیپلوی + +```bash +# نصب CLI (یک‌بار) +npm i -g @liara/cli +liara login + +# از ریشهٔ clinicpro: +liara deploy --app clinicpro-api --platform docker --port 8080 +``` + +`liara.json` بیشتر این فلگ‌ها را پوشش می‌دهد، پس `liara deploy` خالی هم کافی است. در اولین بالا آمدن، `entrypoint.sh` صبر می‌کند تا DB جواب دهد، کلید JWT می‌سازد (روی دیسک `jwt` ماندگار)، و migrationها را می‌زند. + +### ۶. تأیید سلامت + +- لاگ‌ها: `liara logs --app clinicpro-api` — باید php-fpm، nginx، و هر دو worker بالا باشند. +- `GET https:///health` → `200`. +- ورود ادمین در `/admin` و تست یک endpoint تا اتصال DB/Redis تأیید شود. + +--- + +## گزینه‌های جایگزین (در صورت نیاز، نه پیش‌فرض) + +**الف) workerها به‌صورت برنامهٔ داکر مجزا** (به‌جای افزودن به Supervisor): همین repo را دوبار دیگر با `liara.json` متفاوت دیپلوی کن که `command` را override کند تا فقط `messenger:consume ...` اجرا شود و `RUN_INIT=0` و **بدون** پورت/healthcheck وب. گران‌تر (سه برنامهٔ داکر) ولی ایزوله‌تر. + +**ب) supercronic برای scheduler:** اگر بخواهی scheduler را به‌جای مصرف‌کنندهٔ بلندمدت، به cron تبدیل کنی: +- در `Dockerfile`: ‏`COPY --from=liaracloud/supercronic:v0.1.11 /usr/local/bin/supercronic /usr/local/bin/supercronic` +- فایل `crontab` در ریشه: `* * * * * cd /app && php bin/console messenger:consume scheduler_default --limit=1` +- یک `[program:supercronic]` در supervisord با `command=supercronic /app/crontab`. +- توجه: این الگو فقط برای کارهای **دوره‌ای** مناسب است؛ `worker-async` (SMS) باید مصرف‌کنندهٔ بلندمدت بماند. مگر نیاز خاص، **گزینهٔ پیش‌فرض (Supervisor) را نگه دار.** + +--- + +## نکات مهم + +- **بدون docker-compose:** هیچ `depends_on`/`service_healthy` روی لیارا نیست؛ منطق wait-for-DB در `entrypoint.sh` (تا ~۶۰s) دقیقاً برای همین لازم است — دست نخورد. +- **دیسک نه volume:** اگر روزی `VOLUME` در Dockerfile اضافه شد، طبق مستند لیارا حذفش کن و از دیسک لیارا استفاده کن. الان Dockerfile دستور `VOLUME` ندارد — خوب است. +- **ماندگاری کلید JWT:** دیسک `jwt` حتماً mount شود، وگرنه هر دیپلوی کلید نو می‌سازد و همهٔ توکن‌های صادرشده باطل می‌شوند (`--skip-if-exists` فقط وقتی دیسک ماندگار باشد کار می‌کند). +- **`.env` عمداً در image است** (به‌خاطر `Dotenv::bootEnv`)؛ مقادیر واقعی از env لیارا می‌آیند و Dotenv متغیر ازقبل‌ست‌شده را بازنویسی نمی‌کند (`clear_env=no` در `docker/php/zz-pool.conf`). این رفتار را خراب نکن. +- **هماهنگی نسخهٔ DB:** `serverVersion` در `DATABASE_URL` باید با نسخهٔ دیتابیس مدیریت‌شدهٔ لیارا یکی باشد (11.8). +- **پورت تک‌وب:** لیارا فقط یک پورت HTTP بیرونی می‌دهد (8080)؛ MariaDB/Redis فقط روی شبکهٔ خصوصی در دسترس‌اند — درست با معماری ما می‌خواند. +- **CORS چنددامنه:** بک‌اند برای ده‌ها دامنهٔ فرانت سرویس می‌دهد؛ بعد از تغییر `docker/frontend-domains.json` دوباره `gen-cors-env.php` بزن و env را به‌روزرسانی کن. +- این تغییر فقط زیرساخت دیپلوی است و هیچ endpoint/route را عوض نمی‌کند، پس به‌روزرسانی `docs/api/*` لازم نیست. diff --git a/.env.liara.example b/.env.liara.example new file mode 100644 index 00000000..cd10089a --- /dev/null +++ b/.env.liara.example @@ -0,0 +1,64 @@ +# ============================================================ +# Liara Environment Variables — ClinicPro (Docker, single app) +# ------------------------------------------------------------ +# Liara does NOT support docker-compose. The whole app (web + both message +# consumers) runs as ONE Docker app via Supervisor (docker/supervisord.conf). +# MariaDB and Redis are SEPARATE Liara managed services on the PRIVATE NETWORK. +# +# Set these in the Liara console (Environment tab) or via the CLI: +# liara env set KEY=VALUE --app clinicpro-api +# Build/deploy config (platform, port, disks, healthcheck) lives in liara.json. +# ============================================================ + +APP_ENV=prod +APP_DEBUG=0 + +# ── Secrets (REQUIRED — set before the first deploy) ── +APP_SECRET= # php -r "echo bin2hex(random_bytes(32));" +JWT_PASSPHRASE= # openssl rand -hex 32 (JWT keypair is generated with it on first boot) + +# ── Database / Redis (point at the Liara MANAGED services via PRIVATE hostnames) ── +# Create a MariaDB 11.8 service + a Redis service in Liara, enable private network, +# put both + this app on the SAME private network, then copy their private hosts here. +# serverVersion MUST match the MariaDB service (11.8). +DATABASE_URL="mysql://:@:3306/?serverVersion=mariadb-11.8.0&charset=utf8mb4" +REDIS_URL="redis://:6379" +MESSENGER_TRANSPORT_DSN="redis://:6379/messages" +# If the Redis service has a password: redis://:@:6379 + +# ── JWT key paths (keys live on the persistent 'jwt' disk, see liara.json) ── +JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem +JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem + +# ── Backend's own domain (API host — used for payment callbacks & absolute URLs) ── +APP_BASE_URL=https:// +DEFAULT_URI=https:// + +# ── Reverse proxy (Liara router) — trust X-Forwarded-* from the private ranges ── +TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1 + +# ── Frontend domains (MANY) — GENERATED from docker/frontend-domains.json ── +# To add/remove a city domain: edit that file, then run: +# ddev exec php docker/gen-cors-env.php (or: php docker/gen-cors-env.php on the server) +# and paste the new output into both vars below. +ALLOWED_FRONTEND_HOSTS= +CORS_ALLOW_ORIGIN= + +# ── api.ir identity inquiry (Shahkar + IbanMatch) ── +# Empty token => fail-closed (representative verification is rejected). +API_IR_BASE_URL=https://s.api.ir +API_IR_TOKEN= + +# ── Fixed app params (same values as the compose stack) ── +REFRESH_TOKEN_TTL=2592000 +OTP_TTL=1200 +MAX_FILE_SIZE_BYTES=5242880 +UPLOAD_DIR=var/uploads + +# ── Notes ── +# • Do NOT set RUN_INIT — entrypoint.sh defaults it to 1, and this is a single app, +# so JWT keygen + doctrine migrations run once on boot (correct). +# • SMS keys (kavenegar/rangineh) and payment gateway keys (mellat/sep) are read +# from the DB ("Site Settings"), NOT from env. No need to set them here. +# • Liara exposes only ONE external HTTP port (8080, from liara.json). MariaDB/Redis +# are reachable on the private network only. diff --git a/docker/supervisord.conf b/docker/supervisord.conf index c617c9ab..c8409dfa 100644 --- a/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -38,3 +38,30 @@ stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 + +# Message consumers. On Coolify these were separate compose services; Liara has +# no docker-compose, so they run here inside the single Docker app. --time-limit +# makes each consumer exit cleanly every hour; autorestart brings it back (guards +# against memory growth / stale DB+Redis connections). Mirrors the compose commands. +[program:worker-async] +command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v +autorestart=true +priority=30 +# messenger handles SIGTERM gracefully — finish the in-flight message, then exit. +stopsignal=TERM +stopwaitsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:worker-scheduler] +command=php bin/console messenger:consume scheduler_default --time-limit=3600 -v +autorestart=true +priority=30 +stopsignal=TERM +stopwaitsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docs/deploy-liara.md b/docs/deploy-liara.md new file mode 100644 index 00000000..c2e3336d --- /dev/null +++ b/docs/deploy-liara.md @@ -0,0 +1,173 @@ +# راهنمای دیپلوی ClinicPro روی لیارا (Docker) + +این راهنما قدم‌به‌قدم نشان می‌دهد چطور بک‌اند Symfony را روی **لیارا** با پلتفرم **Docker** بالا بیاوری. + +> چرا متفاوت با Coolify؟ لیارا **از docker-compose پشتیبانی نمی‌کند**. استک ۳-سرویسی compose (وب + ۲ worker) به یک **برنامهٔ داکر تکی** تبدیل شده که هر سه پروسه را با Supervisor اجرا می‌کند. MariaDB و Redis سرویس‌های مدیریت‌شدهٔ جدا روی شبکهٔ خصوصی‌اند. + +--- + +## نمای کلی معماری + +``` +┌─────────────────────────── Liara private network ───────────────────────────┐ +│ │ +│ ┌────────────────────────────┐ ┌───────────────┐ ┌──────────────┐ │ +│ │ clinicpro-api (Docker) │ │ MariaDB 11.8 │ │ Redis │ │ +│ │ Supervisor: │◄────►│ (managed) │ │ (managed) │ │ +│ │ • php-fpm + nginx :8080 │ └───────────────┘ └──────────────┘ │ +│ │ • worker-async │ │ +│ │ • worker-scheduler │ disks: jwt / uploads / var-uploads │ +│ └─────────────┬──────────────┘ │ +└─────────────────┼────────────────────────────────────────────────────────────┘ + │ :8080 (تنها پورت HTTP بیرونی) + ▼ + https:// +``` + +فایل‌های کلیدی در ریپو: +- [`liara.json`](../liara.json) — پیکربندی دیپلوی (platform، port، دیسک‌ها، healthcheck) +- [`Dockerfile`](../Dockerfile) — image چندمرحله‌ای (بدون تغییر برای لیارا) +- [`docker/supervisord.conf`](../docker/supervisord.conf) — اجرای php-fpm + nginx + دو worker +- [`docker/entrypoint.sh`](../docker/entrypoint.sh) — wait-for-DB، تولید JWT، migration +- [`.env.liara.example`](../.env.liara.example) — مرجع متغیرهای محیطی + +--- + +## پیش‌نیازها + +```bash +npm i -g @liara/cli +liara login +``` + +مقادیری که از قبل آماده کن: + +```bash +# APP_SECRET +php -r "echo bin2hex(random_bytes(32)).\"\n\";" + +# JWT_PASSPHRASE +openssl rand -hex 32 + +# دامنه‌های فرانت (ALLOWED_FRONTEND_HOSTS + CORS_ALLOW_ORIGIN) +ddev exec php docker/gen-cors-env.php +``` + +--- + +## گام ۱ — ساخت دیتابیس و Redis + +در [کنسول لیارا](https://console.liara.ir): + +1. **MariaDB 11.8** بساز. (نسخه باید با `serverVersion=mariadb-11.8.0` در `DATABASE_URL` یکی باشد.) +2. **Redis** بساز. +3. روی هر دو **شبکهٔ خصوصی** را فعال کن. +4. **هاست خصوصی** و کاربر/رمز هرکدام را از صفحهٔ سرویس یادداشت کن (مثلاً `clinicpro-db`، `clinicpro-redis`). + +--- + +## گام ۲ — ساخت برنامهٔ داکر + +1. در کنسول یک **App** از نوع **Docker** بساز با شناسهٔ `clinicpro-api` (همان `app` در `liara.json`). +2. آن را به **همان شبکهٔ خصوصیِ** DB و Redis متصل کن. + +--- + +## گام ۳ — ساخت دیسک‌های ماندگار + +در صفحهٔ برنامه، بخش **Disks**، این سه دیسک را با **همین نام‌ها** بساز (مسیر mount از `liara.json` خوانده می‌شود): + +| نام دیسک | mountTo | محتوا | +|---|---|---| +| `jwt` | `/app/config/jwt` | کلید JWT (نباید هر دیپلوی نو شود) | +| `uploads` | `/app/public/uploads` | فایل‌های عمومی آپلودی | +| `var-uploads` | `/app/var/uploads` | فایل‌های خصوصی آپلودی | + +> ⚠️ بدون دیسک `jwt`، هر دیپلوی کلید جدید می‌سازد و **همهٔ توکن‌های صادرشده باطل** می‌شوند. + +--- + +## گام ۴ — ست‌کردن متغیرهای محیطی + +از روی [`.env.liara.example`](../.env.liara.example) مقادیر را در تب **Environment** یا با CLI ست کن: + +```bash +liara env set APP_ENV=prod APP_DEBUG=0 --app clinicpro-api +liara env set APP_SECRET=<...> JWT_PASSPHRASE=<...> --app clinicpro-api + +# هاست‌ها = هاست خصوصی سرویس‌های لیارا +liara env set DATABASE_URL="mysql://:@:3306/?serverVersion=mariadb-11.8.0&charset=utf8mb4" --app clinicpro-api +liara env set REDIS_URL="redis://:6379" --app clinicpro-api +liara env set MESSENGER_TRANSPORT_DSN="redis://:6379/messages" --app clinicpro-api + +liara env set APP_BASE_URL=https:// DEFAULT_URI=https:// --app clinicpro-api +liara env set TRUSTED_PROXIES="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1" --app clinicpro-api +liara env set ALLOWED_FRONTEND_HOSTS="" CORS_ALLOW_ORIGIN="" --app clinicpro-api +``` + +نکته‌ها: +- `RUN_INIT` را ست **نکن** — `entrypoint.sh` پیش‌فرض `1` می‌گیرد و چون تک‌برنامه است، تولید JWT + migration یک‌بار اجرا می‌شود. +- کلیدهای SMS و درگاه پرداخت از **دیتابیس** خوانده می‌شوند، نه env. + +--- + +## گام ۵ — دیپلوی + +از ریشهٔ `clinicpro/`: + +```bash +liara deploy +``` + +`liara.json` تنظیمات platform/port/disks/healthcheck را می‌دهد، پس فلگ اضافه لازم نیست. در اولین بالا آمدن: + +1. `entrypoint.sh` تا ~۶۰ ثانیه صبر می‌کند تا DB جواب دهد. +2. کلید JWT می‌سازد (روی دیسک `jwt`). +3. کش prod را warmup می‌کند. +4. `doctrine:migrations:migrate` را اجرا می‌کند. +5. Supervisor، nginx/php-fpm و هر دو worker را بالا می‌آورد. + +--- + +## گام ۶ — اتصال دامنه و تأیید + +1. در بخش **Domains** برنامه، دامنهٔ API را وصل کن و TLS بگیر. +2. تست سلامت: + ```bash + curl -i https:///health # باید 200 بدهد + ``` +3. لاگ‌ها: + ```bash + liara logs --app clinicpro-api --follow + ``` + باید php-fpm، nginx، `worker-async` و `worker-scheduler` هر چهار بالا باشند. +4. ورود به پنل ادمین `/admin` و تست یک endpoint برای تأیید اتصال DB/Redis. + +--- + +## دیپلوی‌های بعدی + +```bash +git pull # یا تغییرات محلی +liara deploy +``` + +migrationهای جدید خودکار در `entrypoint.sh` اجرا می‌شوند. دیسک‌ها و env بین دیپلوی‌ها حفظ می‌شوند. + +--- + +## رفع اشکال + +| نشانه | علت محتمل | راه‌حل | +|---|---|---| +| `Database not reachable after 60s` در لاگ | شبکهٔ خصوصی وصل نیست یا `DATABASE_URL` غلط | DB و app روی یک شبکهٔ خصوصی باشند؛ هاست خصوصی و رمز را چک کن | +| توکن‌ها بعد از هر دیپلوی باطل | دیسک `jwt` mount نشده | دیسک `jwt` روی `/app/config/jwt` بساز | +| `/health` غیر-۲۰۰ | DB/Redis در دسترس نیست یا migration نخورده | لاگ entrypoint را ببین | +| خطای CORS از فرانت | `CORS_ALLOW_ORIGIN`/`ALLOWED_FRONTEND_HOSTS` قدیمی | `docker/gen-cors-env.php` را دوباره بزن و env را به‌روزرسانی کن | +| آپلودها بعد از دیپلوی گم می‌شوند | دیسک uploads mount نشده | دیسک‌های `uploads` و `var-uploads` را بساز | + +--- + +## گزینهٔ جایگزین — worker مجزا + +اگر خواستی workerها ایزوله باشند (به‌جای Supervisor تک‌کانتینر)، همین ریپو را به‌عنوان برنامهٔ داکر دومی با `liara.json` متفاوت دیپلوی کن که `command` را به `php bin/console messenger:consume ...` override کند، `RUN_INIT=0` بدهد و **بدون** پورت/دامنه باشد. گران‌تر (دو برنامهٔ داکر) ولی ایزوله‌تر. برای اکثر موارد گزینهٔ پیش‌فرض (Supervisor) کافی است. diff --git a/liara.json b/liara.json new file mode 100644 index 00000000..716b3a9f --- /dev/null +++ b/liara.json @@ -0,0 +1,16 @@ +{ + "app": "clinicpro-api", + "platform": "docker", + "port": 8080, + "healthCheck": { + "command": "/usr/local/bin/healthcheck.sh", + "interval": 30, + "timeout": 5, + "initialDelaySeconds": 60 + }, + "disks": [ + { "name": "jwt", "mountTo": "/app/config/jwt" }, + { "name": "uploads", "mountTo": "/app/public/uploads" }, + { "name": "var-uploads", "mountTo": "/app/var/uploads" } + ] +} diff --git a/tsconfig.json b/tsconfig.json index d918e4bc..c3c36c30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,11 +8,17 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, - "baseUrl": ".", "paths": { - "@/*": ["assets/admin/*"] - } + "@/*": [ + "./assets/admin/*" + ] + }, }, - "include": ["assets/admin/**/*"], - "exclude": ["node_modules", "public"] -} + "include": [ + "assets/admin/**/*" + ], + "exclude": [ + "node_modules", + "public" + ] +} \ No newline at end of file