initial commit 2

This commit is contained in:
David
2026-05-22 18:58:35 -06:00
parent 1334540303
commit abfbb255fe
157 changed files with 8071 additions and 1 deletions

View File

@@ -0,0 +1,132 @@
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;
}