Documentation UrbaFood

Architecture & Guide technique

Documentation technique et fonctionnelle complète de la plateforme UrbaFood — plateforme municipale de livraison alimentaire équitable.

v1.7.0NestJS 10Next.js 14MariaDB 11.5📱 PWA Vite 5Mise à jour : 20 février 2026

🏛️ Vue d'ensemble

Présentation

UrbaFood est une plateforme municipale de livraison alimentaire conçue pour soutenir l'économie locale. Contrairement aux plateformes commerciales (Uber Eats, Deliveroo…) qui prélèvent 25–30% de commission, UrbaFood prélève 1% seulement, uniquement pour couvrir les frais de maintenance.

💡L'argent reste dans le territoire local. Chaque euro dépensé bénéficie directement aux restaurateurs et coursiers de la commune.

Objectifs

ObjectifDescription
Équité économiqueCommission de 1% pour les restaurateurs vs 25–30% sur les plateformes commerciales
Rémunération justeLes coursiers reçoivent une rémunération transparente et équitable
Économie locale100% des revenus restent dans le territoire municipal
Gouvernance publiquePlateforme sans actionnaires ni pression de rentabilité commerciale
TransparenceComptes publics, données ouvertes, gouvernance municipale

Stack technique

CoucheTechnologieVersionRôle
BackendNestJS10.xAPI REST + auth JWT + modules métier
FrontendNext.js14.x (App Router)Interface SSR/SSG React 18
Base de donnéesMariaDB11.5Stockage principal, InnoDB, UTF8MB4
Cache / QueueRedis7.xSessions, BullMQ, rate limiting
ORMTypeORM0.3.xEntités, repositories, migrations
AuthPassport.js + JWTStratégie JWT access+refresh tokens
ProxyNginx1.27Reverse proxy interne, rate limiting
SSL/LoadHAProxyLatestSSL termination, routing domaine
RuntimeNode.js20 (LTS)Alpine, dumb-init, non-root user
DevOpsDocker + ComposeOrchestration multi-services

🏗️ Architecture système

Infrastructure

L'architecture suit un modèle en couches avec HAProxy comme point d'entrée unique, délégant vers Nginx qui route vers le backend ou le frontend selon le chemin de la requête.

🌐 InternetHTTPS :443HTTPSHAProxySSL termination · :80/:443HTTP interneNginx (urbafood_web)Reverse proxy · :80/* → :3001/coursier-app/*/api/* → :3000Frontend Next.jsurbafood_frontend · :3001📱 PWA Coursiersurbafood_coursiers · :80Backend NestJSurbafood_backend · :3000TypeORMMariaDB 11.5urbafood_mariadb · :3306Redis 7urbafood_redis · :6379Workerurbafood_worker · queue─ ─ connexion async / queue
Internet (HTTPS :443)
    ↓  SSL termination
HAProxy  (/srv/haproxy)
    ↓  HTTP interne  (réseau: urbafood_network)
Nginx  (urbafood_web:80)
    ├─ /api/*             →  Backend NestJS        (urbafood_backend:3000)
    ├─ /coursier-app/*    →  PWA Coursiers React   (urbafood_coursiers:80)
    └─ /*                 →  Frontend Next.js       (urbafood_frontend:3001)

Backend NestJS
    ├─ TypeORM  →  MariaDB  (urbafood_mariadb:3306)
    ├─ Redis    →  Cache / Queue  (urbafood_redis:6379)
    └─ Worker   →  BullMQ processors (urbafood_worker)

Services Docker

ServiceContainerPortImageRôle
nginxurbafood_web80 (interne)nginx:1.27-alpinePoint d'entrée HAProxy, routing /api/* / /coursier-app/* / /*
backendurbafood_backend3000 (interne)urbafood-backend (custom)API REST NestJS
frontendurbafood_frontend3001 (interne)urbafood-frontend (custom)Interface Next.js SSR (clients + restaurateurs)
coursiersurbafood_coursiers80 (interne)urbafood-coursiers (custom)PWA Coursiers — Vite/React SPA, installable Android/iOS
workerurbafood_workerurbafood-worker (custom)Queue BullMQ + cron jobs
mariadburbafood_mariadb3306 (interne)mariadb:11.5Base de données principale
redisurbafood_redis6379 (interne)redis:7-alpineCache, sessions, queue

Réseau

RéseauTypeUsage
urbafood_networkExterne (bridge)Connexion HAProxy ↔ Nginx. Ce réseau est partagé avec les autres projets du serveur.
internal_networkInterne (bridge)Communication entre services UrbaFood uniquement (backend, DB, redis, worker)
⚠️Le container Nginx doit s'appeler urbafood_web et écouter sur le port 80 — configuré en dur dans HAProxy.

🗄️ Base de données

Schéma

MariaDB 11.5, moteur InnoDB, charset UTF8MB4 (support émojis), timestamps UTC, soft deletes sur toutes les tables (deleted_at).

ℹ️Le schéma est initialisé via /srv/urbafood/database/init/01-schema.sql et les seeds via 02-seeds.sql. TypeORM est configuré avec synchronize: false — toute modification passe par SQL ou migration.

Tables

TableRôleClés importantes
usersTous les utilisateurs de la plateformeemail UNIQUE, role ENUM (CLIENT/RESTAURANT/COURIER/ADMIN)
restaurantsÉtablissements validés par la municipalitéowner_id → users, commission_rate DEFAULT 1.00, is_validated
dishesPlats des menus restaurateursrestaurant_id → restaurants, category, is_available
couriersCoursiers avec disponibilitéuser_id → users, vehicle_type, is_available
ordersCommandes avec workflow de statut completstatus ENUM 8 états, commission_amount = total × 1%
order_itemsLignes d'une commande (snapshot prix)dish_name snapshot au moment de la commande
paymentsPaiements Stripestripe_payment_intent_id, status
reviewsAvis clients post-livraisonrating 1–5, restaurant_id + courier_id

Diagramme ERD

users🔑 idBIGINT emailVARCHAR password_hashVARCHAR first_nameVARCHAR roleENUM is_activeBOOLrestaurants🔑 idBIGINT🔗 owner_id→ users nameVARCHAR commission_rateDECIMAL is_validatedBOOL total_ordersINTdishes🔑 idBIGINT🔗 restaurant_id→ restaurants nameVARCHAR categoryVARCHAR priceDECIMAL is_availableBOOLorders🔑 idBIGINT🔗 client_id→ users🔗 restaurant_id→ restaurants order_numberVARCHAR statusENUM total_amountDECIMAL commission_amountDECIMALorder_items🔑 idBIGINT🔗 order_id→ orders🔗 dish_id→ dishes dish_nameVARCHAR quantityINT unit_priceDECIMALpayments🔑 idBIGINT🔗 order_id→ orders stripe_idVARCHAR statusENUM amountDECIMALreviews🔑 idBIGINT🔗 order_id→ orders ratingTINYINT commentTEXT

🔌 API REST

Base URL : https://urbafood.me/api — Documentation Swagger interactive : https://urbafood.me/api/docs

ℹ️Tous les endpoints protégés nécessitent Authorization: Bearer <accessToken> dans le header.

Authentification

MéthodeRouteAuthDescription
POST/auth/registerNonCréer un compte (email, password, firstName, lastName, role)
POST/auth/loginNonConnexion → accessToken (15 min) + refreshToken (7 j)
POST/auth/refreshNonRenouveler le token — body: { refreshToken } → nouveaux tokens
GET/auth/meJWTProfil de l'utilisateur connecté

Restaurants

MéthodeRouteAuthDescription
GET/restaurants/search?q=&city=NonRecherche plein-texte — nom restaurant, plat, catégorie, ingrédients. city inclut les communes limitrophes (même département).
GET/restaurants/categoriesNonListe des catégories de plats distinctes (pour les chips de filtre)
GET/restaurants/myJWTRestaurants du restaurateur connecté
POST/restaurantsJWT RESTAURANTCréer un restaurant
PUT/restaurants/:idJWT propriétaireModifier le restaurant
GET/restaurants/:id/statsJWT propriétaireStatistiques du restaurant

Plats

MéthodeRouteAuthDescription
GET/restaurants/:rId/dishesJWTListe groupée par catégorie
POST/restaurants/:rId/dishesJWT propriétaireAjouter un plat
PUT/restaurants/:rId/dishes/:idJWT propriétaireModifier un plat
PATCH/restaurants/:rId/dishes/:id/toggleJWT propriétaireActiver / désactiver
DELETE/restaurants/:rId/dishes/:idJWT propriétaireSoft delete

Commandes

MéthodeRouteAuthDescription
GET/orders/restaurant/:rIdJWTListe commandes avec filtre ?status=
GET/orders/restaurant/:rId/dashboardJWTKPIs du tableau de bord
PATCH/orders/:id/advanceJWT propriétaireAvancer au statut suivant
POST/orders/:id/cancelJWT propriétaireAnnuler + raison

Google Business Profile

Ces endpoints permettent à un restaurateur de connecter et synchroniser sa fiche Google Business Profile avec son restaurant UrbaFood. L'OAuth Google est géré côté backend — aucun secret n'est exposé au frontend.

MéthodeRouteAuthDescription
GET/restaurants/:id/google/authJWTInitie le flux OAuth → redirige vers Google (ou ?format=json pour obtenir l'URL)
GET/restaurants/google/callbackPublicReçoit le code Google, importe les données, redirige vers le frontend
GET/restaurants/:id/google/syncJWTForce une re-synchro des données GBP (refresh token auto)
GET/restaurants/:id/google/statusJWTStatut de la connexion Google (connecté, dernière synchro, expiration token)
ℹ️Les tokens Google sont chiffrés en AES-256-GCM avant stockage. Le state OAuth est signé HMAC-SHA256 avec une durée de vie de 10 minutes — aucune session serveur n'est nécessaire.

Coursiers

Tous les endpoints coursiers nécessitent un token JWT valide (role: COURIER recommandé). Le profil coursier est créé juste après l'inscription via POST /couriers/register.

MéthodeRouteAuthDescription
POST/couriers/registerJWTCréer ou récupérer le profil coursier (vehicleType, licensePlate)
GET/couriers/meJWTProfil complet du coursier connecté (avec données user)
PATCH/couriers/meJWTModifier véhicule et plaque d'immatriculation
PATCH/couriers/availabilityJWTBasculer disponible / hors ligne
GET/couriers/orders/availableJWTCommandes READY sans coursier assigné (triées par date)
GET/couriers/orders/my?filter=active|historyJWTMes livraisons — active : picked_up / history : delivered + cancelled
POST/couriers/orders/:id/acceptJWTAccepter une livraison → courier_id assigné, status → picked_up. 409 si déjà actif.
POST/couriers/orders/:id/deliverJWTMarquer livrée → status → delivered, deliveredAt = NOW(), totalDeliveries++
GET/couriers/statsJWTStats du jour : livraisons, gains, commande active, note moyenne, isAvailable
💡La documentation interactive complète (Swagger) est disponible ci-dessous dans la section API Reference.

📖 API Reference (Swagger)

L'interface Swagger UI générée automatiquement par NestJS documente l'intégralité des endpoints disponibles, avec schémas de requête/réponse, codes d'erreur et possibilité de tester chaque route directement depuis le navigateur.

💡Pour tester les routes protégées, cliquez sur Authorize (cadenas) dans l'interface ci-dessous et collez votre accessToken obtenu via POST /api/auth/login.

Interface interactive

https://urbafood.me/api/docsOuvrir en plein écran ↗
Chargement de l'interface Swagger…

Utilisation rapide

ÉtapeActionDétail
1Obtenir un tokenPOST /api/auth/login → copier l'accessToken
2S'authentifierCliquer sur Authorize → coller le token dans le champ BearerAuth
3Tester un endpointDéplier la route, cliquer Try it outExecute
4Lire la réponseCorps JSON, code HTTP et headers sont affichés directement

Lien direct en plein écran : https://urbafood.me/api/docs ↗

🔐 Authentification

JWT Flow

Client/AppBackend NestJSMariaDBPOST /auth/login { email, password }SELECT user WHERE email = ?user row{ accessToken (15m), refreshToken (7j) }GET /api/... + Bearer <token>

Le système utilise deux tokens : un access token (15 min) pour les requêtes API, et un refresh token (7 jours) pour renouveler l'accès sans re-authentification. Le renouvellement est automatique et transparent côté frontend — lib/api.ts intercepte les 401, appelle POST /auth/refresh, puis rejoue la requête initiale.

// Payload JWT
{
  "sub": 1,               // user.id
  "email": "user@ex.fr",
  "role": "RESTAURANT",
  "iat": 1234567890,
  "exp": 1234568790       // +15min
}
⚠️Les mots de passe sont hashés avec Argon2id. Le sel est intégré au hash. Ne jamais stocker les mots de passe en clair.

Rôles & permissions

RôleDescriptionAccès
CLIENTClient finalParcourir restaurants, passer commandes, laisser avis
RESTAURANTRestaurateurGérer menu, traiter commandes, voir stats
COURIERCoursierGérer disponibilité, accepter/livrer courses
ADMINAdministration municipaleValider restaurants, gérer utilisateurs, rapports

🔄 Flux fonctionnels

Cycle de vie d'une commande

En attenteConfirméePréparationPrêteRécupéréeLivraisonLivrée ✓ConfirmerPréparerPrêtPrise en chargeEn routeConfirméAnnulée ✗Annulation possible avant livraison
StatutActeurAction déclenchante
pendingSystèmeCommande créée par le client, paiement en attente
confirmedRestaurantLe restaurateur confirme la commande
preparingRestaurantLa cuisine commence la préparation
readyRestaurantLe plat est prêt, en attente coursier
picked_upCoursierRécupération au restaurant
deliveringCoursierEn route vers le client
deliveredCoursier / SystèmeLivraison confirmée
cancelledRestaurant / ClientAnnulation avant livraison

Parcours restaurateur

1. Inscription → /login (role: RESTAURANT)
2. Créer restaurant → /restaurant/profil
   └─ En attente validation municipale (is_validated: false)
3. Ajouter menu → /restaurant/menu
   └─ Plats groupés par catégorie
   └─ Toggle disponibilité en temps réel
4. Traiter commandes → /restaurant/commandes
   └─ pending → confirmer → préparer → prêt
5. Suivre revenus → /restaurant/statistiques
   └─ CA brut, commission 1%, net, panier moyen

Parcours coursier

💡Le parcours coursier est disponible en deux versions : l'espace Next.js intégré (/coursier/*) et la PWA installable (/coursier-app/) — optimisée mobile avec mode hors-ligne et icône sur l'écran d'accueil.
1. Découverte → /rejoindre-coursier (page marketing)
2. Inscription → /login?mode=register&role=COURIER   (Next.js)
              ou directement sur https://urbafood.me/coursier-app/  (PWA)
   └─ Sélection du véhicule (vélo, scooter, voiture)
   └─ POST /couriers/register appelé automatiquement
   └─ Redirection vers /coursier  (Next.js)  ou /  (PWA)
3. Dashboard → /coursier  (Next.js)  |  /coursier-app/  (PWA)
   └─ Toggle disponible / hors ligne (PATCH /couriers/availability)
   └─ Stats du jour : livraisons, gains, note
   └─ Aperçu des livraisons disponibles (3 premières)
4. Accepter une livraison → /coursier/livraisons  |  /coursier-app/livraisons
   └─ GET /couriers/orders/available (statut READY, courier_id IS NULL)
   └─ POST /couriers/orders/:id/accept
      → courier_id = courseId, status → picked_up, picked_up_at = NOW()
      → 409 si le coursier a déjà une livraison active
5. Suivre la livraison → /coursier/en-cours  |  /coursier-app/en-cours
   └─ Timeline : récupération au restaurant → livraison en cours
   └─ POST /couriers/orders/:id/deliver
      → status → delivered, delivered_at = NOW(), totalDeliveries++
6. Consulter les gains → /coursier/gains  |  /coursier-app/gains
   └─ Filtre aujourd'hui / 7 jours / 30 jours
   └─ 100 % des frais de livraison reversés au coursier
7. Gérer le profil → /coursier/profil  |  /coursier-app/profil
   └─ Modifier véhicule, plaque
   └─ Voir note moyenne, total livraisons
💡Le coursier reçoit 100 % des frais de livraison. UrbaFood prélève uniquement 1 % du montant commande, côté restaurant — jamais sur les gains du coursier.

🖥️ Frontend

Pages & routes

RoutePageAuthDescription
/Landing pageNonPrésentation plateforme + moteur de recherche (restaurant, plat, ingrédient, ville/commune limitrophe)
/loginConnexionNonLogin + inscription. Supporte ?mode=register&role=COURIER pour le parcours coursier
/rejoindre-restaurantMarketing restaurateursNonPage présentation + avantages + FAQ pour les restaurateurs
/rejoindre-coursierMarketing coursiersNonPage présentation + comparatif rémunération + FAQ pour les coursiers
/restaurantDashboardRESTAURANTKPIs + commandes en attente
/restaurant/commandesCommandesRESTAURANTGestion complète avec filtres
/restaurant/menuMenuRESTAURANTCRUD plats par catégorie
/restaurant/statistiquesStatsRESTAURANTFinances + comparaison marché
/restaurant/profilProfilRESTAURANTInfos restaurant + badge validation
/coursierDashboardCOURIERToggle dispo, stats du jour, livraisons disponibles, commande active
/coursier/livraisonsLivraisons dispoCOURIERListe commandes READY à accepter — bouton Accepter
/coursier/en-coursLivraison activeCOURIERTimeline récupération → livraison — bouton Commande livrée
/coursier/gainsGainsCOURIERHistorique gains par période (aujourd'hui / 7j / 30j)
/coursier/profilProfil coursierCOURIERVéhicule, stats, toggle dispo, déconnexion
/docsDocumentationNonCette page — inclut Swagger UI embarqué
/api/docsSwagger UINonDocumentation API interactive (NestJS, aussi accessible depuis /docs)

Système de design

TokenValeurUsage
Primary Blue#1E3A8ATitres, structure, sidebar, liens actifs
Action Orange#F97316Boutons CTA uniquement
Accent Green#16A34ABadges positifs, disponibilité, checkmarks
Light Grey#F3F4F6Fonds de sections alternées
Border#E5E7EBSéparateurs, bordures cartes
Border Radius Card12pxToutes les cartes
Border Radius Button8pxTous les boutons
FontInterPoids 400–900, Google Fonts

📱 PWA Coursiers

Présentation

La PWA Coursiers est une application Progressive Web App installable sur Android et iOS, accessible à https://urbafood.me/coursier-app/. Elle offre une expérience native — plein écran sans barre Chrome, icône sur l'écran d'accueil, fonctionnement partiel hors-ligne — tout en étant une application web standard.

💡La PWA est distincte de l'espace coursier Next.js (/coursier/*) : elle est optimisée pour les appareils mobiles, supporte le mode hors-ligne via Service Worker (Workbox), et peut être installée comme une app native depuis Chrome Android.
PageRoute PWADescription
Connexion/loginConnexion + inscription (rôle COURIER auto-assigné, sélection véhicule)
Tableau de bord/Toggle disponibilité, stats du jour, aperçu commandes disponibles
Livraisons dispo/livraisonsListe des commandes READY à accepter (auto-refresh 30 s)
En cours/en-coursLivraison active : timeline + bouton « Livrée »
Gains/gainsHistorique des gains par période (jour / 7 j / 30 j)
Profil/profilVéhicule, plaque, toggle disponibilité, logout

Installation Android / iOS

Android (Chrome) :

1. Ouvrir https://urbafood.me/coursier-app/ dans Chrome Android
2. Se connecter avec un compte COURIER
3. Chrome affiche automatiquement le bandeau "Installer l'application"
4. Ou : menu ⋮ → "Installer l'application" / "Ajouter à l'écran d'accueil"
5. L'icône UrbaFood Coursier apparaît sur l'écran d'accueil
6. Au lancement : mode plein écran sans barre de navigation Chrome
⚠️HTTPS obligatoire — le Service Worker ne s'enregistre qu'en contexte sécurisé. En production, HAProxy gère le certificat Let's Encrypt.

Critères d'installabilité (Chrome Android) :

CritèreStatut
Servi en HTTPS✅ HAProxy + Let's Encrypt
manifest.webmanifest valide✅ vite-plugin-pwa (généré automatiquement)
display: standalone
Service Worker enregistré✅ Workbox (generateSW)
Icône ≥ 192×192⚠️ SVG par défaut — exécuter npm run generate-icons pour PNG
start_url accessible

Stack & architecture

TechnologieVersionRôle
Vite5Bundler + dev server
React18UI
TypeScript5Typage statique
React Router6Navigation SPA avec basename=/coursier-app/
vite-plugin-pwa0.20Service Worker (Workbox) + manifest automatiques
Workbox7Stratégies de cache : CacheFirst assets, NetworkFirst API

Comportement hors-ligne :

RessourceStratégieTTL
Assets statiques (JS, CSS, HTML, SVG)CacheFirst (précache)Permanent (versioning par hash)
Appels API /api/*NetworkFirst5 minutes
ℹ️L'app reste partiellement fonctionnelle sans réseau : les données du dernier chargement sont affichées depuis le cache. Les actions (accepter, livrer) nécessitent une connexion.

Auth JWT : même système que le frontend principal — uf_token (15 min), uf_refresh_token (7 j), refresh automatique sur 401 dans src/api.ts.

Déploiement

# 1. Build de l'image Docker (depuis /srv/urbafood/)
docker build \
  --build-arg VITE_API_URL=https://urbafood.me/api \
  --build-arg VITE_BASE=/coursier-app/ \
  -t urbafood-coursiers \
  ./coursiers/

# 2. Lancer le service
docker-compose up -d coursiers
docker-compose restart nginx

# 3. Vérifier
curl https://urbafood.me/coursier-app/
curl https://urbafood.me/coursier-app/manifest.webmanifest
curl https://urbafood.me/coursier-app/sw.js

Développement local avec ngrok (test PWA sur Android) :

cd apps/coursiers
npm install
npm run dev -- --host          # Terminal 1 — accessible sur LAN

npx ngrok http 5173            # Terminal 2 — tunnel HTTPS
# → https://xxxx.ngrok-free.app

# Sur Chrome Android : ouvrir l'URL ngrok → menu ⋮ → Installer
ℹ️Le Service Worker exige HTTPS. Sans ngrok (en HTTP sur LAN), le SW n'est pas enregistré et la PWA n'est pas installable.

⚙️ Exploitation

Déploiement

# Depuis /srv/urbafood

# Rebuild après modification du code source
docker-compose build backend frontend
docker-compose up -d --force-recreate backend frontend

# Rebuild complet (sans cache)
docker-compose build --no-cache backend
docker-compose up -d

# Vérifier l'état
docker-compose ps
docker-compose logs -f backend --tail=50

SSL & HAProxy

Le certificat Let's Encrypt est géré par HAProxy. Il expire le 17 mai 2026 (renouvellement automatique configuré par certbot).

# Renouvellement manuel si nécessaire
cd /srv/haproxy && docker-compose stop
sudo certbot certonly --standalone -d urbafood.me -d www.urbafood.me
sudo cat /etc/letsencrypt/live/urbafood.me/fullchain.pem \
         /etc/letsencrypt/live/urbafood.me/privkey.pem \
         > /srv/haproxy/certs/urbafood.pem
cd /srv/haproxy && docker-compose up -d

Logs & monitoring

ServiceCommandeNotes
Backenddocker-compose logs -f backendNestJS logs + SQL queries (désactivé en prod)
Frontenddocker-compose logs -f frontendNext.js server-side logs
Nginxdocker-compose logs -f nginxAccès + erreurs HTTP
HAProxydocker logs haproxyRouting, SSL handshakes
MariaDBdocker-compose logs mariadbErreurs DB, slow queries
Healthcurl https://urbafood.me/api/healthStatut backend + uptime

📋 Changelog

v1.7.020 fév. 2026
  • Documentation : nouvelle section PWA Coursiers (présentation, installation Android/iOS, stack technique, déploiement)
  • Architecture : diagramme mis à jour — ajout du service urbafood_coursiers (PWA Vite/React)
  • Docker : tableau des services mis à jour avec le container coursiers
  • Parcours coursier : routes PWA (/coursier-app/*) documentées en parallèle des routes Next.js
  • Header : badge PWA Vite 5 ajouté
v1.6.018 fév. 2026
  • Module Coursiers : backend complet (NestJS) — profil, disponibilité, acceptation/livraison commandes, stats
  • API : 9 nouveaux endpoints /couriers/* (register, me, availability, orders/available, orders/my, accept, deliver, stats)
  • Frontend : page marketing /rejoindre-coursier (comparatif rémunération, avantages, FAQ)
  • Frontend : login mis à jour — ?role=COURIER, sélecteur véhicule, POST /couriers/register auto, redirect /coursier
  • Frontend : dashboard coursier mobile-first /coursier (layout bottom nav + sidebar desktop)
  • Frontend : /coursier/livraisons (liste READY, acceptation, gestion conflit 409)
  • Frontend : /coursier/en-cours (timeline visuelle, bouton livrée)
  • Frontend : /coursier/gains (historique par période — aujourd'hui / 7j / 30j)
  • Frontend : /coursier/profil (stats, véhicule, toggle dispo)
  • Modèle économique : 100 % des frais de livraison reversés aux coursiers
v1.5.018 fév. 2026
  • Landing page : moteur de recherche fonctionnel (GET /restaurants/search) — recherche par nom restaurant, plat, catégorie, ingrédient
  • Recherche géographique : filtre ville + communes limitrophes via préfixe département du code postal
  • Backend : endpoints publics GET /restaurants/search et GET /restaurants/categories (sans JWT)
  • Frontend : chips de catégories chargées dynamiquement, skeleton loader, état vide, bouton réinitialiser
v1.4.018 fév. 2026
  • Auth : endpoint POST /auth/refresh pour renouveler l'access token (TTL 15 min → refresh 7 j)
  • Frontend : renouvellement JWT automatique dans lib/api.ts — intercepte les 401, rafraîchit silencieusement et rejoue la requête
  • Fix : order-item.entity.ts — propriété notes mappée sur la colonne existante special_instructions (évite l'erreur Unknown column)
  • Fix : nginx redémarré après rebuilds multiples du backend pour vider les connexions keepalive stales
v1.3.017 fév. 2026
  • Intégration Swagger UI dans /docs (section API Reference)
  • Ajout section Google Business Profile : OAuth 2.0, routes GBP, chiffrement AES-256-GCM
  • Backend : module google/ (controller, service, utils, migration TypeORM, tests unitaires)
v1.2.017 fév. 2026
  • Ajout documentation technique et fonctionnelle (/docs)
  • Diagrammes : architecture, ERD, JWT flow, ordre workflow
v1.1.017 fév. 2026
  • Espace restaurateur complet : dashboard, menu, commandes, stats, profil
  • Backend : modules Auth, Restaurants, Dishes, Orders avec JWT
  • Page login avec inscription
  • Client API TypeScript (lib/api.ts)
v1.0.016 fév. 2026
  • Landing page responsive avec Tailwind CSS
  • Navigation fixe avec scroll-to-section
  • Search bar avec filtres, aperçu restaurants, témoignages
  • Certificat SSL Let's Encrypt pour urbafood.me
  • Stack Docker complète opérationnelle (6 services)
💡Ce changelog est à maintenir manuellement dans /srv/urbafood/frontend/src/app/docs/page.tsx à chaque évolution majeure.