using System.Net; using System.Net.Http.Json; using LehrerApp.Sync; namespace LehrerApp.Desktop.Services; public class SyncAuthException(string userMessage) : Exception(userMessage); public enum SyncConnectionTestResult { Ok, Unauthorized, IncompatibleVersion, Unreachable } /// /// Login/Verbindungstest gegen den eigenen Sync-Server (LehrerApp.Api) — getrennt vom bereits /// bestehenden (anderes Backend, anderer Wire-Vertrag). Nimmt die /// Server-URL je Aufruf entgegen statt fest im Konstruktor, weil sie über die Einstellungen zur /// Laufzeit geändert werden kann (anders als die feste KI-Backend-URL). /// public class SyncAuthService(HttpClient http) { /// /// Liefert neben dem Token auch die kanonische userId aus der Server-Antwort - LiteDBs /// case-insensitive Standard-Collation lässt einen Login mit abweichender /// Groß-/Kleinschreibung des Benutzernamens erfolgreich durch (siehe TODO 10.2.5); die /// zurückgegebene userId ist deshalb der einzige verlässliche Weg für den Aufrufer, einen /// Kontowechsel von einer bloßen Schreibweisen-Abweichung zu unterscheiden (siehe TODO 10.3.5). /// public async Task<(string Token, string UserId)> LoginAsync(string serverUrl, string username, string password) { HttpResponseMessage resp; try { using var req = SyncProtocol.CreateRequest(HttpMethod.Post, CombineUrl(serverUrl, "/api/auth/login")); req.Content = JsonContent.Create(new { username, password }); resp = await http.SendAsync(req); } catch (HttpRequestException) { throw new SyncAuthException( "Der Sync-Server ist nicht erreichbar. Bitte Adresse und Internetverbindung prüfen."); } if (resp.StatusCode == HttpStatusCode.Unauthorized) throw new SyncAuthException("Benutzername oder Passwort ist falsch."); if (!resp.IsSuccessStatusCode) throw new SyncAuthException("Anmeldung fehlgeschlagen. Bitte später erneut versuchen."); var result = await resp.Content.ReadFromJsonAsync(); return result is null ? throw new SyncAuthException("Unerwartete Antwort des Sync-Servers.") : (result.Token, result.UserId); } public async Task TestConnectionAsync(string serverUrl, string? token) { using var req = new HttpRequestMessage(HttpMethod.Get, CombineUrl(serverUrl, "/api/sync/status")); req.Headers.Add(SyncProtocol.VersionHeaderName, SyncProtocol.CurrentVersion); if (token is not null) req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); try { var resp = await http.SendAsync(req); if (resp.StatusCode == HttpStatusCode.Unauthorized) return SyncConnectionTestResult.Unauthorized; if (resp.StatusCode == HttpStatusCode.UpgradeRequired) return SyncConnectionTestResult.IncompatibleVersion; return resp.IsSuccessStatusCode ? SyncConnectionTestResult.Ok : SyncConnectionTestResult.Unreachable; } catch (HttpRequestException) { return SyncConnectionTestResult.Unreachable; } } private static string CombineUrl(string serverUrl, string path) => new Uri(new Uri(serverUrl), path).ToString(); private record LoginResult(string Token, string UserId); }