aves_mio1/lib/remote/remote_client.dart
FabioMich66 19a982ede6
Some checks are pending
Quality check / Flutter analysis (push) Waiting to run
Quality check / CodeQL analysis (java-kotlin) (push) Waiting to run
first commit
2026-03-05 15:51:30 +01:00

77 lines
2.5 KiB
Dart

// lib/remote/remote_client.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'remote_models.dart';
import 'auth_client.dart';
class RemoteJsonClient {
final Uri indexUri; // es. https://prova.patachina.it/photos/
final RemoteAuth? auth; // opzionale: se presente, aggiunge Bearer
RemoteJsonClient(
String baseUrl,
String indexPath, {
this.auth,
}) : indexUri = Uri.parse(baseUrl.endsWith('/') ? baseUrl : '$baseUrl/')
.resolve(indexPath);
Future<List<RemotePhotoItem>> fetchAll() async {
Map<String, String> headers = {};
if (auth != null) {
headers = await auth!.authHeaders();
}
// DEBUG: stampa la URL precisa
// ignore: avoid_print
print('[remote-client] GET $indexUri');
http.Response res;
try {
res = await http.get(indexUri, headers: headers).timeout(const Duration(seconds: 20));
} catch (e) {
throw Exception('Errore rete su $indexUri: $e');
}
// Retry 1 volta in caso di 401 (token scaduto/invalidato)
if (res.statusCode == 401 && auth != null) {
headers = await auth!.refreshAndHeaders();
res = await http.get(indexUri, headers: headers).timeout(const Duration(seconds: 20));
}
// Follow 301/302 mantenendo Authorization
if ({301,302,307,308}.contains(res.statusCode) && res.headers['location'] != null) {
final loc = res.headers['location']!;
final redirectUri = indexUri.resolve(loc);
res = await http.get(redirectUri, headers: headers).timeout(const Duration(seconds: 20));
}
if (res.statusCode != 200) {
final snippet = utf8.decode(res.bodyBytes.take(200).toList());
throw Exception('HTTP ${res.statusCode} ${res.reasonPhrase} su $indexUri. Body: $snippet');
}
final body = utf8.decode(res.bodyBytes);
// DEBUG
print('DEBUG JSON: $body');
// Qui siamo espliciti: ci aspettiamo SEMPRE una lista top-level
final dynamic decoded = json.decode(body);
if (decoded is! List) {
throw Exception('JSON inatteso: atteso array top-level, ricevuto ${decoded.runtimeType}');
}
final List<dynamic> rawList = decoded;
// Costruiamo a mano la List<RemotePhotoItem>, tipizzata esplicitamente
final List<RemotePhotoItem> items = rawList.map<RemotePhotoItem>((e) {
if (e is! Map<String, dynamic>) {
throw Exception('Elemento JSON non è una mappa: ${e.runtimeType} -> $e');
}
return RemotePhotoItem.fromJson(e);
}).toList();
return items;
}
}