117 lines
3.2 KiB
Dart
117 lines
3.2 KiB
Dart
// lib/remote/remote_client.dart
|
||
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'dart:io';
|
||
import 'package:http/http.dart' as http;
|
||
|
||
import 'remote_models.dart';
|
||
import 'auth_client.dart';
|
||
|
||
class RemoteJsonClient {
|
||
final Uri indexUri;
|
||
final RemoteAuth? auth;
|
||
|
||
RemoteJsonClient(
|
||
String baseUrl,
|
||
String indexPath, {
|
||
this.auth,
|
||
}) : indexUri = Uri.parse(
|
||
baseUrl.endsWith('/') ? baseUrl : '$baseUrl/',
|
||
).resolve(indexPath);
|
||
|
||
// 🔥 PING SUPER LEGGERO → usa /ping
|
||
Future<void> ping() async {
|
||
final client = HttpClient();
|
||
client.connectionTimeout = const Duration(seconds: 2);
|
||
|
||
// Costruisce l’URL /ping a partire dalla baseUrl
|
||
final pingUri = Uri.parse(indexUri.origin + '/ping');
|
||
|
||
try {
|
||
final req = await client.getUrl(pingUri);
|
||
req.followRedirects = false;
|
||
|
||
final res = await req.close().timeout(const Duration(seconds: 2));
|
||
|
||
print('[remote-client] PING url=$pingUri status=${res.statusCode}');
|
||
|
||
// 5xx → errore server
|
||
if (res.statusCode >= 500) {
|
||
throw HttpException('HTTP ${res.statusCode}', uri: pingUri);
|
||
}
|
||
|
||
// 200, 401, 403, 404 → server raggiungibile
|
||
} on TimeoutException {
|
||
throw TimeoutException('Ping timeout');
|
||
} on SocketException catch (e) {
|
||
throw SocketException('Network error: ${e.message}');
|
||
} on HttpException {
|
||
rethrow;
|
||
} catch (e) {
|
||
rethrow;
|
||
} finally {
|
||
client.close();
|
||
}
|
||
}
|
||
|
||
// FETCH ALL (come prima)
|
||
Future<List<RemotePhotoItem>> fetchAll() async {
|
||
Map<String, String> headers = {};
|
||
if (auth != null) {
|
||
headers = await auth!.authHeaders();
|
||
}
|
||
|
||
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');
|
||
}
|
||
|
||
if (res.statusCode == 401 && auth != null) {
|
||
headers = await auth!.refreshAndHeaders();
|
||
res = await http
|
||
.get(indexUri, headers: headers)
|
||
.timeout(const Duration(seconds: 20));
|
||
}
|
||
|
||
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 HttpException(
|
||
'HTTP ${res.statusCode} ${res.reasonPhrase}. Body: $snippet',
|
||
uri: indexUri,
|
||
);
|
||
}
|
||
|
||
final body = utf8.decode(res.bodyBytes);
|
||
final dynamic decoded = json.decode(body);
|
||
|
||
if (decoded is! List) {
|
||
throw Exception(
|
||
'JSON inatteso: atteso array top-level, ricevuto ${decoded.runtimeType}',
|
||
);
|
||
}
|
||
|
||
final rawList = decoded as List<dynamic>;
|
||
|
||
return 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();
|
||
}
|
||
}
|