File size: 2,846 Bytes
78013c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:smart_attendance_app/core/constants.dart';
import 'package:smart_attendance_app/core/exceptions.dart';
import 'package:smart_attendance_app/core/events.dart';
import 'package:smart_attendance_app/data/local/secure_storage.dart';
import 'package:smart_attendance_app/utils/logger.dart';

final dioProvider = Provider<Dio>((ref) {
  final dio = Dio(BaseOptions(
    baseUrl: kApiBaseUrl,
    connectTimeout: const Duration(milliseconds: kConnectTimeout),
    receiveTimeout: const Duration(milliseconds: kReceiveTimeout),
    headers: {'Accept': 'application/json'},
  ));

  final storage = ref.read(secureStorageProvider);

  dio.interceptors.add(InterceptorsWrapper(
    onRequest: (options, handler) async {
      final token = await storage.getToken();
      if (token != null) {
        options.headers['Authorization'] = 'Bearer $token';
      }
      handler.next(options);
    },
    onResponse: (response, handler) {
      handler.next(response);
    },
    onError: (error, handler) async {
      AppLogger.error('[HTTP] ${error.requestOptions.method} ${error.requestOptions.path}: ${error.message}');
      if (error.response?.statusCode == 401 &&
          !error.requestOptions.path.endsWith('/auth/login') &&
          !error.requestOptions.path.endsWith('/auth/logout')) {
        await storage.clearAll();
        AppEvents.broadcastAuthError('session_expired');
      }
      handler.next(error);
    },
  ));

  return dio;
});

AppException mapDioError(DioException error) {
  switch (error.type) {
    case DioExceptionType.connectionTimeout:
    case DioExceptionType.sendTimeout:
    case DioExceptionType.receiveTimeout:
      return const NetworkException('Connection timed out. Please try again.');
    case DioExceptionType.connectionError:
      return const NetworkException(
          'Unable to connect to the server. Check your internet connection.');
    case DioExceptionType.badResponse:
      final statusCode = error.response?.statusCode ?? 0;
      final detail = _extractDetail(error.response?.data);
      if (statusCode == 401) {
        return AuthException(detail, statusCode: statusCode);
      }
      if (statusCode == 403) {
        return AuthException('Access forbidden', statusCode: statusCode);
      }
      if (statusCode >= 400 && statusCode < 500) {
        return ValidationException(detail, statusCode: statusCode);
      }
      return ServerException(detail, statusCode: statusCode);
    default:
      return NetworkException(
          error.message ?? 'An unexpected network error occurred.');
  }
}

String _extractDetail(dynamic data) {
  if (data is Map<String, dynamic>) {
    return data['detail']?.toString() ?? 'Something went wrong';
  }
  return 'Something went wrong';
}