# đ Documentation API - Plateforme de Gestion de Commandes
**Version:** 5.4.0
**Date:** 2026-06-11
**Base URL prod:** `https://mln-uber.club` (HTTPS via WAF nginx + ModSecurity)
**Base URL dev:** `http://localhost:8080`
**Technologies:** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19 + Vite, Expo 54 (React Native), TomTom API
## đ Table des MatiĂšres
1. [Vue d'ensemble](#vue-densemble)
2. [Infrastructure Serveurs](#-infrastructure-serveurs)
3. [Déploiement Production](#-déploiement-production)
4. [Authentication](#authentication)
5. [API Client (v1)](#api-client-v1)
6. [API Admin (v2)](#api-admin-v2)
7. [API Cabine (v1)](#api-cabine-v1)
8. [API Livreur (v1)](#api-livreur-v1)
9. [Notifications Push & Telegram](#-notifications-push--telegram)
10. [Paiements Crypto](#-paiements-crypto)
11. [Systeme GPS Integre](#-systeme-gps-integre)
12. [Codes d'Erreur](#codes-derreur)
---
## đŻ Vue d'ensemble
Cette API REST complÚte gÚre une plateforme de livraison avec assignation automatique GPS des livreurs, suivi en temps réel, et systÚme de pénalités avancé.
### ⚠Fonctionnalités Principales
- **đ Authentication JWT** multi-rĂŽles (Client, Admin, Livreur, Cabine) avec rĂ©vocation de token
- **đ Auto-assignation GPS** des livreurs les plus proches (worker toutes les 1 minute)
- **đ Suivi temps rĂ©el** avec ETA et gĂ©olocalisation
- **đ SystĂšme de queues** Redis optimisĂ© pour les livraisons
- **⥠Workers automatiques** (nettoyage, assignation, notifications)
- **đŻ SystĂšme de pĂ©nalitĂ©s clients** â amendes progressives sur annulations (livreurs non concernĂ©s)
- **đ€ Notifications Telegram** pour clients, livreurs et admins (push Expo abandonnĂ©)
- **đž Paiements crypto** via NowPayments (webhook HMAC)
- **đĄïž WAF nginx + ModSecurity** (OWASP CRS) en production
- **đ± Applications mobiles** Expo 54 (client + admin/livreur)
- **đ·ïž Prix par quantitĂ© activables/dĂ©sactivables** â visibilitĂ© client filtrĂ©e automatiquement
- **â±ïž Bouton "Client absent"** â timer 5 min au statut `arrived`, amende automatique appliquĂ©e au **client** si absent
- **đČ OTA updates** canaux nommĂ©s par rĂŽle (`pre-prod-client`, `production-client`, `pre-prod-admin`, etc.)
### đïž Stack Technique
| Composant | Technologie |
|-----------|-------------|
| **Backend** | Go 1.24 + Gin · port 8080 |
| **Base de données** | PostgreSQL 16 |
| **Cache / Sessions / Queues** | Redis 7 |
| **Frontend web** | React 19 + TypeScript + Vite (`frontend-prep/`) |
| **App mobile client** | Expo 54 + React Native (`mobile/`) |
| **App mobile admin/livreur** | Expo 54 + React Native (`frontend-admin/`) |
| **WAF / Reverse proxy** | Nginx + ModSecurity OWASP CRS |
| **Géocodage** | Nominatim (OpenStreetMap) |
| **Routing / ETA** | TomTom Routing API |
| **Push notifications** | Expo Push Notifications |
| **Paiements** | NowPayments (crypto) |
| **Messagerie** | Telegram Bot API |
---
### đïž Architecture Globale du Systeme
```mermaid
graph TB
subgraph Frontend["đ„ïž Frontend (React/TypeScript)"]
UI_CLIENT[Interface Client]
UI_ADMIN[Interface Admin]
UI_LIVREUR[Interface Livreur]
UI_CABINE[Interface Cabine]
TOMTOM_MAP[TomTomMap Component]
end
subgraph API_GATEWAY["đ API Gateway (Go/Gin)"]
AUTH[Middleware Auth JWT]
ROUTES[Router]
end
subgraph API_V1["đ± API v1"]
CLIENT_API["/api/v1/client"]
LIVREUR_API["/api/v1/livreur"]
CABINE_API["/api/v1/cabine"]
PUBLIC_API["/api/v1/public"]
end
subgraph API_V2["đšâđŒ API v2 Admin"]
ADMIN_API["/api/v2/admin"]
end
subgraph HANDLERS["âïž Handlers"]
H_AUTH[Auth Handler]
H_PRODUCT[Product Handler]
H_PANIER[Panier Handler]
H_COMMAND[Command Handler]
H_DELIVERY[Delivery Handler]
H_GPS[GPS Handler]
H_ETA[ETA Handler]
H_PENALTY[Penalty Handler]
end
subgraph SERVICES["đ§ Services"]
S_GEO[Geo Service]
S_TOMTOM[TomTom Service]
S_QUEUE[Queue Service]
S_NOTIF[Notification Service]
end
subgraph EXTERNAL["đ APIs Externes"]
NOMINATIM[Nominatim API]
TOMTOM_API[TomTom API]
end
subgraph STORAGE["đŸ Stockage"]
POSTGRES[(PostgreSQL)]
REDIS[(Redis)]
end
subgraph WORKERS["⥠Workers"]
W_ASSIGN[Auto-Assign Worker]
W_CLEANUP[Cleanup Worker]
W_ETA[ETA Update Worker]
end
%% Frontend connections
UI_CLIENT --> AUTH
UI_ADMIN --> AUTH
UI_LIVREUR --> AUTH
UI_CABINE --> AUTH
TOMTOM_MAP --> TOMTOM_API
%% API Gateway
AUTH --> ROUTES
ROUTES --> API_V1
ROUTES --> API_V2
%% API to Handlers
CLIENT_API --> H_AUTH
CLIENT_API --> H_PANIER
CLIENT_API --> H_COMMAND
LIVREUR_API --> H_DELIVERY
LIVREUR_API --> H_GPS
CABINE_API --> H_COMMAND
PUBLIC_API --> H_PRODUCT
ADMIN_API --> H_COMMAND
ADMIN_API --> H_DELIVERY
ADMIN_API --> H_GPS
ADMIN_API --> H_PENALTY
%% Handlers to Services
H_GPS --> S_GEO
H_GPS --> S_TOMTOM
H_ETA --> S_TOMTOM
H_DELIVERY --> S_QUEUE
H_COMMAND --> S_NOTIF
%% Services to External
S_GEO --> NOMINATIM
S_TOMTOM --> TOMTOM_API
%% Services to Storage
S_GEO --> REDIS
S_QUEUE --> REDIS
H_COMMAND --> POSTGRES
H_PRODUCT --> POSTGRES
H_DELIVERY --> POSTGRES
%% Workers
W_ASSIGN --> S_GEO
W_ASSIGN --> S_QUEUE
W_ASSIGN --> POSTGRES
W_CLEANUP --> REDIS
W_CLEANUP --> POSTGRES
W_ETA --> S_TOMTOM
W_ETA --> REDIS
```
### đ Flux des Donnees par Role
```mermaid
flowchart LR
subgraph Client["đ€ Client"]
C1[Consulter Produits]
C2[Gerer Panier + Stock auto]
C3[Passer Commande]
C4[Suivre Livraison + ETA]
C5[Approuver/Annuler]
C6[Notifications Push]
C7[Solde Parrainage]
end
subgraph Admin["đšâđŒ Admin"]
A1[Gerer Produits/Clients]
A2[Voir/Modifier Commandes]
A3[Assigner Livreurs GPS]
A4[Gerer Penalites]
A5[Surveiller GPS + Queues]
A6[Parametres Globaux]
end
subgraph Livreur["đ Livreur"]
L1[Voir Mes Livraisons]
L2[Mettre a Jour Position GPS]
L3[Changer Statut]
L4[Voir Ma Queue]
L5[Alertes Police]
L6[Notifications Push]
end
subgraph Cabine["đ Cabine/Cuisine"]
K1[Voir Articles Commande]
K2[Preparer/Marquer Pret]
K3[Assigner Livreur]
K4[Gerer Penalites Client]
K5[Voir Alertes]
end
subgraph Backend["âïž Backend"]
API[API REST]
GPS[Service GPS]
QUEUE[Gestion Queue]
end
C1 & C2 & C3 & C4 & C5 & C6 & C7 --> API
A1 & A2 & A3 & A4 & A5 & A6 --> API
L1 & L2 & L3 & L4 & L5 & L6 --> API
K1 & K2 & K3 & K4 & K5 --> API
API --> GPS
API --> QUEUE
```
### đĄ Architecture des Endpoints API
```mermaid
graph TD
subgraph PUBLIC["đ Endpoints Publics (sans auth)"]
P1["GET /api/v1/products"]
P2["GET /api/v1/products/:id"]
P3["GET /api/v1/products/category/:category"]
P4["GET /api/v1/categories"]
P5["GET /api/v1/app-settings"]
P6["POST /api/v1/webhooks/nowpayments"]
P7["POST /webhook/telegram"]
end
subgraph AUTH_PUBLIC["đ Auth Publique (rate-limited)"]
AP1["POST /api/v1/auth/login"]
AP2["POST /api/v1/auth/logout"]
AP3["PUT /api/v1/auth/change-password (JWT)"]
end
subgraph CLIENT_AUTH["đ Client (JWT Required)"]
C1["POST /api/v1/panier/add"]
C2["GET /api/v1/panier/:username"]
C3["DELETE /api/v1/panier/remove"]
C4["DELETE /api/v1/panier/clear"]
C5["POST /api/v1/checkout"]
C6["GET /api/v1/my-commands"]
C7["GET /api/v1/my-commands/history"]
C8["GET /api/v1/my-commands/history/detailed"]
C9["GET /api/v1/commands/:id"]
C10["GET /api/v1/commands/:id/status"]
C11["GET /api/v1/commands/:id/tracking"]
C12["GET /api/v1/commands/:id/eta"]
C13["GET /api/v1/commands/:id/items"]
C14["GET /api/v1/commands/:id/history"]
C15["POST /api/v1/commands/:id/approve"]
C16["POST /api/v1/commands/:id/cancel"]
C17["POST /api/v1/commands/:id/address/respond"]
C18["GET /api/v1/my-cancellation-history"]
C19["GET /api/v1/penalties"]
C20["GET /api/v1/notifications"]
C21["POST /api/v1/notifications/read"]
C22["GET /api/v1/profile"]
C23["PUT /api/v1/profile/update"]
C24["GET /api/v1/referral/balance"]
C25["GET /api/v1/parrain"]
C26["GET /api/v1/commands/:id/payment-status"]
C27["POST /api/v1/telegram/link-token"]
C28["GET /api/v1/telegram/status"]
C29["DELETE /api/v1/telegram/unlink"]
end
subgraph ADMIN_AUTH["đšâđŒ Admin v2 (JWT Required)"]
A1["GET /api/v2/admin/protected/orders"]
A2["GET /api/v2/admin/protected/orders/:id"]
A3["PUT /api/v2/admin/protected/orders/:id/status"]
A4["PUT /api/v2/admin/protected/orders/:id/address"]
A5["POST /api/v2/admin/protected/orders/:id/notify-client"]
A6["POST /api/v2/admin/protected/orders/:id/auto-assign"]
A7["POST /api/v2/admin/protected/orders/auto-assign-all"]
A8["POST /api/v2/admin/protected/orders/:id/force-validate"]
A9["POST /api/v2/admin/protected/orders/:id/confirm-reception"]
A10["GET /api/v2/admin/protected/delivery-persons"]
A11["GET /api/v2/admin/protected/delivery-persons/:username"]
A12["GET /api/v2/admin/protected/delivery-persons/:username/location"]
A13["GET /api/v2/admin/protected/delivery/queues"]
A14["POST /api/v2/admin/protected/delivery/distances"]
A15["POST/GET /api/v2/admin/protected/products/:id"]
A16["POST /api/v2/admin/protected/categories"]
A17["POST /api/v2/admin/protected/penalty"]
A18["GET /api/v2/admin/protected/all/clients"]
A19["PUT /api/v2/admin/protected/clients/:id"]
A20["GET /api/v2/admin/protected/alerts"]
A21["GET/PUT /api/v2/admin/protected/settings"]
A22["GET /api/v2/admin/protected/penalties/all"]
A23["GET /api/v2/admin/protected/addresses"]
end
subgraph LIVREUR_AUTH["đ Livreur (JWT Required)"]
L1["GET /api/v1/livreur/deliveries"]
L2["GET /api/v1/livreur/deliveries/:id"]
L3["POST /api/v1/livreur/deliveries/:id/start"]
L4["PUT /api/v1/livreur/deliveries/:id/status"]
L5["GET /api/v1/livreur/deliveries/:id/nav-link"]
L6["POST /api/v1/livreur/location/update"]
L7["GET /api/v1/livreur/location"]
L8["POST /api/v1/livreur/update/status"]
L9["GET /api/v1/livreur/status"]
L10["GET /api/v1/livreur/queue"]
L11["POST /api/v1/livreur/alert"]
L12["DELETE /api/v1/livreur/alert/:id"]
L13["GET /api/v1/livreur/alerts"]
L14["GET /api/v1/livreur/notifications"]
L15["POST /api/v1/livreur/notifications/read"]
L16["POST /api/v1/livreur/telegram/link-token"]
L17["GET /api/v1/livreur/telegram/status"]
L18["DELETE /api/v1/livreur/telegram/unlink"]
end
subgraph CABINE_AUTH["đ Cabine (JWT Required)"]
K1["GET /api/v1/cabine/commands/:id/items"]
K2["PUT /api/v1/cabine/commands/:id/status"]
K3["PUT /api/v1/cabine/items/:item_id/status"]
K4["POST /api/v1/cabine/commands/:id/confirm-reception"]
K5["POST /api/v1/cabine/commands/:id/assign"]
K6["POST /api/v1/cabine/commands/:id/notify-client"]
K7["GET /api/v1/cabine/all/deliveryman"]
K8["GET /api/v1/cabine/alerts"]
K9["GET /api/v1/cabine/penalties/all"]
K10["GET /api/v1/cabine/addresses"]
K11["GET /api/v1/cabine/notifications"]
K12["POST /api/v1/cabine/telegram/link-token"]
end
GW[API Gateway :8080]
GW --> PUBLIC
GW --> AUTH_PUBLIC
GW --> CLIENT_AUTH
GW --> ADMIN_AUTH
GW --> LIVREUR_AUTH
GW --> CABINE_AUTH
```
### đ Flux d'Authentification
```mermaid
sequenceDiagram
participant U as Utilisateur
participant F as Frontend
participant API as Backend API
participant DB as PostgreSQL
participant R as Redis
participant TG as Telegram
rect rgb(200, 230, 200)
Note over U,R: Registration
U->>F: Remplir formulaire inscription
F->>API: POST /auth/register
API->>DB: Creer utilisateur
DB-->>API: User cree
API-->>F: 201 Created + User info
F-->>U: Compte cree
end
rect rgb(200, 200, 230)
Note over U,TG: Login Standard (2FA desactivee)
U->>F: Entrer credentials
F->>API: POST /auth/login
API->>DB: Verifier credentials
DB-->>API: User valide
API->>API: Generer JWT
API->>R: Stocker session
API-->>F: 200 OK + { access_token, user }
F->>F: Stocker token
F-->>U: Connecte
end
rect rgb(255, 240, 200)
Note over U,TG: Login avec 2FA (Telegram active)
U->>F: Entrer credentials
F->>API: POST /auth/login
API->>DB: Verifier credentials + verifier 2FA active
DB-->>API: User valide, 2FA requise
API->>R: Stocker session_token (TTL 5min)
API->>TG: Envoyer code 6 chiffres via bot Telegram
API-->>F: 200 OK + { requires_2fa: true, session_token }
F-->>U: Afficher saisie du code Telegram
U->>F: Entrer code recu sur Telegram
F->>API: POST /auth/2fa/verify { session_token, code }
API->>R: Verifier code + session_token
R-->>API: Code valide
API->>API: Generer JWT
API->>R: Stocker session
API-->>F: 200 OK + { access_token, user }
F->>F: Stocker token
F-->>U: Connecte
end
rect rgb(230, 200, 200)
Note over U,R: Requete Authentifiee
U->>F: Action protegee
F->>API: Request + Authorization: Bearer JWT
API->>API: Valider JWT
API->>R: Verifier session active
R-->>API: Session valide
API->>DB: Executer action
DB-->>API: Resultat
API-->>F: Response
F-->>U: Resultat affiche
end
```
### đŠ Flux Complet d'une Commande
```mermaid
sequenceDiagram
participant C as Client
participant F as Frontend
participant API as Backend
participant GPS as Service GPS
participant Q as Queue Service
participant L as Livreur
participant DB as PostgreSQL
participant R as Redis
rect rgb(220, 240, 220)
Note over C,R: 1. Ajout au panier
C->>F: Ajouter produit
F->>API: POST /panier/add {product_id, quantity}
API->>DB: Verifier stock (GetProductStockByID)
DB-->>API: Stock OK
API->>DB: Decrementer stock (DecrementProductStockByID)
API->>DB: Ajouter ligne panier
API-->>F: 201 Created
F-->>C: Panier mis a jour
Note over C,R: Si suppression panier â stock restitue automatiquement
end
rect rgb(230, 245, 230)
Note over C,R: 2. Checkout
C->>F: Valider panier
F->>API: POST /checkout {address}
API->>GPS: Geocoder adresse
GPS->>R: Check cache
R-->>GPS: Miss
GPS->>GPS: Appel Nominatim
GPS->>R: Cache resultat (7j)
GPS-->>API: Coordonnees
API->>DB: Creer commande
API->>Q: Chercher livreur proche
Q->>R: Get positions livreurs
Q->>Q: Calcul distances Haversine
Q->>GPS: Calculer ETA (TomTom)
Q-->>API: Livreur assigne
API->>DB: Update commande
API->>R: Ajouter a queue livreur
API-->>F: Commande creee + ETA
F-->>C: Confirmation
end
rect rgb(230, 230, 245)
Note over C,R: 3. Livraison
L->>API: GET /livreur/deliveries
API-->>L: Liste commandes
L->>API: POST /deliveries/:id/start
L->>API: POST /location/update {lat, lng}
API->>R: Update position
API->>R: Publish update
R-->>F: Notification position
F-->>C: Carte mise a jour
L->>API: PUT /deliveries/:id/status {en_route}
Note over L,API: ETA calcule et stocke dans Redis
L->>API: PUT /deliveries/:id/status {arrived}
L->>API: PUT /deliveries/:id/status {livre}
end
rect rgb(245, 230, 230)
Note over C,R: 4. Finalisation
C->>F: Approuver livraison
F->>API: POST /commands/:id/approve {rating}
API->>DB: Update statut approved
API->>R: Retirer de queue
API-->>F: Confirmation
F-->>C: Livraison terminee
end
```
### đŸ Schema de la Base de Donnees
```mermaid
erDiagram
CLIENTS {
int id PK
string username UK
string password
string nom
string prenom
string telephone UK
int command
int point
int points_zipette
float amende
int cancellations_count
timestamp created_at
}
ADMINS {
int id PK
string username UK
string password
string role
timestamp created_at
}
LIVREURS {
int id PK
string username UK
string password
string status
timestamp created_at
}
CABINES {
int id PK
string username UK
string password
timestamp created_at
}
PRODUCTS {
int id PK
string nom
string description
string category
int stock
float prix
timestamp created_at
}
PRODUCT_PRICES {
int id PK
int product_id FK
int quantity
float price
boolean active_price
}
PRODUCT_MEDIA {
int id PK
int product_id FK
string type
string url
}
PANIER {
int id PK
string username FK
string name_product
string category
int quantity
float price
timestamp created_at
}
COMMANDS {
int id PK
string username FK
string status
float total
string adresse
float dest_latitude
float dest_longitude
string livreur_assign FK
timestamp created_at
timestamp updated_at
}
COMMAND_ITEMS {
int id PK
int command_id FK
string produit
int quantite
float prix
string status
string category
}
RATINGS {
int id PK
int command_id FK
string deliveryman
int rating
string comment
timestamp created_at
}
CLIENTS ||--o{ PANIER : "possede"
CLIENTS ||--o{ COMMANDS : "passe"
PRODUCTS ||--o{ PRODUCT_PRICES : "a"
PRODUCTS ||--o{ PRODUCT_MEDIA : "a"
COMMANDS ||--o{ COMMAND_ITEMS : "contient"
COMMANDS ||--o| RATINGS : "a"
LIVREURS ||--o{ COMMANDS : "livre"
```
### đïž Structure Redis
```mermaid
graph LR
subgraph Sessions["đ Sessions"]
S1["session:{token}
TTL: 5h (client) / 2h (admin)"]
end
subgraph Positions["đ Positions GPS"]
P1["delivery:location:{username}
TTL: 2h"]
P2["delivery:status:{username}
TTL: 1h"]
end
subgraph Queues["đ Queues Livraison"]
Q1["livreur:queue:{username}
List of command_ids"]
Q2["queue:size:{username}
Integer"]
end
subgraph Cache["đŸ Cache"]
C1["geocode:cache:{hash}
TTL: 7j"]
C2["command:destination:{id}
TTL: 4h"]
C3["product:cache:{id}
TTL: 1h"]
end
subgraph PubSub["ïżœïżœ Pub/Sub Channels"]
PS1["channel:position_updates"]
PS2["channel:order_status"]
PS3["channel:notifications"]
end
REDIS[(Redis Server)]
REDIS --- Sessions
REDIS --- Positions
REDIS --- Queues
REDIS --- Cache
REDIS --- PubSub
```
---
## đ„ïž Infrastructure Serveurs
### Réseau VPN (WireGuard)
Tous les serveurs backend communiquent via un rĂ©seau WireGuard privĂ© `10.0.0.0/24`. Le SSH est restreint Ă l'IP VPN uniquement sur les serveurs sensibles â il faut ĂȘtre connectĂ© au VPN pour s'y connecter.
| Serveur | IP Publique | IP VPN | RĂŽle |
|---------|-------------|--------|------|
| **vpn-uber** | `45.150.111.158` | `10.0.0.1` | Serveur WireGuard â point d'entrĂ©e VPN et jump host SSH |
| **monitoring-uber** | `185.103.167.138` | `10.0.0.2` | Wazuh · Dozzle · Beszel hub · SSH via VPN uniquement |
| **backup-mln** | `85.121.176.241` | `10.0.0.4` | MinIO S3 · ClamAV · Beszel agent · SSH via VPN uniquement |
| **bdd-redis-prod** | `132.243.162.62` | `10.0.0.5` | PostgreSQL 16 · Redis 7 · Beszel agent · SSH via VPN uniquement |
| **prod-uber** | `185.103.166.119` | `10.0.0.6` | Backend Go + WAF nginx · accessible publiquement sur 80/443 |
| **pre-prod-uber** | `185.103.166.112` | â | Environnement de prĂ©-production |
| **s3-uber** | `80.96.58.164` | â | Stockage S3 externe · Dozzle agent |
### Architecture DMZ / LAN
```
Internet
â
âŒ
prod-uber (DMZ â 185.103.166.119)
â 80/443 public
â ââââ VPN (10.0.0.6) âââââș bdd-redis-prod (LAN â 10.0.0.5)
â âââ PostgreSQL :5432
â âââ Redis :6379
â
vpn-uber (10.0.0.1) â jump host SSH pour accĂšs aux autres serveurs
```
L'application prod-uber se connecte Ă PostgreSQL et Redis via les IP VPN :
- `DB_HOST=10.0.0.5` (PostgreSQL sur bdd-redis-prod)
- `REDIS_HOST=10.0.0.5` (Redis sur bdd-redis-prod)
### Monitoring
Toutes les ressources monitoring sont accessibles via VPN (`10.0.0.2`) :
| Outil | URL | Description |
|-------|-----|-------------|
| **Wazuh** | `https://10.0.0.2` | SIEM â alertes sĂ©curitĂ©, logs agents |
| **Dozzle** | `https://10.0.0.2:8080` | Logs Docker de tous les serveurs en temps réel |
| **Beszel** | `https://10.0.0.2:8090` | Métriques systÚme (CPU, RAM, disque, réseau) |
Dozzle agrĂšge les logs de : `pre-prod-uber`, `prod-uber`, `backup-mln (VPN)`, `s3-uber`.
Beszel surveille : `monitoring-uber`, `backup-mln` (agent Docker port 10001), `bdd-redis-prod` (agent binaire systemd port 10001).
Nettoyage automatique des logs Wazuh : cron tous les dimanches Ă 3h00 sur monitoring-uber (`/usr/local/bin/clean-wazuh-logs.sh`).
### Sécurité réseau
- **UFW** activé sur monitoring-uber et backup-mln : SSH bloqué depuis IP publique, accessible uniquement via VPN
- **UFW** activé sur bdd-redis-prod : SSH, PostgreSQL et Redis accessibles uniquement depuis le réseau VPN (`10.0.0.0/24`)
- Ports Docker liés à l'IP VPN (`10.0.0.4:port:port`) pour ne pas bypasser UFW
- **ClamAV** sur backup-mln : scan antivirus quotidien de `/mnt/data`
### AccĂšs SSH aux serveurs VPN-only
```bash
# Via le jump host vpn-uber
ssh -J root@45.150.111.158 root@10.0.0.2 # monitoring-uber
ssh -J root@45.150.111.158 root@10.0.0.4 # backup-mln
ssh -J root@45.150.111.158 root@10.0.0.5 # bdd-redis-prod
```
### Sauvegarde S3 (backup-mln)
MinIO S3 tourne sur backup-mln avec nginx SSL proxy :
| Endpoint | Adresse |
|----------|---------|
| API S3 | `https://10.0.0.4:9000` (via VPN) |
| Console MinIO | `https://10.0.0.4:9001` (via VPN) |
| Console nginx | `https://10.0.0.4:8080` (via VPN) |
Les données sont montées sur `/mnt/data`.
---
## đ DĂ©ploiement Production
### Prérequis
- Docker + Docker Compose
- Certificats SSL : `fullchain.pem` + `privkey.pem` (Let's Encrypt recommandé)
- Fichier `.env` complété (voir `.env.example`)
### Structure des fichiers
```
docker/
âââ backend/
â âââ Dockerfile # Stage builder (Go), runtime, waf (nginx+ModSec)
â âââ nginx.conf # Config nginx hardened (TLS 1.2/1.3, HSTS, CSP)
â âââ custom-rules.conf # RĂšgles ModSecurity personnalisĂ©es
â âââ entrypoint.sh # Entrypoint backend Go
âââ frontend/
â âââ Dockerfile # Build React/Vite + nginx interne
â âââ nginx.conf # Nginx interne (HTTP, sans TLS)
âââ certs/ # Certificats SSL (non versionnĂ©s)
â âââ fullchain.pem
â âââ privkey.pem
âââ docker-compose-prod.yml
```
### Déploiement
```bash
# 1. Copier les certificats
mkdir -p docker/certs
cp /etc/letsencrypt/live/votre-domaine/fullchain.pem docker/certs/
cp /etc/letsencrypt/live/votre-domaine/privkey.pem docker/certs/
chmod 644 docker/certs/privkey.pem
# 2. Configurer les variables d'environnement
cp docker/.env.example docker/.env
# Ăditer docker/.env avec vos valeurs
# 3. Lancer la stack
docker compose -f docker/docker-compose-prod.yml up -d --build
# 4. Vérifier les logs
docker compose -f docker/docker-compose-prod.yml logs -f waf
```
### Services Docker (prod-uber)
| Service | Image | RĂŽle |
|---------|-------|------|
| `waf` | owasp/modsecurity-crs:nginx-alpine | Point d'entrée HTTPS (ports 80/443) |
| `backend` | Go 1.24 alpine | API REST (port 8080 interne) |
| `frontend` | nginx:alpine | SPA React (port 80 interne) |
> PostgreSQL et Redis **ne tournent plus sur prod-uber**. Ils sont hébergés sur le serveur dédié `bdd-redis-prod` (`10.0.0.5`) et accessibles via le VPN WireGuard. Voir la section [Infrastructure Serveurs](#-infrastructure-serveurs).
### Variables d'environnement requises
```bash
# Base de données (bdd-redis-prod via VPN)
DB_HOST=10.0.0.5 # IP VPN de bdd-redis-prod
DB_PORT=5432
DB_PASSWORD= # Mot de passe PostgreSQL
# Redis (bdd-redis-prod via VPN)
REDIS_HOST=10.0.0.5 # IP VPN de bdd-redis-prod
REDIS_PORT=6379
REDIS_PASSWORD= # Mot de passe Redis
# JWT
USER_JWT_SECRET= # Secret JWT clients (min 32 chars)
ADMIN_JWT_SECRET= # Secret JWT admin/livreur/cabine (min 32 chars)
# TomTom (rotation automatique entre les 3 clés)
TOMTOM_API_KEY= # Clé TomTom principale / legacy
TOMTOM_API_KEY_1= # Clé TomTom #1
TOMTOM_API_KEY_2= # Clé TomTom #2
TOMTOM_API_KEY_3= # Clé TomTom #3
# Divers
SESSION_SECRET= # Secret sessions
TELEGRAM_WEBHOOK_URL= # URL webhook Telegram
TELEGRAM_WEBHOOK_SECRET= # Secret webhook Telegram
NOWPAYMENTS_IPN_SECRET= # Secret IPN NowPayments
```
---
## đ Notifications
Les notifications clients et livreurs sont gĂ©rĂ©es **exclusivement via Telegram** â les push notifications Expo (iOS/Android) ne sont plus utilisĂ©es.
### Notifications Telegram
Clients, livreurs et admins reçoivent leurs alertes via un bot Telegram lié à leur compte.
**Types de notifications envoyées :**
- `assigned` â Commande assignĂ©e Ă un livreur
- `en_route` â Livreur en route (avec ETA)
- `arrived` â Livreur arrivĂ©
- `livre` â Commande livrĂ©e
- `ready_pickup` â Cabine : "descendez chercher votre commande"
- `address_proposal` â Proposition de changement d'adresse
Les admins et livreurs peuvent lier leur compte Telegram pour recevoir des alertes.
#### Générer un token de liaison (Admin)
```bash
POST /api/v2/admin/protected/telegram/link-token
Authorization: Bearer
```
**Réponse:**
```json
{
"link_token": "abc123xyz",
"expires_in": 300,
"bot_url": "https://t.me/votre_bot?start=abc123xyz"
}
```
#### Générer un token de liaison (Livreur)
```bash
POST /api/v1/livreur/telegram/link-token
Authorization: Bearer
```
#### Statut Telegram (Livreur)
```bash
GET /api/v1/livreur/telegram/status
Authorization: Bearer
```
#### Délier Telegram
```bash
DELETE /api/v1/livreur/telegram/unlink
Authorization: Bearer
```
---
## đž Paiements Crypto
Le systĂšme intĂšgre NowPayments pour les paiements en cryptomonnaie.
### Webhook IPN (Instant Payment Notification)
Endpoint public sécurisé par signature HMAC-SHA512.
```bash
POST /api/v1/webhooks/nowpayments
Content-Type: application/json
x-nowpayments-sig:
```
Le webhook met automatiquement Ă jour le statut de paiement de la commande correspondante.
#### Vérifier le statut de paiement d'une commande
```bash
GET /api/v1/commands/:id/payment-status
Authorization: Bearer
```
**Réponse:**
```json
{
"command_id": 1234,
"payment_status": "confirmed",
"amount_paid": 25.00,
"currency": "USDT"
}
```
---
## đ Authentication
### Création de compte client
Les comptes clients sont créés **uniquement par un administrateur** via `POST /api/v2/admin/protected/clients`. Il n'existe pas d'endpoint d'auto-inscription.
---
### Login Client
**Se connecter et obtenir un token JWT**
```bash
POST /api/v1/auth/login
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"username": "jean_dupont",
"password": "SecurePass123!"
}
```
**RĂ©ponse standard (200 OK) â 2FA dĂ©sactivĂ©e:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 18000,
"user": {
"username": "jean_dupont",
"role": "client"
}
}
```
**RĂ©ponse avec 2FA (200 OK) â quand 2FA activĂ©e par le client ET Telegram liĂ© ET admin l'a activĂ©e:**
```json
{
"requires_2fa": true,
"session_token": "550e8400-e29b-41d4-a716-446655440000"
}
```
> Un code à 6 chiffres est envoyé automatiquement sur le compte Telegram lié. Le `session_token` expire dans **5 minutes**.
**Erreurs possibles:**
- `400` - Données manquantes
- `401` - Identifiants incorrects
---
### Vérifier le code 2FA
**Valider le code Telegram reçu pour finaliser la connexion**
```bash
POST /api/v1/auth/2fa/verify
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"session_token": "550e8400-e29b-41d4-a716-446655440000",
"code": "483721"
}
```
**Réponse (200 OK):**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 18000,
"user": {
"username": "jean_dupont",
"role": "client"
}
}
```
**Erreurs possibles:**
- `400` - `session_token` ou `code` manquant
- `401` - Code incorrect ou session expirée (TTL 5 min)
- `429` - Trop de tentatives (rate limiting)
---
### Statut 2FA du compte
**Obtenir l'état de la 2FA pour le compte connecté**
```bash
GET /api/v1/two-fa/status
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"two_fa_enabled": true,
"telegram_linked": true,
"admin_2fa_enabled": true
}
```
| Champ | Description |
|-------|-------------|
| `two_fa_enabled` | La 2FA est activée sur ce compte client |
| `telegram_linked` | Un compte Telegram est lié (prérequis pour activer) |
| `admin_2fa_enabled` | L'admin a activé la 2FA dans les paramÚtres globaux |
---
### Activer / Désactiver la 2FA
**Basculer l'état de la 2FA sur son propre compte**
```bash
POST /api/v1/two-fa/toggle
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"enabled": true
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"two_fa_enabled": true
}
```
**Prérequis pour activer (`"enabled": true`):**
1. Le client doit avoir lié son compte Telegram (via `/api/v1/auth/link-telegram`)
2. L'administrateur doit avoir activé `telegram_2fa_enabled` dans les paramÚtres globaux
**Erreurs possibles:**
- `400` - Telegram non lié (impossible d'activer sans compte Telegram)
- `403` - La 2FA n'est pas autorisée par l'administrateur
- `401` - Non authentifié
> **Interface utilisateur:** Sur le frontend web (page profil) et l'app mobile, un toggle permet d'activer/désactiver la 2FA directement depuis les paramÚtres du compte.
---
### Logout Client
**Se déconnecter (invalider le token)**
```bash
POST /api/v1/auth/logout
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Déconnexion réussie"
}
```
---
## đ API Client (v1)
**Toutes les routes clients nécessitent le header:**
```
Authorization: Bearer
```
### Produits (Public - Pas d'auth requis)
> **Filtrage des prix inactifs :** Chaque prix d'un produit porte un flag `active_price` (booléen). Les endpoints publics et client **n'exposent que les prix actifs** (`active_price = true`). Les rÎles `admin` et `cabine` reçoivent tous les prix (actifs et inactifs) pour permettre la gestion complÚte. CÎté frontend (web + mobile), les prix sont également filtrés au chargement (`filter(p => p.active_price !== false)`).
#### Liste des produits
```bash
GET /api/v1/products
```
**Réponse (200 OK):**
```json
{
"success": true,
"products": [
{
"id": 1,
"nom": "Pizza Margherita",
"description": "Pizza traditionnelle avec tomate, mozzarella et basilic",
"category": "pizza",
"stock": 50,
"prix": 12.50,
"prices": [
{
"id": 1,
"product_id": 1,
"quantity": 1,
"price": 12.50,
"active_price": true
}
],
"media": [
{
"id": 1,
"product_id": 1,
"type": "image",
"url": "/uploads/pizza_margherita.jpg"
}
],
"created_at": "2025-01-18T00:00:00Z"
}
],
"total": 1
}
```
---
#### Produit par ID
```bash
GET /api/v1/products/:id
```
**Réponse (200 OK):**
```json
{
"success": true,
"product": {
"id": 1,
"nom": "Pizza Margherita",
"description": "Pizza traditionnelle avec tomate, mozzarella et basilic",
"category": "pizza",
"stock": 50,
"prix": 12.50,
"prices": [...],
"media": [...],
"created_at": "2025-01-18T00:00:00Z"
}
}
```
**Erreurs possibles:**
- `404` - Produit non trouvé
---
### Gestion du Panier
> **Note stock :** Le stock est décrémenté dÚs l'ajout au panier et restitué automatiquement lors de la suppression d'un article ou du vidage du panier.
#### Ajouter au panier
```bash
POST /api/v1/panier/add
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"name_product": "Pizza Margherita",
"category": "pizza",
"quantity": 2
}
```
**Réponse (201 Created):**
```json
{
"success": true,
"message": "Produit ajouté au panier avec succÚs",
"panier": {
"id": 15,
"username": "jean_dupont",
"name_product": "Pizza Margherita",
"category": "pizza",
"quantity": 2,
"price": 25.00,
"created_at": "2025-01-18T14:35:00Z"
}
}
```
**Erreurs possibles:**
- `400` - Données invalides ou stock insuffisant
- `401` - Non authentifié
- `404` - Produit non trouvé
---
#### Voir mon panier
```bash
GET /api/v1/panier/:username
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Panier récupéré avec succÚs",
"panier": [
{
"id": 15,
"username": "jean_dupont",
"name_product": "Pizza Margherita",
"category": "pizza",
"quantity": 2,
"price": 25.00,
"created_at": "2025-01-18T14:35:00Z"
}
],
"count": 1,
"total_amount": 25.00
}
```
**Erreurs possibles:**
- `403` - AccĂšs au panier d'un autre utilisateur
- `404` - Utilisateur non trouvé
---
#### Supprimer du panier
```bash
DELETE /api/v1/panier/remove
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"id": 15
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Produit supprimé du panier avec succÚs",
"item_id": 15
}
```
**Erreurs possibles:**
- `403` - Tentative de supprimer l'article d'un autre utilisateur
- `404` - Article non trouvé
---
#### Vider le panier
```bash
DELETE /api/v1/panier/clear
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Panier vidé avec succÚs"
}
```
---
### Commandes
#### Valider le panier (Checkout avec Auto-assignation)
```bash
POST /api/v1/checkout
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"delivery_address": "15 Rue de la Paix, 75002 Paris, France"
}
```
**Réponse avec auto-assignation (200 OK):**
```json
{
"success": true,
"message": "Commande créée et livreur assigné automatiquement",
"command_id": 1234,
"delivery_address": "15 Rue de la Paix, 75002 Paris, France",
"status": "assigned",
"auto_assigned": true,
"assigned_to": {
"username": "john_deliveryman",
"distance_km": 1.5,
"travel_time": 8
}
}
```
**Réponse sans auto-assignation (200 OK):**
```json
{
"success": true,
"message": "Commande créée - En attente d'assignation",
"command_id": 1234,
"delivery_address": "15 Rue de la Paix, 75002 Paris, France",
"status": "pending",
"auto_assigned": false
}
```
**Erreurs possibles:**
- `400` - Panier vide ou adresse manquante
- `401` - Non authentifié
- `500` - Erreur création commande
---
#### Mes commandes avec suivi
```bash
GET /api/v1/my-commands
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"commands": [
{
"id": 1234,
"username": "jean_dupont",
"status": "assigned",
"total": 25.00,
"delivery_address": "15 Rue de la Paix, 75002 Paris",
"livreur_assign": "john_deliveryman",
"created_at": "2025-01-18T14:40:00Z",
"updated_at": "2025-01-18T14:42:00Z",
"tracking": {
"eta_minutes": 25,
"estimated_arrival": "15:05",
"deliveryman_status": "en_route",
"queue_position": 2
}
}
],
"total": 1
}
```
---
#### Statut temps réel d'une commande
```bash
GET /api/v1/commands/:id/status
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"command_id": 1234,
"status": "en_route",
"deliveryman": "john_deliveryman",
"updated_at": "2025-01-18T15:20:00Z"
}
```
**Statuts possibles:**
| Statut | Description | Qui peut le définir |
|--------|-------------|---------------------|
| `pending` | En attente d'assignation | SystĂšme (checkout) |
| `assigned` | Assignée à un livreur | SystÚme (auto-assign), Admin |
| `en_route` | Livreur en chemin | Livreur, Admin |
| `arrived` | Livreur arrivé à destination | Livreur, Admin, Cabine (bouton "Le livreur est là ") |
| `livre` | Livrée, en attente d'approbation | Livreur (validation GPS), Admin |
| `approved` | Approuvée et finalisée | Client (approve), Admin, Cabine |
| `cancelled` | Annulée | Client, Livreur, Admin |
---
#### Suivi détaillé avec ETA
```bash
GET /api/v1/commands/:id/tracking
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"command_id": 1234,
"status": "en_route",
"deliveryman": "john_deliveryman",
"eta": {
"minutes": 22,
"estimated_arrival": "15:02",
"arrival_time_unix": 1705669320,
"updated_at": "2025-01-18T14:40:00Z"
},
"queue_position": 2,
"total_queue_size": 5
}
```
---
#### ETA de livraison
```bash
GET /api/v1/commands/:id/eta
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"command_id": 1234,
"eta_minutes": 22,
"estimated_arrival": "15:02:00",
"queue_position": 2,
"updated_at": "2025-01-18T14:40:00Z"
}
```
---
#### Approuver une livraison
```bash
POST /api/v1/commands/:id/approve
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"rating": 5,
"comment": "Excellent service, livreur trĂšs professionnel !"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Livraison approuvée avec succÚs",
"command_id": 1234,
"status": "approved",
"rating": {
"deliveryman": "john_deliveryman",
"rating": 5,
"comment": "Excellent service, livreur trĂšs professionnel !",
"created_at": "2025-01-18T15:05:00Z"
}
}
```
---
#### Annuler une commande
```bash
POST /api/v1/commands/:id/cancel
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"reason": "Changement de plans"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Commande annulée avec succÚs",
"command_id": 1234,
"penalty_applied": true,
"penalty_details": {
"cancellations_count": 1,
"penalty_amount": 20.0,
"warning": "Pénalité appliquée pour annulation"
}
}
```
---
### Profil et Historique
#### Mettre Ă jour mon profil
```bash
PUT /api/v1/profile/update
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"nom": "Nouveau Nom",
"prenom": "Nouveau Prénom",
"telephone": "+33687654321",
"password": "NewSecurePass456!"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Profil mis Ă jour avec succĂšs",
"client": {
"id": 42,
"username": "jean_dupont",
"nom": "Nouveau Nom",
"prenom": "Nouveau Prénom",
"telephone": "+33687654321",
"updated_at": "2025-01-18T15:30:00Z"
}
}
```
---
#### Mes pénalités
```bash
GET /api/v1/penalties
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"client": {
"username": "jean_dupont",
"amende": 20.0,
"cancellations_count": 1,
"last_penalty_reason": "Annulation tardive"
},
"penalty_scale": {
"1st": 20.0,
"2nd": 50.0,
"3rd": 100.0,
"4th+": 150.0
},
"next_penalty": 50.0,
"warning": "La prochaine annulation entraßnera une pénalité de 50 points"
}
```
---
## đšâđŒ API Admin (v2)
**Toutes les routes admin nécessitent le header:**
```
Authorization: Bearer
```
### Authentication Admin
#### Login Admin
```bash
POST /api/v2/admin/auth/login
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"username": "admin_master",
"password": "AdminSecure123!"
}
```
**Réponse (200 OK):**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200,
"user": {
"username": "admin_master",
"role": "admin"
}
}
```
---
### Gestion des Commandes
#### Liste des commandes
```bash
GET /api/v2/admin/protected/orders
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"orders": [
{
"id": 1234,
"username": "jean_dupont",
"status": "assigned",
"total": 25.00,
"delivery_address": "15 Rue de la Paix, 75002 Paris",
"livreur_assign": "john_deliveryman",
"dest_latitude": 48.8698,
"dest_longitude": 2.3311,
"created_at": "2025-01-18T14:40:00Z",
"updated_at": "2025-01-18T14:42:00Z"
}
],
"total": 1
}
```
---
#### Modifier le statut d'une commande
```bash
PUT /api/v2/admin/protected/orders/:id/status
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{ "status": "arrived" }
```
**Statuts acceptés:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled`
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Statut mis Ă jour",
"command_id": 1234,
"new_status": "arrived"
}
```
**Erreurs possibles:**
- `400` - Statut invalide
- `403` - RĂŽle insuffisant (admin ou cabine requis)
- `404` - Commande non trouvée
---
#### Notifier le client de descendre
Envoie une notification (Telegram + in-app) au client pour récupérer sa commande, puis passe le statut en `arrived`.
```bash
POST /api/v2/admin/protected/orders/:id/notify-client
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Client notifié",
"client_username": "jean_dupont"
}
```
---
#### Assignation automatique GPS
```bash
POST /api/v2/admin/protected/orders/:id/auto-assign
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Commande assignée automatiquement",
"command_id": 1234,
"assigned_to": "john_deliveryman",
"distance": {
"km": 1.5,
"eta_minutes": 8
},
"queue_position": 3
}
```
**Erreurs possibles:**
- `404` - Commande non trouvée
- `400` - Aucun livreur disponible
- `409` - Commande déjà assignée
---
#### Assigner toutes les commandes en attente
```bash
POST /api/v2/admin/protected/orders/auto-assign-all
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Auto-assignation effectuée",
"assigned_count": 5,
"failed_count": 0,
"details": [
{
"command_id": 1234,
"assigned_to": "john_deliveryman",
"distance_km": 1.5,
"eta_minutes": 8
}
]
}
```
---
### Gestion des Livreurs
#### Livreurs disponibles
```bash
GET /api/v2/admin/protected/delivery-persons
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"delivery_persons": [
{
"username": "john_deliveryman",
"status": "available",
"current_command": 0,
"queue_size": 2,
"location": {
"latitude": 48.8566,
"longitude": 2.3522,
"last_update": "2025-01-18T16:30:00Z"
}
}
],
"total": 1
}
```
---
#### Position d'un livreur
```bash
GET /api/v2/admin/protected/delivery-persons/:username/location
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"deliveryman": "john_deliveryman",
"location": {
"latitude": 48.8566,
"longitude": 2.3522,
"last_update": "2025-01-18T16:25:00Z"
},
"status": "available"
}
```
---
#### Queues des livreurs
```bash
GET /api/v2/admin/protected/delivery/queues
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"queues": {
"john_deliveryman": {
"queue_size": 3,
"commands": [1234, 1235, 1236],
"can_accept_more": true,
"capacity": "3/10",
"status": "available"
}
},
"total_pending": 3
}
```
---
### Gestion des Produits
#### Créer un produit
```bash
POST /api/v2/admin/protected/products
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"nom": "Pizza Pepperoni",
"category": "pizza",
"description": "Pizza avec pepperoni et mozzarella",
"stock": 30,
"prix": 14.50
}
```
**Réponse (201 Created):**
```json
{
"success": true,
"message": "Produit créé avec succÚs",
"product": {
"id": 10,
"nom": "Pizza Pepperoni",
"category": "pizza",
"description": "Pizza avec pepperoni et mozzarella",
"stock": 30,
"prix": 14.50,
"created_at": "2025-01-18T16:10:00Z"
}
}
```
---
### SystÚme de Pénalités (Clients uniquement)
Les amendes s'appliquent **uniquement aux clients**. Les livreurs n'ont pas d'amende.
Les amendes sont stockées dans `clients.amende` (PostgreSQL). Tant que `amende > 0`, le middleware `BlockClientIfPenalty` bloque toute tentative de checkout.
**Ăchelle progressive (annulations) :**
| Nbre d'annulations | Amende |
|---|---|
| 1Úre | 20 ⏠|
| 2Úme | 50 ⏠|
| 3Úme | 100 ⏠|
| 4Úme et + | 150 ⏠|
**Sources d'amende :**
- Client annule sa propre commande â `ApplyCancellationPenalty` (incrĂ©mente `cancellations_count`)
- Livreur marque le client absent depuis le statut `arrived` â amende appliquĂ©e automatiquement sur le **client**
**Message d'erreur au checkout bloqué :**
```json
{
"error": "Commande bloquée : vous avez une amende de 20⏠en attente de paiement. Prenez attache avec Milieu Nantais sur signal pour régulariser votre situation..",
"amende": 20.0,
"blocked": true
}
```
#### Appliquer une pénalité (Admin)
```bash
POST /api/v2/admin/protected/penalty
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"username": "jean_dupont",
"amount": 50.0,
"reason": "Comportement inapproprié"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Pénalité appliquée",
"client": "jean_dupont",
"penalty": {
"amount": 50.0,
"reason": "Comportement inapproprié",
"applied_at": "2025-01-18T17:00:00Z"
},
"total_amende": 70.0
}
```
---
## đȘ API Cabine (v1)
**Toutes les routes cabine nécessitent le header:**
```
Authorization: Bearer
```
#### Articles d'une commande
```bash
GET /api/v1/cabine/commands/:id/items
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"command_id": 1234,
"items": [
{
"id": 1,
"command_id": 1234,
"produit": "Pizza Margherita",
"quantite": 2,
"prix": 25.0,
"status": "pending",
"category": "pizza"
}
],
"total": 1
}
```
---
#### Modifier le statut d'une commande
```bash
PUT /api/v1/cabine/commands/:id/status
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{ "status": "arrived" }
```
**Statuts acceptés:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled`
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Statut mis Ă jour",
"command_id": 1234,
"new_status": "arrived"
}
```
---
#### Notifier le client de descendre
```bash
POST /api/v1/cabine/commands/:id/notify-client
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Client notifié",
"client_username": "jean_dupont"
}
```
---
#### Modifier le statut d'un article
```bash
PUT /api/v1/cabine/items/:item_id/status
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"status": "prepared"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Statut mis Ă jour",
"item": {
"id": 1,
"command_id": 1234,
"status": "prepared",
"updated_at": "2025-01-18T17:15:00Z"
}
}
```
**Statuts disponibles:** `pending`, `prepared`, `packed`, `ready`
---
#### Confirmer réception (approbation finale)
```bash
POST /api/v1/cabine/commands/:id/confirm-reception
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Réception confirmée",
"command_id": 1234,
"new_status": "approved"
}
```
---
#### Assigner un livreur
```bash
POST /api/v1/cabine/commands/:id/assign
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{ "username": "john_deliveryman" }
```
---
#### Livreurs disponibles
```bash
GET /api/v1/cabine/all/deliveryman
Authorization: Bearer
```
---
## đ API Livreur (v1)
**Toutes les routes livreur nécessitent le header:**
```
Authorization: Bearer
```
### Livraisons
#### Mes livraisons (données filtrées)
```bash
GET /api/v1/livreur/deliveries
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"deliveries": [
{
"id": 1234,
"status": "assigned",
"adresse": "15 Rue de la Paix, 75002 Paris",
"total_prix": 25.0,
"created_at": "2025-01-18T14:40:00Z",
"client_info": {
"nom": "Dupont",
"prenom": "Jean"
},
"items": [
{
"produit": "Pizza Margherita",
"quantite": 2,
"prix": 25.0
}
],
"items_count": 1,
"eta": {
"minutes": 25,
"estimated_arrival": "15:05"
}
}
],
"count": 1
}
```
**Note:** Les données sensibles comme le téléphone client sont filtrées pour les livreurs.
---
#### Mettre Ă jour le statut d'une livraison
```bash
PUT /api/v1/livreur/deliveries/:id/status
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"status": "en_route",
"notes": "En route vers le client",
"latitude": 48.8566,
"longitude": 2.3522
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Statut mis Ă jour",
"command_id": 1234,
"status": "en_route",
"eta_minutes": 15,
"eta_message": "Arrivée prévue dans 15 minutes"
}
```
**Statuts valides pour livreur (via cet endpoint) :**
- `en_route` â En route vers le client (dĂ©clenche calcul ETA + notification Telegram avec ETA)
- `arrived` â ArrivĂ© Ă destination (notifie le client de descendre)
- `livre` â LivrĂ© (via validation GPS uniquement, voir endpoint `force-validate`)
- `cancelled` â Annulation par le livreur
> **Note :** Le passage à `livre` via ce endpoint requiert des coordonnées GPS valides pour la validation de proximité.
> **Amende automatique :** si le livreur annule une commande dont le statut courant est `arrived`, une amende progressive est automatiquement appliquĂ©e au client (`ApplyCancellationPenalty`) â Ă©chelle : 1Ăšre=20âŹ, 2Ăšme=50âŹ, 3Ăšme=100âŹ, 4Ăšme+=150âŹ. Le livreur peut dĂ©clencher cette annulation via le bouton **"Client absent"** (cf. section ci-dessous).
---
### Bouton "Client absent" (DashboardScreen)
Lorsque le statut d'une livraison passe à `arrived`, un timer de **5 minutes** démarre automatiquement dans l'app livreur (`frontend-admin`). Pendant ce délai, un compte à rebours est affiché sur la carte de livraison. Une fois les 5 minutes écoulées, le bouton **"Client absent"** apparaßt.
**Comportement :**
- Appui sur "Client absent" â annulation de la commande avec la raison `client_absent`
- L'API reçoit `status: cancelled` + `issue_type: client_absent`
- Le backend applique automatiquement `ApplyCancellationPenalty(clientUsername)` (amende progressive)
- Le timer est stocké dans un `useRef` (pas de re-render) et comparé à `Date.now()` toutes les secondes
**ImplĂ©mentation :** `frontend-admin/src/screens/delivery/DashboardScreen.tsx` â constante `ABSENT_TIMEOUT_SECS = 300`, ref `arrivedAtRef`, Ă©tat `elapsedSeconds`.
---
### Position GPS
#### Mettre Ă jour ma position
```bash
POST /api/v1/livreur/location/update
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"latitude": 48.8566,
"longitude": 2.3522
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Position mise Ă jour",
"location": {
"latitude": 48.8566,
"longitude": 2.3522,
"updated_at": "2025-01-18T17:35:00Z"
}
}
```
---
#### Ma position actuelle
```bash
GET /api/v1/livreur/location
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"location": {
"latitude": 48.8566,
"longitude": 2.3522,
"last_update": "2025-01-18T17:35:00Z"
}
}
```
---
### Statut et Queue
#### Mettre Ă jour mon statut
```bash
POST /api/v1/livreur/update/status
Authorization: Bearer
Content-Type: application/json
```
**RequĂȘte:**
```json
{
"status": "available"
}
```
**Réponse (200 OK):**
```json
{
"success": true,
"message": "Statut mis Ă jour",
"status": {
"username": "john_deliveryman",
"status": "available",
"updated_at": "2025-01-18T17:40:00Z"
}
}
```
**Statuts disponibles:** `available`, `busy`, `offline`
---
#### Ma queue de livraisons
```bash
GET /api/v1/livreur/queue
Authorization: Bearer
```
**Réponse (200 OK):**
```json
{
"success": true,
"deliveryman": "john_deliveryman",
"queue_size": 3,
"commands": [
{
"position": 1,
"command_id": 1234,
"address": "15 Rue de la Paix, 75002 Paris",
"estimated_eta": 12,
"total_prix": 25.0
}
]
}
```
---
## đșïž Systeme GPS Integre
Le systeme GPS est au coeur de la plateforme de livraison. Il permet l'assignation automatique des livreurs, le calcul d'ETA en temps reel, et le suivi des livraisons.
### Architecture GPS
#### Flux GPS Complet
```mermaid
flowchart TD
A[1. Commande creee par le Client] --> B[2. Geocodage adresse]
B --> |Nominatim API| C[(Cache Redis 7 jours)]
C --> D[3. Worker Auto-Assignation]
D --> |Toutes les 5 min| E[4. Recherche livreur le plus proche]
E --> |Haversine + TomTom| F[5. Calcul ETA avec trafic reel]
F --> G[6. Assignation a la queue du livreur]
G --> H[7. Livreur accepte et met a jour sa position]
H --> |Redis Pub/Sub| I[8. Client/Admin recoivent les mises a jour]
I --> J[9. Navigation vers destination]
J --> |TomTom Routing| K[10. Livraison terminee]
```
#### Architecture des Services GPS
```mermaid
graph TB
subgraph Frontend
MAP[TomTomMap Component]
UI[Interface Utilisateur]
end
subgraph Backend
GEO[Service Geocodage]
DIST[Service Distance]
ETA[Service ETA]
ASSIGN[Service Auto-Assignation]
end
subgraph APIs Externes
NOM[Nominatim API]
TOM[TomTom API]
end
subgraph Stockage
REDIS[(Redis Cache)]
PG[(PostgreSQL)]
end
UI --> MAP
MAP --> TOM
GEO --> NOM
GEO --> REDIS
DIST --> GEO
ETA --> TOM
ETA --> DIST
ASSIGN --> DIST
ASSIGN --> ETA
ASSIGN --> PG
ASSIGN --> REDIS
```
### Services GPS Utilises
| Service | Utilisation | Cache |
|---------|-------------|-------|
| **Nominatim** (OpenStreetMap) | Geocodage d'adresses | Redis 7 jours |
| **TomTom Routing API** | ETA avec trafic reel, itineraires | Non |
| **Haversine** (local) | Calcul de distance a vol d'oiseau | Non |
### Configuration Requise
**Variables d'environnement Backend:**
```bash
TOMTOM_API_KEY=
```
**Variables d'environnement Frontend:**
```bash
VITE_TOMTOM_API_KEY=
```
> **Note:** Obtenez une cle API TomTom gratuite sur [developer.tomtom.com](https://developer.tomtom.com/)
---
### Geocodage d'Adresses
Le geocodage convertit une adresse textuelle en coordonnees GPS (latitude/longitude).
#### Geocoder une adresse
```bash
POST /api/v1/geocode
Content-Type: application/json
```
**Requete:**
```json
{
"address": "15 Rue de la Paix, 75002 Paris, France"
}
```
**Reponse (200 OK):**
```json
{
"success": true,
"address": "15 Rue de la Paix, 75002 Paris, France",
"coordinates": {
"latitude": 48.8698,
"longitude": 2.3311
},
"cached": false
}
```
**Erreurs possibles:**
- `400` - Adresse manquante ou invalide
- `404` - Adresse non trouvee (geocodage echoue)
---
#### Valider une adresse
Verifie si une adresse peut etre geocodee sans la stocker.
```bash
POST /api/v1/validate-address
Content-Type: application/json
```
**Requete:**
```json
{
"address": "15 Rue de la Paix, 75002 Paris"
}
```
**Reponse (200 OK):**
```json
{
"success": true,
"valid": true,
"address": "15 Rue de la Paix, 75002 Paris",
"coordinates": {
"latitude": 48.8698,
"longitude": 2.3311
}
}
```
---
### Calcul de Distance et ETA
#### Formule Haversine
Le systeme utilise la formule Haversine pour calculer la distance a vol d'oiseau entre deux points GPS:
```
a = sinÂČ(Îlat/2) + cos(lat1) Ă cos(lat2) Ă sinÂČ(Îlon/2)
c = 2 Ă atan2(âa, â(1-a))
distance = R Ă c
Ou R = 6371 km (rayon de la Terre)
```
#### ETA avec Trafic (TomTom)
L'ETA est calcule en utilisant l'API TomTom qui prend en compte:
- Les conditions de trafic en temps reel
- Les incidents routiers
- Les travaux
- L'heure de la journee
**Rotation automatique des clĂ©s :** jusqu'Ă 3 clĂ©s TomTom peuvent ĂȘtre configurĂ©es (`TOMTOM_API_KEY`, `TOMTOM_API_KEY_1`, `TOMTOM_API_KEY_2`, `TOMTOM_API_KEY_3`). En cas de quota dĂ©passĂ© (HTTP 403/429), le systĂšme passe automatiquement Ă la clĂ© suivante sans interruption de service.
```mermaid
flowchart TD
A[Demande ETA] --> B{Clés TomTom configurées?}
B -->|Non| E[Calcul Haversine]
B -->|Oui| C[Appel TomTom â clĂ© active]
C --> Q{Quota dépassé 403/429?}
Q -->|Oui| R{Clé suivante disponible?}
R -->|Oui| C
R -->|Non| E
Q -->|Non| D[ETA avec trafic réel]
E --> F[Distance Ă vol d'oiseau]
F --> G[Vitesse moyenne 30 km/h]
G --> H[ETA estimé]
D --> I[Retourner ETA]
H --> I
```
**Fallback:** Si toutes les clés TomTom sont épuisées ou indisponibles, le systÚme utilise un calcul local base sur:
- Distance Haversine
- Vitesse moyenne estimee (30 km/h en ville)
---
### Auto-Assignation GPS
Le systeme assigne automatiquement les commandes au livreur le plus proche.
#### Processus d'Auto-Assignation
```mermaid
flowchart LR
A[Nouvelle Commande] --> B{Adresse geocodee?}
B -->|Non| C[Geocoder adresse]
C --> D
B -->|Oui| D[Recuperer livreurs disponibles]
D --> E[Calculer distances Haversine]
E --> F[Trier par proximite]
F --> G{Livreur avec capacite?}
G -->|Oui| H[Calculer ETA TomTom]
G -->|Non| I{Forcer assignation?}
I -->|Oui| H
I -->|Non| J[Commande en attente]
H --> K[Assigner a la queue]
K --> L[Notifier livreur]
```
#### Regles de Capacite
- Chaque livreur peut avoir jusqu'a **10 commandes** dans sa queue
- Si tous les livreurs sont a capacite maximale, le systeme peut forcer l'assignation
- Les livreurs avec le statut `offline` ne recoivent pas de commandes
#### Auto-assigner une commande specifique
```bash
POST /api/v2/admin/protected/orders/:id/auto-assign
Authorization: Bearer
```
**Reponse (200 OK):**
```json
{
"success": true,
"message": "Commande assignee automatiquement",
"command_id": 1234,
"assigned_to": "john_deliveryman",
"assignment_details": {
"distance_km": 1.5,
"eta_minutes": 8,
"queue_position": 3,
"method": "nearest_available"
}
}
```
---
#### Lister les livreurs par distance
Obtient la liste des livreurs tries par distance depuis une adresse.
```bash
POST /api/v2/admin/protected/delivery/distances
Authorization: Bearer
Content-Type: application/json
```
**Requete:**
```json
{
"address": "15 Rue de la Paix, 75002 Paris"
}
```
**Reponse (200 OK):**
```json
{
"success": true,
"address": "15 Rue de la Paix, 75002 Paris",
"destination": {
"latitude": 48.8698,
"longitude": 2.3311
},
"delivery_persons": [
{
"username": "john_deliveryman",
"distance_km": 1.5,
"eta_minutes": 8,
"status": "available",
"queue_size": 2,
"location": {
"latitude": 48.8566,
"longitude": 2.3522
}
},
{
"username": "jane_delivery",
"distance_km": 3.2,
"eta_minutes": 15,
"status": "available",
"queue_size": 5,
"location": {
"latitude": 48.8456,
"longitude": 2.3789
}
}
]
}
```
---
### Liens de Navigation
Le systeme genere des liens vers plusieurs applications de cartographie.
#### Obtenir les liens carte d'un livreur
```bash
GET /api/v2/admin/protected/delivery-persons/:username/map-links
Authorization: Bearer
```
**Reponse (200 OK):**
```json
{
"success": true,
"deliveryman": "john_deliveryman",
"location": {
"latitude": 48.8566,
"longitude": 2.3522
},
"map_links": {
"google_maps": "https://www.google.com/maps?q=48.8566,2.3522",
"google_maps_app": "comgooglemaps://?center=48.8566,2.3522",
"waze": "https://www.waze.com/ul?ll=48.8566,2.3522&navigate=yes",
"waze_app": "waze://?ll=48.8566,2.3522&navigate=yes",
"apple_maps": "https://maps.apple.com/?ll=48.8566,2.3522",
"openstreetmap": "https://www.openstreetmap.org/?mlat=48.8566&mlon=2.3522",
"bing_maps": "https://www.bing.com/maps?cp=48.8566~2.3522",
"here_maps": "https://share.here.com/l/48.8566,2.3522"
}
}
```
---
#### Obtenir les liens de navigation pour une commande
Genere des liens de navigation depuis la position du livreur vers la destination de livraison.
```bash
GET /api/v2/admin/protected/commands/:id/navigation-links
Authorization: Bearer
```
**Reponse (200 OK):**
```json
{
"success": true,
"command_id": 1234,
"origin": {
"latitude": 48.8566,
"longitude": 2.3522,
"description": "Position du livreur"
},
"destination": {
"latitude": 48.8698,
"longitude": 2.3311,
"address": "15 Rue de la Paix, 75002 Paris"
},
"navigation_links": {
"google_maps": "https://www.google.com/maps/dir/48.8566,2.3522/48.8698,2.3311",
"waze": "https://www.waze.com/ul?ll=48.8698,2.3311&navigate=yes&from=48.8566,2.3522",
"apple_maps": "https://maps.apple.com/?saddr=48.8566,2.3522&daddr=48.8698,2.3311"
}
}
```
---
### Gestion des Positions en Temps Reel
#### Flux de Mise a Jour de Position
```mermaid
sequenceDiagram
participant L as Livreur (App)
participant API as Backend API
participant R as Redis
participant PS as Redis Pub/Sub
participant A as Admin/Client
L->>API: POST /location/update {lat, lng}
API->>API: Valider coordonnees
API->>R: SET delivery:location:{username}
API->>PS: PUBLISH position_update
PS-->>A: Notification temps reel
API-->>L: 200 OK {location, updated_at}
```
#### Mise a jour de position (Livreur)
```bash
POST /api/v1/livreur/location/update
Authorization: Bearer
Content-Type: application/json
```
**Requete:**
```json
{
"latitude": 48.8566,
"longitude": 2.3522
}
```
**Validation des coordonnees:**
- Latitude: entre -90 et +90
- Longitude: entre -180 et +180
**Reponse (200 OK):**
```json
{
"success": true,
"message": "Position mise a jour",
"location": {
"latitude": 48.8566,
"longitude": 2.3522,
"updated_at": "2025-01-18T17:35:00Z"
}
}
```
**Erreurs possibles:**
- `400` - Coordonnees invalides (hors limites)
- `401` - Non authentifie
---
### Stockage Redis des Positions
Les positions GPS sont stockees dans Redis pour un acces rapide:
```mermaid
graph LR
subgraph Redis Cache
A[delivery:location:username
TTL: 2h]
B[delivery:status:username
TTL: 1h]
C[geocode:cache:address_hash
TTL: 7j]
D[command:destination:id
TTL: 4h]
end
subgraph Donnees
A --> A1[latitude, longitude, updated_at]
B --> B1[status, latitude, longitude]
C --> C1[latitude, longitude, address]
D --> D1[dest_lat, dest_lng]
end
```
| `command:destination:{command_id}` | Coordonnees destination | 4 heures |
---
### Composant Carte Frontend (TomTomMap)
Le frontend inclut un composant React/TypeScript pour afficher la carte interactive.
#### Fonctionnalites du Composant
- **Carte interactive** avec marqueurs livreur/destination
- **Calcul d'itineraire** en temps reel
- **Instructions de navigation** (tourner a gauche, a droite, rond-point, etc.)
- **Affichage distance/duree** du trajet
- **Mode 3D** avec orientation selon la direction
- **Support francais** pour toutes les instructions
#### Exemple d'Utilisation
```tsx
import TomTomMap from './components/TomTomMap';
function DeliveryTracking() {
return (
);
}
```
#### Props du Composant
| Prop | Type | Description |
|------|------|-------------|
| `driverPosition` | `{lat: number, lng: number}` | Position du livreur |
| `destinationPosition` | `{lat: number, lng: number}` | Destination de livraison |
| `showRoute` | `boolean` | Afficher l'itineraire |
| `showInstructions` | `boolean` | Afficher le panneau d'instructions |
| `onEtaUpdate` | `(eta: number) => void` | Callback quand l'ETA change |
---
### Worker d'Auto-Assignation
Un worker CRON s'execute automatiquement pour assigner les commandes en attente.
#### Configuration
- **Frequence:** Toutes les 1 minute
- **Fichier:** `backend/gestion/workers/cron_auto_assign.go`
#### Processus du Worker
```mermaid
flowchart TD
START((Demarrage CRON)) --> A[Recuperer commandes pending]
A --> B{Commandes a traiter?}
B -->|Non| END((Fin))
B -->|Oui| C[Prendre commande suivante]
C --> D{Adresse geocodee?}
D -->|Non| E[Geocoder via Nominatim]
E --> F
D -->|Oui| F[Recuperer livreurs disponibles]
F --> G[Calculer distances]
G --> H[Selectionner le plus proche]
H --> I{Capacite disponible?}
I -->|Non| J[Marquer pour retry]
I -->|Oui| K[Calculer ETA TomTom]
K --> L[Assigner commande]
L --> M[Mettre a jour statut]
M --> N{Autres commandes?}
J --> N
N -->|Oui| C
N -->|Non| O[Logger resultats]
O --> END
```
#### Logs d'Exemple
```
[AUTO-ASSIGN] Processing 5 pending orders
[AUTO-ASSIGN] Order #1234: Geocoded to (48.8698, 2.3311)
[AUTO-ASSIGN] Order #1234: Nearest driver is john_deliveryman (1.5 km)
[AUTO-ASSIGN] Order #1234: ETA calculated: 8 minutes
[AUTO-ASSIGN] Order #1234: Assigned successfully
[AUTO-ASSIGN] Completed: 5 assigned, 0 failed
```
---
### Cycle de Vie d'une Livraison (GPS)
```mermaid
stateDiagram-v2
[*] --> pending: Commande creee (checkout)
pending --> assigned: Auto-assignation GPS / Admin
pending --> cancelled: Annulation client ou admin
assigned --> en_route: Livreur demarre (start)
assigned --> cancelled: Annulation
en_route --> arrived: Livreur arrive (GPS ou Admin/Cabine)
en_route --> en_route: Mise a jour position GPS
en_route --> livre: Validation GPS livreur / Admin
arrived --> livre: Remise au client (validation GPS livreur)
arrived --> approved: Admin/Cabine force approbation
livre --> approved: Client confirme / Admin / Cabine
approved --> [*]
cancelled --> [*]
note right of arrived
Declenche par :
- Livreur PUT status=arrived
- Admin/Cabine bouton "Le livreur est la"
Notifie le client (Telegram + push)
Timer 5min â bouton "Client absent"
Annulation depuis arrived â amende client
end note
note right of en_route
ETA calcule et stocke dans Redis
(command:eta:{id})
Utilise pour les notifications Telegram
end note
```
---
### Gestion des Erreurs GPS
#### Geocodage Echoue
Si une adresse ne peut pas etre geocodee:
- La commande reste en statut `pending`
- Un log d'erreur est genere
- L'admin peut corriger l'adresse manuellement
#### API TomTom Indisponible / Quota dépassé
Le systÚme tente d'abord toutes les clés disponibles en rotation, puis bascule sur le calcul local :
- Utilise la formule Haversine pour la distance
- Estime l'ETA avec une vitesse moyenne de 30 km/h
- Un flag `fallback_used: true` est ajoute a la reponse
---
### Bonnes Pratiques
#### Pour les Livreurs
1. **Mettre a jour la position** frequemment (toutes les 30 secondes recommande)
2. **Verifier le statut** avant de commencer une livraison
3. **Utiliser les liens de navigation** generes par l'API
#### Pour les Admins
1. **Surveiller les queues** des livreurs pour eviter la surcharge
2. **Verifier les adresses** qui echouent au geocodage
3. **Utiliser l'endpoint distances** pour l'assignation manuelle si necessaire
#### Pour les Developpeurs
1. **Toujours valider** les coordonnees avant stockage
2. **Utiliser le cache** Redis pour les adresses frequentes
3. **Implementer un fallback** si TomTom est indisponible
4. **Logger les erreurs** de geocodage pour analyse
---
## â Codes d'Erreur
### Erreurs HTTP Standards
| Code | Signification | Description |
|------|---------------|-------------|
| 200 | OK | SuccĂšs |
| 201 | Created | Ressource créée |
| 400 | Bad Request | Données invalides |
| 401 | Unauthorized | Non authentifié |
| 403 | Forbidden | Non autorisé |
| 404 | Not Found | Ressource introuvable |
| 409 | Conflict | Conflit (username déjà pris) |
| 422 | Unprocessable Entity | Validation échouée |
| 500 | Internal Server Error | Erreur serveur |
### Exemples d'Erreurs
#### 400 - Bad Request
```json
{
"error": "Données invalides",
"details": "Le champ 'username' est requis"
}
```
#### 401 - Unauthorized
```json
{
"error": "Token invalide ou expiré",
"message": "Veuillez vous reconnecter"
}
```
#### 403 - Forbidden
```json
{
"error": "AccÚs refusé",
"message": "Vous n'avez pas les permissions nécessaires"
}
```
#### 403 - Checkout bloqué (amende en attente)
```json
{
"error": "Commande bloquée : vous avez une amende de 20⏠en attente de paiement. Prenez attache avec Milieu Nantais sur signal pour régulariser votre situation..",
"amende": 20.0,
"blocked": true
}
```
#### 404 - Not Found
```json
{
"error": "Ressource introuvable",
"message": "Commande avec l'ID 9999 introuvable"
}
```
#### 409 - Conflict
```json
{
"error": "Conflit",
"message": "Ce nom d'utilisateur est déjà pris"
}
```
#### 422 - Unprocessable Entity
```json
{
"error": "Validation échouée",
"details": {
"field": "telephone",
"message": "Format de téléphone invalide"
}
}
```
#### 500 - Internal Server Error
```json
{
"error": "Erreur serveur interne",
"message": "Une erreur inattendue s'est produite"
}
```
---
## đ Notes Importantes
### Sécurité
- **Tokens JWT** : Expiration variable selon le rÎle (Client: 5h · Admin/Livreur/Cabine: 10h)
- **Validation GPS** : Requis pour certaines actions de livraison
- **Filtrage des données** : Les livreurs n'ont pas accÚs aux téléphones clients
- **Rate Limiting** : Protection contre les abus
### Format des Données
- **Dates** : Format ISO 8601 (`2025-01-18T14:30:00Z`)
- **Coordonnées** : Latitude/Longitude en décimal (WGS84)
- **Prix** : En euros avec 2 décimales
- **Téléphones** : Format international (`+33612345678`)
### Workers Automatiques
- **Auto-Assignment Cron** : Toutes les 1 minute (`workers/cron_auto_assign.go`)
- **Queue Cleanup** : Toutes les 5 minutes
- **ETA Updates** : Toutes les 30 secondes
- **Stock Cleanup** : Toutes les 5 minutes
### Fonctionnalités Avancées
- **Assignation GPS automatique** au checkout
- **Calcul ETA temps réel** avec TomTom API
- **SystÚme de queues optimisé** pour les livreurs
- **Nettoyage automatique** des commandes invalides
- **Notifications temps réel** via Redis Pub/Sub
---
## đ Changelog
### v5.5.0 â 2026-07-08
- **Fix â amende annulation livreur (`ApplyCancellationPenalty`)** : l'amende appliquĂ©e quand un livreur marque le client absent Ă©crasait le montant existant au lieu de l'additionner, et n'Ă©tait pas protĂ©gĂ©e par un verrou (`FOR UPDATE`). Elle est dĂ©sormais cumulative et transactionnelle, cohĂ©rente avec le chemin d'annulation client (`CancelAtomic`).
- **Fix â mot de passe loggĂ© en clair** : la modification d'un client par un admin (`PUT /admin/protected/clients/:id`) journalisait le corps de requĂȘte complet, y compris le nouveau mot de passe. Le log ne contient plus de donnĂ©e sensible.
- **Fix â IDOR consultation d'alerte police** : un livreur pouvait consulter le dĂ©tail de l'alerte d'un autre livreur en devinant l'ID (`GET /api/v1/livreur/alert/:id`). L'accĂšs est dĂ©sormais restreint Ă ses propres alertes ; admin et cabine conservent l'accĂšs complet.
- **Nettoyage â code mort** : suppression des handlers et fonctions utilitaires non routĂ©s/non appelĂ©s (ancien module `handlers/cabine.go`, `RegisterClient`, `GetCurrentClient`, `GetCurrentAdmin`, `GetMyCompletedOrders`, `GetRealtimeStats`, `StartPaymentChecker`, et helpers internes associĂ©s), identifiĂ©s via `staticcheck` et `deadcode`.
### v5.4.0 â 2026-05-18
- **Rotation automatique des clés TomTom** : jusqu'à 3 clés configurables (`TOMTOM_API_KEY_1/2/3`). En cas de quota dépassé (403/429), le systÚme passe à la clé suivante automatiquement sans interruption. Fallback Haversine si toutes les clés sont épuisées.
- **SĂ©curitĂ© â crĂ©ation d'utilisateurs** : seul un `admin` peut crĂ©er des comptes `livreur` ou `cabine` via l'API. La crĂ©ation de compte `admin` est entiĂšrement bloquĂ©e via l'application â uniquement possible en base de donnĂ©es directement.
- **SĂ©curitĂ© â cabine** : le rĂŽle `cabine` n'a plus aucun droit de crĂ©ation d'utilisateurs ou de clients (retirĂ© cĂŽtĂ© backend).
- **Fix frontend** : `createUserByAdmin` appelait `/admin/auth/register` (inexistant) â corrigĂ© vers `/admin/protected/users`.
- **WAF ModSecurity logs** : les logs nginx et l'audit log ModSecurity sont désormais montés sur l'hÎte (`/var/log/waf/nginx/` et `/var/log/waf/modsec/`) via volumes Docker stables. La variable `MODSEC_AUDIT_LOG` redirige l'audit log vers un fichier (au lieu de stdout) pour collecte Wazuh.
- **Notifications Telegram uniquement** : les push notifications Expo (iOS/Android) sont abandonnées. Clients et livreurs reçoivent désormais toutes leurs alertes via Telegram.
### v5.3.0 â 2026-05-15
- **Prix inactifs filtrés cÎté client** : le flag `active_price` sur `PRODUCT_PRICES` permet de désactiver un tarif sans le supprimer. Les endpoints publics/client et les frontends web+mobile masquent automatiquement les prix inactifs. Admin et cabine voient tous les prix.
- **Désactivation au lieu de suppression** : dans la modal d'édition produit (admin), retirer un prix existant le désactive (`active_price = false`) plutÎt que de le supprimer de la base.
- **Amende automatique client â livreur annule depuis `arrived`** : quand le livreur marque le client absent (`arrived` â `cancelled`), `ApplyCancellationPenalty` est appelĂ© automatiquement sur le **client** (amende progressive : 20â50â100â150âŹ).
- **Bouton "Client absent"** : aprÚs 5 minutes au statut `arrived`, l'app livreur affiche un bouton "Client absent" qui déclenche l'annulation avec amende sur le client.
- **Message d'erreur checkout avec contact** : le message de blocage inclut désormais "Prenez attache avec Milieu Nantais sur signal pour régulariser votre situation."
- **OTA channels par rÎle** : les canaux Expo OTA sont désormais nommés `pre-prod-client` / `production-client` / `pre-prod-admin` / `production-admin` pour éviter les mises à jour croisées entre builds.
### v5.2.0 â 2026-05-01
- Activation/désactivation des prix par quantité (admin)
- Gestion de stock améliorée
- Refactoring handlers produits
---
**Documentation mise Ă jour le :** 2026-07-08
**Version API :** 5.5.0
**Technologies :** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19, Expo 54, TomTom API, ModSecurity WAF
**Déploiement :** Docker Compose · Nginx + ModSecurity OWASP CRS · TLS 1.2/1.3
**Base URL prod :** `https://mln-uber.club`
**Infrastructure :** WireGuard VPN · Wazuh SIEM · Dozzle · Beszel · ClamAV · MinIO S3