# MeshkeeApp — Project Context > **For AI agents:** Read this file at the start of a new chat before making changes. > Update this document when a major feature is completed or architecture changes. Last updated: September 8, 2026 --- ## Repository layout **Primary workspace:** open **`MeshkeeApp`** (this monorepo) as the Cursor workspace root. ``` MeshkeeApp/ apps/ super-admin/ # @meshkee/super-admin — port 5174 — meshkee.app business/ # @meshkee/business-dashboard — port 5173 — business.{domain} customer/ # @meshkee/customer-dashboard — port 5175 — customer.{domain} packages/ dashboard-core/ # @meshkee/dashboard-core — API client, auth types, utils, tokens dashboard-ui/ # @meshkee/dashboard-ui — shared React components docs/ # This file + agent context ``` **Backend (separate repo):** sibling folder `MeshkeeApp Backend` — NestJS + Prisma + PostgreSQL. Not inside this monorepo. **Stale workspace:** `MeshkeeApp Gen2` is empty/outdated — do not use it. --- ## Apps overview | App | Package | Port | Local URL | Domain guard | |-----|---------|------|-----------|--------------| | Super Admin | `@meshkee/super-admin` | 5174 | `https://meshkee.app:5174` | `AdminDomainGuard` | | Business | `@meshkee/business-dashboard` | 5173 | `http://business.sanihome.ir:5173` | `BusinessDomainGuard` | | Customer | `@meshkee/customer-dashboard` | 5175 | `http://customer.sanihome.ir:5175` | `CustomerDomainGuard` | ### Shared packages migration | App | `@meshkee/dashboard-core` | `@meshkee/dashboard-ui` | |-----|---------------------------|-------------------------| | Customer | ✅ wired | ✅ wired | | Super Admin | ⏳ pending | ⏳ pending | | Business | ⏳ pending | ⏳ pending | When migrating an app to shared packages, prefer importing from `@meshkee/dashboard-core` / `@meshkee/dashboard-ui` instead of duplicating code. --- ## Tech stack ### All dashboard apps - React 19, TypeScript, Vite, React Router - CSS Modules + design tokens (`src/index.css`; customer also imports `@meshkee/dashboard-core/styles/tokens.css`) - Auth via JWT (`src/context/AuthContext.tsx`, `src/services/authService.ts`) - API client: `src/lib/api.ts` — base URL from `VITE_API_BASE_URL` ### Backend - NestJS, Prisma, PostgreSQL, Redis - S3-compatible storage (Parspack) for media - API prefix: `/api/v1` - BigInt IDs serialized as strings in JSON --- ## Local development ### 1. Monorepo setup ```bash cd MeshkeeApp npm install ``` ### 2. Backend (sibling folder) ```bash cd "../MeshkeeApp Backend" cp .env.example .env # fill DATABASE_URL, JWT secrets, S3 keys, OPENAI_API_KEY (or GROQ_API_KEY) npm install # Run SQL migrations in database/migrations/ (in order) npx prisma generate npm run start:dev # default http://localhost:3000 ``` ### 3. Dashboard dev servers From the monorepo root: ```bash npm run dev:super-admin # https://meshkee.app:5174 npm run dev:business # http://business.sanihome.ir:5173 npm run dev:customer # http://customer.sanihome.ir:5175 ``` Or from an app directory: ```bash cd apps/business && npm run dev ``` Copy each app's `.env.example` → `.env` before first run. **Super Admin HTTPS:** generate local certs with mkcert (see `apps/super-admin/.env.example`). ### 4. Hosts file Add to `/etc/hosts` (one line per tenant): ``` 127.0.0.1 meshkee.app 127.0.0.1 business.sanihome.ir 127.0.0.1 customer.sanihome.ir 127.0.0.1 business.safeteb.com 127.0.0.1 customer.safeteb.com 127.0.0.1 safeteb.com ``` **Multi-tenant:** leave `VITE_BUSINESS_DOMAIN` unset in business/customer `.env`. Each dashboard build resolves the tenant from `window.location.hostname` (`business.safeteb.com` → `safeteb.com`). One dev server and one production deploy serve all businesses; the backend maps domain → business in the DB. ### 5. Test accounts | App | Phone | Password | Notes | |-----|-------|----------|-------| | Business | `+989122222222` | `password` | sanihome.ir → **business_id `4`** | | Customer | (same user) | `password` | customer.sanihome.ir tenant | | Super Admin | (platform admin) | — | see backend seed / team records | --- ## App routes ### Super Admin (`apps/super-admin`) | Path | Page | |------|------| | `/login` | Login | | `/` | Home | | `/businesses` | Businesses list (migrate-from-old + delete-data for portfolios/blogs(+news)/customers + categories; single-select). **Add/Edit domain** accepts optional Git repo URL → provisions storefront on websites VM and sets `deploy_slug`. Optional **Update DNS records** checkbox upserts tenant DNS (`@` → `ns.meshkee.com`, `www` CNAME → apex, `business`/`customer`/`api` CNAMEs) via **Arvan** or **Cloudflare** (switch; Cloudflare is DNS-only / not proxied). | | `/businesses/:businessId/invoices` | Business invoices list | | `/businesses/:businessId/invoices/new` | Issue invoice (full page) | | `/users` | Users (business filter: Customer vs Manager → Admin/Editor/Viewer; Admin assignable by super-admin only; owners locked) | | `/websites` | Websites / domains (Deploy when `deploy_slug` set). Edit domain can optionally update DNS (Arvan or Cloudflare, same tenant records as Businesses). Overflow ⋯ includes website/dashboard links, **Email DNS** (Stalwart DKIM + MX/SPF on Arvan or Cloudflare; removes extra MX, `dkim._domainkey`, `_dmarc`), **AI prompts**, and remove. **Parked domains** button: alias hosts 301 to the main domain. Per alias, choose **Arvan** or **Cloudflare** for DNS (`@` + `www` only, not proxied); SSL is always Let's Encrypt on the websites VM. | | `/templates` | Global AI prompt templates. Assign from Businesses or Websites ⋯ → **AI prompts** (add from library; public pack link). | | `/settings` | Platform settings (invoice templates + item templates) | | `/settings/invoice-templates/new` | Create invoice template | | `/settings/invoice-templates/:templateId` | Edit invoice template | | `/invoices/:invoiceId` | Public invoice viewer (no auth; print to PDF) | | `/profile` | Profile | ### Customer (`apps/customer`) | Path | Page | |------|------| | `/login` | Login (password, register, forgot-password SMS, one-time OTP login) | | `/checkout` | Shopping cart checkout (standalone layout — login → cart → delivery → payment) | | `/checkout/login` | Checkout sign-in step | | `/checkout/cart` | Cart review | | `/checkout/delivery` | Address or pickup | | `/checkout/payment` | Discount code + payment (online → bank redirect) | | `/checkout/result` | Bank return callback (`status`, `orderId`, payment meta) | | `/checkout/success` | Order confirmation (+ payment data when from gateway) | | `/checkout/failed` | Checkout / payment failure (+ payment data when present) | | `/` | Home | | `/profile` | Profile | | `/addresses` | Addresses | | `/orders` | Orders | | `/favorites` | Favorites | | `/my-products` | Customer stock listings (gated by `customer_products`) — list API | | `/my-products/:id` | User product details | | `/my-products/new` | Add user product (3 steps: basics, images, technical) — create API | | `/my-products/:id/edit` | Edit user product — update API | ### Business (`apps/business`) | Path | Page | Backend connected? | |------|------|-------------------| | `/login` | Login (password, forgot-password SMS, one-time OTP login) | Yes | | `/` | Home | Partial | | `/products` | Products hub | — | | `/products/categories` | Category tree + variations | Yes | | `/products/brands` | Product brands (linear list) | Yes | | `/products/list` | My Products grid | Yes | | `/products/new` | Add product | Yes | | `/products/edit/:id` | Edit product | Yes | | `/products/detail/:id` | Product details + gallery | Yes | | `/products/settings` | Product moderation settings | Yes | | `/store` | Store hub | Partial | | `/store/items` | Store items (product variants) | Yes | | `/store/settings` | Online sell + order process steps | Yes | | `/customers` | Business customers list | Yes | | `/finance` | Finance hub (Invoices + Transactions tiles) | Yes | | `/invoices` | Business-issued invoices list (`?userId=` filter) | Yes | | `/invoices/new` | Issue invoice to a user | Yes | | `/invoices/:invoiceId/edit` | Edit invoice (locked when approved) | Yes | | `/invoices/templates` | Invoice templates + item templates | Yes | | `/invoices/templates/new` | Create invoice template | Yes | | `/invoices/templates/:templateId` | Edit invoice template | Yes | | `/customer-products` | Customer user-product listings (admin API) | Yes | | `/customer-products/new` | Admin create user product (under admin name) | Yes | | `/customer-products/:id` | Customer user-product details | Yes | | `/customer-products/:id/edit` | Admin edit user product | Yes | | `/store/orders` | Orders list + filters | Yes | | `/blog` | Blog hub | Yes | | `/blog/list` | My Blogs grid | Yes | | `/blog/new`, `/blog/edit/:id` | Add/edit blog | Yes | | `/blog/categories` | Blog category tree | Yes | | `/blog/settings` | Comment moderation | Yes | | `/portfolios` | Portfolios hub | Yes | | `/portfolios/list` | My Portfolios grid | Yes | | `/portfolios/detail/:id` | Portfolio detail + gallery | Yes | | `/portfolios/new`, `/portfolios/edit/:id` | Add/edit portfolio | Yes | | `/portfolios/categories` | Portfolio category tree | Yes | | `/portfolios/settings` | Comment moderation | Yes | | `/website` | Website hub | Placeholder | | `/website/static-images` | Named website image slots, grouped by page | Yes | | `/website/special-items` | Special item carousels | Yes | | `/website/settings` | Website settings (special products source; sitemap rebuild; site-wide Schema.org Organization/LocalBusiness; e-payment when store module on) | Yes | | `/website/contact` | Contact us submissions list | Yes | | `/website/subscriptions` | Subscriptions | Placeholder | | `/website/faq` | FAQ | Placeholder | | `/website/badges` | Badges | Placeholder | | `/settings` | General settings | E-payment: Mellat + ZarinPal credentials; stubs for SEP / Snapp Pay / DigiPay | --- ## Auth (login pages) Business and customer login pages share the same SMS-backed flows (super-admin stays password-only). | Flow | API | Notes | |------|-----|--------| | Password login | `POST /auth/login` | Rejects unverified cell when SMS is enabled | | Register | `POST /auth/register` | Customer by website domain; with SMS on, customer goes to OTP step | | Send OTP | `POST /auth/send-otp` | Redis 5-min code via Gama | | One-time login | `POST /auth/login-otp` | Code only → tokens + marks cell verified | | Forgot password | `POST /auth/reset-password` | Code + new password | | Verify only | `POST /auth/verify-otp` | Marks verified; no tokens | | Dashboard SSO | `POST /auth/handoff` + `POST /auth/handoff/consume` | Admin icon on customer dashboard; 60s one-time ticket | Auth helpers: `apps/*/src/services/authService.ts` (`login`, `loginWithOtp`, `resetPassword`, `sendOtp`, `register`, `createHandoff`). `AuthContext` exposes `login` + `loginWithOtp`. Business dashboard active tenant is resolved from the host domain (never `businesses[0]` alone). Customer header “open business dashboard” uses a one-time SSO ticket so staff do not log in again. --- ## API surface (business-scoped) All routes require JWT + business permission. `businessId` comes from tenant context after login. ### Categories - `GET/POST/PATCH/DELETE /businesses/:businessId/categories` - `POST /businesses/:businessId/categories/ai-generate` — AI-generated category tree (`{ prompt }`) - `GET /businesses/:businessId/categories/color-presets` - `GET/PUT /businesses/:businessId/categories/:categoryId/variations` - `POST /businesses/:businessId/categories/:categoryId/technical-form/ai-suggest` Query: `?entityType=product` for product categories. ### Brands - `GET/POST/PATCH/DELETE /businesses/:businessId/brands` - `GET /businesses/:businessId/brands/:brandId` Products accept optional `brandId` on create/update. ### Products - `GET/POST/PATCH/DELETE /businesses/:businessId/products` - `POST /businesses/:businessId/products/ai-create` — AI-generated product draft - `GET /businesses/:businessId/products/:productId` - Translations (EN/AR, stored in `entity_translations`): `GET .../products/:id/translations`, `POST .../translations/:locale/ai`, `PUT .../translations/:locale`. Same three routes on categories. Dashboard category list includes `translationCount`. Bulk: `POST .../categories/translations/ai-all` and `POST .../products/translations/ai-all` (missing EN/AR only; skips existing). Business dashboard shows translation controls only when the `multilanguage_data` module is enabled for that business. ### Contact submissions - `GET /businesses/:businessId/contact-submissions` — paginated list - `GET /businesses/:businessId/contact-submissions/:submissionId` - `POST /tenants/:host/contact-submissions` — public website contact form ### Business settings - `GET/PATCH /businesses/:businessId/settings` — `branding`, `dashboard`, `store`, `modules`, `website` - `website.specialProductsSource`: `product` \| `store_item`. Unset → `store_item` if the store module is enabled, otherwise `product`. - `website.schema`: site-wide Schema.org settings (`enabled`, `type` Organization|LocalBusiness, optional name/description/priceRange/sameAs). Public `GET /tenants/:host/website/business-info` returns `schema.jsonLd` ready for `