fix(front): images validation dossier, traces debug, baseUrl via Env
- ApiConfig.baseUrl dérivé de Env.apiBaseUrl (alignement API / origine médias). - AuthNetworkImage : uploads publics sans Bearer (CORS web) ; log erreurs en debug. - debugPrint kDebugMode : GET dossier, EnfantDossier.fromJson, absoluteMediaUrl, carte enfant. - ParentDossier id depuis user_id ; fallbacks présentation / photo. Made-with: Cursor
This commit is contained in:
@@ -1,5 +1,47 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:p_tits_pas/config/env.dart';
|
||||
|
||||
class ApiConfig {
|
||||
static const String baseUrl = 'https://app.ptits-pas.fr/api/v1';
|
||||
/// Aligné sur [Env.apiBaseUrl] (`--dart-define=API_BASE_URL=...`) pour que les images `/uploads/...` visent le même hôte que l’API.
|
||||
static String get baseUrl {
|
||||
final root = Env.apiBaseUrl.replaceAll(RegExp(r'/+$'), '');
|
||||
return '$root/api/v1';
|
||||
}
|
||||
|
||||
/// Origine (schéma + hôte + port) dérivée de [baseUrl], pour préfixer les chemins `/uploads/...`.
|
||||
static String get apiOrigin {
|
||||
final uri = Uri.parse(baseUrl);
|
||||
if (uri.hasScheme && uri.host.isNotEmpty) {
|
||||
return '${uri.scheme}://${uri.authority}';
|
||||
}
|
||||
return baseUrl.replaceAll(RegExp(r'/api/v1/?.*'), '');
|
||||
}
|
||||
|
||||
/// URL absolue pour une image renvoyée par l’API (chemin type `/uploads/...`).
|
||||
/// On préfixe avec [baseUrl] (…/api/v1), pas seulement l’hôte : Traefik n’expose souvent que `/api`.
|
||||
static String absoluteMediaUrl(String? pathOrUrl) {
|
||||
if (pathOrUrl == null || pathOrUrl.trim().isEmpty) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('[PetitsPas/media] absoluteMediaUrl: entrée vide (null ou "")');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
final u = pathOrUrl.trim();
|
||||
if (u.startsWith('http://') || u.startsWith('https://')) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('[PetitsPas/media] absoluteMediaUrl: déjà absolu → $u');
|
||||
}
|
||||
return u;
|
||||
}
|
||||
final base = baseUrl.replaceAll(RegExp(r'/+$'), '');
|
||||
final out = u.startsWith('/') ? '$base$u' : '$base/$u';
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
'[PetitsPas/media] absoluteMediaUrl: base=$base | chemin brut="$u" → "$out"',
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Auth endpoints
|
||||
static const String login = '/auth/login';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:p_tits_pas/models/user.dart';
|
||||
import 'package:p_tits_pas/models/parent_model.dart';
|
||||
@@ -92,8 +93,12 @@ class UserService {
|
||||
/// Dossier unifié par numéro (AM ou famille). GET /dossiers/:numeroDossier. Ticket #119, #107.
|
||||
static Future<DossierUnifie> getDossier(String numeroDossier) async {
|
||||
final encoded = Uri.encodeComponent(numeroDossier);
|
||||
final uri = Uri.parse('${ApiConfig.baseUrl}${ApiConfig.dossiers}/$encoded');
|
||||
if (kDebugMode) {
|
||||
debugPrint('[PetitsPas/dossier] GET $uri');
|
||||
}
|
||||
final response = await http.get(
|
||||
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.dossiers}/$encoded'),
|
||||
uri,
|
||||
headers: await _headers(),
|
||||
);
|
||||
if (response.statusCode == 404) {
|
||||
@@ -113,7 +118,21 @@ class UserService {
|
||||
if (decoded is! Map<String, dynamic>) {
|
||||
throw FormatException('Réponse invalide');
|
||||
}
|
||||
return DossierUnifie.fromJson(Map<String, dynamic>.from(decoded));
|
||||
final dossier = DossierUnifie.fromJson(Map<String, dynamic>.from(decoded));
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
'[PetitsPas/dossier] réponse OK type=${dossier.type} | '
|
||||
'ApiConfig.baseUrl=${ApiConfig.baseUrl} | apiOrigin=${ApiConfig.apiOrigin}',
|
||||
);
|
||||
if (dossier.isFamily) {
|
||||
final f = dossier.asFamily;
|
||||
debugPrint(
|
||||
'[PetitsPas/dossier] famille ${f.numeroDossier} | '
|
||||
'${f.enfants.length} enfant(s)',
|
||||
);
|
||||
}
|
||||
}
|
||||
return dossier;
|
||||
} catch (e) {
|
||||
if (e is FormatException) rethrow;
|
||||
throw Exception('Réponse invalide (dossier): ${e is Exception ? e.toString() : "format inattendu"}');
|
||||
|
||||
Reference in New Issue
Block a user