86 lines
2.8 KiB
Dart
86 lines
2.8 KiB
Dart
import 'package:dio/dio.dart';
|
|
|
|
typedef TokenRefreshCallback = Future<String?> Function();
|
|
|
|
class ApiClient {
|
|
static const String baseUrl = 'https://api.hidalyx.com';
|
|
static final ApiClient _instance = ApiClient._internal();
|
|
|
|
factory ApiClient() => _instance;
|
|
|
|
final Dio dio;
|
|
TokenRefreshCallback? onTokenRefresh;
|
|
|
|
ApiClient._internal({Dio? customDio})
|
|
: dio = customDio ??
|
|
Dio(
|
|
BaseOptions(
|
|
baseUrl: baseUrl,
|
|
connectTimeout: const Duration(seconds: 15),
|
|
receiveTimeout: const Duration(seconds: 15),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
|
'Cache-Control': 'no-cache',
|
|
'Pragma': 'no-cache',
|
|
},
|
|
),
|
|
) {
|
|
_setupInterceptors();
|
|
}
|
|
|
|
ApiClient.withDio(this.dio) {
|
|
_setupInterceptors();
|
|
}
|
|
|
|
void _setupInterceptors() {
|
|
dio.interceptors.add(
|
|
QueuedInterceptorsWrapper(
|
|
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
|
|
final path = options.path;
|
|
if (path.contains('/auth/login')) {
|
|
// NUNCA enviar Authorization header en el endpoint de login
|
|
options.headers.remove('Authorization');
|
|
}
|
|
return handler.next(options);
|
|
},
|
|
onError: (DioException err, ErrorInterceptorHandler handler) async {
|
|
final statusCode = err.response?.statusCode;
|
|
final path = err.requestOptions.path;
|
|
final isAuthEndpoint = path.contains('/auth/login') ||
|
|
path.contains('/auth/refresh') ||
|
|
path.contains('/auth/logout');
|
|
final isExpiredOrUnauthorized = statusCode == 401 ||
|
|
(statusCode == 403 &&
|
|
err.response?.data?.toString().contains('TOKEN_EXPIRED') == true);
|
|
|
|
if (isExpiredOrUnauthorized && !isAuthEndpoint && onTokenRefresh != null) {
|
|
try {
|
|
final newToken = await onTokenRefresh!();
|
|
if (newToken != null && newToken.isNotEmpty) {
|
|
setAuthToken(newToken);
|
|
final requestOptions = err.requestOptions;
|
|
requestOptions.headers['Authorization'] = 'Bearer $newToken';
|
|
|
|
final retryResponse = await dio.fetch(requestOptions);
|
|
return handler.resolve(retryResponse);
|
|
}
|
|
} catch (_) {
|
|
// Si el refresh falla, continuar el flujo del error normal
|
|
}
|
|
}
|
|
return handler.next(err);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
void setAuthToken(String token) {
|
|
dio.options.headers['Authorization'] = 'Bearer $token';
|
|
}
|
|
|
|
void clearAuthToken() {
|
|
dio.options.headers.remove('Authorization');
|
|
}
|
|
}
|