104 lines
3.6 KiB
Dart
104 lines
3.6 KiB
Dart
// lib/remote/remote_http.dart
|
||
import 'dart:developer' as dev;
|
||
import 'package:flutter/services.dart';
|
||
|
||
import 'remote_settings.dart';
|
||
import 'auth_client.dart';
|
||
|
||
/// Helper HTTP per risorse remote (URL + headers di autenticazione).
|
||
class RemoteHttp {
|
||
static RemoteAuth? _auth;
|
||
static String? _base;
|
||
|
||
// Cache dell'ultimo set di header (mai null: {} se non autenticato)
|
||
static Map<String, String> _cachedHeaders = const {};
|
||
|
||
static Future<void>? _initFuture; // evita init concorrenti
|
||
|
||
/// Inizializza auth e base URL dai settings sicuri (robusto: non lancia).
|
||
static Future<void> init() {
|
||
_initFuture ??= _doInit();
|
||
return _initFuture!;
|
||
}
|
||
|
||
static Future<void> _doInit() async {
|
||
try {
|
||
final s = await RemoteSettings.load(); // safe lato storage
|
||
final base = s.baseUrl.trim();
|
||
_base = base.isEmpty ? null : base;
|
||
_auth = RemoteAuth(baseUrl: s.baseUrl, email: s.email, password: s.password);
|
||
dev.log('[RemoteHttp] init: base=$_base, email=${s.email.isNotEmpty ? '***' : '(none)'}', name: 'RemoteHttp');
|
||
} catch (e, st) {
|
||
dev.log('[RemoteHttp] init ERROR: $e', name: 'RemoteHttp', error: e, stackTrace: st);
|
||
_auth = null;
|
||
_base = null;
|
||
}
|
||
}
|
||
|
||
/// Headers correnti (login on‑demand). Non lancia; ritorna {} se non disponibili.
|
||
static Future<Map<String, String>> headers() async {
|
||
if (_auth == null) {
|
||
await init();
|
||
if (_auth == null) {
|
||
_cachedHeaders = const {};
|
||
dev.log('[RemoteHttp] headers: init failed → NO TOKEN', name: 'RemoteHttp');
|
||
return _cachedHeaders;
|
||
}
|
||
}
|
||
|
||
try {
|
||
final h = await _auth!.authHeaders(); // login on-demand
|
||
_cachedHeaders = Map<String, String>.from(h);
|
||
if ((_cachedHeaders['Authorization']?.isEmpty ?? true)) {
|
||
dev.log('[RemoteHttp] headers: NO Authorization', name: 'RemoteHttp');
|
||
} else {
|
||
dev.log('[RemoteHttp] headers: Authorization: Bearer ***', name: 'RemoteHttp');
|
||
}
|
||
return _cachedHeaders;
|
||
} on PlatformException catch (e, st) {
|
||
// Tipico: secure storage/keystore corrotto → non rompiamo la UI
|
||
dev.log('[RemoteHttp] headers PlatformException: $e → returning {}',
|
||
name: 'RemoteHttp', error: e, stackTrace: st);
|
||
_cachedHeaders = const {};
|
||
return _cachedHeaders;
|
||
} catch (e, st) {
|
||
dev.log('[RemoteHttp] headers ERROR: $e → returning {}',
|
||
name: 'RemoteHttp', error: e, stackTrace: st);
|
||
_cachedHeaders = const {};
|
||
return _cachedHeaders;
|
||
}
|
||
}
|
||
|
||
/// Ultimi header noti (sincrono). Mai null; {} se non autenticato.
|
||
static Map<String, String> peekHeaders() => _cachedHeaders;
|
||
|
||
/// Converte path relativo in assoluto. Se è già http/https lo ritorna com’è.
|
||
static String absUrl(String? relativePath) {
|
||
if (relativePath == null || relativePath.isEmpty) return '';
|
||
final lp = relativePath.trim().toLowerCase();
|
||
if (lp.startsWith('http://') || lp.startsWith('https://')) {
|
||
return relativePath;
|
||
}
|
||
if (_base == null || _base!.isEmpty) return '';
|
||
final b = _base!.endsWith('/') ? _base! : '${_base!}/';
|
||
final rel = relativePath.startsWith('/') ? relativePath.substring(1) : relativePath;
|
||
return '$b$rel';
|
||
}
|
||
|
||
/// Facoltativo: warm‑up all’avvio per scaldare gli header.
|
||
static Future<void> warmUp() async {
|
||
await init();
|
||
try {
|
||
await headers();
|
||
} catch (_) {
|
||
// già loggato in headers()
|
||
}
|
||
}
|
||
|
||
/// Rilegge i settings a runtime (es. utente cambia base/email/password).
|
||
static Future<void> refreshFromSettings() async {
|
||
_initFuture = null;
|
||
await init();
|
||
_cachedHeaders = const {}; // saranno ricaricati al primo uso
|
||
}
|
||
}
|