# MesurePro Next

> **SaaS B2B mobile-first pour pros du bâtiment (BE/FR)** — mesures terrain, rapports PDF, devis, assistant IA, multi-tenant.

![Status](https://img.shields.io/badge/sprint--1-scaffolded-success)
![Sprint 2](https://img.shields.io/badge/sprint--2-in--progress-yellow)
![License](https://img.shields.io/badge/license-MIT-blue)
![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen)

---

## Stats

| Dimension | Valeur |
|---|---|
| Sprints | 5 |
| Durée totale | 10 semaines |
| Fichiers scaffold Sprint 1 | 55+ |
| Workspaces | 3 (`api`, `mobile`, `shared`) |
| Routes API REST | **51** endpoints répartis sur 12 sous-routers (auth, clients, users, interventions, mesures, photos, rapports, devis, articles, ai, webhooks, dashboard) — voir Swagger UI à `/documentation` |
| Pays cibles | Belgique, France |

---

## Stack

| Couche | Techno |
|---|---|
| Mobile | React Native + Expo SDK 51 |
| Navigation | Expo Router v3 |
| State | Zustand + React Query |
| API | Node.js 22 LTS + Fastify |
| ORM | Prisma v5 |
| DB | PostgreSQL 16 (RLS multi-tenant) |
| Cache | Redis 7 |
| Sync offline | PouchDB + CouchDB |
| Auth | Auth0 + JWT RS256 |
| IA | Claude API (streaming SSE) |
| PDF | Puppeteer + Handlebars |
| Paiements | Stripe (cartes, abonnements) + Mollie (Bancontact / iDEAL / SEPA BE/NL) |
| Email | Resend |
| Stockage | Cloudflare R2 |
| Monitoring | Pino + Sentry |
| Validation | Zod (schemas partagés) |
| Tests | Vitest (api) + Jest (mobile) |
| CI | GitHub Actions |
| Monorepo | Turbo + npm workspaces |

---

## Quick start

```bash
git clone <repo-url>
cd mesurepro-next

# 1. Copier les variables d'env (remplir au minimum DATABASE_URL + Auth0 + Anthropic)
cp .env.example .env

# 2. Bootstrap (Docker compose + npm install + prisma generate)
bash scripts/setup.sh

# 3. Lancer API + Mobile en parallèle
npm run dev
```

Une fois démarré :

| URL | Service |
|---|---|
| http://localhost:3000 | API Fastify |
| http://localhost:3000/documentation | Swagger UI |
| http://localhost:5984/_utils | Fauxton CouchDB |
| http://localhost:8025 | MailHog (email dev) |
| `npx expo start` | App mobile (QR Expo Go) |

---

## Commandes principales

| Action | Commande |
|---|---|
| Setup complet | `bash scripts/setup.sh` |
| Dev API | `npm run dev:api` |
| Dev Mobile | `npm run dev:mobile` |
| Dev tout en // | `npm run dev` |
| Build tout | `npm run build` |
| Lint global | `npm run lint` |
| Typecheck global | `npm run typecheck` |
| Tests | `npm test` |
| Format Prettier | `npm run format` |
| Docker (re)start | `npm run docker:up` |
| Docker reset volumes | `npm run docker:reset` |
| DB migrate (dev) | `npm run db:migrate` |
| DB seed | `npm run db:seed` |
| DB studio | `npm run db:studio` |
| Reset DB complet | `bash scripts/reset-db.sh` |

---

## Documentation

| Document | Sujet |
|---|---|
| [`CLAUDE.md`](./CLAUDE.md) | Fichier maître Claude Code (ADR, sprints, conventions, pièges) |
| [`docs/architecture.md`](./docs/architecture.md) | Architecture haut niveau + flux offline-first |
| [`docs/adrs.md`](./docs/adrs.md) | Architecture Decision Records (7 ADR) |
| [`docs/api-conventions.md`](./docs/api-conventions.md) | Conventions REST (versioning, formats, pagination) |
| [`docs/auth-flow.md`](./docs/auth-flow.md) | Flow Auth0 + JWT RS256 + tenant injection |
| [`docs/sync-offline.md`](./docs/sync-offline.md) | Stratégie sync PouchDB ↔ CouchDB |
| [`docs/tva-rules.md`](./docs/tva-rules.md) | Règles TVA BE/FR (refs légales) |
| [`docs/contributing.md`](./docs/contributing.md) | Workflow Git, conventional commits, code review |

---

## Sprints

| # | Période | Thème | Status |
|---|---|---|---|
| 1 | Sem. 1-2 | Auth + Infrastructure | Scaffold |
| 2 | Sem. 3-4 | Module Mesures (core) | À venir |
| 3 | Sem. 5-6 | Rapports PDF | À venir |
| 4 | Sem. 7-8 | Devis + Assistant IA | À venir |
| 5 | Sem. 9-10 | Équipes + Facturation | À venir |

Détail dans [`CLAUDE.md`](./CLAUDE.md#-plan-des-5-sprints-10-semaines).

---

## Sécurité — register-tenant

L'endpoint public `POST /api/v1/auth/register-tenant` est protégé par
**deux mécanismes** (audit P1-1, 13 mai 2026) :

1. **Rate-limit dédié** : 3 requêtes maximum / heure / IP, isolé du
   rate-limit global (n'affecte pas les autres routes).
2. **Vérification JWT Auth0** : si un header `Authorization: Bearer <jwt>`
   est présent, l'API extrait `sub` + `email` du token (priorité absolue)
   et IGNORE les champs `auth0Sub` / `adminEmail` du body (défense en
   profondeur — empêche un attaquant d'usurper un `sub` arbitraire).

### Mode legacy (dev uniquement)

| Var env | Défaut | Effet |
|---|---|---|
| `ALLOW_LEGACY_REGISTER` | `true` en dev/test, `false` en prod | Si `true` ET aucun JWT envoyé, l'API accepte `auth0Sub` + `adminEmail` du body avec un warning Pino. À NE JAMAIS activer en prod. |

En prod (`NODE_ENV=production`), le défaut est `false` : toute requête
`register-tenant` sans `Authorization: Bearer <jwt>` retourne `401`.

Pour basculer explicitement en prod (dev locale, démos…) :
```bash
export ALLOW_LEGACY_REGISTER=true
```

---

## Paiements (Stripe + Mollie)

L'API supporte deux providers de paiement, mutuellement exclusifs côté tenant
(au choix selon le pays / la méthode demandée).

### Variables d'environnement

| Variable | Service | Note |
|---|---|---|
| `STRIPE_SECRET_KEY` | Stripe | `sk_test_…` / `sk_live_…`. Absent → mode stub (URL factice). |
| `STRIPE_WEBHOOK_SECRET` | Stripe | `whsec_…` requis pour vérifier la signature webhook. |
| `STRIPE_PRICE_STARTER` / `_PRO` / `_BUSINESS` | Stripe | Price-IDs Stripe Checkout. |
| `MOLLIE_API_KEY` | Mollie | `test_…` / `live_…`. Absent ou `test_dummy*` → mode stub. |

### Webhooks

- `POST /api/v1/webhooks/stripe` — vérifie la signature `stripe-signature`, parse
  l'event, update `tenant.plan` + `tenant.status` + `tenant.stripeCustomerId`.
- `POST /api/v1/webhooks/mollie` — reçoit `id=tr_…` (Mollie n'envoie jamais le
  status pour éviter le spoofing), refait un `GET /v2/payments/:id` côté API
  Mollie pour récupérer le status réel, puis update `tenant.plan` +
  `tenant.status` + `tenant.mollieCustomerId`.

Mollie doit être configuré pour appeler le webhook en `x-www-form-urlencoded`
(c'est le défaut du SDK). L'API retourne `200` même en cas de status
échoué/inconnu pour éviter les retries inutiles, et `500` seulement si la mise
à jour DB échoue (Mollie retry alors automatiquement).

---

## Coexistence avec mesurepro v1

`~/Desktop/.../mesurepro/` (webapp Vite + R3F mono-fichier) reste **en prod** sur https://geniuspro71.github.io/mesurepro/ comme démo publique du concept.

`mesurepro-next/` (ce repo) est le **SaaS B2B monétisable** destiné aux artisans/entreprises avec besoins d'équipes, factures, signatures clients, sync multi-device.

Les deux produits ne partagent **rien en runtime**. Les apprentissages produit/UX de v1 informent le design de next, c'est tout.

---

## License

[MIT](./LICENSE) © 2026 Davide Zaffaroni.
