feat(docker): multi-stage Dockerfiles with npmmirror registry

Rewrites dashboard and finder Dockerfiles to use a clean multi-stage
build (deps → builder → runner) that installs npm packages inside
Alpine Linux, avoiding the SWC musl binary issue when building from
Windows host. Uses registry.npmmirror.com for reliable installs from
restricted networks (Iran).

- docker/api/Dockerfile: .NET 10 multi-stage build
- docker/web/Dockerfile: Node 20-alpine multi-stage, npmmirror
- docker/finder/Dockerfile: Node 20-alpine multi-stage, npmmirror
- docker/website/Dockerfile: marketing website build
- scripts/: PowerShell helper scripts for local dev

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-05-27 21:33:29 +03:30
parent 45cd028d1c
commit 03376b3ea1
20 changed files with 5519 additions and 0 deletions
+230
View File
@@ -0,0 +1,230 @@
# Meezi — Current State & Handoff for Next-Step Planning
> **Purpose:** Give this file to Claude (or any planner) to design the next implementation batch.
> **Product:** Meezi (میزی) — Persian-first SaaS POS + community for Iranian cafés (Tehran/Karaj V1).
> **Full product context:** `.cursorrules`, `MEEZI_CURSOR_GUIDE.md` (read before planning).
**Last updated:** 2026-05-21
---
## 1. Stack snapshot (current)
| Layer | Technology | Location |
|-------|------------|----------|
| Backend | **ASP.NET Core 10**, C# | `src/Meezi.API` |
| Core / infra | EF Core **10**, Npgsql | `src/Meezi.Core`, `src/Meezi.Infrastructure` |
| Web dashboard | Next.js 14, TypeScript, next-intl | `web/dashboard` |
| Mobile | Flutter 3 | `mobile/meezi_app`, `mobile/meezi_pos` |
| DB / cache | PostgreSQL 16, Redis | `docker-compose.yml` |
| Jobs / realtime | Hangfire, SignalR KDS | API |
| SDK pin | `global.json` → 10.0.100 | repo root |
| Central packages | `Directory.Packages.props` | repo root |
| Target framework | `net10.0` via `Directory.Build.props` | all C# projects |
**Build / test status (local):**
- `dotnet build src/Meezi.API/Meezi.API.csproj -c Release`**OK**
- `dotnet test tests/Meezi.API.Tests/Meezi.API.Tests.csproj -c Release`**13/13 passed**
- CI (`.github/workflows/ci.yml`) — API on `10.0.x`, web `npm run build`, Flutter analyze (continue-on-error)
---
## 2. Recently completed (do not re-plan unless fixing gaps)
### 2.1 .NET 10 migration
- All backend projects on `net10.0`.
- Central package management; Microsoft + EF + Npgsql at **10.0.0**.
- FluentValidation: `FluentValidation` + `FluentValidation.DependencyInjectionExtensions` (no `FluentValidation.AspNetCore`).
- Docker API image: `dotnet/sdk:10.0`, `dotnet/aspnet:10.0`; Dockerfile copies `global.json`, `Directory.Build.props`, `Directory.Packages.props`.
- `Program.cs` refactored: `Program.BuildWebApplication(args, configureBeforeServices, configureAfterServices)` + `Main` for integration tests.
- Testing mode: `Testing:Enabled=true` → Hangfire memory storage, no Hangfire server/dashboard/recurring jobs; faster Redis connect options.
- Fix: `RefreshTokenStore` uses `value.ToString()` for JSON deserialize (.NET 10 overload ambiguity).
- Integration tests: `MeeziWebApplicationFactory` + `UseTestServer()`, in-memory EF, config **before** `AddMeeziServices`.
### 2.2 POS table-session workflow (backend + dashboard)
**Domain / DB**
- `Order.GuestPhone`, `Table.IsCleaning`, `TableBoardStatus.Cleaning`.
- Migration: `PosTableSessionFields` (`20260520165836_PosTableSessionFields`).
**API (`OrderService`, controllers)**
- Single open order per table (merge/upsert).
- `POST /api/cafes/{cafeId}/orders/{id}/items` — append lines.
- `PATCH /api/cafes/{cafeId}/orders/{id}/session` — guest name/phone, customer link.
- `GET .../orders/open?search=` — open orders search.
- `GET .../tables/{id}/active-order`.
- `PATCH .../tables/{id}/cleaning`.
- Guards: `TABLE_OCCUPIED`, cleaning blocks new orders.
- `OrderDto`: `GuestPhone`, `CustomerPhone`, `PaidAmount`, `Payments[]`.
**Dashboard POS (`web/dashboard`)**
- `pos-table-board.tsx` — table board (order + pay modes).
- `pos-screen.tsx` — URL session `?tableId=&orderId=`, hydrate/append, debounced session PATCH.
- `pos-pay-panel.tsx` — pay by table board, dropdown, search, split payments (Cash/Card/Credit).
- `pos-customer-picker.tsx` — CRM search + quick-create guest.
- `cart.store.ts``customerId`, `activeOrderId`, `hydrateFromOrder`, `getPendingLines`.
- i18n: `fa.json`, `en.json`, `ar.json` POS strings.
**Tests**
- `tests/Meezi.API.Tests/OrderSessionTests.cs` — merge per table, append, search, payment frees board, cleaning block (in-memory DB).
### 2.3 Docker / local dev (Iran constraints)
- `docker-compose.yml` — default `up` runs full stack (no `full` profile gate).
- `docker/api/Dockerfile` — Docker Hub images (not `mcr.microsoft.com`).
- `scripts/docker-up-full.ps1`, `scripts/run-local-dev.ps1`, `docs/DOCKER.md`, registry mirror example.
- **Known ops:** Docker Desktop/WSL 500/EOF; image pulls often need VPN/mirror in Iran. Documented workaround: postgres+redis in Docker, API via `dotnet run`, dashboard via `npm run dev`.
---
## 3. Key files (quick navigation)
```
src/Meezi.API/
Program.cs # BuildWebApplication + Main
Extensions/ServiceCollectionExtensions.cs # DI, Hangfire, Redis, Testing:Enabled
Services/OrderService.cs # Table session / append / search
Services/TableService.cs # Board, cleaning
Services/RefreshTokenStore.cs
Controllers/OrdersController.cs
Controllers/TablesController.cs
src/Meezi.Core/Entities/
Order.cs, Table.cs
src/Meezi.Infrastructure/Data/Migrations/
*PosTableSessionFields*
web/dashboard/src/components/pos/
pos-screen.tsx, pos-table-board.tsx, pos-pay-panel.tsx, pos-customer-picker.tsx
web/dashboard/src/lib/stores/cart.store.ts
tests/Meezi.API.Tests/
OrderSessionTests.cs
Integration/HealthIntegrationTests.cs
Integration/MeeziWebApplicationFactory.cs
docker/api/Dockerfile
docker-compose.yml
global.json, Directory.Build.props, Directory.Packages.props
```
---
## 4. Open issues & tech debt (good planning inputs)
| Item | Severity | Notes |
|------|----------|--------|
| NU1903 AutoMapper 12.0.1 | Medium | Transitive vulnerability warning on build |
| NU1903 System.Security.Cryptography.Xml 9.0.0 | Medium | Via Infrastructure transitive |
| Docker full stack in Iran | Ops | Pull timeouts; mirror/VPN documented |
| E2E tests | Low | No Playwright/API E2E for POS flow yet |
| Flutter / mobile | — | Not updated for table-session APIs |
| `MEEZI_PRD.md` | — | Referenced in rules but may be missing at repo root; use `.cursorrules` + guide |
| Hangfire in production | — | PostgreSQL storage; tests use memory only when `Testing:Enabled` |
---
## 5. Suggested themes for the *next* planning batch
Use these as prompts for Claude; pick 13 per sprint.
### A. Hardening & quality
- Bump or replace packages with NU1903 warnings.
- More integration tests: auth, append-items HTTP, payment split, cleaning API.
- E2E: dashboard POS happy path (table → items → pay → board free).
### B. POS / operations polish
- Receipt print preview / thermal bridge alignment with session order id.
- Table board realtime (SignalR invalidate on order/payment/cleaning).
- Edge cases: void line, transfer table, merge tables, staff permissions per action.
### C. CRM & customer on orders
- Enforce plan limits on CRM create from POS picker.
- Sync `GuestPhone` with SMS OTP / Kavenegar flows where applicable.
- Customer history on pay panel (last visit, points if in scope).
### D. Infrastructure & deploy
- Verify `docker compose` build on .NET 10 in CI (optional job).
- Arvan Cloud deploy checklist; env-specific `appsettings`.
- Redis required services: mock or Testcontainers for CI integration tests.
### E. Mobile (Flutter POS)
- Offline Drift sync for open table orders.
- Call new append/session/active-order endpoints from `meezi_pos`.
### F. Billing / plan limits
- Enforce `PLAN_LIMIT_REACHED` on daily orders, terminals, SMS from POS paths.
- Upgrade CTA in dashboard when limits hit.
---
## 6. API conventions (must keep in plans)
- Multi-tenant: every EF query filters `CafeId == _tenant.CafeId`.
- Responses: `ApiResponse<T>` / `ApiError` with codes e.g. `PLAN_LIMIT_REACHED`, `TABLE_OCCUPIED`.
- Protected routes: `/api/cafes/{cafeId}/...` — JWT `cafeId` must match.
- i18n: no hardcoded UI strings in dashboard; use `messages/{fa,ar,en}.json`.
- RTL: `ms-*` / `me-*` only in dashboard CSS.
---
## 7. How to run locally (for validators)
```powershell
# DB + Redis
docker compose up -d postgres redis
# API (.NET 10)
cd src/Meezi.API
dotnet run
# http://localhost:5080 (or per launchSettings)
# Dashboard
cd web/dashboard
npm run dev
# http://localhost:3101 (typical)
# Tests
dotnet test tests/Meezi.API.Tests/Meezi.API.Tests.csproj -c Release
```
**appsettings.json (API):** Postgres `localhost:5434`, Redis `localhost:6381` — matches `docker-compose` / `.env.example` defaults.
---
## 8. Planning instructions for Claude
When generating a plan from this file:
1. Read `.cursorrules` and `MEEZI_CURSOR_GUIDE.md` for non-negotiables.
2. Do **not** redo .NET 10 migration or POS table-session core unless fixing a listed gap.
3. Propose **small, reviewable PRs** (backend / web / mobile / infra separated).
4. Include **test plan** per feature (unit + manual steps).
5. Call out **Iran/Docker** constraints if the plan involves container builds or image pulls.
6. Respect plan tiers: Free / Pro / Business / Enterprise limits on orders, CRM, SMS, branches.
---
## 9. Out of scope for next batch (unless user asks)
- Rewriting Next.js or Flutter major versions.
- Full Sepidz parity / enterprise white-label.
- Tax (Taraz) production integration beyond stubs.
- Snappfood production webhook hardening (HMAC exists; expand when needed).
---
*End of handoff — attach this file plus the specific user goal when asking Claude to plan.*