Baustein 8: Settings-Tab "Synchronisation" (Kapitel 10)
Neuer Tab in den Einstellungen: Server-URL, Login, Verbindungstest, Logout. - Neu SyncSettingsService (Muster AiSettingsService): Token AES-256-verschluesselt ueber SyncCrypto mit eigenem, rein lokalem Schluessel - ersetzt die bisherigen Klartext-Helfer AppBootstrapper.LoadServerUrl/SaveServerUrl und die unverschluesselte auth.token-Datei - Neu SyncAuthService fuer den Login-HTTP-Aufruf gegen /api/auth/login und den Verbindungstest (unterscheidet erreichbar & angemeldet / erreichbar aber nicht angemeldet / nicht erreichbar) - Speichern/Anmelden startet die App neu (AppBootstrapper. RestartApplication, gleiches Muster wie bei DB-Passwort/AppLock- Aenderungen) - SyncEngine/SnapshotService werden nur einmalig beim Start registriert, es gibt keinen Live-Re-Registrierungspfad Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace LehrerApp.Desktop.Services;
|
||||
|
||||
public class SyncAuthException(string userMessage) : Exception(userMessage);
|
||||
|
||||
public enum SyncConnectionTestResult { Ok, Unauthorized, Unreachable }
|
||||
|
||||
/// <summary>
|
||||
/// Login/Verbindungstest gegen den eigenen Sync-Server (LehrerApp.Api) — getrennt vom bereits
|
||||
/// bestehenden <see cref="AiPlanningService"/> (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).
|
||||
/// </summary>
|
||||
public class SyncAuthService(HttpClient http)
|
||||
{
|
||||
public async Task<string> LoginAsync(string serverUrl, string username, string password)
|
||||
{
|
||||
HttpResponseMessage resp;
|
||||
try
|
||||
{
|
||||
resp = await http.PostAsJsonAsync(CombineUrl(serverUrl, "/api/auth/login"), new { username, password });
|
||||
}
|
||||
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<LoginResult>();
|
||||
return result?.Token ?? throw new SyncAuthException("Unerwartete Antwort des Sync-Servers.");
|
||||
}
|
||||
|
||||
public async Task<SyncConnectionTestResult> TestConnectionAsync(string serverUrl, string? token)
|
||||
{
|
||||
using var req = new HttpRequestMessage(HttpMethod.Get, CombineUrl(serverUrl, "/api/sync/status"));
|
||||
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;
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user