Files
Mikrotik-Installer/src/MikrotikInstaller.App/ViewModels/Steps/ConnectionStepViewModel.cs
T
adminandClaude Sonnet 5 b7af5175dd
Build / build (push) Successful in 37s
Bekannte Router: gespeicherte Verbindungen im Verbindungs-Schritt
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>
2026-09-16 21:08:01 +02:00

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();
}