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:
@@ -164,6 +164,15 @@ public partial class SettingsViewModel : ObservableObject
|
||||
[ObservableProperty] private bool _aiIsLoggedIn;
|
||||
[ObservableProperty] private string _aiBalanceDisplay = "";
|
||||
|
||||
// ── Synchronisation (Kapitel 10) ──────────────────────────────────────────
|
||||
|
||||
[ObservableProperty] private string _syncServerUrl = "";
|
||||
[ObservableProperty] private string _syncUsername = "";
|
||||
[ObservableProperty] private string _syncPassword = "";
|
||||
[ObservableProperty] private string _syncLoginError = "";
|
||||
[ObservableProperty] private bool _syncIsLoggedIn;
|
||||
[ObservableProperty] private string _syncConnectionStatus = "";
|
||||
|
||||
// ── Konstruktor ───────────────────────────────────────────────────────────
|
||||
|
||||
private readonly ISchoolHolidayRepository _schoolHolidays;
|
||||
@@ -172,6 +181,8 @@ public partial class SettingsViewModel : ObservableObject
|
||||
private readonly ISupervisionDutyRepository _supervisionDuties;
|
||||
private readonly AiSettingsService _aiSettings;
|
||||
private readonly AiPlanningService _aiPlanning;
|
||||
private readonly SyncSettingsService _syncSettings;
|
||||
private readonly SyncAuthService _syncAuth;
|
||||
private readonly CompetencyCatalogImportService _catalogImport;
|
||||
|
||||
public SettingsViewModel(ISubjectRepository subjects, ICompetencyDomainRepository domainRepo,
|
||||
@@ -182,7 +193,8 @@ public partial class SettingsViewModel : ObservableObject
|
||||
IShorthandCodeRepository shorthandCodes, ISchoolHolidayRepository schoolHolidays,
|
||||
SchoolCalendarSettingsService calendarSettings, PeriodScheduleService periodSchedule,
|
||||
ISupervisionDutyRepository supervisionDuties, LetterTemplateService letterTemplates,
|
||||
AiSettingsService aiSettings, AiPlanningService aiPlanning)
|
||||
AiSettingsService aiSettings, AiPlanningService aiPlanning,
|
||||
SyncSettingsService syncSettings, SyncAuthService syncAuth)
|
||||
{
|
||||
_subjects = subjects;
|
||||
_domainRepo = domainRepo;
|
||||
@@ -204,6 +216,8 @@ public partial class SettingsViewModel : ObservableObject
|
||||
_letterTemplates = letterTemplates;
|
||||
_aiSettings = aiSettings;
|
||||
_aiPlanning = aiPlanning;
|
||||
_syncSettings = syncSettings;
|
||||
_syncAuth = syncAuth;
|
||||
_catalogImport = new CompetencyCatalogImportService(domainRepo);
|
||||
LoadSubjects();
|
||||
LoadShorthandCodes();
|
||||
@@ -221,6 +235,7 @@ public partial class SettingsViewModel : ObservableObject
|
||||
LoadSupervisionDuties();
|
||||
LoadLetterTemplates();
|
||||
LoadAiSettings();
|
||||
LoadSyncSettings();
|
||||
}
|
||||
|
||||
// ── Word-Briefvorlagen: Import und Validierung ──────────────────────────
|
||||
@@ -362,6 +377,65 @@ public partial class SettingsViewModel : ObservableObject
|
||||
AiBalanceDisplay = "";
|
||||
}
|
||||
|
||||
// ── Synchronisation: Laden / Anmelden / Abmelden / Verbindungstest ───────
|
||||
//
|
||||
// Server-URL, Zugangsdaten und Token werden erst nach erfolgreichem Login zusammen
|
||||
// gespeichert (ein Restart deckt beides ab) — SyncEngine/SnapshotService werden nur einmalig
|
||||
// beim Start registriert (siehe AppBootstrapper), es gibt keinen Live-Re-Registrierungspfad.
|
||||
|
||||
private void LoadSyncSettings()
|
||||
{
|
||||
SyncServerUrl = _syncSettings.ServerUrl;
|
||||
SyncUsername = _syncSettings.Username;
|
||||
SyncIsLoggedIn = _syncSettings.IsLoggedIn;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SyncTestConnection()
|
||||
{
|
||||
SyncConnectionStatus = "Teste Verbindung…";
|
||||
if (string.IsNullOrWhiteSpace(SyncServerUrl))
|
||||
{
|
||||
SyncConnectionStatus = "Bitte Server-Adresse eingeben.";
|
||||
return;
|
||||
}
|
||||
var result = await _syncAuth.TestConnectionAsync(SyncServerUrl, _syncSettings.GetToken());
|
||||
SyncConnectionStatus = result switch
|
||||
{
|
||||
SyncConnectionTestResult.Ok => "Verbindung erfolgreich.",
|
||||
SyncConnectionTestResult.Unauthorized => "Server erreichbar, aber nicht angemeldet oder Anmeldung abgelaufen.",
|
||||
_ => "Server nicht erreichbar. Bitte Adresse und Internetverbindung prüfen.",
|
||||
};
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SyncLogin()
|
||||
{
|
||||
SyncLoginError = "";
|
||||
var valid = true;
|
||||
if (string.IsNullOrWhiteSpace(SyncServerUrl)) { SyncLoginError = "Server-Adresse erforderlich."; valid = false; }
|
||||
if (string.IsNullOrWhiteSpace(SyncUsername)) { SyncLoginError = "Benutzername erforderlich."; valid = false; }
|
||||
if (string.IsNullOrWhiteSpace(SyncPassword)) { SyncLoginError = "Passwort erforderlich."; valid = false; }
|
||||
if (!valid) return;
|
||||
|
||||
try
|
||||
{
|
||||
var token = await _syncAuth.LoginAsync(SyncServerUrl, SyncUsername, SyncPassword);
|
||||
_syncSettings.SetServerUrl(SyncServerUrl);
|
||||
_syncSettings.SetCredentialsAndToken(SyncUsername, token);
|
||||
SyncPassword = "";
|
||||
AppBootstrapper.RestartApplication();
|
||||
}
|
||||
catch (SyncAuthException ex) { SyncLoginError = ex.Message; }
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SyncLogout()
|
||||
{
|
||||
_syncSettings.Logout();
|
||||
AppBootstrapper.RestartApplication();
|
||||
}
|
||||
|
||||
// ── Stundenraster: Laden / Speichern ─────────────────────────────────────
|
||||
|
||||
private void LoadPeriodTimes()
|
||||
|
||||
Reference in New Issue
Block a user