Build / build (push) Successful in 37s
Nach erfolgreicher Verbindung wird Host/Benutzername/Modellname lokal gemerkt (SavedRoutersStore, JSON unter %AppData%), mit editierbarem Namen und Standort-Feld. Das Passwort liegt separat im Windows- Anmeldeinformationsspeicher (WindowsCredentialStore, P/Invoke auf advapi32.dll) statt im Klartext -- bewusst in App statt Core, da Core plattformneutral bleibt. Da viele MikroTik-Geraete werksseitig dieselbe Standardadresse nutzen (192.168.88.1/admin), waere Host+Benutzername allein nicht eindeutig: RouterDevice liest jetzt zusaetzlich die Seriennummer aus (/system/routerboard), SavedRoutersStore legt bei Host+Benutzername- Treffer mit unterschiedlicher Seriennummer einen eigenen Eintrag an statt den bestehenden zu ueberschreiben. Ansatz 1:1 von der parallel entwickelten Mac-Version uebernommen (dort M26/M27), vom Nutzer live bestaetigt. README zusaetzlich um den auf dem mac-version-merge-Branch liegen gebliebenen Ablaufplan/M-*-Milestone-Katalog ergaenzt, der bisher nicht in main uebernommen war, und auf den aktuellen Stand gebracht (v1.2, CI/CD, M-Bekannte-Router als abgeschlossen markiert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
145 lines
4.5 KiB
C#
145 lines
4.5 KiB
C#
using System.Collections.ObjectModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using MikrotikInstaller.App.Credentials;
|
|
using MikrotikInstaller.Core.Connectivity;
|
|
using MikrotikInstaller.Core.Devices;
|
|
|
|
namespace MikrotikInstaller.App.ViewModels.Steps;
|
|
|
|
/// <summary>
|
|
/// Erster Schritt: Verbindungsdaten zum MikroTik-Gerät erfassen und testen.
|
|
/// Erst nach einer erfolgreich getesteten Verbindung darf im Assistenten weitergegangen werden.
|
|
/// "Bekannte Router" (zuvor erfolgreich verbundene Geräte) werden erst im nächsten Schritt
|
|
/// (Geräte-Erkennung) tatsächlich gespeichert/aktualisiert — dort sind Modell und Seriennummer für
|
|
/// den Namensvorschlag bzw. die Unterscheidung zweier Geräte mit gleichem Host+Benutzername bekannt.
|
|
/// </summary>
|
|
public partial class ConnectionStepViewModel : WizardStepViewModelBase
|
|
{
|
|
private readonly WizardSession _session;
|
|
private readonly SavedRoutersStore _savedRoutersStore = new();
|
|
|
|
public ConnectionStepViewModel(WizardSession session)
|
|
: base(
|
|
title: "Verbindung",
|
|
description: "Gib die Adresse deines MikroTik-Geräts sowie Benutzername und Passwort ein.")
|
|
{
|
|
_session = session;
|
|
RefreshSavedRouters();
|
|
}
|
|
|
|
public ObservableCollection<SavedRouterEntry> SavedRouters { get; } = [];
|
|
|
|
public bool HasSavedRouters => SavedRouters.Count > 0;
|
|
|
|
[ObservableProperty]
|
|
private string _host = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _username = "admin";
|
|
|
|
[ObservableProperty]
|
|
private string _password = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private bool _useEncryption;
|
|
|
|
[ObservableProperty]
|
|
private bool _isTesting;
|
|
|
|
[ObservableProperty]
|
|
private string? _statusMessage;
|
|
|
|
[ObservableProperty]
|
|
private bool _statusIsError;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(CanGoNext))]
|
|
private bool _isConnected;
|
|
|
|
public override bool CanGoNext => IsConnected;
|
|
|
|
private bool CanTestConnection => !IsTesting && !string.IsNullOrWhiteSpace(Host) && !string.IsNullOrWhiteSpace(Username);
|
|
|
|
public override Task OnActivatedAsync()
|
|
{
|
|
RefreshSavedRouters();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private void RefreshSavedRouters()
|
|
{
|
|
SavedRouters.Clear();
|
|
var routers = _savedRoutersStore.Load().OrderByDescending(r => r.LastConnectedAt);
|
|
|
|
foreach (var router in routers)
|
|
{
|
|
var entry = new SavedRouterEntry(router);
|
|
entry.PropertyChanged += (_, _) => _savedRoutersStore.Update(entry.ToSavedRouter());
|
|
SavedRouters.Add(entry);
|
|
}
|
|
|
|
OnPropertyChanged(nameof(HasSavedRouters));
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void UseSavedRouter(SavedRouterEntry entry)
|
|
{
|
|
Host = entry.Host;
|
|
Username = entry.Username;
|
|
Password = WindowsCredentialStore.TryRead(entry.Id) ?? string.Empty;
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void RemoveSavedRouter(SavedRouterEntry entry)
|
|
{
|
|
_savedRoutersStore.Remove(entry.Id);
|
|
WindowsCredentialStore.Delete(entry.Id);
|
|
SavedRouters.Remove(entry);
|
|
OnPropertyChanged(nameof(HasSavedRouters));
|
|
}
|
|
|
|
[RelayCommand(CanExecute = nameof(CanTestConnection))]
|
|
private async Task TestConnectionAsync()
|
|
{
|
|
IsTesting = true;
|
|
StatusMessage = "Verbindung wird getestet …";
|
|
StatusIsError = false;
|
|
IsConnected = false;
|
|
|
|
await _session.DisposeClientAsync();
|
|
_session.Device = null;
|
|
|
|
try
|
|
{
|
|
var credentials = new RouterOsCredentials(Host.Trim(), Username.Trim(), Password)
|
|
{
|
|
UseEncryption = UseEncryption,
|
|
};
|
|
|
|
_session.Client = await RouterOsClientFactory.ConnectAsync(credentials);
|
|
_session.Credentials = credentials;
|
|
|
|
IsConnected = true;
|
|
StatusIsError = false;
|
|
StatusMessage = "Verbindung erfolgreich hergestellt.";
|
|
}
|
|
catch (RouterOsException ex)
|
|
{
|
|
IsConnected = false;
|
|
StatusIsError = true;
|
|
StatusMessage = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
IsTesting = false;
|
|
}
|
|
}
|
|
|
|
partial void OnHostChanged(string value) => TestConnectionCommand.NotifyCanExecuteChanged();
|
|
|
|
partial void OnUsernameChanged(string value) => TestConnectionCommand.NotifyCanExecuteChanged();
|
|
|
|
partial void OnIsTestingChanged(bool value) => TestConnectionCommand.NotifyCanExecuteChanged();
|
|
}
|