Module im WebUI aktivieren und deaktivieren
Umsetzung von #560. Die Ansicht "Moduleinstellungen" zeigte bisher nur an, ob ein Modul laeuft. Jetzt laesst sich dort wie im Modul-Tab des Desktops festlegen, welche Module beim naechsten Start geladen werden. POST /api/modules/{name}/enabled nimmt den gewuenschten Zustand ausdruecklich entgegen ({"enabled": true|false}) und schaltet nicht einfach um. Zwei Klicks aus zwei Fenstern heben sich damit nicht auf, und ein wiederholter Aufruf aendert nichts mehr. Fehlerfaelle: - Unbekanntes Modul: 404. - Modul mit Aktivierungs-Blocker: 409. - Fehlender Zustand: 400. Die Pruefung war zuerst lueckenhaft, der Test hat es gefunden: Mit "bool Enabled" machte ein leerer Rumpf {} daraus false, und ein unvollstaendiger Aufruf haette das Modul deaktiviert. Gemeinsame Quelle statt Kopien: - KnownModules: Die Modulliste stand viermal im Code (Program, CoreViews, ModuleViews, hartkodiert im WebUI). Der Start registriert jetzt die eine Liste. - ModuleActivationView: Die Statusregeln des Desktops ("Aktiv - stoppt nach Neustart" usw.) liegen im Core. Desktop und WebUI zeigen dasselbe. ServerSettingsFile - gefunden, bevor das WebUI in die Datei schreiben durfte: ServerSettings.Load verschluckt jeden Lesefehler und liefert Vorgaben. "Frisch lesen, ein Feld aendern, speichern" haette eine gesperrte oder beschaedigte server_settings.xml durch Vorgaben ersetzt, und mit ihr Lizenzschluessel, Watchdog-Token, Mullvad-Account und Alchemy-Key. Genau so schrieb auch der Modul-Schalter des Desktops. ServerSettingsFile: - liest streng und schreibt bei unlesbarer Datei nichts, - schreibt atomar ueber eine Nachbardatei, - erhaelt unter Linux die Dateirechte, - serialisiert Aenderungen prozessweit. Der Desktop-Schalter benutzt ihn jetzt ebenfalls. Tests: Lizenzschluessel, Token und Alchemy-Key bleiben beim Umschalten in der Datei; eine kaputte Datei bleibt Byte fuer Byte unveraendert und die Anzeige bietet dann kein Umschalten an; Blocker, 404, 400, Idempotenz; die Statustabelle; strenges Lesen, atomares Schreiben und Rechte am Dateiobjekt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using PolyTrader.Core.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
@@ -248,34 +249,11 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
_modules.Clear();
|
||||
foreach (var m in _moduleInfos)
|
||||
{
|
||||
bool blocked = !string.IsNullOrEmpty(m.Blocker);
|
||||
bool desiredEnabled = !disabled.Contains(m.Name);
|
||||
|
||||
string status, hint, action;
|
||||
if (blocked)
|
||||
{
|
||||
status = "⚠ Nicht aktivierbar"; hint = m.Blocker!; action = "—";
|
||||
}
|
||||
else if (m.IsRunning && desiredEnabled)
|
||||
{
|
||||
status = "Aktiv"; hint = ""; action = "Deaktivieren";
|
||||
}
|
||||
else if (m.IsRunning && !desiredEnabled)
|
||||
{
|
||||
status = "Aktiv – stoppt nach Neustart"; hint = "Änderung greift nach Neustart"; action = "Aktivieren";
|
||||
}
|
||||
else if (!m.IsRunning && desiredEnabled)
|
||||
{
|
||||
status = "Startet nach Neustart"; hint = "Änderung greift nach Neustart"; action = "Deaktivieren";
|
||||
}
|
||||
else
|
||||
{
|
||||
status = "Deaktiviert"; hint = ""; action = "Aktivieren";
|
||||
}
|
||||
|
||||
// #560: dieselben Regeln wie im WebUI, an einer Stelle (ModuleActivationView).
|
||||
var view = ModuleActivationView.Describe(m, desiredEnabled: !disabled.Contains(m.Name));
|
||||
_modules.Add(new ModuleRow
|
||||
{
|
||||
Modul = m.Name, Status = status, Hinweis = hint, Aktion = action, CanToggle = !blocked
|
||||
Modul = view.Name, Status = view.Status, Hinweis = view.Hint, Aktion = view.Action, CanToggle = view.CanToggle
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -290,15 +268,15 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
|
||||
try
|
||||
{
|
||||
// Frisch aus der Datei lesen, damit parallele Änderungen nicht überschrieben werden.
|
||||
var settings = ServerSettings.Load(_settingsPath!);
|
||||
var set = new HashSet<string>(settings.DisabledModules, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (set.Contains(info.Name)) set.Remove(info.Name);
|
||||
else set.Add(info.Name);
|
||||
|
||||
settings.DisabledModules = set.OrderBy(x => x).ToList();
|
||||
settings.Save(_settingsPath!);
|
||||
// Frisch lesen, aendern, atomar schreiben (#560). Vorher ServerSettings.Load/Save: Load
|
||||
// liefert bei einer unlesbaren Datei still Vorgaben, das anschliessende Save haette damit
|
||||
// Lizenzschluessel und Watchdog-Token ueberschrieben.
|
||||
new ServerSettingsFile(_settingsPath!).Update(settings =>
|
||||
{
|
||||
var set = new HashSet<string>(settings.DisabledModules, StringComparer.OrdinalIgnoreCase);
|
||||
if (!set.Remove(info.Name)) set.Add(info.Name);
|
||||
settings.DisabledModules = set.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
});
|
||||
|
||||
PopulateModules();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user