- #17: Amélioration gestion des secteurs et statistiques - #18: Optimisation services API et logs - #19: Corrections Flutter widgets et repositories - #20: Fix création passage - détection automatique ope_users.id vs users.id Suppression dossier web/ (migration vers app Flutter) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
244 lines
8.5 KiB
Markdown
Executable File
244 lines
8.5 KiB
Markdown
Executable File
# Documentation Technique API GeoSector
|
|
|
|
## 🏗️ Infrastructure
|
|
|
|
**Stack** : PHP 8.3 | NGINX | MariaDB 10.11 | Alpine Linux (Incus)
|
|
|
|
| Environnement | Container | DB (IP) | URL | Serveur |
|
|
|---------------|-----------|---------|-----|---------|
|
|
| **DEV** | dva-geo | maria3 (13.23.33.4) | https://dapp.geosector.fr | IN3 (195.154.80.116) |
|
|
| **REC** | rca-geo | maria3 (13.23.33.4) | https://rapp.geosector.fr | IN3 (195.154.80.116) |
|
|
| **PROD** | pra-geo | maria4 (13.23.33.4) | https://app3.geosector.fr | IN4 (51.159.7.190) |
|
|
|
|
**Architecture** : MVC sans framework | Point d'entrée : `index.php` | Config : `AppConfig.php` singleton
|
|
|
|
## 🗄️ Base de données
|
|
|
|
### Modèle d'isolation par opération (CRITIQUE)
|
|
|
|
**Principe** : Chaque opération est un **univers fermé** isolé de la table centrale `users`.
|
|
|
|
```
|
|
users (table centrale, conservée après suppression opération)
|
|
└── ope_users (id, fk_user, fk_operation) ← PIVOT par opération
|
|
├── ope_users_sectors (fk_user → ope_users.id)
|
|
└── ope_pass (fk_user → ope_users.id)
|
|
```
|
|
|
|
**⚠️ IMPORTANT pour Flutter** :
|
|
- **`users.id`** = Identifiant global (gestion membres, login)
|
|
- **`ope_users.id`** = Identifiant opération (passages, secteurs)
|
|
- **Flutter doit gérer les 2 IDs** lors du login et des CRUD
|
|
|
|
**Réponse login :**
|
|
```json
|
|
{
|
|
"users_sectors": [
|
|
{
|
|
"id": 123, // users.id (gestion membres)
|
|
"ope_user_id": 50, // ope_users.id (passages/secteurs)
|
|
"name": "John Doe",
|
|
"fk_sector": 456
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Requêtes API depuis Flutter :**
|
|
```json
|
|
// ✅ Création secteur : envoyer ope_users.id
|
|
POST /api/sectors { "users": [50, 51] } // ope_users.id
|
|
|
|
// ✅ Création passage : fk_user = ope_users.id
|
|
POST /api/passages { "fk_user": 50 } // ope_users.id
|
|
|
|
// ✅ Modification membre : utiliser users.id
|
|
PUT /api/users/123 // users.id
|
|
```
|
|
|
|
### Tables principales
|
|
|
|
- **`entites`** : Amicales (chiffrement AES-256-CBC sur nom, email, IBAN)
|
|
- **`users`** : Table centrale utilisateurs (conservée même si opération supprimée)
|
|
- **`operations`** : Campagnes liées à une entité
|
|
- **`ope_users`** : **PIVOT** users ↔ opérations (ON DELETE CASCADE depuis operations)
|
|
- **`ope_sectors`** : Secteurs géographiques (polygones)
|
|
- **`ope_users_sectors`** : Affectation users ↔ secteurs (FK → `ope_users.id`)
|
|
- **`ope_pass`** : Passages (FK → `ope_users.id`, ON DELETE CASCADE)
|
|
- **`medias`** : Fichiers (logos, exports, reçus) - Stockage : `/uploads/`
|
|
|
|
## 🔒 Sécurité
|
|
|
|
- **Auth** : Sessions PHP (httpOnly, secure, SameSite=Strict)
|
|
- **Mots de passe** : NIST SP 800-63B, bcrypt, HIBP check (k-anonymity)
|
|
- **Chiffrement** : AES-256-CBC (noms, emails, téléphones, IBAN)
|
|
- **Protection** : Brute force (8 tentatives/5min), IP blocking, PDO prepared statements
|
|
- **Monitoring** : `SecurityMonitor`, `AlertService`, `IPBlocker`
|
|
|
|
## 💳 Stripe Connect
|
|
|
|
- **DEV** : Clés TEST Pierre
|
|
- **REC** : Clés TEST client + webhook `webhook-rca`
|
|
- **PROD** : Clés LIVE client + webhook `webhook-pra`
|
|
- **API** : `2025-08-27.basil`
|
|
- **Tap to Pay** : iOS 16.4+ (iPhone XS+) | Android 11+ (95+ devices certifiés)
|
|
- **Flow** : Passage créé → PaymentIntent → Tap to Pay → Mise à jour `stripe_payment_id`
|
|
|
|
## 📦 Fonctionnalités
|
|
|
|
1. **Reçus fiscaux** : PDF auto (<5KB) pour dons, envoi email queue
|
|
2. **Logos entités** : Upload PNG/JPG, redimensionnement 250x250px, base64
|
|
3. **Migration** : Endpoints REST par entité (9 phases)
|
|
4. **CRONs** : Email queue (*/5), cleanup sécurité (2h)
|
|
|
|
## 📊 Statistiques Events (Admin Flutter)
|
|
|
|
### Architecture
|
|
|
|
**Principe** : Stats pré-agrégées en SQL + détail JSONL à la demande
|
|
|
|
| Source | Usage | Performance |
|
|
|--------|-------|-------------|
|
|
| Table `event_stats_daily` | Dashboard, graphiques, tendances | Instantané (~1ms) |
|
|
| Fichiers JSONL | Détail événements (clic sur stat) | Paginé (~50-100ms) |
|
|
|
|
### Flux de données
|
|
|
|
1. **EventLogService** écrit les événements dans `/logs/events/YYYY-MM-DD.jsonl`
|
|
2. **CRON nightly** agrège J-1 dans `event_stats_daily`
|
|
3. **API** sert les stats agrégées (SQL) ou le détail paginé (JSONL)
|
|
4. **Flutter Admin** affiche dashboard avec drill-down
|
|
|
|
### Table d'agrégation
|
|
|
|
**`event_stats_daily`** : Une ligne par (date, entité, type d'événement)
|
|
|
|
| Colonne | Description |
|
|
|---------|-------------|
|
|
| `stat_date` | Date des stats |
|
|
| `entity_id` | Entité (NULL = global super-admin) |
|
|
| `event_type` | Type événement (login_success, passage_created, etc.) |
|
|
| `count` | Nombre d'occurrences |
|
|
| `sum_amount` | Somme montants (passages) |
|
|
| `unique_users` | Utilisateurs distincts |
|
|
| `metadata` | JSON agrégé (top secteurs, erreurs fréquentes, etc.) |
|
|
|
|
### Endpoints API
|
|
|
|
| Endpoint | Période | Source | Taille réponse |
|
|
|----------|---------|--------|----------------|
|
|
| `GET /events/stats/summary` | Jour courant | SQL | ~1 KB |
|
|
| `GET /events/stats/daily` | Plage dates | SQL | ~5 KB |
|
|
| `GET /events/stats/weekly` | Calculé depuis daily | SQL | ~2 KB |
|
|
| `GET /events/stats/monthly` | Calculé depuis daily | SQL | ~1 KB |
|
|
| `GET /events/details` | Détail paginé | JSONL | ~10 KB |
|
|
|
|
### Optimisations transfert Flutter
|
|
|
|
- **Pagination** : 50 events max par requête détail
|
|
- **Champs filtrés** : Pas d'IP ni user_agent complet dans les réponses
|
|
- **Compression gzip** : -70% sur JSON
|
|
- **Cache HTTP** : ETag sur stats (changent 1x/jour)
|
|
- **Calcul hebdo/mensuel** : À la volée depuis `daily` (pas de tables supplémentaires)
|
|
|
|
### Types d'événements agrégés
|
|
|
|
| Catégorie | Events |
|
|
|-----------|--------|
|
|
| **Auth** | login_success, login_failed, logout |
|
|
| **Passages** | passage_created, passage_updated, passage_deleted |
|
|
| **Secteurs** | sector_created, sector_updated, sector_deleted |
|
|
| **Users** | user_created, user_updated, user_deleted |
|
|
| **Entités** | entity_created, entity_updated, entity_deleted |
|
|
| **Opérations** | operation_created, operation_updated, operation_deleted |
|
|
| **Stripe** | stripe_payment_created, stripe_payment_success, stripe_payment_failed, stripe_payment_cancelled, stripe_terminal_error |
|
|
|
|
### Accès et sécurité
|
|
|
|
- **Rôle requis** : Admin entité (role_id = 2) ou Super-admin (role_id = 1)
|
|
- **Isolation** : Admin voit uniquement les stats de son entité
|
|
- **Super-admin** : Accès global (entity_id = NULL dans requêtes)
|
|
|
|
## 🚀 Déploiement
|
|
|
|
```bash
|
|
./deploy-api.sh # Local → dva-geo (DEV)
|
|
./deploy-api.sh rca # dva-geo → rca-geo (REC)
|
|
./deploy-api.sh pra # rca-geo → pra-geo (PROD)
|
|
```
|
|
|
|
- Backup auto (10 versions)
|
|
- Préservation `/logs/` et `/uploads/`
|
|
- Permissions : `nginx:nginx` (code), `nginx:nginx` (logs/uploads)
|
|
- Composer install avec `--optimize-autoloader`
|
|
|
|
## ⚠️ Points d'attention API ↔ Flutter
|
|
|
|
### 1. Isolation opérations (depuis Oct 2025)
|
|
|
|
**Avant** : `ope_pass.fk_user` → `users.id` (table centrale)
|
|
**Après** : `ope_pass.fk_user` → `ope_users.id` (pivot opération)
|
|
|
|
**Impact Flutter** :
|
|
- Login retourne **2 IDs** : `id` (users.id) + `ope_user_id` (ope_users.id)
|
|
- Création secteur/passage : envoyer `ope_user_id`
|
|
- Affichage passages : mapper avec `ope_user_id`
|
|
|
|
### 2. Endpoints critiques modifiés
|
|
|
|
| Endpoint | Body envoyé | Mapping |
|
|
|----------|-------------|---------|
|
|
| `POST /api/sectors` | `users: [50, 51]` | ope_users.id |
|
|
| `PUT /api/sectors/{id}` | `users: [50, 51]` | ope_users.id |
|
|
| `POST /api/passages` | `fk_user: 50` | ope_users.id |
|
|
| `PUT /api/passages/{id}` | `fk_user: 50` | ope_users.id |
|
|
| `POST /api/users` | - | Retourne `ope_user_id` |
|
|
|
|
### 3. Requête SQL typique
|
|
|
|
```sql
|
|
-- ❌ AVANT (CASSÉ)
|
|
SELECT op.*, u.encrypted_name
|
|
FROM ope_pass op
|
|
JOIN users u ON op.fk_user = u.id
|
|
|
|
-- ✅ APRÈS (CORRECT)
|
|
SELECT op.*, u.encrypted_name
|
|
FROM ope_pass op
|
|
JOIN ope_users ou ON op.fk_user = ou.id
|
|
JOIN users u ON ou.fk_user = u.id
|
|
```
|
|
|
|
### 4. Suppression en cascade
|
|
|
|
```sql
|
|
DELETE FROM operations WHERE id = 850;
|
|
-- Supprime automatiquement (CASCADE) :
|
|
-- - ope_users
|
|
-- - ope_users_sectors
|
|
-- - ope_pass
|
|
-- - ope_sectors
|
|
-- ✅ users conservé (table centrale)
|
|
```
|
|
|
|
## 📝 Changelog critique
|
|
|
|
**Version 3.3.7 (26 Oct 2025)** :
|
|
- 🔧 Correction bug `SectorController::update()` : Recherche users par `ope_users.id` au lieu de `users.id`
|
|
- 🔧 Permissions logs corrigées : `nginx:nginx` + `750/640`
|
|
- 🔧 PHP `display_errors = Off` (warnings loggés dans `/var/log/php83/error.log`)
|
|
|
|
**Version 3.3.6 (21 Oct 2025)** :
|
|
- Validation inscription : Code postal + ville (doublon)
|
|
|
|
**Version 3.2.7 (16 Oct 2025)** :
|
|
- Migration RCA-GEO vers maria3 complétée
|
|
- URL PROD : `app3.geosector.fr`
|
|
|
|
**Version 3.2.4-3.2.6 (Sep 2025)** :
|
|
- Stripe Connect complet (Tap to Pay, webhooks multi-env)
|
|
|
|
---
|
|
|
|
**Mis à jour : 22 Décembre 2025**
|