Architecture technique¶
Vue d'ensemble de la stack LObsTer.
Vue macro¶
flowchart TB
subgraph Client
UB[Navigateur
utilisateur]
end
subgraph "Cloudflare (edge)"
CF[Caddy + Cloudflare
HTTPS + DDoS]
end
subgraph "VPS Hetzner"
FRONT[Frontend React
Vite build statique]
API[FastAPI backend
uvicorn + asyncpg]
PG[(PostgreSQL 15
+ PostGIS)]
end
subgraph "APIs externes"
ANTHROPIC[Anthropic API
Claude Sonnet 4.6]
STRIPE[Stripe
Checkout + webhooks]
BREVO[Brevo
emails transactionnels]
ADEME[ADEME DPE
API live]
end
subgraph "Storage"
R2[Cloudflare R2
rapports PDF signés]
end
UB --> CF
CF --> FRONT
CF --> API
API --> PG
API --> ANTHROPIC
API --> STRIPE
API --> BREVO
API --> ADEME
API --> R2
classDef client fill:#f0f4fa,stroke:#0d1f3c
classDef edge fill:#e87722,stroke:#0d1f3c,color:#fff
classDef backend fill:#0d1f3c,stroke:#e87722,color:#fff
classDef ext fill:#2a7f7f,stroke:#0d1f3c,color:#fff
classDef store fill:#c9a84c,stroke:#0d1f3c,color:#0d1f3c
class UB client
class CF edge
class FRONT,API,PG backend
class ANTHROPIC,STRIPE,BREVO,ADEME ext
class R2 store
Stack¶
Backend¶
- Python 3.10+
- FastAPI — framework async
- asyncpg — driver PostgreSQL natif async (plus rapide que psycopg2)
- Pydantic — validation des inputs API et des outputs LLM
- Playwright — headless Chromium pour rendu PDF (via
html_to_pdf.py) - python-jose — JWT tokens
- bcrypt — hash passwords
- slowapi — rate limiting
- stripe-python — SDK Stripe
- brevo-python — SDK Brevo (emails)
- anthropic — SDK Claude API
Frontend¶
- React 18 + TypeScript
- Vite — build tool
- React Router — navigation
- TanStack Query — data fetching + cache
- Recharts — graphs (avec fix infinite loop via
minWidth) - Leaflet — cartes interactives (SITADEL, EPCI, drill-down BPE)
- TailwindCSS — styling
Base de données¶
- PostgreSQL 15
- PostGIS — requêtes spatiales (communes, parcelles, EPCI)
- Vues matérialisées — cache pour agrégations lourdes
- pgAdmin4 — UI admin optionnelle (Docker Compose)
Infrastructure production¶
- Hetzner CX22 — 2 vCPU, 4 Go RAM, 40 Go SSD, ~4,51 €/mois
- Docker + Docker Compose — orchestration locale
- Caddy — reverse proxy + HTTPS automatique (Let's Encrypt)
- systemd — services backend
- cron — backups quotidiens
Infrastructure wiki¶
- Cloudflare Pages — hébergement statique
- Pages Functions — Basic Auth middleware (100k invocations/j gratuit)
- GitHub (privé) — repo source avec CI automatique
Flow d'une commande¶
sequenceDiagram
actor Client
participant Front as Frontend React
participant API as FastAPI
participant Stripe
participant DB as PostgreSQL
participant Claude as Anthropic
participant Brevo
participant R2 as Cloudflare R2
Client->>Front: choisit commune + étude
Front->>API: POST /api/orders (tunnel Observatoire)
API->>DB: INSERT order status=pending
API->>Stripe: create checkout session
Stripe-->>Client: redirect Stripe Checkout
Client->>Stripe: paiement CB
Stripe-->>API: webhook checkout.session.completed
API->>DB: UPDATE order status=paid
API->>API: déclenche pipeline
API->>DB: collector récupère données
API->>Claude: mega-prompt étude
Claude-->>API: JSON structuré
API->>API: render HTML template
API->>API: html_to_pdf.py (Playwright)
API->>R2: upload PDF, retourne URL signée
API->>DB: UPDATE order status=delivered + download_url
API->>Brevo: send livraison rapport email
Brevo-->>Client: email avec lien de téléchargement
Client->>R2: télécharge PDF
Schéma base de données (principales tables)¶
Référentiel¶
communes(36 000 rows)epci(~1 250 rows)arrondissements(PLM)
Démographie¶
rp_demographics— agrégats par communerp_details— pyramide âges, CSP, ménagesfilosofi— revenus, pauvretéprojections— OMPHALE
Logement¶
rp_housing— RP logementlovac— vacance structurellerpls— parc socialzonage_abc— tension marchéanil_loyers(à créer Sprint 1)
Immobilier¶
sitadel— 2,3M permisdvf— transactions (réimport en cours)dvf_staging— 13,9M rows en staging (à nettoyer)parcelles— cadastre (réimport 99 fichiers dept)
Équipements / social / santé¶
bpe_equipementscaf_communal— 157K rowsaides_dept— APA, PCHrpps— 1,2M professionnelsfiness— 86K établissements
Business¶
users,orders,subscriptionsreports— métadonnées rapports générés
Vues matérialisées¶
mv_commune_summary— agrégation pour territoire portraitmv_housing_diagnostic— pour LOG-01
Conventions code¶
Backend¶
- Naming :
snake_casepour fonctions,PascalCasepour classes Pydantic - Async : tout endpoint est
async def, jamais derequests.getbloquant - Erreurs :
HTTPExceptionuniquement (propagée en 4xx/5xx), pas de rawraise Exception - Logs :
loggingstandard Python, pas deprint - Tests : pytest async,
test_<module>.pyà côté du module
Frontend¶
- Composants : un fichier par composant, pas de fichiers géants
- Style : TailwindCSS, éviter CSS custom
- State : TanStack Query pour le remote, React Context pour le global,
useStatepour le local
Pipeline études¶
- Data collector :
backend/app/report_pipeline/data_collectors/<code>.py— retournedictplat - Prompt :
backend/app/report_pipeline/prompts/<code>_html_report.py— mega-prompt templaté - Schema :
backend/app/report_pipeline/schemas/<code>.py— Pydantic strict - Template HTML :
backend/app/templates/<code>.html— format paysage A4 IBM Plex
Sécurité¶
- JWT : expiration 24h pour access tokens, refresh tokens 30j
- Rate limiting : 10/min login, 5/min register, 3/min forgot-password
- Bcrypt : hash password coût 12
- CORS : whitelist
https://l-obster.fr+ domaine wiki - Stripe webhook : signature vérifiée via
STRIPE_WEBHOOK_SECRET - Secrets : jamais en dur, toujours env vars (voir Déploiement)
Points techniques à renforcer¶
- Tests automatisés pipeline (0 actuellement — red-team finding)
- Monitoring Sentry pour erreurs prod
- CI GitHub Actions (lint + tests)
- Redis pour rate limiting distribué (si multi-instance)
- Queue asynchrone (Celery/Dramatiq) pour pipelines longs