✨ Nouvelles fonctionnalités: - Ajout du mode terrain pour utilisation mobile hors connexion - Génération automatique de reçus PDF avec template personnalisé - Révision complète du système de cartes avec amélioration des performances 🔧 Améliorations techniques: - Refactoring du module chat avec architecture simplifiée - Optimisation du système de sécurité NIST SP 800-63B - Amélioration de la gestion des secteurs géographiques - Support UTF-8 étendu pour les noms d'utilisateurs 📱 Application mobile: - Nouveau mode terrain dans user_field_mode_page - Interface utilisateur adaptative pour conditions difficiles - Synchronisation offline améliorée 🗺️ Cartographie: - Optimisation des performances MapBox - Meilleure gestion des tuiles hors ligne - Amélioration de l'affichage des secteurs 📄 Documentation: - Ajout guide Android (ANDROID-GUIDE.md) - Documentation sécurité API (API-SECURITY.md) - Guide module chat (CHAT_MODULE.md) 🐛 Corrections: - Résolution des erreurs 400 lors de la création d'utilisateurs - Correction de la validation des noms d'utilisateurs - Fix des problèmes de synchronisation chat 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
25 KiB
Executable File
Documentation Technique API RESTful PHP 8.3
Table des matières
- Structure du projet
- Configuration du serveur
- Flux d'une requête
- Architecture des composants
- Base de données
- Sécurité
- Gestion des mots de passe (NIST SP 800-63B)
- Endpoints API
- Changements récents
Structure du projet
/api/
├── docs/
│ └── TECHBOOK.md
├── src/
│ ├── Controllers/
│ │ └── UserController.php
│ ├── Core/
│ │ ├── Router.php
│ │ ├── Request.php
│ │ ├── Response.php
│ │ ├── Session.php
│ │ └── Database.php
│ └── Config/
│ └── config.php
├── index.php
└── .htaccess
Configuration du serveur
Prérequis
- Debian 12
- NGINX
- PHP 8.3-FPM
- MariaDB 10.11
Configuration NGINX
Le serveur NGINX est configuré pour rediriger toutes les requêtes vers le point d'entrée index.php de l'API.
Configuration PHP-FPM
PHP-FPM est configuré pour gérer les processus PHP avec des paramètres optimisés pour une API.
Flux d'une requête
Exemple détaillé du parcours d'une requête POST /api/users :
-
Entrée de la requête
- La requête arrive sur le serveur NGINX
- NGINX redirige vers PHP-FPM via le socket unix
- Le fichier .htaccess redirige vers index.php
-
Initialisation (index.php)
- Chargement des dépendances
- Initialisation de la configuration
- Démarrage de la session
- Configuration des headers CORS
- Initialisation du routeur
-
Routage
- Le Router analyse la méthode HTTP (POST)
- Analyse de l'URI (/api/users)
- Correspondance avec les routes enregistrées
- Instanciation du Controller approprié
-
Traitement (UserController)
- Vérification de l'authentification
- Récupération des données JSON
- Validation des données reçues
- Traitement métier
- Interaction avec la base de données
- Préparation de la réponse
-
Réponse
- Formatage de la réponse en JSON
- Configuration des headers de réponse
- Envoi au client
Architecture des composants
Core Components
Router
- Gère le routage des requêtes
- Associe les URLs aux Controllers
- Gère les paramètres d'URL
- Dispatch vers les méthodes appropriées
Request
- Parse les données entrantes
- Gère les différents types de contenu
- Nettoie et valide les entrées
- Fournit une interface unifiée pour accéder aux données
Response
- Formate les réponses en JSON
- Gère les codes HTTP
- Configure les headers de réponse
- Assure la cohérence des réponses
Session
- Gère l'état des sessions
- Stocke les données d'authentification
- Vérifie les permissions
- Sécurise les données de session
Database
- Gère la connexion à MariaDB
- Fournit une interface PDO
- Gère le pool de connexions
- Assure la sécurité des requêtes
Base de données
Structure des tables principales
Table users
encrypted_user_name: Identifiant de connexion chiffré (unique)encrypted_email: Email chiffré (unique)user_pass_hash: Hash du mot de passeencrypted_name,encrypted_phone,encrypted_mobile: Données personnelles chiffrées- Autres champs :
first_name,sect_name,fk_role,fk_entite, etc.
Table entites (Amicales)
chk_mdp_manuel(DEFAULT 0) : Gestion manuelle des mots de passechk_username_manuel(DEFAULT 0) : Gestion manuelle des identifiantschk_stripe: Activation des paiements Stripe- Données chiffrées :
encrypted_name,encrypted_email,encrypted_phone, etc.
Table medias
support: Type de support (entite, user, operation, passage)support_id: ID de l'élément associéfile_category: Catégorie (logo, export, carte, etc.)file_path: Chemin complet du fichierprocessed_width/height: Dimensions après traitement- Utilisée pour stocker les logos des entités
Chiffrement des données
Toutes les données sensibles sont chiffrées avec AES-256-CBC :
- Emails, noms, téléphones
- Identifiants de connexion
- Informations bancaires (IBAN, BIC)
Migration de base de données
Script SQL pour ajouter les nouveaux champs :
-- Ajout de la gestion manuelle des usernames
ALTER TABLE `entites`
ADD COLUMN `chk_username_manuel` tinyint(1) unsigned NOT NULL DEFAULT 0
COMMENT 'Gestion des usernames manuelle (1) ou automatique (0)'
AFTER `chk_mdp_manuel`;
-- Index pour optimiser la vérification d'unicité
ALTER TABLE `users`
ADD INDEX `idx_encrypted_user_name` (`encrypted_user_name`);
Sécurité
Mesures implémentées
- Validation stricte des entrées
- Protection contre les injections SQL (PDO)
- Hachage sécurisé des mots de passe
- Headers de sécurité HTTP
- Gestion des CORS
- Session sécurisée
- Authentification requise
- Chiffrement AES-256 des données sensibles
- Envoi séparé des identifiants par email
Gestion des mots de passe (NIST SP 800-63B)
Vue d'ensemble
L'API implémente un système de gestion des mots de passe conforme aux recommandations NIST SP 800-63B, avec quelques adaptations spécifiques demandées par le client.
Service PasswordSecurityService
Le service PasswordSecurityService (src/Services/PasswordSecurityService.php) gère :
- Validation des mots de passe selon NIST
- Vérification contre les bases de données de mots de passe compromis (HIBP)
- Génération de mots de passe sécurisés
- Estimation de la force des mots de passe
Conformités NIST respectées
| Recommandation NIST | Notre Implémentation | Status |
|---|---|---|
| Longueur minimale : 8 caractères | ✅ MIN = 8 caractères | ✅ CONFORME |
| Longueur maximale : 64 caractères minimum | ✅ MAX = 64 caractères | ✅ CONFORME |
| Accepter TOUS les caractères ASCII imprimables | ✅ Aucune restriction sur les caractères | ✅ CONFORME |
| Accepter les espaces | ✅ Espaces acceptés (début, milieu, fin) | ✅ CONFORME |
| Accepter Unicode (émojis, accents, etc.) | ✅ Support UTF-8 avec mb_strlen() |
✅ CONFORME |
| Vérifier contre les mots de passe compromis | ✅ API Have I Been Pwned avec k-anonymity | ✅ CONFORME |
| Pas d'obligation de composition | ✅ Pas d'erreur si manque majuscules/chiffres/spéciaux | ✅ CONFORME |
| Pas de changement périodique forcé | ✅ Aucune expiration automatique | ✅ CONFORME |
| Permettre les phrases de passe | ✅ "Mon chat Félix a 3 ans!" accepté | ✅ CONFORME |
Déviations par choix du client
| Recommandation NIST | Notre Implémentation | Raison |
|---|---|---|
| Email unique par compte | ❌ Plusieurs comptes par email autorisés | Demande client |
| Mot de passe ≠ identifiant | ❌ Mot de passe = identifiant autorisé | Demande client |
| Vérifier contexte utilisateur | ❌ Pas de vérification nom/email dans mdp | Demande client |
Vérification contre les mots de passe compromis
Have I Been Pwned (HIBP) API
L'implémentation utilise l'API HIBP avec la technique k-anonymity pour préserver la confidentialité :
- Hash SHA-1 du mot de passe
- Envoi des 5 premiers caractères du hash à l'API
- Comparaison locale avec les suffixes retournés
- Aucun mot de passe en clair n'est transmis
Mode "Fail Open"
En cas d'erreur de l'API HIBP :
- Le système laisse passer le mot de passe
- Un avertissement est enregistré dans les logs
- L'utilisateur n'est pas bloqué
Exemples de mots de passe
Acceptés (conformes NIST)
monmotdepasse→ Accepté (≥8 caractères, pas compromis)12345678→ Accepté SI pas dans HIBPMon chat s'appelle Félix!→ Accepté (phrase de passe)→ Accepté si ≥8 espaces😀🎉🎈🎁🎂🍰🎊🎀→ Accepté (8 émojis)jean.dupont→ Accepté même si = username
Refusés
pass123→ Refusé (< 8 caractères)password→ Refusé (compromis dans HIBP)123456789→ Refusé (compromis dans HIBP)- Mot de passe > 64 caractères → Refusé
Force des mots de passe
Le système privilégie la LONGUEUR sur la complexité (conforme NIST) :
| Longueur | Force | Score |
|---|---|---|
| < 8 car. | Trop court | 0-10 |
| 8-11 car. | Acceptable | 20-40 |
| 12-15 car. | Bon | 40-60 |
| 16-19 car. | Fort | 60-80 |
| ≥20 car. | Très fort | 80-100 |
| Compromis | Compromis | ≤10 |
Génération automatique
Pour la génération automatique, le système reste strict pour garantir des mots de passe forts :
- Longueur : 12-16 caractères
- Contient : majuscules + minuscules + chiffres + spéciaux
- Vérifié contre HIBP (10 tentatives max)
- Exemple :
Xk9#mP2$nL5!
Gestion des comptes multiples par email
Depuis janvier 2025, le système permet plusieurs comptes avec le même email :
Fonction lostPassword adaptée
- Recherche TOUS les comptes avec l'email fourni
- Génère UN SEUL mot de passe pour tous ces comptes
- Met à jour TOUS les comptes en une requête
- Envoie UN SEUL email avec la liste des usernames concernés
Exemple de comportement
Si 3 comptes partagent l'email contact@amicale.fr :
jean.dupontmarie.martinpaul.durand
L'email contiendra :
Bonjour,
Voici votre nouveau mot de passe pour les comptes : jean.dupont, marie.martin, paul.durand
Mot de passe : XyZ123!@#
Endpoints API dédiés aux mots de passe
Vérification de force (public)
POST /api/password/check
Content-Type: application/json
{
"password": "monmotdepasse",
"check_compromised": true
}
Réponse :
{
"status": "success",
"valid": false,
"errors": [
"Ce mot de passe a été trouvé 23 547 fois dans des fuites de données."
],
"warnings": [
"Suggestion : Évitez les séquences communes pour plus de sécurité"
],
"strength": {
"score": 20,
"strength": "Faible",
"feedback": ["Ce mot de passe a été compromis"],
"length": 13,
"diversity": 1
},
"compromised": {
"compromised": true,
"occurrences": 23547,
"message": "Ce mot de passe a été trouvé 23 547 fois dans des fuites de données"
}
}
Vérification de compromission uniquement (public)
POST /api/password/compromised
Content-Type: application/json
{
"password": "monmotdepasse"
}
Génération automatique (authentifié)
GET /api/password/generate?length=14
Authorization: Bearer {session_id}
Réponse :
{
"status": "success",
"password": "Xk9#mP2$nL5!qR",
"length": 14,
"strength": {
"score": 85,
"strength": "Très fort",
"feedback": []
}
}
Configuration et sécurité
Paramètres de sécurité
- Timeout API HIBP : 5 secondes
- Cache : 15 minutes pour les vérifications répétées
- Logging : Aucun mot de passe en clair dans les logs
- K-anonymity : Seuls 5 caractères du hash SHA-1 envoyés
Points d'intégration
LoginController::register: Validation lors de l'inscriptionLoginController::lostPassword: Génération sécuriséeUserController::createUser: Validation si mot de passe manuelUserController::updateUser: Validation lors du changementApiService::generateSecurePassword: Génération avec vérification HIBP
Résumé
✅ 100% CONFORME NIST pour les aspects techniques
✅ Adapté aux besoins du client (emails multiples, mdp=username)
✅ Sécurité maximale avec vérification HIBP
✅ Expérience utilisateur optimale (souple mais sécurisé)
Endpoints API
Routes Publiques vs Privées
L'API distingue deux types de routes :
Routes Publiques
- POST /api/login
- POST /api/register
- GET /api/health
Routes Privées (Nécessitent une session authentifiée)
- Toutes les autres routes
Authentification
L'authentification utilise le système de session PHP natif.
Login
POST /api/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePassword123"
}
Réponse réussie :
{
"message": "Connecté avec succès",
"user": {
"id": 123,
"email": "user@example.com"
}
}
Notes importantes :
- Un cookie de session PHP sécurisé est automatiquement créé
- Le cookie est httpOnly, secure et SameSite=Strict
- L'ID de session est régénéré à chaque login réussi
Logout
POST /api/logout
Réponse réussie :
{
"message": "Déconnecté avec succès"
}
Sécurité des Sessions
La configuration des sessions inclut :
-
Sessions PHP natives sécurisées
-
Protection contre la fixation de session
-
Cookies httpOnly (protection XSS)
-
Mode strict pour les cookies
-
Validation côté serveur à chaque requête
-
use_strict_mode = 1
-
cookie_httponly = 1
-
cookie_secure = 1
-
cookie_samesite = Strict
-
Régénération de l'ID de session après login
-
Destruction complète de la session au logout
Users
Création d'utilisateur
La création d'utilisateur s'adapte aux paramètres de l'entité (amicale) :
POST /api/users
Content-Type: application/json
Authorization: Bearer {session_id}
{
"email": "john@example.com",
"name": "John Doe",
"first_name": "John",
"role": 1,
"fk_entite": 5,
"username": "j.doe38", // Requis si chk_username_manuel=1 pour l'entité
"password": "SecurePass123", // Requis si chk_mdp_manuel=1 pour l'entité
"phone": "0476123456",
"mobile": "0612345678",
"sect_name": "Secteur A",
"date_naissance": "1990-01-15",
"date_embauche": "2020-03-01"
}
Comportement selon les paramètres de l'entité :
| chk_username_manuel | chk_mdp_manuel | Comportement |
|---|---|---|
| 0 | 0 | Username et password générés automatiquement |
| 0 | 1 | Username généré, password requis dans le payload |
| 1 | 0 | Username requis dans le payload, password généré |
| 1 | 1 | Username et password requis dans le payload |
Validation du username (si manuel) :
- Format : 10-30 caractères
- Commence par une lettre
- Caractères autorisés : a-z, 0-9, ., -, _
- Doit être unique dans toute la base
Réponse réussie :
{
"status": "success",
"message": "Utilisateur créé avec succès",
"id": 123,
"username": "j.doe38", // Toujours retourné
"password": "xY7#mK9@pL2" // Retourné seulement si généré automatiquement
}
Envoi d'emails :
- Email 1 : Identifiant de connexion (toujours envoyé)
- Email 2 : Mot de passe (toujours envoyé, 1 seconde après le premier)
Codes de statut :
- 201: Création réussie
- 400: Données invalides ou username/password manquant si requis
- 401: Non authentifié
- 403: Accès non autorisé (rôle insuffisant)
- 409: Email ou username déjà utilisé
- 500: Erreur serveur
Vérification de disponibilité du username
POST /api/users/check-username
Content-Type: application/json
Authorization: Bearer {session_id}
{
"username": "j.doe38"
}
Réponse si disponible :
{
"status": "success",
"available": true,
"message": "Nom d'utilisateur disponible",
"username": "j.doe38"
}
Réponse si déjà pris :
{
"status": "success",
"available": false,
"message": "Ce nom d'utilisateur est déjà utilisé",
"suggestions": ["j.doe38_42", "j.doe381234", "j.doe3825"]
}
Autres endpoints
- GET /api/users
- GET /api/users/{id}
- PUT /api/users/{id}
- DELETE /api/users/{id}
- POST /api/users/{id}/reset-password
Entités (Amicales)
Upload du logo d'une entité
POST /api/entites/{id}/logo
Content-Type: multipart/form-data
Authorization: Bearer {session_id}
Body:
logo: File (image/png, image/jpeg, image/jpg)
Restrictions :
- Réservé aux administrateurs d'amicale (fk_role == 2)
- L'admin ne peut uploader que le logo de sa propre amicale
- Un seul logo actif par entité (le nouveau remplace l'ancien)
Traitement de l'image :
- Formats acceptés : PNG, JPG, JPEG
- Redimensionnement automatique : 250x250px maximum (ratio conservé)
- Résolution : 72 DPI (standard web)
- Préservation de la transparence pour les PNG
Stockage :
- Chemin :
/uploads/entites/{id}/logo/logo_{id}_{timestamp}.{ext} - Enregistrement dans la table
medias - Suppression automatique de l'ancien logo
Réponse réussie :
{
"status": "success",
"message": "Logo uploadé avec succès",
"media_id": 42,
"file_name": "logo_5_1234567890.jpg",
"file_path": "/entites/5/logo/logo_5_1234567890.jpg",
"dimensions": {
"width": 250,
"height": 180
}
}
Récupération du logo d'une entité
GET /api/entites/{id}/logo
Authorization: Bearer {session_id}
Réponse :
{
"status": "success",
"logo": {
"id": 42,
"data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...",
"file_name": "logo_5_1234567890.png",
"mime_type": "image/png",
"width": 250,
"height": 180,
"size": 15234
}
}
Note : Le logo est également inclus automatiquement dans la réponse du login si disponible.
Mise à jour d'une entité
PUT /api/entites/{id}
Content-Type: application/json
Authorization: Bearer {session_id}
{
"name": "Amicale de Grenoble",
"adresse1": "123 rue de la Caserne",
"adresse2": "",
"code_postal": "38000",
"ville": "Grenoble",
"phone": "0476123456",
"mobile": "0612345678",
"email": "contact@amicale38.fr",
"chk_stripe": true, // Activation paiement Stripe
"chk_mdp_manuel": false, // Génération auto des mots de passe
"chk_username_manuel": false, // Génération auto des usernames
"chk_copie_mail_recu": true,
"chk_accept_sms": false
}
Paramètres de gestion des membres :
| Paramètre | Type | Description |
|---|---|---|
| chk_mdp_manuel | boolean | true: L'admin saisit les mots de passefalse: Génération automatique |
| chk_username_manuel | boolean | true: L'admin saisit les identifiantsfalse: Génération automatique |
| chk_stripe | boolean | Active/désactive les paiements Stripe |
Note : Ces paramètres sont modifiables uniquement par les administrateurs (fk_role > 1).
Réponse du login avec paramètres entité
Lors du login, les paramètres de l'entité sont retournés dans le groupe amicale :
{
"status": "success",
"session_id": "abc123...",
"session_expiry": "2025-01-09T15:30:00+00:00",
"user": {
"id": 9999980,
"fk_entite": 5,
"fk_role": 2,
"fk_titre": null,
"first_name": "Pierre",
"sect_name": "",
"date_naissance": "1990-01-15", // Maintenant correctement récupéré
"date_embauche": "2020-03-01", // Maintenant correctement récupéré
"username": "pv_admin",
"name": "VALERY ADM",
"phone": "0476123456", // Maintenant correctement récupéré
"mobile": "0612345678", // Maintenant correctement récupéré
"email": "contact@resalice.com"
},
"amicale": {
"id": 5,
"name": "Amicale de Grenoble",
"chk_mdp_manuel": 0,
"chk_username_manuel": 0,
"chk_stripe": 1,
"logo": { // Logo de l'entité (si disponible)
"id": 42,
"data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...",
"file_name": "logo_5_1234567890.png",
"mime_type": "image/png",
"width": 250,
"height": 180
}
// ... autres champs
}
}
Ces paramètres permettent à l'application Flutter d'adapter dynamiquement le formulaire de création de membre.
Intégration Frontend
Configuration des Requêtes
Toutes les requêtes API depuis le frontend doivent inclure :
fetch('/api/endpoint', {
credentials: 'include', // Important pour les cookies de session
// ... autres options
});
Gestion des Sessions
- Les cookies de session sont automatiquement gérés par le navigateur
- Pas besoin de stocker ou gérer des tokens manuellement
- Redirection vers /login si session expirée (401)
Maintenance et Déploiement
Logs
- Logs d'accès NGINX : /var/log/nginx/api-access.log
- Logs d'erreur NGINX : /var/log/nginx/api-error.log
- Logs PHP : /var/log/php/php-error.log
Déploiement
- Pull du repository
- Vérification des permissions
- Configuration de l'environnement
- Tests des endpoints
- Redémarrage des services
Surveillance
- Monitoring des processus PHP-FPM
- Surveillance de la base de données
- Monitoring des performances
- Alertes sur erreurs critiques
Changements récents
Version 3.0.7 (Août 2025)
1. Implémentation complète de la norme NIST SP 800-63B pour les mots de passe
- Nouveau service :
PasswordSecurityServicepour la gestion sécurisée des mots de passe - Vérification HIBP : Intégration de l'API Have I Been Pwned avec k-anonymity
- Validation souple : Suppression des obligations de composition (majuscules, chiffres, spéciaux)
- Support Unicode : Acceptation de tous les caractères, incluant émojis et espaces
- Nouveaux endpoints :
/api/password/check,/api/password/compromised,/api/password/generate
2. Autorisation des emails multiples
- Suppression de l'unicité : Un même email peut être utilisé pour plusieurs comptes
- Adaptation de
lostPassword: Mise à jour de tous les comptes partageant l'email - Un seul mot de passe : Tous les comptes avec le même email reçoivent le même nouveau mot de passe
3. Autorisation mot de passe = identifiant
- Choix client : Permet d'avoir un mot de passe identique au nom d'utilisateur
- Pas de vérification contextuelle : Aucune vérification nom/email dans le mot de passe
Version 3.0.6 (Août 2025)
1. Correction des rôles administrateurs
- Avant : Les administrateurs d'amicale devaient avoir
fk_role > 2 - Après : Les administrateurs d'amicale ont
fk_role > 1(donc rôle 2 et plus) - Impact : Les champs
chk_stripe,chk_mdp_manuel,chk_username_manuelsont maintenant modifiables par les admins d'amicale (rôle 2)
2. Envoi systématique des deux emails lors de la création d'utilisateur
- Avant : Le 2ème email (mot de passe) n'était envoyé que si le mot de passe était généré automatiquement
- Après : Les deux emails sont toujours envoyés lors de la création d'un membre
- Email 1 : Identifiant (username)
- Email 2 : Mot de passe (1 seconde après)
- Raison : Le nouveau membre a toujours besoin des deux informations pour se connecter
3. Ajout des champs manquants dans la réponse du login
- Champs ajoutés dans la requête SQL :
fk_titredate_naissancedate_embaucheencrypted_phoneencrypted_mobile
- Impact : Ces données sont maintenant correctement retournées dans l'objet
userlors du login
4. Système de gestion des logos d'entité
- Nouvelle fonctionnalité : Upload et gestion des logos pour les amicales
- Routes ajoutées :
POST /api/entites/{id}/logo: Upload d'un nouveau logoGET /api/entites/{id}/logo: Récupération du logo
- Caractéristiques :
- Réservé aux administrateurs d'amicale (fk_role == 2)
- Un seul logo actif par entité
- Redimensionnement automatique (250x250px max)
- Format base64 dans les réponses JSON (compatible Flutter)
- Logo inclus automatiquement dans la réponse du login
5. Amélioration de l'intégration Flutter
- Format d'envoi des images : Base64 data URL pour compatibilité multiplateforme
- Structure de réponse enrichie : Le logo est inclus dans l'objet
amicalelors du login - Optimisation : Pas de requête HTTP supplémentaire nécessaire pour afficher le logo
Version 3.0.8 (Janvier 2025)
1. Système de génération automatique de reçus fiscaux pour les dons
- Nouveau service :
ReceiptServicepour la génération automatique de reçus PDF - Déclencheurs automatiques :
- Création d'un passage avec
fk_type=1(don) et email valide - Mise à jour d'un passage en don si
nom_recuest vide/null
- Création d'un passage avec
- Caractéristiques techniques :
- PDF ultra-légers (< 5KB) générés en format natif sans librairie externe
- Support des caractères accentués avec conversion automatique
- Stockage structuré :
/uploads/entites/{entite_id}/recus/{operation_id}/ - Enregistrement dans la table
mediasavec catégorierecu
- Queue d'envoi email :
- Envoi automatique par email avec pièce jointe PDF
- Format MIME multipart pour compatibilité maximale
- Gestion dans la table
email_queueavec statut de suivi
- Nouvelle route API :
GET /api/passages/{id}/receipt: Récupération du PDF d'un reçu- Retourne le PDF en base64 ou téléchargement direct selon Accept header
- Champs base de données utilisés :
nom_recu: Nom du fichier PDF générédate_creat_recu: Date de génération du reçudate_sent_recu: Date d'envoi par emailchk_email_sent: Indicateur d'envoi réussi