Files
geo/api/docs/TECHBOOK.md
Pierre 232940b1eb feat: Version 3.6.2 - Correctifs tâches #17-20
- #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>
2026-01-16 14:11:15 +01:00

8.5 KiB
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 :

{
  "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 :

// ✅ 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

./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_userusers.id (table centrale) Après : ope_pass.fk_userope_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

-- ❌ 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

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