feat(inscription): payload enfant/photo et limite JSON 15 Mo côté API
- Express/Nest : body parser json et urlencoded à 15 Mo pour les photos base64. - Front : envoi d’inscription parent (authService, payload, étape 3). Made-with: Cursor
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
class ApiConfig {
|
||||
// static const String baseUrl = 'http://localhost:3000/api/v1/';
|
||||
static const String baseUrl = 'https://app.ptits-pas.fr/api/v1';
|
||||
|
||||
// Auth endpoints
|
||||
|
||||
@@ -185,7 +185,7 @@ class AuthService {
|
||||
return;
|
||||
}
|
||||
|
||||
final decoded = response.body.isNotEmpty ? jsonDecode(response.body) : null;
|
||||
final decoded = _tryDecodeJsonMap(response.body);
|
||||
final message = _extractErrorMessage(decoded, response.statusCode);
|
||||
throw Exception(message);
|
||||
}
|
||||
@@ -199,37 +199,123 @@ class AuthService {
|
||||
}
|
||||
|
||||
final body = ParentRegistrationPayload.toJson(data);
|
||||
final String encodedBody;
|
||||
try {
|
||||
encodedBody = jsonEncode(body);
|
||||
} catch (_) {
|
||||
throw Exception(
|
||||
'Impossible de préparer l’envoi (données trop volumineuses ou invalides). '
|
||||
'Réessayez avec une photo plus légère ou sans caractères inhabituels.',
|
||||
);
|
||||
}
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.registerParent}'),
|
||||
headers: ApiConfig.headers,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
late final http.Response response;
|
||||
try {
|
||||
response = await http.post(
|
||||
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.registerParent}'),
|
||||
headers: ApiConfig.headers,
|
||||
body: encodedBody,
|
||||
);
|
||||
} on http.ClientException {
|
||||
throw Exception(
|
||||
'Connexion à ${ApiConfig.baseUrl} impossible. Vérifiez votre réseau, '
|
||||
'un pare-feu ou un bloqueur, puis réessayez.',
|
||||
);
|
||||
}
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return;
|
||||
}
|
||||
|
||||
final decoded = response.body.isNotEmpty ? jsonDecode(response.body) : null;
|
||||
final message = _extractErrorMessage(decoded, response.statusCode);
|
||||
final decoded = _tryDecodeJsonMap(response.body);
|
||||
var message = _extractErrorMessage(decoded, response.statusCode);
|
||||
message = _humanizeParentRegistrationHttpError(message, response.statusCode);
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
static Map<String, dynamic>? _tryDecodeJsonMap(String body) {
|
||||
if (body.isEmpty) return null;
|
||||
try {
|
||||
final decoded = jsonDecode(body);
|
||||
if (decoded is Map) {
|
||||
return Map<String, dynamic>.from(decoded);
|
||||
}
|
||||
return null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Aplatit `message` (string, liste, ou objet Nest / AllExceptionsFilter).
|
||||
static String? _flattenApiMessageField(dynamic field) {
|
||||
if (field == null) return null;
|
||||
if (field is String) {
|
||||
final t = field.trim();
|
||||
return t.isEmpty ? null : t;
|
||||
}
|
||||
if (field is List) {
|
||||
final parts = field
|
||||
.map(_flattenApiMessageField)
|
||||
.whereType<String>()
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList();
|
||||
if (parts.isEmpty) return null;
|
||||
return parts.join('. ');
|
||||
}
|
||||
if (field is Map) {
|
||||
if (field['message'] != null) {
|
||||
final inner = _flattenApiMessageField(field['message']);
|
||||
if (inner != null) return inner;
|
||||
}
|
||||
final err = field['error'];
|
||||
if (err is String && err.trim().isNotEmpty) return err.trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Extrait le message d'erreur des réponses NestJS (message string, array, ou objet).
|
||||
static String _extractErrorMessage(dynamic decoded, int statusCode) {
|
||||
const fallback = 'Erreur lors de l\'inscription';
|
||||
if (decoded == null || decoded is! Map) return '$fallback ($statusCode)';
|
||||
final msg = decoded['message'];
|
||||
if (msg == null) {
|
||||
final err = decoded['error'];
|
||||
return (err is String ? err : err?.toString()) ?? '$fallback ($statusCode)';
|
||||
if (decoded == null || decoded is! Map) {
|
||||
return '$fallback ($statusCode)';
|
||||
}
|
||||
if (msg is String) return msg;
|
||||
if (msg is List) return msg.map((e) => e.toString()).join('. ').trim();
|
||||
if (msg is Map && msg['message'] != null) return msg['message'].toString();
|
||||
final map = decoded;
|
||||
final fromMessage = _flattenApiMessageField(map['message']);
|
||||
if (fromMessage != null) return fromMessage;
|
||||
final err = map['error'];
|
||||
if (err is String && err.trim().isNotEmpty) return err.trim();
|
||||
return '$fallback ($statusCode)';
|
||||
}
|
||||
|
||||
/// Remplace « Internal server error » (souvent contrainte SQL / 500) par un texte utile à l’inscription.
|
||||
static String _humanizeParentRegistrationHttpError(String msg, int statusCode) {
|
||||
if (statusCode == 409) return msg;
|
||||
|
||||
final lower = msg.toLowerCase().trim();
|
||||
final looksLikeGenericServerError = lower == 'internal server error' ||
|
||||
lower == 'internal server error.' ||
|
||||
lower.contains('internal server error');
|
||||
|
||||
if (statusCode >= 500 && looksLikeGenericServerError) {
|
||||
return 'Impossible d\'enregistrer votre demande pour le moment. '
|
||||
'Souvent, cela signifie que cette adresse e-mail est déjà utilisée : '
|
||||
'connectez-vous ou utilisez une autre adresse pour le parent principal '
|
||||
'(et pour le co-parent si vous en indiquez un). '
|
||||
'Si le problème continue, réessayez plus tard ou contactez le support.';
|
||||
}
|
||||
|
||||
if (statusCode >= 500) {
|
||||
if (lower.contains('duplicate') ||
|
||||
lower.contains('unique constraint') ||
|
||||
lower.contains('23505') ||
|
||||
lower.contains('already exist')) {
|
||||
return 'Cette adresse e-mail semble déjà enregistrée. '
|
||||
'Essayez de vous connecter ou modifiez l\'adresse du parent ou du co-parent.';
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// Rafraîchit le profil utilisateur depuis l'API
|
||||
static Future<AppUser?> refreshCurrentUser() async {
|
||||
final token = await TokenService.getToken();
|
||||
|
||||
Reference in New Issue
Block a user