import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:dio/dio.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:moneymgr_mobile/services/api/api_token.dart'; import 'package:moneymgr_mobile/utils/string_utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'api_client.g.dart'; /// API token header const apiTokenHeader = "X-Auth-Token"; /// Client API class ApiClient { final ApiToken token; final Dio client; ApiClient({required this.token}) : client = Dio(BaseOptions(baseUrl: token.apiUrl)); /// Get Dio instance Future> execute(String uri, {String method = "GET"}) async { return client.request( uri, options: Options( method: method, headers: {apiTokenHeader: _genJWT(method, uri)}, ), ); } /// Generate authentication JWT String _genJWT(String method, String uri) { final jwt = JWT( {"nonce": getRandomString(15), "met": method, "uri": uri}, header: {"kid": token.tokenId.toString()}, ); return jwt.sign( SecretKey(token.tokenValue), algorithm: JWTAlgorithm.HS256, expiresIn: Duration(minutes: 15), ); } } /// An API service that handles authentication and exposes an [ApiClient]. /// /// Every API call coming from UI should watch/read this provider instead of /// instantiating the [ApiClient] itself. When being watched, it will force any /// data provider (provider that fetches data) to refetch when the /// authentication state changes. /// /// The API client is kept alive to follow dio's recommendation to use the same /// client instance for the entire app. @riverpod ApiClient apiService(Ref ref) { /*final token = ref.watch(currentAuthStateProvider); final ApiClient client; const mock = bool.fromEnvironment('MOCK_API', defaultValue: false); client = switch (mock) { true => token != null ? MockedApiClient.withToken(token) : MockedApiClient(), false => token != null ? ApiClient.withToken(token) : ApiClient(), }; ref.keepAlive(); return client;*/ throw Exception("TODO"); // TODO }