Update: 2026-08-06 18:32:03
This commit is contained in:
@@ -1,8 +1,41 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'api_config.dart';
|
||||
import 'hmac_interceptor.dart';
|
||||
import '../storage/secure_storage.dart';
|
||||
|
||||
/// Request/response fields that must never reach a log sink.
|
||||
const _sensitiveKeys = {
|
||||
'password',
|
||||
'password_confirmation',
|
||||
'old_password',
|
||||
'new_password',
|
||||
'otp',
|
||||
'access_token',
|
||||
'refresh_token',
|
||||
'device_secret',
|
||||
'push_token',
|
||||
};
|
||||
|
||||
/// Replaces sensitive values with a placeholder, recursing into nested maps.
|
||||
///
|
||||
/// Logging raw payloads printed live credentials into Xcode/logcat, where they
|
||||
/// persist well beyond the debug session.
|
||||
Object? _redact(Object? data) {
|
||||
if (data is Map) {
|
||||
return data.map((key, value) => MapEntry(
|
||||
key,
|
||||
_sensitiveKeys.contains(key.toString().toLowerCase())
|
||||
? '***'
|
||||
: _redact(value),
|
||||
));
|
||||
}
|
||||
if (data is List) {
|
||||
return data.map(_redact).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
class DioClient {
|
||||
static const String baseUrl = ApiConfig.baseUrl;
|
||||
late final Dio dio;
|
||||
@@ -23,29 +56,33 @@ class DioClient {
|
||||
// Add Interceptors
|
||||
dio.interceptors.add(HmacInterceptor(SecureStorage()));
|
||||
|
||||
// Custom Logging Interceptor as requested
|
||||
dio.interceptors.add(InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
print('--- API REQUEST ---');
|
||||
print('URL: ${options.method} ${options.uri}');
|
||||
print('Payload: ${options.data}');
|
||||
return handler.next(options);
|
||||
},
|
||||
onResponse: (response, handler) {
|
||||
print('--- API RESPONSE ---');
|
||||
print('URL: ${response.requestOptions.method} ${response.requestOptions.uri}');
|
||||
print('Status Code: ${response.statusCode}');
|
||||
print('Response: ${response.data}');
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (DioException e, handler) {
|
||||
print('--- API ERROR ---');
|
||||
print('URL: ${e.requestOptions.method} ${e.requestOptions.uri}');
|
||||
print('Status Code: ${e.response?.statusCode}');
|
||||
print('Response: ${e.response?.data ?? e.message}');
|
||||
return handler.next(e);
|
||||
},
|
||||
));
|
||||
// Logging interceptor. Debug builds only, and credentials are redacted:
|
||||
// these used to be bare print() calls, which also ran in release builds.
|
||||
if (kDebugMode) {
|
||||
dio.interceptors.add(InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
debugPrint('--- API REQUEST ---');
|
||||
debugPrint('URL: ${options.method} ${options.uri}');
|
||||
debugPrint('Payload: ${_redact(options.data)}');
|
||||
return handler.next(options);
|
||||
},
|
||||
onResponse: (response, handler) {
|
||||
debugPrint('--- API RESPONSE ---');
|
||||
debugPrint(
|
||||
'URL: ${response.requestOptions.method} ${response.requestOptions.uri}');
|
||||
debugPrint('Status Code: ${response.statusCode}');
|
||||
debugPrint('Response: ${_redact(response.data)}');
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (DioException e, handler) {
|
||||
debugPrint('--- API ERROR ---');
|
||||
debugPrint('URL: ${e.requestOptions.method} ${e.requestOptions.uri}');
|
||||
debugPrint('Status Code: ${e.response?.statusCode}');
|
||||
debugPrint('Response: ${_redact(e.response?.data) ?? e.message}');
|
||||
return handler.next(e);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Dio get client => dio;
|
||||
|
||||
Reference in New Issue
Block a user