Files
imen_pishghaman/README.md
T
S. EsmaeiliandClaude Haiku 5.5 10e453e4be 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>
2026-10-24 14:41:31 +03:30

14 KiB

ایمن پیشگامان — مونوریپو (سایت + API + پنل مدیریت)

سایت ایمن پیشگامان به‌همراه بکند و پنل مدیریت محتوا. همه‌ی متن‌ها، تصاویر، دسته‌ها و محصولات سایت از پنل مدیریت قابل ویرایش‌اند.

بخش مسیر فناوری
سایت apps/web Nuxt 4 (SSR + کش SWR)، Vue 3، Tailwind v4، Ant Design Vue، Pinia
API apps/api NestJS 12، Prisma 7، PostgreSQL، ذخیره‌ساز S3 (MinIO)
پنل مدیریت apps/admin Vue 3 + Vite، Ant Design Vue، Tailwind v4، Pinia، Vue Router
کد مشترک packages/shared اسکیماهای Zod و تایپ‌ها (مشترک بین هر سه اپ)

ابزار مونوریپو: pnpm workspaces + Turborepo.

مرورگر ──► gateway (nginx)
            ├─ imenpishgaman.ir        → web  (Nuxt SSR، صفحات کش‌شده)
            ├─ admin.imenpishgaman.ir  → admin (فایل‌های استاتیک)
            ├─ /api/*                   → api  (NestJS) ──► PostgreSQL
            └─ /media/*                 → MinIO (تصاویر آپلودشده)

ذخیره در پنل ──► API ──► POST web:/_internal/revalidate ──► کش صفحات سایت پاک می‌شود

ساختار پوشه‌ها

apps/
  web/                 سایت (همان طراحی قبلی؛ داده‌ها از API)
    app/stores/site.ts   محتوای سایت از GET /api/public/site (یک بار در هر رندر)
    app/plugins/api.ts   $api — در SSR آدرس داخلی API، در مرورگر /api
    server/routes/       sitemap.xml (از دیتابیس)، robots.txt، manifest، _internal/revalidate
  api/
    prisma/schema.prisma مدل داده؛ migrations/ کنار آن
    src/                 ماژول‌ها: auth, users, catalog, media, sections, contact, public, dashboard
    src/seed/            مقداردهی اولیه (محتوای فعلی سایت + ۸ دسته / ۸۹ محصول)
    seed-assets/         تصاویر اولیه که seed در MinIO آپلود می‌کند
    test/                تست‌های e2e
  admin/
    src/pages/           داشبورد، محصولات، دسته‌ها، بخش‌های سایت، پیام‌ها، رسانه، کاربران
    src/sections/        فرم اختصاصی هر بخش سایت
packages/shared/src/     sections.ts (اسکیمای بخش‌ها)، catalog.ts، admin.ts، icons.ts
docker/                  Dockerfile هر اپ و پیکربندی nginx
docker-compose.yml       استقرار production
docker-compose.dev.yml   PostgreSQL + MinIO برای توسعه

راه‌اندازی محیط توسعه

پیش‌نیاز: Node.js 22.19 یا جدیدتر (۲۴ توصیه می‌شود)، Docker.

corepack enable pnpm          # در ویندوز بدون دسترسی ادمین:
                              # corepack enable pnpm --install-directory "%APPDATA%\npm"
pnpm install

pnpm infra:up                 # PostgreSQL روی 5433 و MinIO روی 9000 (کنسول: 9001)

cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env

pnpm db:migrate               # ساخت جدول‌ها
pnpm db:seed                  # محتوای اولیه + کاربر مدیر (ADMIN_PHONE / ADMIN_PASSWORD در apps/api/.env)

pnpm dev                      # هر سه اپ با هم
آدرس چیست
http://localhost:3000 سایت
http://localhost:5173 پنل مدیریت
http://localhost:4000/api API
http://localhost:9001 کنسول MinIO

در توسعه، سرور Nuxt و Vite مسیرهای /api و /media را به API و MinIO پراکسی می‌کنند؛ در production همین کار را nginx انجام می‌دهد. به همین دلیل آدرس تصاویر همه‌جا به‌صورت /media/... ذخیره می‌شود.

اسکریپت‌ها (از ریشه‌ی پروژه)

دستور کار
pnpm dev اجرای همه‌ی اپ‌ها در حالت توسعه
pnpm build build همه‌ی پکیج‌ها
pnpm typecheck بررسی TypeScript همه‌ی پکیج‌ها
pnpm test تست‌های e2e بکند (روی دیتابیس توسعه؛ داده‌های تست پاک می‌شوند)
pnpm db:migrate ساخت/اعمال migration (prisma migrate dev)
pnpm db:seed مقداردهی اولیه؛ اجرای دوباره چیزی را بازنویسی نمی‌کند
pnpm db:studio Prisma Studio برای دیدن داده‌ها
pnpm infra:up / infra:down روشن/خاموش کردن PostgreSQL و MinIO

برای برگرداندن متن‌های سایت به محتوای اولیه: SEED_OVERWRITE_SECTIONS=1 pnpm db:seed.

پنل مدیریت

  • محصولات: افزودن/ویرایش، تصویر اصلی و گالری، توضیحات کامل، جدول مشخصات فنی، ویژگی‌های کلیدی، کاربردها، استانداردها، سایزها و مدل‌ها، برند و کشور سازنده، بسته‌بندی و حداقل سفارش، سئو، انتشار یا پیش‌نویس، کپی، مرتب‌سازی (با فیلتر یک دسته). هر محصول صفحه‌ی اختصاصی /products/<اسلاگ> دارد (گالری با بزرگ‌نمایی، مشخصات، محصولات مرتبط، دکمه‌ی درخواست قیمت که فرم تماس را با نام محصول پر می‌کند، JSON-LD نوع Product). بخش‌هایی که پر نشده‌اند در صفحه نمایش داده نمی‌شوند.
  • دسته‌بندی‌ها: کاور، زیرگروه‌ها (فیلترهای صفحه‌ی دسته)، سئو، ترتیب نمایش.
  • بخش‌های سایت: Hero، نوار متحرک، آمار، معرفی محصولات، درباره ما، خدمات، استانداردها، صنایع، نظرات، تماس، فوتر و تنظیمات سایت (تلفن، ایمیل، نشانی، ساعات کاری، شبکه‌های اجتماعی، سئوی پیش‌فرض).
  • پیام‌های تماس: درخواست‌های فرم مشاوره‌ی سایت، با وضعیت جدید / خوانده‌شده / بایگانی.
  • کتابخانه رسانه: همه‌ی تصاویر به‌صورت خودکار بهینه و به WebP تبدیل می‌شوند. تصویری که جایی استفاده شده قابل حذف نیست.
  • کاربران و نقش‌ها: «مدیر کل» به همه‌چیز دسترسی دارد. «ویرایشگر» به‌جز کاربران و تنظیمات سایت، همه‌ی بخش‌ها را می‌بیند.

تغییرات پس از ذخیره، حداکثر ظرف چند ثانیه روی سایت دیده می‌شوند. اگر فراخوانی پاک‌کردن کش به هر دلیلی نرسد، صفحه‌ها در هر صورت هر ۶۰ ثانیه تازه می‌شوند.

امنیت ورود: access token کوتاه‌عمر (۱۵ دقیقه) فقط در حافظه‌ی مرورگر نگه داشته می‌شود و refresh token در کوکی httpOnly است. refresh token در هر استفاده عوض می‌شود. اگر یک توکن باطل‌شده دوباره استفاده شود، همه‌ی نشست‌های آن کاربر بسته می‌شوند. رمزها با argon2 ذخیره می‌شوند و تعداد تلاش برای ورود و ارسال فرم تماس محدود است.

راهنمای توسعه

افزودن فیلد به یک بخش سایت (مثلاً یک متن جدید در Hero):

  1. اسکیمای آن بخش را در packages/shared/src/sections.ts تغییر دهید. اگر فیلد اجباری است، مقدار پیش‌فرض بدهید (text())، وگرنه داده‌های فعلی دیتابیس دیگر معتبر نیستند.
  2. فیلد را به فرم همان بخش در apps/admin/src/sections/ اضافه کنید.
  3. در کامپوننت سایت (apps/web/app/components/sections/) از آن استفاده کنید.
  4. مقدار اولیه را در apps/api/src/seed/data/sections.ts اضافه کنید.

یک اسکیمای Zod هم ورودی API را اعتبارسنجی می‌کند و هم فرم پنل را. برای همین پیام‌های خطا در هر دو یکسان و فارسی‌اند.

تغییر مدل دیتابیس: apps/api/prisma/schema.prisma را ویرایش کنید و pnpm db:migrate را اجرا کنید. اسم migration را می‌پرسد. فایل migration را هم commit کنید.

آیکون‌ها: آیکون‌های قابل‌انتخاب در پنل در packages/shared/src/icons.ts تعریف شده‌اند. برای افزودن آیکون جدید، آن را به همین لیست، به apps/web/app/components/ui/DynamicIcon.vue و به apps/admin/src/components/icons.ts اضافه کنید. این تکرار عمدی است تا فقط همین آیکون‌ها وارد باندل شوند.

Ant Design و Tailwind: استایل‌های Tailwind v4 داخل cascade layer هستند و CSS-in-JS آنت‌دیزاین بیرون از layer. بنابراین روی کامپوننت‌های antd کلاس ظاهری Tailwind نگذارید. در سایت، پالت رنگ در دو جا تعریف شده است: @theme در main.css و palette در app/design/theme.ts. هنگام تغییر، هر دو را هم‌گام کنید.

استقرار (production)

cp .env.example .env            # همه‌ی رمزها و دامنه‌ها را تنظیم کنید
docker compose up -d --build
docker compose exec api node dist/seed/seed.js   # فقط بار اول
  • کانتینر gateway روی پورت HTTP_PORT (پیش‌فرض 8080) گوش می‌دهد و هر دو دامنه (SITE_DOMAIN و ADMIN_DOMAIN) را سرو می‌کند. TLS را nginx یا Traefik روی سرور یا CDN جلوی آن تأمین می‌کند. هر دو دامنه باید به سرور اشاره کنند.
  • سرویس یک‌باره‌ی 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) هم جداگانه بکاپ بگیرید.

روی سرور چیزی نصب یا 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 نیازی نیست.

کارهای باقی‌مانده و پیشنهادها

  • پر کردن جزئیات محصولات (توضیحات کامل، مشخصات فنی، استانداردها و ...) از پنل؛ این فیلدها برای محصولات فعلی خالی‌اند و فعلاً صفحه‌ی محصول توضیح دسته را نشان می‌دهد.
  • عکس برای ۱۷ محصول پوشاک کار و تجهیزات ترافیک (فعلاً کاور دسته نمایش داده می‌شود).
  • ایمپورت/اکسپورت اکسل محصولات برای ورود دسته‌جمعی.
  • اعلان پیام تماس جدید با ایمیل، پیامک یا ربات بله/تلگرام.
  • پوشه‌ی محصولات/ (عکس‌ها و فایل‌های Word خام) و فایل‌های _dev.log و landing-nextjs-seo.skill بهتر است از ریپو خارج شوند.
  • لوگوی رسمی (apps/web/public/images/logo.png)، تصویر اختصاصی Open Graph با ابعاد ۱۲۰۰×۶۳۰ و آیکون‌های PNG برای PWA.
  • CI روی سرور git (pnpm typecheck && pnpm build && pnpm test).