v2.35 — Vollständiges Sync-System: Playlisten, Favoriten, Auto-Sync, Persistent Login, Benutzerdefinierte Namen
## Server (melo_cloud.py v3.0.0) - Neue DB-Tabellen: user_playlists, user_playlist_songs, user_favorites, user_history, sync_meta - Playlist-Endpunkte: GET/POST /api/cloud/playlists, GET /api/cloud/playlists/<id>, POST songs, DELETE songs, PUT positions - Favoriten-Endpunkte: GET/POST /api/cloud/favorites, POST /api/cloud/favorites/toggle - History-Endpunkte: GET/POST /api/cloud/history (mit Dedup) - Rename-Endpunkt: POST /api/cloud/rename (custom_title/custom_artist) - Sync-Endpunkte: POST /api/cloud/sync-all, GET /api/cloud/sync-status - delete-all erweitert um Playlists + Favorites + History ## App - db_helper.dart: Migration v4→v5 (cloud_id, cloud_title, cloud_artist, server_favorites, sync_metadata) - auth_service.dart: Token-Validierung beim Start (online check + Offline-Fallback), Persistent Login - cloud_service.dart: Neue Methoden für Playlists (CRUD), Favorites (get/sync/toggle), Rename, History, syncAll/syncStatus - cloud_screen.dart: Komplett-Rewrite mit Sync-Modus (Manuell/Auto), Intervall-Auswahl, Sync-Fortschritt-Anzeige, Playlist-Verwaltung, Favoriten-Anzeige + Umbenennen, Sync-Info-Karte, nächster Sync - main.dart: Auto-Sync beim App-Start (nur wenn konfiguriert + fällig) ## Flutter Analyze - 0 neue Issues — nur 7 pre-existing
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
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
|
||||
/// Baka-Auth Service – JWT-basierte Authentifizierung (v3 — Persistent Login)
|
||||
/// Integriert mit https://baka-net.de/auth
|
||||
class AuthService {
|
||||
static final AuthService _instance = AuthService._();
|
||||
@@ -31,24 +30,53 @@ class AuthService {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/// Lädt gespeicherten Token beim App-Start
|
||||
Future<void> initialisieren() async {
|
||||
if (_initialisiert) return;
|
||||
/// Lädt gespeicherten Token beim App-Start und validiert ihn
|
||||
Future<bool> initialisieren() async {
|
||||
if (_initialisiert) return istEingeloggt;
|
||||
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});
|
||||
// Token beim Server validieren (nicht blind vertrauen)
|
||||
final valid = await _tokenOnlinePruefen();
|
||||
if (!valid) {
|
||||
// Token ist abgelaufen — nicht als eingeloggt behandeln
|
||||
MeloLogger().zustand('auth_token_expired', {'user': _user});
|
||||
_token = null;
|
||||
_user = '';
|
||||
await prefs.remove('baka_token');
|
||||
await prefs.remove('baka_user');
|
||||
} else {
|
||||
MeloLogger().zustand('auth_restored', {'user': _user});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
MeloLogger().fehler('auth_init', e);
|
||||
}
|
||||
_initialisiert = true;
|
||||
return istEingeloggt;
|
||||
}
|
||||
|
||||
/// Prüft Token beim Baka-Auth-Server (online)
|
||||
Future<bool> _tokenOnlinePruefen() 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 (_) {
|
||||
// Bei Netzwerkfehler: Token lokal akzeptieren (Offline-Modus)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Login über Baka-Auth-Server
|
||||
/// Gibt true zurück bei Erfolg, false bei Fehler
|
||||
Future<AuthResult> login(String user, String password) async {
|
||||
try {
|
||||
final response = await http
|
||||
@@ -74,14 +102,13 @@ class AuthService {
|
||||
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';
|
||||
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) {
|
||||
@@ -92,7 +119,8 @@ class AuthService {
|
||||
}
|
||||
|
||||
/// Registrierung über Baka-Auth-Server
|
||||
Future<AuthResult> registrieren(String user, String password, String email) async {
|
||||
Future<AuthResult> registrieren(
|
||||
String user, String password, String email) async {
|
||||
try {
|
||||
final response = await http
|
||||
.post(
|
||||
@@ -115,7 +143,6 @@ class AuthService {
|
||||
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;
|
||||
}
|
||||
@@ -126,27 +153,15 @@ class AuthService {
|
||||
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
|
||||
/// Token beim Server validieren (public)
|
||||
Future<bool> 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;
|
||||
}
|
||||
return _tokenOnlinePruefen();
|
||||
}
|
||||
|
||||
/// Ausloggen – Token löschen
|
||||
|
||||
Reference in New Issue
Block a user