Files
imen_pishghaman/README.md
T
S. EsmaeiliandCursor 49f30a96b1 feat: polish admin UI, phone login, and product details
Switch admin auth to mobile, restyle shell/login with Vuesax icons, and add product detail fields plus public product pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-22 17:18:17 +03:30

13 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های جدید را اعمال می‌کند و خارج می‌شود. Prisma CLI فقط در همین image هست و image اجرایی API سبک می‌ماند.
  • نام پروژه‌ی 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ها را روی سیستم توسعه بسازید و به سرور منتقل کنید:
    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
    

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).