132 lines
4.1 KiB
Dart
132 lines
4.1 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../app_config.dart';
|
|
import '../models/auth_session.dart';
|
|
|
|
abstract class AuthRepository {
|
|
Future<AuthSession?> restoreSession();
|
|
Future<AuthSession> signIn({required String email, required String password});
|
|
Future<AuthSession> signUp({required String name, required String email, required String password});
|
|
Future<void> signOut();
|
|
}
|
|
|
|
class HttpAuthRepository implements AuthRepository {
|
|
const HttpAuthRepository({http.Client? client}) : _client = client;
|
|
|
|
final http.Client? _client;
|
|
|
|
static const String _tokenKey = 'auth_token';
|
|
static const String _emailKey = 'auth_email';
|
|
static const String _nameKey = 'auth_name';
|
|
|
|
@override
|
|
Future<AuthSession?> restoreSession() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final token = prefs.getString(_tokenKey);
|
|
final email = prefs.getString(_emailKey);
|
|
final name = prefs.getString(_nameKey);
|
|
|
|
if (token == null || email == null) {
|
|
return null;
|
|
}
|
|
|
|
return AuthSession(token: token, email: email, displayName: name ?? email);
|
|
}
|
|
|
|
@override
|
|
Future<AuthSession> signIn({required String email, required String password}) {
|
|
return _authenticate(
|
|
endpoint: '/auth/login',
|
|
body: <String, dynamic>{'email': email, 'password': password},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<AuthSession> signUp({required String name, required String email, required String password}) {
|
|
return _authenticate(
|
|
endpoint: '/auth/register',
|
|
body: <String, dynamic>{'name': name, 'email': email, 'password': password},
|
|
);
|
|
}
|
|
|
|
Future<AuthSession> _authenticate({required String endpoint, required Map<String, dynamic> body}) async {
|
|
final uri = Uri.parse('${AppConfig.apiBaseUrl}$endpoint');
|
|
|
|
late final http.Response response;
|
|
try {
|
|
response = await (_client ?? http.Client()).post(
|
|
uri,
|
|
headers: const <String, String>{
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: jsonEncode(body),
|
|
);
|
|
} catch (_) {
|
|
throw AuthException(
|
|
'No se pudo conectar con el backend en ${AppConfig.apiBaseUrl}. Verifica que el servicio esté activo.',
|
|
);
|
|
}
|
|
|
|
final Map<String, dynamic> payload = _decodeJson(response.body);
|
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
final message = payload['message']?.toString() ?? 'Credenciales inválidas o usuario no disponible.';
|
|
throw AuthException(message);
|
|
}
|
|
|
|
final token = payload['token']?.toString();
|
|
if (token == null || token.isEmpty) {
|
|
throw AuthException('El backend respondió sin token de sesión.');
|
|
}
|
|
|
|
final user = payload['user'] is Map<String, dynamic>
|
|
? payload['user'] as Map<String, dynamic>
|
|
: <String, dynamic>{};
|
|
final emailValue = (user['email'] ?? body['email'])?.toString() ?? body['email'].toString();
|
|
final displayName = (user['name'] ?? user['fullName'] ?? body['name'] ?? emailValue).toString();
|
|
|
|
final session = AuthSession(token: token, email: emailValue, displayName: displayName);
|
|
await _persistSession(session);
|
|
return session;
|
|
}
|
|
|
|
Future<void> _persistSession(AuthSession session) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString(_tokenKey, session.token);
|
|
await prefs.setString(_emailKey, session.email);
|
|
await prefs.setString(_nameKey, session.displayName);
|
|
}
|
|
|
|
Map<String, dynamic> _decodeJson(String responseBody) {
|
|
if (responseBody.trim().isEmpty) {
|
|
return <String, dynamic>{};
|
|
}
|
|
|
|
final decoded = jsonDecode(responseBody);
|
|
if (decoded is Map<String, dynamic>) {
|
|
return decoded;
|
|
}
|
|
|
|
return <String, dynamic>{};
|
|
}
|
|
|
|
@override
|
|
Future<void> signOut() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove(_tokenKey);
|
|
await prefs.remove(_emailKey);
|
|
await prefs.remove(_nameKey);
|
|
}
|
|
}
|
|
|
|
class AuthException implements Exception {
|
|
AuthException(this.message);
|
|
|
|
final String message;
|
|
|
|
@override
|
|
String toString() => message;
|
|
} |