feat: migrate prosappco from Firebase to NestJS REST API (Fase 2)
- Replace all Firebase* repositories with Api* repositories using HTTP + SharedPreferences JWT - Remove Firebase.initializeApp() and firebase_messaging background handler from main.dart - Update DI (app_di.dart) to inject Api* repositories instead of Firebase* ones - Replace all Timestamp/cloud_firestore usage with ISO 8601 String dates - Stub PhoneVerificationService (Firebase phone OTP → backend OTP when implemented) - Add ApiService singleton with JWT management in lib/services/ - Legacy firebase_*_repository.dart files preserved for Fase 4 cleanup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
726cf12fd2
commit
733384091c
@@ -0,0 +1,92 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const String _baseUrl = 'https://backend.prosapp.co/api/v1';
|
||||
|
||||
class ApiException implements Exception {
|
||||
final int statusCode;
|
||||
final String message;
|
||||
ApiException(this.statusCode, this.message);
|
||||
@override
|
||||
String toString() => 'ApiException($statusCode): $message';
|
||||
}
|
||||
|
||||
class ApiService {
|
||||
static ApiService? _instance;
|
||||
static ApiService get instance => _instance ??= ApiService._();
|
||||
ApiService._();
|
||||
|
||||
String? _token;
|
||||
|
||||
Future<String?> getToken() async {
|
||||
if (_token != null) return _token;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_token = prefs.getString('token');
|
||||
return _token;
|
||||
}
|
||||
|
||||
Future<void> saveToken(String token) async {
|
||||
_token = token;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('token', token);
|
||||
}
|
||||
|
||||
Future<void> clearToken() async {
|
||||
_token = null;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('token');
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _headers({bool auth = true}) async {
|
||||
final headers = <String, String>{'Content-Type': 'application/json'};
|
||||
if (auth) {
|
||||
final token = await getToken();
|
||||
if (token != null) headers['Authorization'] = 'Bearer $token';
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
dynamic _parse(http.Response res) {
|
||||
final body = jsonDecode(res.body);
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) return body;
|
||||
final msg = body is Map ? (body['message'] ?? res.reasonPhrase) : res.reasonPhrase;
|
||||
throw ApiException(res.statusCode, msg.toString());
|
||||
}
|
||||
|
||||
Future<dynamic> get(String path, {bool auth = true, Map<String, String>? query}) async {
|
||||
final uri = Uri.parse('$_baseUrl$path').replace(queryParameters: query);
|
||||
final res = await http.get(uri, headers: await _headers(auth: auth));
|
||||
return _parse(res);
|
||||
}
|
||||
|
||||
Future<dynamic> post(String path, Map<String, dynamic> body, {bool auth = false}) async {
|
||||
final res = await http.post(
|
||||
Uri.parse('$_baseUrl$path'),
|
||||
headers: await _headers(auth: auth),
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
return _parse(res);
|
||||
}
|
||||
|
||||
Future<dynamic> patch(String path, Map<String, dynamic> body, {bool auth = true}) async {
|
||||
final res = await http.patch(
|
||||
Uri.parse('$_baseUrl$path'),
|
||||
headers: await _headers(auth: auth),
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
return _parse(res);
|
||||
}
|
||||
|
||||
Future<String> uploadFile(String filePath) async {
|
||||
final token = await getToken();
|
||||
final req = http.MultipartRequest('POST', Uri.parse('$_baseUrl/storage/upload'));
|
||||
if (token != null) req.headers['Authorization'] = 'Bearer $token';
|
||||
req.files.add(await http.MultipartFile.fromPath('file', filePath));
|
||||
final streamed = await req.send();
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
final body = jsonDecode(res.body);
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) return body['url'] as String;
|
||||
throw ApiException(res.statusCode, body['message']?.toString() ?? 'Upload failed');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user