build: ship pre-built API bundle; images copy output instead of installing

- API: `pnpm build` bundles dist/ into apps/api/.output (esbuild), with
  Linux musl native packages for sharp and argon2 kept alongside
- docker/api.Dockerfile copies that output; no pnpm install on the server
- add dist/migrate.js (prisma migrate deploy equivalent, same
  _prisma_migrations table and lock) so the Prisma CLI is not needed at runtime
- pnpm-workspace: supportedArchitectures for linux/x64/musl
- admin: apps/admin/.env.production sets VITE_SITE_URL for the production build
- drop NPM_REGISTRY / SITE_URL build args from compose and .env.example
- gitignore: anchor node_modules rules so pre-built node_modules are committed
- README: describe the pre-built deploy flow

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
S. Esmaeili
2026-10-24 14:41:31 +03:30
co-authored by Claude Haiku 5.5
parent ee8258556d
commit 10e453e4be
692 changed files with 303601 additions and 147 deletions
+5 -10
View File
@@ -126,21 +126,16 @@ docker compose exec api node dist/seed/seed.js # فقط بار اول
```
- کانتینر `gateway` روی پورت `HTTP_PORT` (پیش‌فرض 8080) گوش می‌دهد و هر دو دامنه (`SITE_DOMAIN` و `ADMIN_DOMAIN`) را سرو می‌کند. TLS را nginx یا Traefik روی سرور یا CDN جلوی آن تأمین می‌کند. هر دو دامنه باید به سرور اشاره کنند.
- سرویس یک‌باره‌ی `migrate` پیش از `api` اجرا می‌شود، migrationهای جدید را اعمال می‌کند و خارج می‌شود. Prisma CLI فقط در همین image هست و image اجرایی API سبک می‌ماند.
- سرویس یک‌باره‌ی `migrate` پیش از `api` اجرا می‌شود، migrationهای جدید را اعمال می‌کند و خارج می‌شود. این کار را `dist/migrate.js` انجام می‌دهد که هم‌ارز `prisma migrate deploy` است و از همان جدول `_prisma_migrations` استفاده می‌کند، پس Prisma CLI روی سرور لازم نیست.
- نام پروژه‌ی Compose ثابت و برابر `imen` است، تا با `docker-compose.dev.yml` (PostgreSQL و MinIO توسعه) هیچ کانتینر یا volume مشترکی نداشته باشد.
- سرویس `backup` هر روز یک dump از دیتابیس در `./backups` می‌سازد و dumpهای قدیمی‌تر از ۱۴ روز را پاک می‌کند. بازگردانی:
`docker compose exec -T postgres pg_restore -U imen -d imen --clean < backups/imen-YYYY-MM-DD.dump`
- از فایل‌های MinIO (volume `miniodata`) هم جداگانه بکاپ بگیرید.
**اگر سرور به رجیستری npm دسترسی ندارد**، دو راه هست:
1. در `.env` مقدار `NPM_REGISTRY` را روی یک mirror در دسترس بگذارید.
2. imageها را روی سیستم توسعه بسازید و به سرور منتقل کنید:
```bash
docker compose build
docker save imen-api imen-migrate imen-web imen-admin | gzip > imen-images.tar.gz
# روی سرور:
gunzip -c imen-images.tar.gz | docker load && docker compose up -d
```
**روی سرور چیزی نصب یا build نمی‌شود.** imageها فقط خروجی آماده‌ای را که در ریپو commit شده کپی می‌کنند، پس سرور به رجیستری npm نیازی ندارد. قبل از هر push روی سیستم توسعه `pnpm build` بگیرید و خروجی‌ها را هم commit کنید:
- `apps/web/.output` (همراه `server/node_modules`)، `apps/admin/dist` و `apps/api/.output`.
- `apps/api/.output` را `apps/api/scripts/bundle.mjs` می‌سازد. کد API و همه‌ی وابستگی‌های JS در آن bundle می‌شوند و فقط پکیج‌های native (`sharp` و `@node-rs/argon2`) با باینری Linux (Alpine، x64) کنارش کپی می‌شوند. این باینری‌ها را `supportedArchitectures` در `pnpm-workspace.yaml` موقع `pnpm install` دانلود می‌کند.
- آدرس سایت در لینک‌های پنل ادمین موقع build از `apps/admin/.env.production` خوانده می‌شود.
**Object Storage ابرآروان (یا هر S3 دیگر) به‌جای MinIO**: در سرویس `api`، مقادیر `S3_ENDPOINT`، `S3_BUCKET` و کلیدها را تنظیم کنید و `S3_ENSURE_BUCKET` را `false` بگذارید. `MEDIA_PUBLIC_URL` را روی آدرس عمومی باکت یا CDN بگذارید. بعد از آن به سرویس `minio` و مسیر `/media` در gateway نیازی نیست.