Skip to content

#01 — rgz-api

PLANIFIÉ

Priorité: 🔴 CRITIQUE · Type: TYPE A · Conteneur: rgz-api · Code: app/main.py

Dépendances: #04 rgz-db, #05 rgz-redis


Description

rgz-api est le hub central de toute la plateforme RGZ. C'est le point de convergence de tous les échanges entre services : le portail captif, le dashboard admin, FreeRADIUS, les tâches Celery, et les outils terrain y font tous appel. Aucun service ne communique directement avec la base de données sans passer par l'API.

L'API est construite avec FastAPI (Python 3.11), ce qui offre une validation automatique des entrées/sorties via Pydantic, une documentation OpenAPI auto-générée accessible sur /docs, et des performances asynchrones natives. Le schéma de données suit strictement les contrats définis dans API_Contracts_RGZ.md : snake_case partout, UUIDs v4, dates ISO8601 UTC, préfixe /api/v1/.

La logique métier est découplée dans app/services/ (billing, fraud, onboarding, etc.) tandis que les endpoints REST sont dans app/api/v1/endpoints/. Cette séparation permet aux tests unitaires d'exercer les services indépendamment du framework HTTP. SQLAlchemy gère l'ORM avec PostgreSQL, et le client Redis est injecté via la dépendance FastAPI get_redis() définie dans app/deps.py.

Le même Dockerfile (docker/api/Dockerfile) est partagé avec rgz-beat (outil #10). L'image embarque Tesseract OCR (pour l'outil #11 inscription-ocr), WeasyPrint (pour les factures PDF #24), et le client SSH (pour les opérations CPE #33). Le conteneur écoute sur le port 8000 en interne ; Traefik gère le TLS et l'exposition publique sur api-rgz.duckdns.org.

Architecture Interne

Requête entrante


  FastAPI app (app/main.py)

      ├── CORS middleware (whitelist explicite — SEC-11, LL#2)
      ├── Audit middleware (app/middleware/audit.py — outil #48)
      ├── Rate limiting (via Redis — SEC-14)


  Router /api/v1/ (app/api/v1/router.py)

      ├── /auth       → endpoints/auth.py       (#12 OTP, auth flow)
      ├── /portal     → endpoints/portal.py     (#11 OCR, #13 devices, #14 forfaits)
      ├── /payments   → endpoints/payments.py   (#15 KKiaPay, #22 refunds)
      ├── /billing    → endpoints/billing.py    (#19 facturation, #20 vouchers)
      ├── /reports    → endpoints/reports.py    (#24 PDF, #47 ARCEP)
      ├── /resellers  → endpoints/resellers.py  (#56 onboarding)
      ├── /network    → endpoints/network.py    (#31 VLAN, #29 fair-use)
      ├── /compliance → endpoints/compliance.py (#46 APDP, #48 audit)
      ├── /rma        → endpoints/rma.py        (#57 RMA, #58 incidents)
      ├── /radius     → endpoints/radius.py     (REST module FreeRADIUS)
      └── /health     → { status: "ok" }


      Services (app/services/)   ←→   DB (SQLAlchemy + PostgreSQL)
                                 ←→   Cache (Redis 7)

Configuration

Variables d'environnement

VariableExempleDescription
DATABASE_URLpostgresql+asyncpg://rgz:pass@rgz-db:5432/rgzURL connexion PostgreSQL
REDIS_URLredis://:pass@rgz-redis:6379/0URL connexion Redis
SECRET_KEYchangeme-64-chars-randomClé HMAC JWT / OTP
CORS_ORIGINShttps://admin-rgz.duckdns.org,https://access-rgz.duckdns.orgWhitelist CORS (jamais *)
LOG_LEVELINFONiveau de logs (DEBUG/INFO/WARNING/ERROR)
KKIAPAY_SECRET...Clé secrète KKiaPay pour vérification webhook
LETEXTO_API_KEY...Clé API Letexto SMS
RADIUS_SECRET...Secret partagé FreeRADIUS ↔ API
ENVIRONMENTproductionContrôle comportements dev/prod

Fichiers de configuration

app/
├── main.py          # Point d'entrée FastAPI, lifespan, CORS, middleware
├── config.py        # Settings Pydantic BaseSettings (lecture .env)
├── database.py      # Engine SQLAlchemy async + session factory
├── deps.py          # Dépendances FastAPI: get_db(), get_redis(), get_current_user()
└── api/v1/
    └── router.py    # include_router() de tous les endpoints

Endpoints API

Structure de réponse standardisée

json
// Liste
{ "items": [...], "total": 42, "page": 1, "pages": 5 }

// Erreur
{ "error": { "code": "ERR_XXXX", "message": "...", "details": {} } }

Endpoints principaux

MéthodeRouteCode HTTPDescription
GET/api/v1/health200Healthcheck applicatif
POST/api/v1/auth/otp/request201Demande OTP par MSISDN
POST/api/v1/auth/otp/verify200Vérifie OTP → session token
POST/api/v1/portal/register201Inscription abonné (avec OCR)
GET/api/v1/portal/forfaits200Liste forfaits actifs
POST/api/v1/payments/initiate201Initier paiement KKiaPay
POST/api/v1/payments/webhook200Webhook confirmation KKiaPay
GET/api/v1/resellers/{id}/stats200Stats revendeur (dashboard #51)
POST/api/v1/radius/authorize200Autorisation RADIUS (FreeRADIUS REST)
POST/api/v1/radius/accounting200Accounting RADIUS

Exemple curl — healthcheck

bash
curl -s https://api-rgz.duckdns.org/api/v1/health | jq .
# { "status": "ok", "version": "1.0.0", "db": "connected", "redis": "connected" }

Exemple curl — demande OTP

bash
curl -s -X POST https://api-rgz.duckdns.org/api/v1/auth/otp/request \
  -H "Content-Type: application/json" \
  -d '{"msisdn": "+22997123456", "nas_id": "access_kossou"}' | jq .
# { "otp_token": "...", "expires_in": 300 }

Redis Keys

CléTypeTTLUsage
rgz:session:{subscriber_id}Stringsession_durationToken de session actif
rgz:otp:{phone}String300sCode OTP en cours
rgz:rate:{phone}String60sCompteur rate limiting OTP
rgz:devices:{subscriber_id}SetMACs connues de l'abonné
rgz:voucher:used:{code}String86400sAnti-replay vouchers

Healthcheck

bash
# Healthcheck Docker (dans docker-compose.core.yml)
# LL#43 : utiliser 127.0.0.1 JAMAIS localhost (IPv6 ::1 dans Docker)
curl --fail http://127.0.0.1:8000/api/v1/health

# Depuis l'hôte
curl https://api-rgz.duckdns.org/api/v1/health

# Depuis un autre conteneur sur rgz-net
docker exec rgz-db curl -s http://rgz-api:8000/api/v1/health

Sécurité

RègleImplémentation
SEC-01 IDORif current_user.reseller_id != resource.owner_id: raise HTTPException(403) dans chaque endpoint
SEC-02 Race conditionRedis WATCH/MULTI/EXEC pour la consommation de vouchers
SEC-03 WebhookVérification header x-kkiapay-secret + idempotency key en DB
SEC-04 MontantsEntiers FCFA uniquement — jamais float pour les paiements
SEC-06 OTPLié au subscriber_id UUID, comparaison via hmac.compare_digest()
SEC-10 JWTRS256, expiration 15min, refresh 7j, révocation via jti blacklist Redis
SEC-11 CORSWhitelist explicite dans CORS_ORIGINS, SameSite=Strict cookies
LL#2CORS entre services configuré, jamais wildcard en production
python
# app/main.py — CORS correct
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,  # liste explicite, jamais ["*"]
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

Commandes Utiles

bash
# Démarrage avec rebuild
docker compose -f /home/claude-dev/RGZ/docker-compose.core.yml up -d --build rgz-api

# Logs temps réel
docker logs rgz-api -f --tail=100

# Status healthcheck
docker inspect rgz-api --format='{}'

# Restart sans rebuild
docker restart rgz-api

# Accéder au shell
docker exec -it rgz-api bash

# Tester la connexion DB depuis l'API
docker exec rgz-api python -c "from app.database import engine; print('DB OK')"

# Tester Redis depuis l'API
docker exec rgz-api python -c "import redis; r=redis.from_url('redis://:pass@rgz-redis:6379/0'); print(r.ping())"

# Voir la documentation OpenAPI
curl -s https://api-rgz.duckdns.org/docs
# Ou en JSON : curl -s https://api-rgz.duckdns.org/openapi.json | jq .

Implémentation TODO

  • [x] Dockerfile créé (docker/api/Dockerfile)
  • [x] Service défini dans docker-compose.core.yml
  • [x] Variables d'env dans .env.example
  • [ ] app/main.py — FastAPI app avec lifespan, CORS, middleware
  • [ ] app/config.py — Pydantic BaseSettings
  • [ ] app/database.py — SQLAlchemy async engine
  • [ ] app/deps.py — Dépendances FastAPI
  • [ ] app/models/ — Modèles SQLAlchemy (subscriber, reseller, billing, monitoring)
  • [ ] app/schemas/ — Schémas Pydantic input/output
  • [ ] app/api/v1/router.py — Aggregation des routers
  • [ ] app/api/v1/endpoints/ — Tous les endpoints (12 fichiers)
  • [ ] app/services/ — Business logic découplée
  • [ ] app/middleware/audit.py — Audit trail (#48)
  • [ ] Tests unitaires app/tests/
  • [ ] Tests d'intégration (prepare-tests Phase 2)

Dernière mise à jour: 2026-02-21

PROJET MOSAÏQUE — 81 outils, 22 conteneurs, 500+ revendeurs WiFi Zone