API REST¶
⬇️ Télécharger cette page en Markdown
Endpoints Publics¶
Base URL : https://urbafix.fr/api/
Format : JSON
Authentification : Headers X-Fingerprint (device ID) et X-API-Key (optionnel)
GET /get_types.php¶
Description¶
Récupère la liste des types d'incidents pour un code postal.
Paramètres¶
| Nom | Type | Requis | Description |
|---|---|---|---|
| code_postal | string | Oui | Code postal (5 chiffres) |
Exemple Requête¶
Réponse Succès¶
{
"success": true,
"types": [
{
"id": 1,
"nom": "Nid-de-poule",
"description": "Chaussée dégradée",
"couleur": "#e74c3c",
"ordre": 1,
"actif": true,
"contacts": [
{"nom": "Service Voirie", "email": "voirie@mairie.test"}
]
}
]
}
GET /get_incidents.php¶
Description¶
Liste les incidents pour un code postal avec filtres optionnels.
Paramètres¶
| Nom | Type | Requis | Description |
|---|---|---|---|
| code_postal | string | Oui | Code postal (5 chiffres) |
| statut | string | Non | nouveau, en_cours, resolu, ferme |
| type_id | int | Non | Filtre par type |
| limit | int | Non | Limite résultats (défaut: 100) |
Exemple Requête¶
Réponse Succès¶
{
"success": true,
"incidents": [
{
"id": 123,
"type_id": 1,
"type_nom": "Nid-de-poule",
"type_couleur": "#e74c3c",
"adresse": "123 Rue Example",
"latitude": 43.7750,
"longitude": 7.5030,
"description": "Gros trou dans la chaussée",
"statut": "nouveau",
"date_creation": "2026-01-04 10:30:00",
"group_id": 5,
"photos": [
{"id": 1, "url": "https://urbafix.fr/uploads/incidents/photo1.jpg"}
]
}
]
}
POST /submit_incident.php¶
Description¶
Création d'un nouvel incident avec photos/vidéos.
Headers¶
Body JSON¶
{
"mairie_id": 1,
"type_id": 1,
"adresse": "123 Rue Example",
"latitude": 43.7750,
"longitude": 7.5030,
"description": "Description du problème",
"device_id": "android_abc123",
"photos": [
"data:image/jpeg;base64,/9j/4AAQSkZJRg..."
],
"videos": [
{
"video": "data:video/mp4;base64,AAAAHGZ0eXBpc29t...",
"latitude": 43.7750,
"longitude": 7.5030
}
]
}
Réponse Succès¶
{
"success": true,
"incident_id": 123,
"photos_uploaded": 1,
"videos_uploaded": 1,
"gps_warnings": [
"Vidéo 0: GPS distant de 150m de l'incident"
]
}
Codes d'Erreur¶
| Code | Signification |
|---|---|
| 400 | Paramètres invalides |
| 401 | Authentification requise |
| 413 | Fichier trop volumineux |
| 500 | Erreur serveur |
GET /get_user_incidents.php¶
Description¶
Récupère les incidents créés par un device ID.
Paramètres¶
| Nom | Type | Requis | Description |
|---|---|---|---|
| code_postal | string | Oui | Code postal |
| device_id | string | Oui | Device fingerprint |
Exemple Requête¶
Authentification¶
Device Fingerprint¶
Header X-Fingerprint généré côté mobile :
API Key (Optionnel)¶
Header X-API-Key pour applications tierces.
Génération :
Configuration : Stocké dans table api_keys
Rate Limiting¶
Implémenté par SecurityHelpers::checkRateLimit() (compteur APCu, par endpoint). Défaut 100 req/min si un endpoint n'appelle pas la fonction avec des valeurs explicites ; la plupart des endpoints d'écriture sont bien plus stricts :
| Limite | Endpoints (exemples) |
|---|---|
| 5 / heure | send_registration_email.php, submit_demande_collectivite.php, submit_interest.php |
| 10 / heure | check_mairie_registration.php, confirm_registration.php, submit_alerte.php, submit_feedback.php, submit_waitlist.php |
| 20 / heure | verify_registration_token.php |
| 50 / heure | submit_incident.php |
| 10 / 5 min | admin_login.php |
| 20–200 / min | Endpoints de lecture (get_*.php) et add_photos.php/delete_incident.php/register_push_endpoint.php |
Identifiant : SecurityHelpers::getRateLimitIdentifier() — IP réelle du client (dernier maillon de X-Forwarded-For ajouté par le reverse proxy, jamais falsifiable par le client), combinée au header X-Fingerprint quand présent. Voir Conformité ANSSI §18.
Dépassement : HTTP 429, header Retry-After: <secondes>, body {"success":false,"error":"Too Many Requests","message":"..."}. Pas de headers X-RateLimit-*.
Si APCu indisponible : la fonction autorise la requête par défaut (error_log de l'incident) plutôt que de bloquer tout le trafic.
CORS¶
Géré par SecurityHelpers::setCorsHeaders() — whitelist codée en dur (pas de variable d'environnement CORS_ALLOWED_ORIGINS) :
$origins = [
'https://urbafix.fr',
'https://www.urbafix.fr',
'android://org.urbanappslab.urbafix',
'capacitor://localhost',
];
// + localhost:3000/8080 si APP_ENV !== 'production'
Headers envoyés :
Access-Control-Allow-Origin: <origine si whitelistée, sinon absent>
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Fingerprint, X-API-Key, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
Origine non whitelistée → pas de Access-Control-Allow-Origin dans la réponse (le navigateur bloque côté client, la requête serveur s'exécute quand même : ce n'est pas un contrôle d'accès, seulement une protection navigateur).
Exemples cURL¶
Création Incident Simple¶
curl -X POST https://urbafix.fr/api/submit_incident.php \
-H "Content-Type: application/json" \
-H "X-Fingerprint: test-device-123" \
-d '{
"mairie_id": 1,
"type_id": 1,
"adresse": "123 Rue Test",
"latitude": 43.7750,
"longitude": 7.5030,
"description": "Test incident",
"device_id": "test-device-123"
}'
Récupération Types¶
GET+POST /api/admin/email_test_mode.php¶
Description¶
Lit ou modifie le mode d'envoi des emails (test vs production). Protégé par session admin (requireAuth()).
GET — Lire le mode actuel¶
Réponse¶
| Valeur | Signification |
|---|---|
true |
Mode TEST — emails redirigés vers l'adresse de test |
false |
Mode PROD — emails envoyés à la mairie réelle |
POST — Modifier le mode¶
curl -X POST "https://urbafix.fr/api/admin/email_test_mode.php" \
-H "Content-Type: application/json" \
-b "PHPSESSID=..." \
-d '{"enabled": false}'
Body JSON¶
| Champ | Type | Description |
|---|---|---|
enabled |
boolean | true = activer mode test, false = activer mode production |
Réponse¶
Stockage¶
Persisté dans la table app_settings :
Utilisation Android¶
Appelé par ProfileViewModel (build DEBUG uniquement) via MonQuartierApi.getEmailTestMode() et setEmailTestMode().
Format d'Erreur Standard¶
{
"success": false,
"error": "Message d'erreur lisible",
"code": "ERROR_CODE",
"details": {
"field": "Description spécifique"
}
}
Versioning¶
Version actuelle : v1
Future : API v2 (GraphQL) en Q4 2026
Rétrocompatibilité : v1 maintenue 2 ans après sortie v2