# Balout Pastry — Backend (setup context) NestJS API for شیرینی‌فروشی بلوط. Serves the admin Dashboards SPA (and future customer apps). Repo: `https://git.meshkee.com/BaloutPastry/backend.git` Dashboards repo: `https://git.meshkee.com/BaloutPastry/dashboards.git` ## Stack - NestJS 11 / TypeScript - Prisma 6 + PostgreSQL 16 (Docker Compose) - JWT access + refresh tokens (bcrypt password hashes) - Parspack S3-compatible object storage (prefix `balout/`) ## Prerequisites - Node.js **20+** (LTS recommended) - npm - Docker + Docker Compose (for local Postgres) - S3 credentials if you need media upload (can leave blank for auth/users-only local work) ## Setup on a new device ```bash git clone https://git.meshkee.com/BaloutPastry/backend.git cd backend cp .env.example .env # Fill S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY if you need uploads # Change JWT_* secrets before any shared/staging use npm install npm run db:up # Postgres on 127.0.0.1:5434 npx prisma migrate deploy # apply migrations npx prisma generate # if client missing after install npm run create-super-admin -- --phone 09120000000 --password secret123 # optional: --first علی --last رضایی --title "جناب آقای" npm run start:dev # http://localhost:3100/api/v1 ``` Health check: `GET http://localhost:3100/api/v1/auth/me` → `401` without token is expected (server is up). ### Pair with Dashboards 1. Keep this API on **3100** 2. In dashboards: `VITE_API_BASE_URL=http://localhost:3100/api/v1` 3. Backend `CORS_ORIGIN` must include local dashboard origins, e.g. `http://baloutpastry.com:5173,http://admin.baloutpastry.com:5173,http://customer.baloutpastry.com:5173,http://localhost:5173` 4. Add local DNS in `/etc/hosts` for `baloutpastry.com`, `admin.baloutpastry.com`, `customer.baloutpastry.com` → `127.0.0.1` 5. Log in with the super-admin phone/password you created - Admin: `http://admin.baloutpastry.com:5173` - Customer: `http://customer.baloutpastry.com:5173` ## Scripts | Command | What it does | |---------|----------------| | `npm run start:dev` | Nest watch mode → `http://localhost:3100/api/v1` | | `npm run start:prod` | Run compiled `dist/main` | | `npm run build` | Compile Nest → `dist/` | | `npm run db:up` | `docker compose up -d` (Postgres) | | `npm run db:down` | Stop Postgres container | | `npm run prisma:migrate` | Create/apply migrations in dev | | `npm run prisma:deploy` | Apply existing migrations (CI / new device) | | `npm run prisma:generate` | Regenerate Prisma Client | | `npm run create-super-admin` | Create first `superAdmin` user | | `npm run send-sms` | Send SMS via Meshkee (`--to` / `--message`) | | `npm run lint` | ESLint | ## Environment Copy from `.env.example`. Do **not** commit `.env`. | Variable | Notes | |----------|--------| | `DATABASE_URL` | Prisma connection string (default → local Docker on **5434**) | | `POSTGRES_*` | Used by Docker Compose | | `PORT` | API port, default **3100** | | `CORS_ORIGIN` | Comma-separated origins; include dashboard URL | | `JWT_ACCESS_SECRET` / `JWT_REFRESH_SECRET` | Change from example values | | `JWT_ACCESS_EXPIRES_IN` / `JWT_REFRESH_EXPIRES_IN` | e.g. `15m` / `7d` | | `STORAGE_DISK` | `s3` for Parspack | | `S3_*` | Endpoint, bucket, public URL, keys | | `MEDIA_MAX_FILE_SIZE_MB` | Upload size cap | | `MESHKEE_SMS_URL` | Meshkee public SMS send endpoint | | `MESHKEE_SMS_API_KEY` | Partner API key (`X-Api-Key`) — server only | | `MESHKEE_SMS_DOMAIN` | Partner domain, e.g. `baloutpastry.com` | Inject `SmsService` from `SmsModule` to send SMS from the backend (never from the frontend). Limits: 30/partner/min, 5/destination/min. ```bash npm run send-sms -- --to 09127004945 --message "متن پیام" ``` Ports are intentional vs Meshkee: API **3100**, Postgres host **5434** (Meshkee uses 3000 / 5432). ## Auth rules - Login: `POST /auth/login` with `{ "cellNumber": "09…", "password": "…" }` - Login with SMS: `POST /auth/login/send-code` then `POST /auth/login/verify` - Forgot password: `POST /auth/forgot-password/send-code` → `/verify` → `/reset` - Register: `POST /auth/register/send-code` then `POST /auth/register/verify` (SMS OTP → create `customer`) - Only `admin` and `superAdmin` can log in to the admin API/dashboard - Only `superAdmin` can assign `admin` or `superAdmin` roles - `customer` users exist for orders / future customer UI - Access token in `Authorization: Bearer …`; refresh via `POST /auth/refresh` ## Main routes (`/api/v1`) | Area | Methods | |------|---------| | Auth | `POST /auth/login`, `/auth/login/send-code`, `/auth/login/verify`, `/auth/forgot-password/send-code`, `/auth/forgot-password/verify`, `/auth/forgot-password/reset`, `/auth/register/send-code`, `/auth/register/verify`, `/auth/refresh`, `/auth/logout`, `GET /auth/me` | | Users | CRUD + `PATCH /users/:id/role`, `/password` + addresses under `/users/:id/addresses` | | Flavors | CRUD | | Categories | tree CRUD + `GET\|PUT /categories/:id/options` | | Products | CRUD (options must match category templates); list filters `q`, `categoryId`, `minPrice`, `maxPrice` | | Orders | `GET /orders`, `GET /orders/:id`, `POST /orders`, `PATCH /orders/:id/status` | | Media | `POST /media/upload?kind=main\|gallery\|temp` | | Settings | districts, shipping exceptions, branches | Guards: most routes require JWT + `admin`/`superAdmin`. Role changes require `superAdmin`. ## Orders Create body example: ```json { "customerId": "...", "deliveryType": "pickup", "branchId": "...", "note": "optional", "items": [ { "productId": "...", "quantity": 2, "optionIds": ["..."] } ] } ``` For shipping: `deliveryType: "shipping"` + `shippingAddressId` (from user addresses). Prices/names are snapshotted. Display code: `BL-{number}`. ## Domain notes - Prices are integer **تومان** - Phone numbers: `09` + 9 digits (English digits) - User titles/categories are fixed Persian enums in `src/users/users.constants.ts` ## Project layout ``` prisma/ schema + migrations scripts/ create-super-admin.ts src/ auth/ JWT login, refresh, guards, roles users/ users + addresses flavors/ categories/ products/ orders/ media/ uploads settings/ districts, shipping, branches storage/ S3 driver prisma/ PrismaModule / PrismaService main.ts global prefix api/v1, CORS, ValidationPipe docker-compose.yml local Postgres ``` ## Production sketch ```bash cp .env.example .env # real secrets, DB, CORS, S3 npm ci npx prisma migrate deploy npm run build npm run start:prod ``` Ensure Postgres is reachable via `DATABASE_URL` and `CORS_ORIGIN` lists the real dashboard origin(s). ## Common issues | Symptom | Likely cause | |---------|----------------| | `ECONNREFUSED` / Prisma errors | Postgres not up → `npm run db:up`, check `DATABASE_URL` / port **5434** | | Port already in use | Another process on 3100 or 5434 | | Login rejected for valid user | Role is `customer`, or wrong phone format | | CORS errors from dashboard | `CORS_ORIGIN` missing `http://localhost:5173` | | Upload fails | Missing/invalid `S3_*` keys or endpoint | | `User already exists` on create-super-admin | That phone is already in DB | ## Related - Dashboards setup: clone `BaloutPastry/dashboards` and read `CONTEXT.md` - Website (storefront): clone `BaloutPastry/website` and read `CONTEXT.md` (dev port **5174**) - Short API overview also in `README.md`