import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../config/app_config.dart'; import '../services/melo_logger.dart'; /// Baka-Auth Service – JWT-basierte Authentifizierung /// Integriert mit https://baka-net.de/auth class AuthService { static final AuthService _instance = AuthService._(); factory AuthService() => _instance; AuthService._(); String? _token; String _user = ''; bool _initialisiert = false; bool get istEingeloggt => _token != null && _token!.isNotEmpty; String get benutzer => _user; String? get token => _token; /// Auth-Header für API-Requests Map get authHeader { final headers = { 'Content-Type': 'application/json', }; if (_token != null && _token!.isNotEmpty) { headers['Authorization'] = 'Bearer $_token'; } return headers; } /// Lädt gespeicherten Token beim App-Start Future initialisieren() async { if (_initialisiert) return; try { final prefs = await SharedPreferences.getInstance(); _token = prefs.getString('baka_token'); _user = prefs.getString('baka_user') ?? ''; if (_token != null && _token!.isNotEmpty) { MeloLogger().zustand('auth_restored', {'user': _user}); } } catch (e) { MeloLogger().fehler('auth_init', e); } _initialisiert = true; } /// Login über Baka-Auth-Server /// Gibt true zurück bei Erfolg, false bei Fehler Future login(String user, String password) async { try { final response = await http .post( Uri.parse('${AppConfig.authUrl}/login'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'username': user, 'password': password, }), ) .timeout(const Duration(seconds: 10)); if (response.statusCode == 200) { final data = jsonDecode(response.body); _token = data['token'] as String?; _user = user; if (_token != null && _token!.isNotEmpty) { final prefs = await SharedPreferences.getInstance(); await prefs.setString('baka_token', _token!); await prefs.setString('baka_user', _user); MeloLogger().aktion('auth_login_ok', {'user': _user}); return AuthResult.ok; } // 200 aber kein Token → Fehler vom Server final serverMsg = data['message'] as String? ?? data['error'] as String? ?? 'Unbekannt'; return AuthResult.fehlgeschlagen(serverMsg); } // Nicht-200 String fehler = 'Login fehlgeschlagen (${response.statusCode})'; MeloLogger().fehler('auth_login_fail', fehler); return AuthResult.fehlgeschlagen(fehler); } catch (e) { final msg = 'Keine Verbindung zum Auth-Server'; MeloLogger().fehler('auth_login_error', e); return AuthResult.fehlgeschlagen(msg); } } /// Registrierung über Baka-Auth-Server Future registrieren(String user, String password, String email) async { try { final response = await http .post( Uri.parse('${AppConfig.authUrl}/register'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'username': user, 'password': password, 'email': email, }), ) .timeout(const Duration(seconds: 10)); if (response.statusCode == 200 || response.statusCode == 201) { final data = jsonDecode(response.body); _token = data['token'] as String?; _user = user; if (_token != null && _token!.isNotEmpty) { final prefs = await SharedPreferences.getInstance(); await prefs.setString('baka_token', _token!); await prefs.setString('baka_user', _user); MeloLogger().aktion('auth_register_ok', {'user': _user}); return AuthResult.ok; } } String fehler = 'Registrierung fehlgeschlagen'; try { final data = jsonDecode(response.body); fehler = data['error'] as String? ?? fehler; } catch (_) {} return AuthResult.fehlgeschlagen(fehler); } catch (e) { return AuthResult.fehlgeschlagen('Keine Verbindung zum Server'); } } /// Token beim Server validieren Future tokenPruefen() async { if (_token == null) return false; try { final response = await http .get( Uri.parse('${AppConfig.authUrl}/verify'), headers: authHeader, ) .timeout(const Duration(seconds: 5)); return response.statusCode == 200; } catch (_) { return false; } } /// Ausloggen – Token löschen Future logout() async { _token = null; _user = ''; final prefs = await SharedPreferences.getInstance(); await prefs.remove('baka_token'); await prefs.remove('baka_user'); MeloLogger().aktion('auth_logout', {}); } } /// Ergebnis eines Auth-Versuchs class AuthResult { final bool erfolg; final String? fehler; const AuthResult._(this.erfolg, this.fehler); static const ok = AuthResult._(true, null); static AuthResult fehlgeschlagen(String msg) => AuthResult._(false, msg); }