# 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: July 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 (Parmin) 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, GROQ_API_KEY (or OPENAI_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 | | `/users` | Users | | `/websites` | Websites / domains | | `/profile` | Profile | ### Customer (`apps/customer`) | Path | Page | |------|------| | `/login` | 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 | | `/checkout/success` | Order confirmation | | `/` | Home | | `/profile` | Profile | | `/addresses` | Addresses | | `/orders` | Orders | | `/favorites` | Favorites | ### Business (`apps/business`) | Path | Page | Backend connected? | |------|------|-------------------| | `/login` | 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 | | `/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/sliders` | Homepage sliders | Yes | | `/website/special-categories` | Featured category groups | Yes | | `/website/special-brands` | Featured brand groups | Yes | | `/website/special-items` | Special item carousels | Yes | | `/website/contact` | Contact us submissions list | Yes | | `/website/subscriptions` | Subscriptions | Placeholder | | `/website/faq` | FAQ | Placeholder | | `/website/badges` | Badges | Placeholder | | `/website/e-payment` | E-payment | Placeholder | --- ## 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` ### 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` — includes `branding.primaryColor` ### Portfolios - `GET/POST/PATCH/DELETE /businesses/:businessId/portfolios` - `GET /businesses/:businessId/portfolios/:portfolioId` - Portfolio categories: `?entityType=portfolio` ### Store items (sellable product variants) - `GET /businesses/:businessId/store-items` — paginated list - `POST /businesses/:businessId/store-items` — batch create with price + stock ### Store specials - `GET/POST/PATCH/DELETE /businesses/:businessId/store-specials` - `GET /tenants/:host/store-specials` — public API for websites ### Website homepage widgets - `GET/POST/PATCH/DELETE /businesses/:businessId/website/category-groups` — groups with `categoryIds[]` - `GET/POST/PATCH/DELETE /businesses/:businessId/website/brand-groups` — groups with `brandIds[]` - `GET /tenants/:host/website/category-groups` — public category groups - `GET/POST/PATCH/DELETE /businesses/:businessId/website/sliders` — sliders with `slides[]` (imageMediaId, title, linkUrl) - `GET /tenants/:host/website/sliders` — public sliders ### Customers - `GET/POST/PATCH/DELETE /businesses/:businessId/customers` - `GET /businesses/:businessId/customers/search` — autocomplete for order cart ### Orders - `GET/POST/PATCH/DELETE /businesses/:businessId/orders` ### Product variants & variation values - `GET/POST/DELETE /businesses/:businessId/products/:productId/variants` - `GET/PUT /businesses/:businessId/products/:productId/variations` ### Technical forms - `GET/PUT /businesses/:businessId/categories/:categoryId/technical-form` - `POST .../technical-form/ai-suggest` - `GET/PUT /businesses/:businessId/products/:productId/technical-info` ### Media - `POST /businesses/:businessId/media` — upload to S3 --- ## Feature status (business app) ### Connected to backend - Auth, tenant, business context - Product categories, variations, products CRUD, media upload - Product variation values, store items, draft cart, admin orders - Category technical data forms, product technical data - Product/store settings, blog, portfolios, contact submissions ### Still local / dummy data - Product comments modal (`apps/business/src/data/productComments.ts`) - Legacy seeds in `apps/business/src/data/` --- ## Product variations — business rule **Product Variations modal:** user selects which category variation **values** apply to the product. Saved via `PUT .../products/:id/variations`. **Store items:** sellable variants with one value per variation type + price + stock. Saved via `POST .../store-items` (batch). --- ## Database migrations (Backend) Run in order from `MeshkeeApp Backend/database/migrations/`: | Migration | Purpose | |-----------|---------| | `009_categories_name_fa.sql` | `name_fa` on categories — **required** or categories 500 | | `010_category_variations.sql` | variation tables | | `016_product_variation_values.sql` | product variation values | After schema changes: `npx prisma generate` and restart the backend. --- ## Key files by area ### Shared packages | Area | Path | |------|------| | API client factory | `packages/dashboard-core/src/api/createApiClient.ts` | | IRT price utils | `packages/dashboard-core/src/utils/irtPrice.ts` | | Design tokens | `packages/dashboard-core/src/styles/tokens.css` | | Toast, Breadcrumbs, etc. | `packages/dashboard-ui/src/` | ### Business app | Area | Path | |------|------| | Categories | `apps/business/src/pages/CategoriesPage.tsx` | | Products | `apps/business/src/pages/MyProductsPage.tsx`, `AddNewProductPage.tsx` | | Store / orders | `apps/business/src/pages/StoreItemsPage.tsx`, `OrdersPage.tsx` | | Services | `apps/business/src/services/` | | ID helper | `apps/business/src/utils/id.ts` — `createId()` fallback | ### Backend (sibling repo) | Area | Path | |------|------| | Products | `src/products/products.service.ts` | | Categories | `src/categories/categories.service.ts` | | Media / S3 | `src/media/media.service.ts` | | Prisma schema | `prisma/schema.prisma` | --- ## UI conventions Match **Super Admin** compact field sizing across all dashboards — see `.cursor/rules/ui-compact-fields.mdc`: ```css --field-height: 38px; --field-padding-y: 9px; --field-font-size: 13px; ``` Global input sizing lives in `packages/dashboard-core/src/styles/tokens.css` (customer) and each app’s `index.css` (business / super-admin). Change password → shared `PasswordResetModal` from `@meshkee/dashboard-ui`. - **Farsi text:** Customer app → Yekan Bakh (`src/fonts/yekanbakh.css`, `--font-fa` in `index.css`); business/super-admin → IranYekan (`iranyekan.css`, `.faText`). Input + placeholder must share the same stack — see `.cursor/rules/ui-farsi-fonts.mdc` - **Per-business theme:** Super Admin sets `branding.primaryColor`; business app applies via `BusinessThemeProvider` - **RTL:** Farsi inputs use `dir="rtl"` and the app’s `--font-fa` - **IRT prices:** comma-separated thousands + `IRT` suffix — see `.cursor/rules/ui-irt-price.mdc` - **Toasts:** see `.cursor/rules/ui-toasts.mdc` - **Icon controls:** see `.cursor/rules/ui-control-buttons.mdc`, `ui-tooltips.mdc` --- ## Known issues & fixes | Problem | Cause | Fix | |---------|-------|-----| | Categories 500 | Missing `name_fa` column | Run migration `009` | | `crypto.randomUUID is not a function` | Non-secure origin | Use `createId()` from `apps/business/src/utils/id.ts` | | Media upload 400 | Bad MIME / empty data URL | `ensureJpegUploadFile()`; backend Sharp validation | | Super Admin HTTPS warning | Missing mkcert certs | Run mkcert per `apps/super-admin/.env.example` | | Workspace approval popups | Wrong workspace root | Open `MeshkeeApp` monorepo root in Cursor | --- ## Build commands ```bash npm run build:packages # shared packages only npm run build:customer npm run build:business npm run build:super-admin npm run build # everything ``` --- ## Git & handoff checklist 1. Push **MeshkeeApp** monorepo and **MeshkeeApp Backend** to remote 2. Copy `.env` files securely (never commit) 3. Run migrations on the database 4. Open **MeshkeeApp** as Cursor workspace root 5. Start a new agent chat: **"Read `docs/PROJECT_CONTEXT.md` and continue…"** --- ## Branding: logo → favicon When a business logo is uploaded/changed in **Business Profile**, the backend generates a **48×48 PNG favicon** (sharp), stores it as media, and links `businesses.favicon_media_id`. | Surface | How favicon is applied | |---------|------------------------| | Business dashboard | `TenantBrandingProvider` → `applyDocumentFavicon` | | Customer dashboard | same | | Public website (sanihome) | `generateMetadata().icons` from `faviconUrl` | APIs expose `faviconUrl` (falls back to `logoUrl` if favicon missing): - `GET /businesses/:id/profile` - `GET /tenants/:host` - `GET /tenants/:host/website/business-info` Migration: `database/migrations/033_business_favicon.sql` --- ## Production deploy (dashboards) | | | |--|--| | VPS | `45.149.76.52` | | Super Admin | `https://manage.meshkee.com` | | Business | `https://business.` (e.g. `business.sanihome.ir`) | | Customer | `https://customer.` | | API | `https://api.meshkee.com/api/v1` (host `185.164.72.119`) | | Docs | `docs/DEPLOY.md` | SSL: Certbot cert `meshkee-dashboards` + cron `ssl-sync.sh` every 2h (option A: `GET /api/v1/internal/ssl/hosts`). --- ## Suggested next work - Migrate business and super-admin to `@meshkee/dashboard-core` / `@meshkee/dashboard-ui` - Connect product comments to backend - Wire order status transitions to store `orderProcessSteps` - Enforce `onlineSellEnabled` on public website checkout - Postman collection updates for variants endpoints - Add `sanihome.ir` (and other tenants) in production Super Admin so tenant APIs resolve --- ## Agent instructions 1. **Minimize scope** — only change what the task requires 2. **Match existing patterns** — services in `src/services/`, CSS modules, NestJS modules per domain 3. **Do not commit** unless the user explicitly asks 4. **Test user:** `+989122222222` / `password`; business id **4** for sanihome.ir 5. **Restart backend** after Prisma or module changes 6. Update **this file** when completing a major feature