From 5cd8683fe65de1d2aa282b50e76150ce02242149 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Thu, 30 Jul 2026 19:21:30 +0330 Subject: [PATCH] Add AGENTS.md and deploy configs to the API repo. Co-authored-by: Cursor --- AGENTS.md | 131 +++++++++++++++++++++++++++++++ deploy/DEPLOY.md | 115 +++++++++++++++++++++++++++ deploy/ecosystem.config.cjs | 24 ++++++ deploy/nginx-adm.montzar.ir.conf | 73 +++++++++++++++++ 4 files changed, 343 insertions(+) create mode 100644 AGENTS.md create mode 100644 deploy/DEPLOY.md create mode 100644 deploy/ecosystem.config.cjs create mode 100644 deploy/nginx-adm.montzar.ir.conf diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0d32d78 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,131 @@ +# AGENTS.md — SuperAdmin آبشده + همکار + خانگی + +این فایل برای ادامه کار در Cursor است (از جمله Workspace سه‌پروژه‌ای). +قبل از تغییر معماری یا اتصال API، این سند را بخوان. + +## پروژه‌ها + +| نام | مسیر تقریبی | نقش | +|-----|-------------|-----| +| SuperAdminAbshode | `E:\projeha\SuperAdminAbshode` | کنترل‌پنل مرکزی شرکت | +| Hamkar | `E:\projeha\Hamkar` | محصول همکار (نسخه‌های فروشی) | +| Khanegi | `E:\projeha\Khanegi` (عمدتاً KhanegiModular) | محصول خانگی | + +Workspace پیشنهادی: `E:\projeha\Abshode-suite.code-workspace` + +## هدف کسب‌وکار + +مدیر شرکت نرم‌افزاری است؛ محصولات به‌صورت نسخه فروشی (white-label) فروخته می‌شوند. +نیاز اصلی: مدیریت متمرکز نسخه‌ها، برند، ظاهر، پیامک، سرور/تمدید/پشتیبانی (جایگزین گوگل‌شیت). + +## تصمیم‌های قطعی تا الان + +1. **استک سوپرادمین:** NestJS + Prisma + Vue 3 + Ant Design Vue + فونت Dana — نه Laravel. +2. **UI:** داشبورد نرم روشن، RTL، بدون سایدبار منو؛ کارت‌های ماژول در صفحه اول. +3. **منبع حقیقت برند/ظاهر:** سوپرادمین و `GET /get_user` (نه SSH/DB برای روزمره). +4. **`/get_user` عمومی است** → رمز/گلوگاه SMS داخلش نرود. +5. **SMS:** پنل کامل داخل سوپرادمین ساخته می‌شود؛ **فعلاً پنل SMS داخل همکار/خانگی دست نخورده می‌ماند** تا تست جواب بدهد؛ بعد حذف/غیرفعال. +6. **منبع حقیقت SMS (پیشنهاد قطعی):** کیف پول / تعرفه / مسدودسازی در سوپرادمین؛ ارسال همچنان با `SmsGatewayService` همان نسخه. +7. **مسئول فنی:** برای برند به DB لوکال نیاز نیست؛ اطلاعات رجیستری از `/get_user`؛ SMS سوپرادمین از صفر. +8. **برای اتصال دوطرفه واقعی** اپ‌ها به سوپرادمین، این پروژه باید روی سرور با دامنه HTTPS باشد. لوکال برای توسعه و push به نسخه‌های آنلاین کافی است. +9. **دیتابیس پروداکشن:** PostgreSQL (نه SQLite). دامنه هدف: `https://adm.montzar.ir` روی سرور مشترک montime؛ API روی پورت `3010`. جزئیات: `deploy/DEPLOY.md`. + +## وضعیت فعلی کد سوپرادمین + +### ساختار +- `apps/api` — NestJS + Prisma (**PostgreSQL**؛ تاریخچه SQLite در `prisma/migrations_sqlite_archive/`) +- `apps/web` — Vue 3 + Vite + Ant Design Vue +- ورود پیش‌فرض: موبایل `SEED_ADMIN_MOBILE` / `SEED_ADMIN_PASSWORD` (مثلاً `09128081392` / `Admin@12345`) +- دیپلوی: `deploy/` داخل همین ریپوی API (nginx + pm2 + راهنما) + +### قابلیت‌های پیاده‌شده +- Auth JWT، CRUD نسخه‌ها (Tenant)، Audit +- مدیران سوپرادمین: نام / نام‌خانوادگی / موبایل / رمز؛ ورود با موبایل؛ صفحه `/admins` +- پیامک هشدار مدیران از API مرکزی اس‌ام‌اس رایگان (`CENTRAL_SMS_*` → `createsms`)؛ رویدادهای نسخه و کیف پول SMS +- پنل سرور: اختصاصی یا مدیریت‌شده؛ نام/مشخصات/قیمت/مدت (۱،۳،۶،۱۲ ماه)؛ هشدار SMS نزدیک انقضا (پیش‌فرض ۳ روز، روزانه تا تمدید) +- پنل افزونه: هر نسخه ۰..N افزونه (`PluginSubscription` + `PluginCharge`)؛ نام اجباری + مشخصات/قیمت/مدت ۱،۳،۶،۱۲ ماه؛ هشدار SMS مثل سرور؛ صفحات `/plugins` و `/plugins/:tenantId` +- `GET /get_user` با تطبیق دامنه اصلی + آرایه `domains` + `app_ui_config` برای خانگی +- `GET /get_version?domain=` — ورژن همان نسخه فروشی (`Tenant.appVersion`)؛ تنظیم از صفحه مدیریت نسخه؛ جایگزین `nerkh.gold/get_version` +- ایمپورت نسخه‌ها از `apps/api/prisma/data/users.json` (`npm run prisma:import-users`) +- تشخیص محصول: `app.` / `khanegi.` → خانگی؛ `hamkar.` → همکار؛ overrides دستی در اسکریپت ایمپورت +- پنل SMS مستقل سوپرادمین: `SmsAccount` + `SmsLedger` (موجودی، شارژ، تعرفه، بلاک، آستانه‌های رنگی، گلوگاه) +- رنگ موجودی: سبز >100000، زرد 10000–100000، قرمز <10000 (ریال) +- داشبورد: ۵ نسخه کم‌موجودی؛ صفحه SMS: کارت‌ها → جزئیات در `/sms/:tenantId` +- ظاهر اپ خانگی: صفحه `UiConfigPage` برای `appUiConfig`؛ بک‌فیل از رجیستری/`users.json` با `npm run prisma:backfill-appearance`؛ در فرانت: `showNotifications` / `header.visible` / `startRoute` +- ظاهر اپ همکار: `/versions/:id/ui-hamkar`؛ رنگ‌ها + حالت رجیستری (`registryMode`)؛ `settingsManaged` باید true باشد تا `ui_settings` بیاید (وگرنه لاراول می‌ماند) + +### مهم +- موجودی SMS اولیه **دمو** بود تا رنگ‌ها دیده شوند؛ منبع واقعی همکار/خانگی نیست مگر بعداً وصل شود. +- چند دامنه دستی اصلاح شده (مثل 24gold.ir، limangold.ir، vahid.gold). +- چند نسخه با تأیید کاربر از همکار به خانگی منتقل شده‌اند (لیست در `FORCE_KHANEGI` داخل `import-users.ts`). + +## معماری هدف اتصال به همکار/خانگی + +دو جهت API: + +### A) اپ‌ها → سوپرادمین +- `GET /get_user` — برند و ظاهر (موجود) +- بعدی: `GET/POST` وضعیت SMS (`status` / `consume`) با `remoteApiToken` + +### B) سوپرادمین → اپ‌ها +- `GET/POST /super/settings` روی Laravel هر نسخه — تنظیمات مدیریتی +- اختیاری: reload برند / health + +احراز هویت: `Tenant.remoteApiToken` در سوپرادمین + همان توکن روی نسخه. + +### منبع حقیقت پیشنهادی + +| موضوع | منبع | +|------|------| +| برند / دامنه / لوگو / رنگ | سوپرادمین `/get_user` | +| `app_ui_config` خانگی | سوپرادمین → مصرف در فرانت خانگی | +| ورژن اپ / رفرش اجباری کش | سوپرادمین `/get_version` (per tenant: `Tenant.appVersion`) | +| کیف پول / تعرفه / بلاک SMS | سوپرادمین | +| ارسال SMS | `SmsGatewayService` همان نسخه | +| پنل SMS داخل محصولات | فعلاً دست‌نخورده | + +## فازهای بعدی (به ترتیب) + +1. **دیپلوی سوپرادمین** روی `adm.montzar.ir` (migrate + pm2 + nginx) — DB `superadmin` آماده است. +2. **پایلوت برند:** یک نسخه تست فقط از `/get_user` سوپرادمین بوت شود. +3. **ظاهر خانگی:** مصرف کامل `app_ui_config` در خانگی. +4. **API `/super/*` روی Laravel همکار و خانگی** با middleware توکن. +5. **SMS دوطرفه:** status/consume در سوپرادمین + هوک قبل/بعد ارسال در محصولات. +6. بعد از پایداری: کاهش/حذف پنل SMS داخلی محصولات. + +## قواعد کار ایجنت + +- بدون درخواست صریح کاربر: commit نکن، force push نکن، پنل SMS همکار/خانگی را حذف نکن. +- تغییرات همکار و خانگی را فقط وقتی کاربر صریحاً خواست؛ فعلاً اولویت سوپرادمین + طراحی قرارداد API است. +- در Workspace چندریشه: قبل از ادیت بگو در کدام ریشه کار می‌کنی (`SuperAdminAbshode` / `Hamkar` / `Khanegi`). +- **امنیت (همیشه):** secret / توکن / رمز را در UI، لاگ، `.env.example`، commit، یا پاسخ چت قرار نده؛ fallback هاردکد برای secret نگذار؛ رمز را فقط hash ذخیره کن و در API برنگردان؛ ورودی‌ها را validate/normalize کن (مثل موبایل). +- **تاریخ/ساعت:** همیشه شمسی (جلالی) و به وقت ایران (`Asia/Tehran`) — انتخابگر تاریخ شمسی و فرمت نمایش از `utils/datetime` / `iran-time.util`. +- رمزها و secretهای `users.json` و `CENTRAL_SMS_SECRET` را وارد UI/لاگ عمومی نکن. +- Prisma روی ویندوز: اگر `EPERM` روی `query_engine`، اول API را متوقف کن بعد `prisma generate`. + +## اجرای لوکال + +`DATABASE_URL` باید PostgreSQL باشد (Docker لوکال یا DB سرور). نمونه: `.env.example`. + +```bash +cd E:\projeha\SuperAdminAbshode\apps\api +npx prisma migrate deploy +npm run start:dev + +cd E:\projeha\SuperAdminAbshode\apps\web +npm run dev +``` + +- Web: http://localhost:5173/ +- API: http://localhost:3001/ + +## چت اول در Workspace جدید — پیشنهاد متن + +> ادامه کار سوپرادمین آبشده با همکار و خانگی. +> حتماً `SuperAdminAbshode/AGENTS.md` (یا `AGENTS.md` داخل ریپوی API) را بخوان. +> تصمیم‌ها و وضعیت فعلی همانجاست؛ از صفر مشورت نکن مگر برای مرحله بعد (پایلوت API / دیپلوی). + +## تاریخ به‌روزرسانی + +2026-07-30 — Prisma → PostgreSQL؛ DB `superadmin` روی سرور montime؛ deploy برای `adm.montzar.ir`. +2026-07-25 — جمع‌بندی گفتگوهای ساخت پنل، ایمپورت نسخه‌ها، SMS مستقل، و نقشه اتصال API. diff --git a/deploy/DEPLOY.md b/deploy/DEPLOY.md new file mode 100644 index 0000000..7722ac9 --- /dev/null +++ b/deploy/DEPLOY.md @@ -0,0 +1,115 @@ +# Deploy — SuperAdmin on shared montime host + +Host: `185.50.38.53` (SSH port `3031`) +Domain: `https://adm.montzar.ir` +DB: Postgres `superadmin` (owner `montime`) — already created +API port: `3010` (3000=montime, 3001=forisanad) + +## Layout on server + +```text +/var/www/superadmin/ + api/ # clone of controlPanel/api.git + deploy/ # nginx + pm2 + this guide (in api repo) + web/ # clone of controlPanel/web.git (Vue dist) +``` + +## 1) Clone / pull + +```bash +sudo mkdir -p /var/www/superadmin +sudo chown montime:montime /var/www/superadmin + +cd /var/www/superadmin +git clone https://get.feep.ir/controlPanel/api.git api +git clone https://get.feep.ir/controlPanel/web.git web +``` + +Later updates: `git pull` in each folder. + +## 2) API `.env` + +```bash +cd /var/www/superadmin/api +cp .env.example .env +nano .env +``` + +Required: + +```env +NODE_ENV=production +PORT=3010 +DATABASE_URL="postgresql://montime:YOUR_PASSWORD@127.0.0.1:5432/superadmin?schema=public" +JWT_SECRET="long-random-production-secret" +JWT_EXPIRES_IN="7d" +CORS_ORIGIN="https://adm.montzar.ir" +PUBLIC_API_URL="https://adm.montzar.ir" +SEED_ADMIN_MOBILE="09128081392" +SEED_ADMIN_PASSWORD="change-me" +# CENTRAL_SMS_* as needed +``` + +Password = same Postgres password that works for `psql -h 127.0.0.1 -U montime -d superadmin`. + +## 3) Install, migrate, build, seed + +```bash +cd /var/www/superadmin/api +npm ci +npx prisma generate +npx prisma migrate deploy +npm run build +npm run prisma:seed +# optional first import: +# npm run prisma:import-users +``` + +## 4) PM2 + +```bash +pm2 start /var/www/superadmin/api/deploy/ecosystem.config.cjs +pm2 save +pm2 status +curl -s http://127.0.0.1:3010/api/health || true +``` + +## 5) Web build + +Same-origin: leave `VITE_API_BASE` unset (defaults to `/api`). + +```bash +cd /var/www/superadmin/web +npm ci +npm run build +# dist → /var/www/superadmin/web/dist +``` + +## 6) Nginx + +```bash +sudo cp /var/www/superadmin/api/deploy/nginx-adm.montzar.ir.conf /etc/nginx/sites-available/adm.montzar.ir +sudo ln -sf /etc/nginx/sites-available/adm.montzar.ir /etc/nginx/sites-enabled/ +sudo nginx -t && sudo systemctl reload nginx +``` + +SSL: if Certbot already issued for `adm.montzar.ir`, paths in the conf should match. Otherwise: + +```bash +sudo certbot --nginx -d adm.montzar.ir +``` + +## 7) Smoke + +1. Open `https://adm.montzar.ir` → login +2. `https://adm.montzar.ir/get_user?domain=montpar.ir` (or any known domain) +3. Confirm uploads/logo URLs use `PUBLIC_API_URL` + +## Local development note + +Prisma provider is **PostgreSQL** only. Local options: + +- Docker: `docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=superadmin postgres:16` +- Or point `DATABASE_URL` at the server DB only for short tests (prefer local DB) + +SQLite history is kept under `prisma/migrations_sqlite_archive/` (reference only; not applied by `migrate deploy`). diff --git a/deploy/ecosystem.config.cjs b/deploy/ecosystem.config.cjs new file mode 100644 index 0000000..cc42a21 --- /dev/null +++ b/deploy/ecosystem.config.cjs @@ -0,0 +1,24 @@ +/** + * PM2 — SuperAdmin API on shared montime host. + * Port 3010 avoids clash with montime-api (:3000) and forisanad (:3001). + * + * Usage (on server, as montime): + * pm2 start /var/www/superadmin/api/deploy/ecosystem.config.cjs + */ +module.exports = { + apps: [ + { + name: 'superadmin-api', + cwd: '/var/www/superadmin/api', + script: 'dist/main.js', + instances: 1, + exec_mode: 'fork', + env: { + NODE_ENV: 'production', + PORT: '3010', + }, + max_memory_restart: '400M', + time: true, + }, + ], +}; diff --git a/deploy/nginx-adm.montzar.ir.conf b/deploy/nginx-adm.montzar.ir.conf new file mode 100644 index 0000000..90576aa --- /dev/null +++ b/deploy/nginx-adm.montzar.ir.conf @@ -0,0 +1,73 @@ +# Place under /etc/nginx/sites-available/adm.montzar.ir +# SSL: assume Certbot already issued for adm.montzar.ir (or enable after first HTTP). +# +# Single domain: SPA at / + Nest API under /api + public get_user/get_version + uploads. + +upstream superadmin_api { + server 127.0.0.1:3010; + keepalive 8; +} + +server { + listen 80; + listen [::]:80; + server_name adm.montzar.ir; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name adm.montzar.ir; + + # Certbot paths — adjust if different on this host + ssl_certificate /etc/letsencrypt/live/adm.montzar.ir/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/adm.montzar.ir/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + root /var/www/superadmin/web/dist; + index index.html; + + client_max_body_size 20M; + + location /api/ { + proxy_pass http://superadmin_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location = /get_user { + proxy_pass http://superadmin_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location = /get_version { + proxy_pass http://superadmin_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /uploads/ { + proxy_pass http://superadmin_api; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } +}