License grace period: keep running when the master is down or rejects the key (LicenseGraceHours, default 72 h), start with the last confirmed license, auto-retry; panel restart button (port/panel settings without program restart, login kept); tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 20:02:05 +02:00
co-authored by Claude Opus 5.5
parent d4e5897b7f
commit 14db3ef4f4
10 changed files with 325 additions and 24 deletions
+47 -1
View File
@@ -155,7 +155,7 @@ public sealed class AdminPanel
app.StartAsync().GetAwaiter().GetResult();
if (users.Empty) CreateSetupCode();
if (users.Empty && SetupCode == null) CreateSetupCode();
if (SharesGamePort)
GameServer.Log($"Web panel: http://<server>:{config.Port}/{(basePath.Length > 0 ? basePath.TrimStart('/') + "/" : "")} (same port as the game)");
else
@@ -165,6 +165,30 @@ public sealed class AdminPanel
/// <summary>Neu gelesene Einstellungen (der Host liest server.json bei jedem Start neu).</summary>
public void UpdateConfig(ServerConfig newConfig) => config = newConfig;
/// <summary>
/// Mit neuen Einstellungen (Port, Unterpfad, Proxy ...) neu starten - Anmeldungen bleiben erhalten. Klappt es nicht
/// (z.B. Port belegt), läuft das Panel mit den bisherigen Einstellungen weiter.
/// </summary>
public bool RestartWeb(ServerConfig newConfig)
{
var previous = config;
Stop();
config = newConfig;
try
{
StartWeb();
GameServer.Log("Web panel restarted with the new settings");
return true;
}
catch (Exception e)
{
GameServer.Log("Web panel could not start with the new settings (" + e.Message + ") - back to the previous settings");
config = previous;
try { StartWeb(); } catch (Exception e2) { GameServer.Log("Web panel could not start: " + e2.Message); }
return false;
}
}
public void Stop()
{
try
@@ -498,6 +522,7 @@ public sealed class AdminPanel
"start" => Ok(host.RequestStart(by)),
"stop" => Ok(host.RequestStop(by)),
"restart" => Ok(host.RequestRestart(by)),
"restart-panel" => PanelRestart(ctx, by),
_ => Error(404, "Unknown action")
};
});
@@ -617,6 +642,27 @@ public sealed class AdminPanel
});
}
/// <summary>Panel neu starten (z.B. nach Port-Änderung). Antwortet zuerst - der Neustart beendet diese Verbindung.</summary>
IResult PanelRestart(HttpContext ctx, string by)
{
var fresh = host.LoadConfigForRestart();
if (fresh == null) return Error(500, "Could not read server.json");
if (!fresh.PanelEnabled) return Error(400, "The panel is turned off in the settings - turn it on again or edit server.json by hand");
bool shares = fresh.PanelPort <= 0 || fresh.PanelPort == fresh.Port;
int port = shares ? fresh.Port : fresh.PanelPort;
string basePath = (fresh.PanelBasePath ?? "").Trim().TrimEnd('/');
if (basePath.Length > 0 && !basePath.StartsWith('/')) basePath = "/" + basePath;
// Neue Adresse für den Browser (hinter einem Proxy bleibt sie gleich)
string url = fresh.PanelTrustProxy ? null : $"{ctx.Request.Scheme}://{ctx.Request.Host.Host}:{port}{basePath}/";
GameServer.Log($"[panel] {by} restarts the web panel");
_ = Task.Run(async () =>
{
await Task.Delay(700); // Antwort erst beim Browser ankommen lassen
host.RestartPanel(fresh, by);
});
return Results.Json(new { ok = true, message = "The panel restarts - reloading in a few seconds", url });
}
// ---------------------------------------------------------------- Hilfen
static string By(PanelUsers.User user) => user.Name + " (panel)";