diff --git a/README.md b/README.md index cab44093..c5243b40 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ # 📚 Documentation API - Plateforme de Gestion de Commandes -**Version:** 5.4.0 -**Date:** 2026-06-11 +**Version:** 5.2.0 +**Date:** 2026-05-01 **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) +2. [DĂ©ploiement Production](#-dĂ©ploiement-production) +3. [Authentication](#authentication) +4. [API Client (v1)](#api-client-v1) +5. [API Admin (v2)](#api-admin-v2) +6. [API Cabine (v1)](#api-cabine-v1) +7. [API Livreur (v1)](#api-livreur-v1) +8. [Notifications Push & Telegram](#-notifications-push--telegram) +9. [Paiements Crypto](#-paiements-crypto) +10. [Systeme GPS Integre](#-systeme-gps-integre) +11. [Codes d'Erreur](#codes-derreur) --- @@ -35,14 +35,12 @@ Cette API REST complĂšte gĂšre une plateforme de livraison avec assignation auto - **📍 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Ă©) +- **🎯 SystĂšme de pĂ©nalitĂ©s** pour la gestion des comportements +- **🔔 Notifications push** Expo (iOS/Android) pour clients et livreurs +- **đŸ€– IntĂ©gration Telegram** pour alertes et notifications admin/livreur - **💾 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 @@ -242,7 +240,7 @@ graph TD P3["GET /api/v1/products/category/:category"] P4["GET /api/v1/categories"] P5["GET /api/v1/app-settings"] - P6["POST /api/v1/webhooks/nowpayments"] + P6["POST /api/v1/webhook/nowpayments"] P7["POST /webhook/telegram"] end @@ -364,7 +362,6 @@ sequenceDiagram 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 @@ -377,36 +374,15 @@ sequenceDiagram end rect rgb(200, 200, 230) - Note over U,TG: Login Standard (2FA desactivee) + Note over U,R: Login 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 + API-->>F: 200 OK + JWT Token + F->>F: Stocker token (localStorage) F-->>U: Connecte end @@ -556,7 +532,6 @@ erDiagram int product_id FK int quantity float price - boolean active_price } PRODUCT_MEDIA { @@ -641,10 +616,6 @@ graph LR C3["product:cache:{id}
TTL: 1h"] end - subgraph Notifs["🔔 Notifications"] - N1["notifications:{username}
TTL: 1h"] - end - subgraph PubSub["ïżœïżœ Pub/Sub Channels"] PS1["channel:position_updates"] PS2["channel:order_status"] @@ -656,95 +627,11 @@ graph LR REDIS --- Positions REDIS --- Queues REDIS --- Cache - REDIS --- Notifs 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 - -```mermaid -graph TB - Internet((Internet)) -->|"80/443 public"| Prod["prod-uber — DMZ
185.103.166.119"] - Prod -->|"VPN (10.0.0.6)"| BDD["bdd-redis-prod — LAN
10.0.0.5"] - BDD --> PG["PostgreSQL :5432"] - BDD --> Redis["Redis :6379"] - - VPN["vpn-uber — 10.0.0.1
jump host SSH"] -.->|"accĂšs admin uniquement"| BDD - VPN -.-> Prod - - style Internet fill:#eee,stroke:#999 - style Prod fill:#fde68a,stroke:#b45309 - style BDD fill:#bbf7d0,stroke:#15803d - style VPN fill:#bfdbfe,stroke:#1d4ed8 -``` - -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 @@ -791,84 +678,53 @@ docker compose -f docker/docker-compose-prod.yml up -d --build docker compose -f docker/docker-compose-prod.yml logs -f waf ``` -### Services Docker (prod-uber) +### Services Docker | 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). +| `postgres` | postgres:16-alpine | Base de donnĂ©es | +| `redis` | redis:7-alpine | Cache + sessions + queues | ### 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 - -# Stockage mĂ©dias produits (photos/vidĂ©os) -STORAGE_DRIVER=local # "local" (disque, dĂ©faut) ou "s3" (RustFS) -# Requis uniquement si STORAGE_DRIVER=s3 : -S3_REGION= # RĂ©gion S3 (arbitraire pour RustFS, ex: us-east-1) -S3_BUCKET= # Nom du bucket -S3_ENDPOINT= # URL du endpoint S3-compatible (RustFS, IP VPN interne) -RUSTFS_ACCESS_KEY= # ClĂ© d'accĂšs RustFS -RUSTFS_SECRET_KEY= # ClĂ© secrĂšte RustFS -``` - -```mermaid -flowchart LR - Upload["CreateProduct /
UploadMedia"] --> Check{"STORAGE_DRIVER"} - Check -->|"local (défaut)"| Local["LocalStorage
disque ./uploads"] - Check -->|"s3"| S3["S3Storage
RustFS (auto-hébergé)"] - - Delete["DeleteProduct /
DeleteMedia"] --> KeyCheck{"media.Key
rempli ?"} - KeyCheck -->|non| Local - KeyCheck -->|oui| S3 +REDIS_PASSWORD= # Mot de passe Redis +TOMTOM_API_KEY= # ClĂ© API TomTom +SESSION_SECRET= # Secret sessions +TELEGRAM_WEBHOOK_URL= # URL webhook Telegram +TELEGRAM_WEBHOOK_SECRET= # Secret webhook Telegram +NOWPAYMENTS_IPN_SECRET= # Secret IPN NowPayments ``` --- -## 🔔 Notifications +## 🔔 Notifications Push & Telegram -Les notifications clients et livreurs sont gĂ©rĂ©es **exclusivement via Telegram** — les push notifications Expo (iOS/Android) ne sont plus utilisĂ©es. +### Push Notifications (Expo) -### Notifications Telegram +Le systĂšme utilise Expo Push Notifications pour envoyer des notifications aux applications mobiles (client et livreur). Les tokens Expo sont envoyĂ©s directement via l'API Expo depuis le backend — il n'y a pas d'endpoint REST dĂ©diĂ© Ă  l'enregistrement du push token. -Clients, livreurs et admins reçoivent leurs alertes via un bot Telegram liĂ© Ă  leur compte. +**Flux :** +1. L'app mobile obtient un `ExponentPushToken` via `expo-notifications` +2. Le backend envoie les notifications via `sendExpoPush()` dans `db/db_notifications.go` +3. Expo relay la notification vers le device cible (iOS/Android) **Types de notifications envoyĂ©es :** - `assigned` — Commande assignĂ©e Ă  un livreur - `en_route` — Livreur en route (avec ETA) -- `arrived` — Livreur arrivĂ© +- `arrived` — Livreur arrivĂ© (bouton "Le livreur est lĂ ") - `livre` — Commande livrĂ©e -- `ready_pickup` — Cabine : "descendez chercher votre commande" +- `ready_pickup` — Notification cabine "descendez chercher" - `address_proposal` — Proposition de changement d'adresse +### Notifications Telegram + Les admins et livreurs peuvent lier leur compte Telegram pour recevoir des alertes. #### GĂ©nĂ©rer un token de liaison (Admin) @@ -947,9 +803,50 @@ Authorization: Bearer ## 🔐 Authentication -### CrĂ©ation de compte client +### Register 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. +**CrĂ©er un nouveau compte client** + +```bash +POST /api/v1/auth/register +Content-Type: application/json +``` + +**RequĂȘte:** +```json +{ + "username": "jean_dupont", + "password": "SecurePass123!", + "nom": "Dupont", + "prenom": "Jean", + "telephone": "+33612345678" +} +``` + +**RĂ©ponse (201 Created):** +```json +{ + "success": true, + "message": "Client créé avec succĂšs", + "client": { + "id": 42, + "username": "jean_dupont", + "nom": "Dupont", + "prenom": "Jean", + "telephone": "+33612345678", + "command": 0, + "point": 0, + "points_zipette": 0, + "amende": 0.0, + "cancellations_count": 0, + "created_at": "2025-01-18T14:30:00Z" + } +} +``` + +**Erreurs possibles:** +- `400` - DonnĂ©es invalides (validation Ă©chouĂ©e) +- `409` - Username ou tĂ©lĂ©phone dĂ©jĂ  utilisĂ© --- @@ -970,7 +867,7 @@ Content-Type: application/json } ``` -**RĂ©ponse standard (200 OK) — 2FA dĂ©sactivĂ©e:** +**RĂ©ponse (200 OK):** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", @@ -983,122 +880,12 @@ Content-Type: application/json } ``` -**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)** @@ -1127,8 +914,6 @@ 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 @@ -1152,8 +937,7 @@ GET /api/v1/products "id": 1, "product_id": 1, "quantity": 1, - "price": 12.50, - "active_price": true + "price": 12.50 } ], "media": [ @@ -1301,7 +1085,8 @@ Content-Type: application/json { "success": true, "message": "Produit supprimĂ© du panier avec succĂšs", - "item_id": 15 + "item_id": 15, + "stock_released": true } ``` @@ -1322,7 +1107,8 @@ Authorization: Bearer ```json { "success": true, - "message": "Panier vidĂ© avec succĂšs" + "message": "Panier vidĂ© avec succĂšs", + "stock_released": 3 } ``` @@ -1379,38 +1165,6 @@ Content-Type: application/json - `401` - Non authentifiĂ© - `500` - Erreur crĂ©ation commande -**RĂ©ponse — adresse connue pour ĂȘtre mal formĂ©e (400 Bad Request):** - -Si l'adresse saisie correspond (match exact ou normalisĂ© — accents/casse/espaces ignorĂ©s) Ă  une entrĂ©e de la table de corrections gĂ©rĂ©e par l'admin/cabine (`POST/DELETE /addresses`), le checkout est volontairement bloquĂ© pour forcer une reconfirmation du client plutĂŽt que d'appliquer la correction en silence : -```json -{ - "error": "Adresse non reconnue", - "corrected_address": "15 Rue de la Paix, 75002 Paris, France" -} -``` -Le client doit renvoyer la requĂȘte avec `delivery_address` = `corrected_address` pour valider le checkout. - -```mermaid -sequenceDiagram - participant C as Client - participant API as API (ValidateBasket) - participant DB as adresse_correction - - C->>API: POST /checkout {delivery_address} - API->>DB: CheckAddress(delivery_address) - DB->>DB: Match exact, sinon fallback normalisĂ©
(accents/casse/espaces) - alt Correction trouvĂ©e - DB-->>API: corrected_address - API-->>C: 400 {error, corrected_address} - C->>C: Affiche la suggestion (modal) - C->>API: POST /checkout {delivery_address: corrected_address} - API-->>C: 200 Commande créée - else Aucune correction connue - DB-->>API: nil - API-->>C: 200 Commande créée - end -``` - --- #### Mes commandes avec suivi @@ -1963,34 +1717,9 @@ Content-Type: application/json --- -### SystĂšme de PĂ©nalitĂ©s (Clients uniquement) +### SystĂšme de PĂ©nalitĂ©s -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) +#### Appliquer une pĂ©nalitĂ© ```bash POST /api/v2/admin/protected/penalty @@ -2272,22 +2001,6 @@ Content-Type: application/json > **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 @@ -2580,26 +2293,23 @@ L'ETA est calcule en utilisant l'API TomTom qui prend en compte: - 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?} + A[Demande ETA] --> B{TomTom API disponible?} + B -->|Oui| C[Appel TomTom Routing API] + C --> D[ETA avec trafic reel] 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] + E --> F[Distance a vol d'oiseau] F --> G[Vitesse moyenne 30 km/h] - G --> H[ETA estimĂ©] + G --> H[ETA estime] D --> I[Retourner ETA] H --> I + I --> J{Fallback utilise?} + J -->|Oui| K[Ajouter flag fallback_used: true] + J -->|Non| L[Response standard] ``` -**Fallback:** Si toutes les clĂ©s TomTom sont Ă©puisĂ©es ou indisponibles, le systĂšme utilise un calcul local base sur: +**Fallback:** Si l'API TomTom est indisponible, le systeme utilise un calcul local base sur: - Distance Haversine - Vitesse moyenne estimee (30 km/h en ville) @@ -2986,8 +2696,6 @@ stateDiagram-v2 - 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 @@ -3008,9 +2716,9 @@ Si une adresse ne peut pas etre geocodee: - Un log d'erreur est genere - L'admin peut corriger l'adresse manuellement -#### API TomTom Indisponible / Quota dĂ©passĂ© +#### API TomTom Indisponible -Le systĂšme tente d'abord toutes les clĂ©s disponibles en rotation, puis bascule sur le calcul local : +Le systeme bascule automatiquement 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 @@ -3084,15 +2792,6 @@ Le systĂšme tente d'abord toutes les clĂ©s disponibles en rotation, puis bascule } ``` -#### 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 { @@ -3159,64 +2858,8 @@ Le systĂšme tente d'abord toutes les clĂ©s disponibles en rotation, puis bascule --- -## 📋 Changelog - -### v5.7.0 — 2026-08-04 - -- **Nouveau — choix du backend de stockage mĂ©dias (`STORAGE_DRIVER`)** : les photos/vidĂ©os produits pouvaient ĂȘtre stockĂ©es soit sur disque local (`CreateProduct`) soit sur S3/RustFS (`UploadMedia`) selon l'endpoint utilisĂ©, sans logique commune. Une interface `Storage` unifiĂ©e (`services/storage.go`, implĂ©mentations `LocalStorage`/`S3Storage`) est maintenant utilisĂ©e par les deux endpoints, pilotĂ©e par la variable d'environnement `STORAGE_DRIVER` (`local` par dĂ©faut, ou `s3`). -- **Fix — nettoyage croisĂ© des mĂ©dias Ă  la suppression** : `DeleteProduct` supprimait toujours sur disque (mĂȘme pour un mĂ©dia stockĂ© sur S3) et `DeleteMedia` supprimait toujours sur S3 (mĂȘme pour un mĂ©dia local), laissant systĂ©matiquement des fichiers orphelins sur l'autre backend. Les deux fonctions branchent dĂ©sormais sur `media.Key` (rempli uniquement pour les mĂ©dias S3) pour cibler le bon backend, indĂ©pendamment du `STORAGE_DRIVER` courant. -- **Fix — correction d'adresse jamais appliquĂ©e au checkout (`CheckAddress`)** : le mĂ©canisme de correction d'adresse (table `adresse_correction`, gĂ©rĂ©e par l'admin/cabine) dĂ©tectait bien une adresse connue pour ĂȘtre mal formĂ©e, mais le checkout Ă©tait systĂ©matiquement abandonnĂ© au lieu de proposer la correction de façon exploitable — le contrat de rĂ©ponse (`corrected_address`) n'Ă©tait lu nulle part cĂŽtĂ© client (mobile : parsing d'un format de message d'erreur obsolĂšte ; web : aucune gestion de ce cas). Mobile et web lisent dĂ©sormais directement `corrected_address` et proposent au client de reprendre le checkout avec l'adresse corrigĂ©e. -- **Fix — matching de correction d'adresse trop strict** : `CheckAddress` ne matchait qu'une Ă©galitĂ© exacte de texte. Ajout d'un fallback normalisĂ© (accents/casse/espaces ignorĂ©s, rĂ©utilise `utils.NormalizeAddress` dĂ©jĂ  utilisĂ©e par le service de gĂ©ocodage) pour rattraper les variantes mineures de saisie. - -### v5.6.0 — 2026-07-11 - -- **Fix — ETA introuvable pour l'annulation tardive (`CheckCommandETAExistsAndValid`)** : la fonction lisait la clĂ© Redis `command:eta:{id}` (un *hash*) avec `Redis.Get` (string), ce qui provoquait systĂ©matiquement une erreur `WRONGTYPE` silencieuse. Une ETA valide n'Ă©tait donc jamais dĂ©tectĂ©e par ce chemin, et un client pouvait annuler sans pĂ©nalitĂ© juste aprĂšs l'assignation d'un livreur (avant le passage au statut `en_route`). CorrigĂ© en `Redis.HGetAll`. -- **Fix — position livreur introuvable (`GetDeliverymanLocationForCommand`)** : mĂȘme bug `WRONGTYPE` (`Redis.Get` sur un hash) empĂȘchant l'affichage de la position temps rĂ©el sur certains suivis de commande. CorrigĂ© en `Redis.HGetAll`. -- **Fix — ETA absente des notifications/app mobile/site web** : trois fonctions qui Ă©crivent l'ETA dans Redis utilisaient des noms de champ incohĂ©rents (`eta_minutes` vs `total_eta_minutes`) alors que les clients ne lisent que `eta_minutes`. Les trois writers Ă©crivent dĂ©sormais les deux champs de façon cohĂ©rente. -- **Fix — boucle infinie de gĂ©ocodage (`ResolveAddress` ↔ `GeocodeAddress`)** : la correction d'adresse mal Ă©crite et le gĂ©ocodage se rappelaient mutuellement sans condition de sortie pour toute adresse Ă©chouant au gĂ©ocodage direct (le cas d'usage mĂȘme de la correction), provoquant un blocage. `ResolveAddress` appelle dĂ©sormais directement le cache/Nominatim sans repasser par `GeocodeAddress`. -- **Fix — rĂ©compenses par points non atomiques** : `ClaimPoolReward` (consommation des points) et `AddRewardsToBasket` (ajout du produit au panier) Ă©taient deux Ă©tapes sĂ©parĂ©es ; un produit rĂ©compense supprimĂ©/introuvable faisait perdre la rĂ©compense au client sans qu'il reçoive rien. FusionnĂ©es dans `ClaimPoolRewardAndAddToBasket`, exĂ©cutĂ©e dans une seule transaction. -- **Fix — annulation admin non atomique** : `UpdateCommandStatusAdmin` pouvait rembourser deux fois le stock en cas d'appels concurrents. Bascule sur `CancelCommandByAdminAtomic` (transaction + verrou `FOR UPDATE`). -- **Fix — proratisation du chiffre d'affaires (stats)** : les commandes avec `referral_used` n'Ă©taient pas correctement proratisĂ©es dans les statistiques de revenu. -- **Notifications — durĂ©e de rĂ©tention rĂ©duite Ă  1h** : les notifications stockĂ©es dans Redis passent d'un TTL de 7 jours Ă  1 heure (cohĂ©rent avec leur usage temps rĂ©el, Ă©vite l'accumulation inutile). -- **Suppression de code mort supplĂ©mentaire** : nettoyage dans `CreateCommand` et `DeleteCommandItem` (rendu atomique). -- **Tests unitaires** : ajout de suites complĂštes couvrant la gestion de stock (checkout/annulation/items), les statistiques, le gĂ©ocodage et la correction d'adresses mal Ă©crites (algorithme pur + intĂ©gration rĂ©seau rĂ©elle rate-limitĂ©e), les calculs de temps/ETA de commande, les rĂ©compenses par points (dĂ©duction de stock, atomicitĂ©), et l'annulation de commande avec pĂ©nalitĂ© de retard (barĂšme, cumul concurrentiel, dĂ©tection via statut ou ETA, flux "client absent"). - -### 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-11 -**Version API :** 5.6.0 +**Documentation mise Ă  jour le :** 2026-05-01 +**Version API :** 5.2.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 +**Base URL prod :** `https://mln-uber.club`