Initial commit: AudiOhm - Alternative Spotify avec streaming YouTube
Backend: - FastAPI avec PostgreSQL et Redis - Authentification JWT complète - API REST pour musique, playlists, recherche - Streaming audio via yt-dlp - SQLAlchemy 2.0 async Frontend: - Flutter avec thème néon cyberpunk - State management Riverpod - Layout adaptatif desktop/mobile - Lecteur audio avec mini-player Infrastructure: - Docker Compose (PostgreSQL + Redis) - Scripts d'installation automatisés - Scripts de build pour exécutables Fichiers ajoutés: - BUILD_CLIENT_*.bat/sh: Scripts de compilation - BUILD_CLIENT_README.md: Documentation compilation - CHECK_FLUTTER.sh: Vérificateur d'environnement - requirements.txt mis à jour pour Python 3.13 - Modèles SQLAlchemy corrigés (metadata -> extra_metadata) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/// API Service - Main HTTP client using Dio
|
||||
library;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
import '../../../core/constants/api_constants.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
|
||||
/// API Service provider
|
||||
final apiServiceProvider = Provider<Dio>((ref) {
|
||||
final authState = ref.watch(authProvider);
|
||||
final token = authState?.accessToken;
|
||||
|
||||
final options = BaseOptions(
|
||||
baseUrl: ApiConstants.baseUrl,
|
||||
connectTimeout: const Duration(milliseconds: ApiConstants.connectionTimeoutMs),
|
||||
receiveTimeout: const Duration(milliseconds: ApiConstants.receiveTimeoutMs),
|
||||
sendTimeout: const Duration(milliseconds: ApiConstants.sendTimeoutMs),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
if (token != null) 'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
final dio = Dio(options);
|
||||
|
||||
// Add logger in debug mode
|
||||
dio.interceptors.add(
|
||||
PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
responseBody: true,
|
||||
responseHeader: false,
|
||||
error: true,
|
||||
compact: true,
|
||||
),
|
||||
);
|
||||
|
||||
// Add token refresh interceptor
|
||||
dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onError: (error, handler) async {
|
||||
if (error.response?.statusCode == 401) {
|
||||
// Try to refresh token
|
||||
try {
|
||||
final newToken = await ref.read(authProvider.notifier).refreshToken();
|
||||
if (newToken != null) {
|
||||
// Retry original request with new token
|
||||
final opts = options.copyWith(
|
||||
headers: {
|
||||
...options.headers,
|
||||
'Authorization': 'Bearer $newToken',
|
||||
},
|
||||
);
|
||||
final clonedReq = await dio.fetch(opts..path = error.requestOptions.path);
|
||||
return handler.resolve(clonedReq);
|
||||
}
|
||||
} catch (e) {
|
||||
// Refresh failed, logout user
|
||||
ref.read(authProvider.notifier).logout();
|
||||
}
|
||||
}
|
||||
return handler.next(error);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return dio;
|
||||
});
|
||||
|
||||
/// Get API client
|
||||
Dio getDio(Ref ref) {
|
||||
return ref.read(apiServiceProvider);
|
||||
}
|
||||
Reference in New Issue
Block a user