175 lines
4.6 KiB
Dart
175 lines
4.6 KiB
Dart
import "dart:convert";
|
|
import "dart:typed_data";
|
|
import "package:http/http.dart" as http;
|
|
|
|
/// Erreur levée par le client API (serveur injoignable ou réponse en échec).
|
|
class ApiException implements Exception {
|
|
const ApiException(this.message);
|
|
|
|
final String message;
|
|
|
|
@override
|
|
String toString() => message;
|
|
}
|
|
|
|
class ApiFile {
|
|
const ApiFile({
|
|
required this.bytes,
|
|
required this.fileName,
|
|
required this.contentType,
|
|
});
|
|
|
|
final Uint8List bytes;
|
|
final String fileName;
|
|
final String contentType;
|
|
}
|
|
|
|
/// Point d'accès unique au backend : centralise l'URL de base, l'en-tête
|
|
/// d'authentification (simulée) et la gestion des erreurs.
|
|
class ApiClient {
|
|
ApiClient._();
|
|
|
|
static const String _baseUrl = "http://localhost:3000/api";
|
|
static const String _utilisateurEmail = "lucas.martin@ensitech.eu";
|
|
static const String responsableEmail = "karim.benali@ensup.eu";
|
|
|
|
static Future<dynamic> get(
|
|
String chemin, {
|
|
String utilisateurEmail = _utilisateurEmail,
|
|
}) async {
|
|
final uri = Uri.parse("$_baseUrl$chemin");
|
|
|
|
late final http.Response reponse;
|
|
try {
|
|
reponse = await http.get(
|
|
uri,
|
|
headers: {"x-user-email": utilisateurEmail},
|
|
);
|
|
} catch (_) {
|
|
throw const ApiException(
|
|
"Impossible de joindre le serveur. Vérifiez que le backend est démarré.",
|
|
);
|
|
}
|
|
|
|
if (reponse.statusCode >= 200 && reponse.statusCode < 300) {
|
|
return jsonDecode(reponse.body);
|
|
}
|
|
throw ApiException(_messageErreur(reponse));
|
|
}
|
|
|
|
static Future<ApiFile> getFile(
|
|
String chemin, {
|
|
required String fallbackFileName,
|
|
String utilisateurEmail = _utilisateurEmail,
|
|
}) async {
|
|
final uri = Uri.parse("$_baseUrl$chemin");
|
|
|
|
late final http.Response reponse;
|
|
try {
|
|
reponse = await http.get(
|
|
uri,
|
|
headers: {"x-user-email": utilisateurEmail},
|
|
);
|
|
} catch (_) {
|
|
throw const ApiException(
|
|
"Impossible de joindre le serveur. Vérifiez que le backend est démarré.",
|
|
);
|
|
}
|
|
|
|
if (reponse.statusCode < 200 || reponse.statusCode >= 300) {
|
|
throw ApiException(_messageErreur(reponse));
|
|
}
|
|
|
|
return ApiFile(
|
|
bytes: reponse.bodyBytes,
|
|
fileName: _fileName(reponse, fallbackFileName),
|
|
contentType:
|
|
reponse.headers["content-type"] ?? "application/octet-stream",
|
|
);
|
|
}
|
|
|
|
static Future<dynamic> post(
|
|
String chemin,
|
|
Map<String, dynamic> body, {
|
|
String utilisateurEmail = _utilisateurEmail,
|
|
}) async {
|
|
final uri = Uri.parse("$_baseUrl$chemin");
|
|
|
|
late final http.Response reponse;
|
|
try {
|
|
reponse = await http.post(
|
|
uri,
|
|
headers: {
|
|
"content-type": "application/json",
|
|
"x-user-email": utilisateurEmail,
|
|
},
|
|
body: jsonEncode(body),
|
|
);
|
|
} catch (_) {
|
|
throw const ApiException(
|
|
"Impossible de joindre le serveur. Vérifiez que le backend est démarré.",
|
|
);
|
|
}
|
|
|
|
if (reponse.statusCode >= 200 && reponse.statusCode < 300) {
|
|
return jsonDecode(reponse.body);
|
|
}
|
|
throw ApiException(_messageErreur(reponse));
|
|
}
|
|
|
|
static Future<dynamic> patch(
|
|
String chemin,
|
|
Map<String, dynamic> body, {
|
|
String utilisateurEmail = _utilisateurEmail,
|
|
}) async {
|
|
final uri = Uri.parse("$_baseUrl$chemin");
|
|
|
|
late final http.Response reponse;
|
|
try {
|
|
reponse = await http.patch(
|
|
uri,
|
|
headers: {
|
|
"content-type": "application/json",
|
|
"x-user-email": utilisateurEmail,
|
|
},
|
|
body: jsonEncode(body),
|
|
);
|
|
} catch (_) {
|
|
throw const ApiException(
|
|
"Impossible de joindre le serveur. Vérifiez que le backend est démarré.",
|
|
);
|
|
}
|
|
|
|
if (reponse.statusCode >= 200 && reponse.statusCode < 300) {
|
|
return jsonDecode(reponse.body);
|
|
}
|
|
throw ApiException(_messageErreur(reponse));
|
|
}
|
|
|
|
static String _messageErreur(http.Response reponse) {
|
|
try {
|
|
final corps = jsonDecode(reponse.body);
|
|
if (corps is Map && corps["error"] is Map) {
|
|
final message = (corps["error"] as Map)["message"];
|
|
if (message is String) {
|
|
return message;
|
|
}
|
|
}
|
|
} catch (_) {
|
|
// Corps non-JSON : on retombe sur le code HTTP.
|
|
}
|
|
return "Erreur ${reponse.statusCode}";
|
|
}
|
|
|
|
static String _fileName(http.Response reponse, String fallback) {
|
|
final disposition = reponse.headers["content-disposition"];
|
|
if (disposition == null) return fallback;
|
|
|
|
final match = RegExp(
|
|
"filename=\"?([^\";]+)\"?",
|
|
caseSensitive: false,
|
|
).firstMatch(disposition);
|
|
return match?.group(1) ?? fallback;
|
|
}
|
|
}
|