import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; /// Gleiche Basis-Idee wie im React-Client (frontend/src/api/client.ts): /// gemeinsamer JSON-Wrapper, Token aus persistentem Speicher, ApiException /// mit HTTP-Status, damit aufrufender Code gezielt reagieren kann (z.B. 409 /// bei Objekt-Sperre). class ApiException implements Exception { final int status; final dynamic detail; ApiException(this.status, this.detail); @override String toString() => 'ApiException($status, $detail)'; } class ApiClient { ApiClient({required this.baseUrl, http.Client? httpClient}) : _http = httpClient ?? http.Client(); final String baseUrl; final http.Client _http; String? _token; Future ladeToken() async { final prefs = await SharedPreferences.getInstance(); _token = prefs.getString('mabea_token'); } Future setzeToken(String? token) async { _token = token; final prefs = await SharedPreferences.getInstance(); if (token == null) { await prefs.remove('mabea_token'); } else { await prefs.setString('mabea_token', token); } } bool get istEingeloggt => _token != null; Future login(String username, String password) async { final response = await _http.post( Uri.parse('$baseUrl/auth/login'), headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: {'username': username, 'password': password}, ); if (response.statusCode != 200) { throw ApiException(response.statusCode, _tryDecode(response.body)); } final data = jsonDecode(response.body) as Map; final token = data['access_token'] as String; await setzeToken(token); return token; } Future request( String path, { String method = 'GET', Object? body, }) async { final uri = Uri.parse('$baseUrl$path'); final headers = { 'Content-Type': 'application/json', if (_token != null) 'Authorization': 'Bearer $_token', }; final encodedBody = body != null ? jsonEncode(body) : null; late http.Response response; switch (method) { case 'POST': response = await _http.post(uri, headers: headers, body: encodedBody); break; case 'PUT': response = await _http.put(uri, headers: headers, body: encodedBody); break; case 'PATCH': response = await _http.patch(uri, headers: headers, body: encodedBody); break; case 'DELETE': response = await _http.delete(uri, headers: headers); break; default: response = await _http.get(uri, headers: headers); } if (response.statusCode >= 400) { throw ApiException(response.statusCode, _tryDecode(response.body)); } if (response.body.isEmpty) return null; return jsonDecode(response.body); } dynamic _tryDecode(String body) { try { return jsonDecode(body); } catch (_) { return null; } } }