feat(app): смена сервера API и очистка данных устройства при выходе
Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
This commit is contained in:
@@ -4,27 +4,21 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../auth/auth_controller.dart';
|
||||
import '../cache/cache_interceptor.dart';
|
||||
import '../cache/response_cache.dart';
|
||||
import '../cache/response_cache_provider.dart';
|
||||
import '../config.dart';
|
||||
|
||||
/// One sqlite-backed cache of the last successful GET per endpoint+params,
|
||||
/// shared by every screen. See `docs/ai/offline-cache.md`.
|
||||
final responseCacheDbProvider = Provider<ResponseCacheDatabase>((ref) {
|
||||
final db = ResponseCacheDatabase();
|
||||
ref.onDispose(db.close);
|
||||
return db;
|
||||
});
|
||||
|
||||
/// Authenticated client: bearer header on every call, one transparent refresh
|
||||
/// on 401, and — since [CacheInterceptor] is added last, so it sees a request
|
||||
/// only after auth has already handled it — the last cached answer when the
|
||||
/// network itself is unreachable.
|
||||
final apiProvider = Provider<FintrackerApi>((ref) {
|
||||
final dio = Dio(BaseOptions(
|
||||
baseUrl: apiBaseUrl(),
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
));
|
||||
final dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: ref.watch(apiBaseUrlProvider),
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
),
|
||||
);
|
||||
dio.interceptors.add(DateQueryInterceptor());
|
||||
dio.interceptors.add(_AuthInterceptor(ref));
|
||||
dio.interceptors.add(CacheInterceptor(ref.watch(responseCacheDbProvider)));
|
||||
@@ -67,7 +61,10 @@ class _AuthInterceptor extends QueuedInterceptor {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
Future<void> onError(
|
||||
DioException err,
|
||||
ErrorInterceptorHandler handler,
|
||||
) async {
|
||||
final retried = err.requestOptions.extra['retried'] == true;
|
||||
if (err.response?.statusCode != 401 || retried) return handler.next(err);
|
||||
|
||||
@@ -81,7 +78,9 @@ class _AuthInterceptor extends QueuedInterceptor {
|
||||
..headers['Authorization'] = 'Bearer $token'
|
||||
..extra['retried'] = true;
|
||||
try {
|
||||
handler.resolve(await Dio(BaseOptions(baseUrl: opts.baseUrl)).fetch(opts));
|
||||
handler.resolve(
|
||||
await Dio(BaseOptions(baseUrl: opts.baseUrl)).fetch(opts),
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
handler.next(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user