# Liara Deployment Playbook (عمومی)

راهنمای عمومی دیپلوی روی Liara — جمع‌بندی مشکلات رایج و راه‌حل‌هاشون.
برای **هر پروژه‌ای** قابل استفاده‌ست. جای `<...>`ها مقدار پروژه‌ی خودت رو بذار.

> قرارداد نام‌گذاری در این سند:
> `<app>` = اسم اپ، `<frontend-app>` = اپ فرانت، `<panel-app>` = اپ بک‌اند/پنل،
> `<db-internal-host>` = هاست داخلی دیتابیس، `<TOKEN>` = API token لیارا،
> `<port>` = پورتی که اپ روش گوش می‌ده (Docker static: معمولاً 7860، Node: 3000).

---

## ۱) CLI — احراز هویت

**مشکل:** دستورها با `Authentication failed` رد می‌شن، حتی بعد از `liara login` (توکن بین شل‌ها/محیط‌ها share نمی‌شه).

**راه‌حل:** روی **هر** دستور `--api-token` بده:
```bash
liara deploy --app <app> --api-token "<TOKEN>"
```
توکن از داشبورد Liara → API.

---

## ۲) `liara deploy` روی prompt پورت هنگ می‌کنه

**مشکل:** دیپلوی روی `Enter the port your app listens to:` گیر می‌کنه و چیزی به Liara نمی‌رسه (مخصوصاً در حالت غیرتعاملی).

**راه‌حل:** پورت رو صریح بده — هم فلگ، هم `liara.json`:
```bash
liara deploy --app <app> --port <port> ...
```
```json
{ "platform": "docker", "port": <port> }
```
اپ هم باید روی `process.env.PORT` گوش بده (Liara اون رو inject می‌کنه).

---

## ۳) `Dockerfile cannot be empty`

**علت:** `.dockerignore` خود `Dockerfile` رو exclude کرده.

**راه‌حل:** `Dockerfile` رو از `.dockerignore` **بردار** — Liara باید Dockerfile رو توی آرشیو ببینه.

---

## ۴) Docker Hub از ایران بلاکه (مهم‌ترین برای Docker)

**مشکل:** `FROM <image>` با `Get "https://registry-1.docker.io/...": Client.Timeout exceeded` تایم‌اوت می‌خوره.

**راه‌حل:** image پایه رو از **میرور Liara** بکش:
```dockerfile
# ایمیج رسمی (node, nginx, postgres, python, ...):
FROM docker-mirror.liara.ir/library/<image>:<tag>
# ایمیج یوزر/سازمان:
FROM docker-mirror.liara.ir/<org>/<image>:<tag>
```

میرورهای دیگر Liara (در صورت نیاز داخل build):
| نوع | میرور |
|---|---|
| Docker Hub | `docker-mirror.liara.ir` |
| Alpine (apk) | `https://linux-mirror.liara.ir/repository/alpine/` |
| npm | `https://package-mirror.liara.ir/repository/npm/` |

> `npm ci`/`pip install` معمولاً از ایران بدون میرور کار می‌کنن؛ فقط Docker Hub همیشه مشکل‌سازه.
> اگه داخل build `apk add` داری، میرور apk رو هم ست کن.

---

## ۵) لوکیشن build

**نکته‌ی کلیدی:** **لوکیشن build ≠ لوکیشن میزبانی.** اپ همیشه روی منطقه‌ی خودش سرو می‌شه؛ `--build-location` فقط می‌گه image کجا کامپایل شه.

- build ایران + میرورهای بالا → سریع و بدون تحریم:
  ```bash
  liara deploy --app <app> --build-location iran ...
  ```
- اگه از میرور استفاده نمی‌کنی، build ایران به Docker Hub نمی‌رسه (از مورد ۴ استفاده کن).

---

## ۶) متغیرهای build-time در فرانت (Vite / CRA / Next static)

**مشکل:** متغیر API URL رو به‌عنوان env روی اپ ست کردی ولی فرانت بهش وصل نشد.

**علت:** باندلرها متغیرهای `VITE_*` / `REACT_APP_*` / `NEXT_PUBLIC_*` رو **موقع build داخل باندل می‌نویسن** — env runtime روی کانتینر استاتیک بی‌اثره.

**راه‌حل:** توی Dockerfile قبل از build:
```dockerfile
ARG VITE_API_URL=https://<panel-app>.liara.run
ENV VITE_API_URL=$VITE_API_URL
RUN npm run build
```

---

## ۷) اتصال اپ به دیتابیس (Private Network)

**مشکل‌ها:**
- آدرس داخلی دیتابیس → `getaddrinfo ENOTFOUND <db-internal-host>`.
- آدرس عمومی دیتابیس از داخل اپ Liara → `ECONNREFUSED`.

**علت:** اپ‌های Liara به endpoint عمومی DB وصل نمی‌شن؛ و هاست داخلی فقط وقتی resolve می‌شه که اپ و DB **روی یک Private Network مشترک** باشن.

**راه‌حل:**
1. هم دیتابیس هم اپ رو به **یک Private Network** وصل کن.
2. **connection string کاملِ** داخلی (نه فقط host:port):
   ```
   DATABASE_URL=postgresql://<user>:<pass>@<db-internal-host>:5432/<db>
   ```
3. روی شبکه‌ی داخلی TLS لازم نیست (برعکس endpoint عمومی که TLS می‌خواد).

---

## ۸) اپ موقع بوت کرش می‌کنه چون env ست نیست

**نشانه:** کانتینر `unhealthy` / `exit 255` و لاگ مثل `FATAL: <X> is not set`.

**راه‌حل:** env های حیاتی رو **قبل** از انتظارِ سالم‌بودن ست کن. تغییر env خودش ری‌استارت می‌کنه.
ست با CLI (به‌جای داشبورد):
```bash
liara env:set "KEY=VALUE" "KEY2=VALUE2" --app <app> --force --api-token "<TOKEN>"
```
> اگه کد بدون یه env حیاتی `process.exit` می‌کنه، تا ست نشه اصلاً بالا نمیاد.

---

## ۹) فایل‌سیستم Ephemeral

**مشکل‌ها:** دیتابیس فایلی (SQLite) و پوشه‌ی آپلود با هر ری‌دیپلوی پاک می‌شن؛ نوشتن/`mkdir` روی مسیرهای read-only کرش می‌کنه.

**راه‌حل:**
- دیتای دائمی → **دیتابیس مدیریت‌شده**.
- فایل/مدیا → **Object Storage** (مورد ۱۰).
- اگه واقعاً دیسک لازمه → یه **Disk** بساز و mount کن؛ مسیر رو **env-based** کن.
- هر نوشتن روی دیسک رو دفاعی بنویس که موقع بوت کرش نکنه:
  ```js
  const dir = process.env.UPLOADS_DIR || path.join(__dirname, 'uploads');
  try { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } catch {}
  ```

---

## ۱۰) Object Storage (S3) برای مدیا

- S3-compatible؛ endpoint مثل `storage.iran.liara.site` + bucket + access key + secret key.
- در کد: SDK اس‌تری (`@aws-sdk/client-s3`)، `forcePathStyle: true`، endpoint = `https://<endpoint>`.
- URL عمومی فایل: `https://<bucket>.<endpoint>/<key>`.
- **باکت رو public کن** وگرنه فایل‌ها 403 می‌شن.

---

## ۱۱) پلتفرم اپ ثابته

نمی‌شه پلتفرم یه اپ رو عوض کرد (Node↔Docker↔Static). اگه اشتباه ساختی، **پاک و با پلتفرم درست دوباره بساز** (env و اتصال شبکه‌ی خصوصی رو هم از نو ست کن).

---

## ۱۲) CORS

**مشکل:** بک‌اند درخواست‌ها رو با `Not allowed by CORS` رد می‌کنه (حتی origin خودش).

**راه‌حل:** allow-list رو شامل **دامنه‌ی خود اپ + دامنه‌ی فرانت** کن:
```
ALLOWED_ORIGINS=https://<panel-app>.liara.run,https://<frontend-app>.liara.run
```

---

## ۱۳) Seed / یوزر ادمین

دیتابیس تازه خالیه → لاگین «رمز اشتباه». seed رو از Console اپ بزن. اگه اسکریپت seed یوزر موجود رو رد می‌کنه و رمز عوض نمی‌شه، از **upsert** استفاده کن.
نکته‌ی مهم برای اسکریپت‌های seed: آدرس و رمز رو **env-based** کن تا هم لوکال هم روی Liara کار کنه:
```js
const BASE = process.env.SEED_BASE || `http://localhost:${process.env.PORT || 3000}`;
```
> روش بدون TLS-dependency: اسکریپت seed که فقط `fetch` می‌زنه رو می‌تونی از ماشین خودت با `SEED_BASE=https://<panel-app>.liara.run` اجرا کنی.

---

## ۱۴) همگام‌سازی lockfile

بعد از هر تغییر وابستگی‌ها، **لوکال `npm install`** بزن تا `package-lock.json` به‌روز شه، بعد دیپلوی — وگرنه `npm ci` روی Liara به‌خاطر mismatch خطا می‌ده. `node_modules` رو پوش نکن (`.liaraignore` / `.dockerignore`).

---

## ۱۵) لاگ و exit code گمراه‌کننده

- `liara deploy` گاهی با `exit code 2` تموم می‌شه ولی واقعاً **موفق** بوده (خطا فقط مرحله‌ی خوندن لاگ بعد از دیپلوی).
- `liara logs` گاهی لاگ **کش‌شده** می‌ده.

**راه‌حل:** وضعیت واقعی رو از خروجی کامل دیپلوی (`√ Release created` / `Deployment finished successfully`) و یه **درخواست HTTP واقعی به دامنه** بگیر؛ به exit code تنها اعتماد نکن.

---

## فایل‌های پایه‌ی هر اپ

`liara.json` (Docker):
```json
{ "platform": "docker", "port": <port> }
```
`liara.json` (Node):
```json
{ "platform": "node" }
```
`.liaraignore`:
```
node_modules
.env
*.db
uploads
```

---

## چک‌لیست دیپلوی (ترتیب درست)

1. **دیتابیس** بساز → Private Network روشن.
2. **اپ بک‌اند** بساز → به همون Private Network وصلش کن.
3. env های بک‌اند رو ست کن (`DATABASE_URL` داخلیِ کامل، سکرت‌ها، `ALLOWED_ORIGINS`، کلیدهای Object Storage، `NODE_ENV=production`).
4. `package-lock.json` رو لوکال sync کن.
5. (Docker) Dockerfile: میرور `docker-mirror.liara.ir` + `ARG` برای متغیرهای build-time. `.dockerignore` نباید `Dockerfile` رو حذف کنه.
6. `liara deploy --app <app> --port <port> --build-location iran --api-token "<TOKEN>"`.
7. سلامت رو با درخواست HTTP به دامنه چک کن (نه فقط exit code).
8. **seed محتوا/ادمین** (قبل از دیپلوی فرانت، تا سایت خالی نباشه).
9. باکت Object Storage رو public کن.
10. **فرانت** رو با متغیر build-time = دامنه‌ی بک‌اند دیپلوی کن؛ `ALLOWED_ORIGINS` بک‌اند رو شامل دامنه‌ی فرانت کن.
