import 'dart:convert'; import 'package:colorful_print/colorful_print.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:jwt_decoder/jwt_decoder.dart'; import 'package:rluv/global/store.dart'; import '../models/token.dart'; final tokenProvider = StateProvider((ref) { final jwt = ref.watch(jwtProvider); printColor('Current token: $jwt', textColor: TextColor.green); if (jwt == null) return null; try { return Token.fromJson(JwtDecoder.decode(jwt)); } catch (_) { return null; } }); final jwtProvider = StateNotifierProvider<_JwtNotifier, String?>((ref) { final prefs = ref.watch(prefsProvider); final jwt = prefs?.getString('jwt'); return _JwtNotifier(ref, jwt); }); class _JwtNotifier extends StateNotifier { _JwtNotifier(this.ref, String? jwt) : super(null) { if (jwt != null) { setToken(jwt); } } final StateNotifierProviderRef ref; void setToken(String jwt) { state = jwt; printColor('Loaded jwt into client: $jwt', textColor: TextColor.cyan); ref.read(prefsProvider)?.setString('jwt', jwt); } void revokeToken() { printColor('jwt token revoked', textColor: TextColor.cyan); state = null; ref.read(prefsProvider)?.remove('jwt'); } } final apiProvider = StateNotifierProvider<_ApiNotifier, Dio>((ref) { return _ApiNotifier(ref, Dio()); }); class _ApiNotifier extends StateNotifier { _ApiNotifier(this.ref, this.dio) : super(dio) { // dio.options.baseUrl = "https://af70-136-36-2-234.ngrok-free.app/"; // dio.options.baseUrl = "http://localhost:8081/"; dio.options.baseUrl = "https://rluv.fosscat.com/"; dio.interceptors.addAll([ InterceptorsWrapper(onRequest: (RequestOptions options, RequestInterceptorHandler handler) { final jwt = ref.read(jwtProvider); if (jwt != null) { options.headers['token'] = jwt; } return handler.next(options); }, onResponse: (Response response, ResponseInterceptorHandler handler) { if (response.statusCode != null) { if (response.statusCode == 200) { try { if ((response.data as Map).containsKey('success')) { if (!response.data['success']) return handler.next(response); } if ((response.data as Map).containsKey('token')) { final jwt = response.data['token']; if (jwt != null) { ref.read(jwtProvider.notifier).setToken(jwt); // ref.read(tokenProvider.notifier).state = // Token.fromJson(jwtData); } } } catch (err) { printColor('Error in interceptor for token: $err', textColor: TextColor.red); return handler.next(response); } } } return handler.next(response); }, onError: (DioException err, ErrorInterceptorHandler handler) { if (err.response?.statusCode == 403) { ref.read(jwtProvider.notifier).revokeToken(); } return handler.next(err); }), _LoggingInterceptor(), ]); } final Dio dio; final StateNotifierProviderRef ref; Future?> get(String path) async { try { final res = await dio.get(path); if (res.data != null) { return res.data as Map; } return null; } catch (err) { return null; } } Future?> put({required String path, Object? data}) async { try { final res = await dio.put(path, data: data); if (res.data != null) { return res.data as Map; } return null; } catch (err) { return null; } } Future?> post({required String path, Object? data}) async { try { final res = await dio.post(path, data: data); if (res.data != null) { return res.data as Map; } return null; } catch (err) { return null; } } Future?> delete({required String path, Object? data}) async { try { final res = await dio.delete(path, data: data); if (res.data != null) { return res.data as Map; } return null; } catch (err) { return null; } } } class _LoggingInterceptor extends Interceptor { _LoggingInterceptor(); @override Future onRequest(RequestOptions options, RequestInterceptorHandler handler) async { logPrint('///*** REQUEST ***\\\\\\'); printKV('URI', options.uri); printKV('METHOD', options.method); logPrint('HEADERS:'); options.headers.forEach((key, v) => printKV(' - $key', v)); logPrint('BODY:'); printJson(options.data); return handler.next(options); } @override void onError(DioException err, ErrorInterceptorHandler handler) { printColor('///*** ERROR RESPONSE ***\\\\\\', textColor: TextColor.red); logPrint('URI: ${err.requestOptions.uri}'); if (err.response != null) { logPrint('STATUS CODE: ${err.response?.statusCode?.toString()}'); } logPrint('$err'); if (err.response != null) { printKV('REDIRECT', err.response?.realUri ?? ''); logPrint('BODY:'); printJson(err.response?.data); } return handler.next(err); } @override Future onResponse(Response response, ResponseInterceptorHandler handler) async { logPrint('///*** RESPONSE ***\\\\\\'); printKV('URI', response.requestOptions.uri); printKV('STATUS CODE', response.statusCode ?? ''); // printKV('REDIRECT', response.isRedirect); logPrint('BODY:'); printJson(response.data); return handler.next(response); } void printKV(String key, Object v) { if (kDebugMode) { printColor('$key: $v', textColor: TextColor.orange); } } void printJson(dynamic s) { try { final data = (s as Map?); if (kDebugMode) { if (data == null) { printColor({}, textColor: TextColor.yellow); return; } JsonEncoder encoder = const JsonEncoder.withIndent(' '); String prettyprint = encoder.convert(s); printColor(prettyprint, textColor: TextColor.yellow); } } catch (_) { printColor(s, textColor: TextColor.yellow); } } void logPrint(String s) { if (kDebugMode) { printColor(s, textColor: TextColor.yellow); } } }