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:
@@ -0,0 +1,55 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MSCO.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Letzte erfolgreiche Lizenzprüfung beim Master (data/license.json). Ist der Master mal nicht erreichbar oder lehnt
|
||||
/// den Schlüssel kurz ab (z.B. nach einem Umzug ohne keys.json), läuft bzw. startet der Server damit weiter - bis
|
||||
/// LicenseGraceHours ohne Bestätigung vergangen sind. Gespeichert wird nur ein Hash des Schlüssels.
|
||||
/// </summary>
|
||||
public static class LicenseCache
|
||||
{
|
||||
public sealed class Entry
|
||||
{
|
||||
public string KeyHash { get; set; }
|
||||
public string Owner { get; set; }
|
||||
public string Label { get; set; }
|
||||
public DateTime LastConfirmedUtc { get; set; }
|
||||
}
|
||||
|
||||
static string PathOf(string dataDir) => Path.Combine(dataDir, "license.json");
|
||||
|
||||
static string Hash(string key) =>
|
||||
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(key ?? ""))).ToLowerInvariant();
|
||||
|
||||
/// <summary>Gemerkte Bestätigung für genau diesen Schlüssel (null = keine).</summary>
|
||||
public static Entry Load(string dataDir, string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = PathOf(dataDir);
|
||||
if (!File.Exists(path)) return null;
|
||||
var entry = JsonSerializer.Deserialize<Entry>(File.ReadAllText(path));
|
||||
return entry != null && entry.KeyHash == Hash(key) ? entry : null;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
public static void Save(string dataDir, string key, string owner, string label, DateTime confirmedUtc)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entry = new Entry { KeyHash = Hash(key), Owner = owner, Label = label, LastConfirmedUtc = confirmedUtc };
|
||||
string path = PathOf(dataDir), tmp = path + ".tmp";
|
||||
File.WriteAllText(tmp, JsonSerializer.Serialize(entry, new JsonSerializerOptions { WriteIndented = true }));
|
||||
File.Move(tmp, path, true);
|
||||
}
|
||||
catch (Exception e) { GameServer.Log("Could not remember the license check: " + e.Message); }
|
||||
}
|
||||
|
||||
/// <summary>"3 h", "2 d 4 h" ...</summary>
|
||||
public static string Span(TimeSpan t) =>
|
||||
t.TotalHours < 1 ? $"{Math.Max(1, (int)t.TotalMinutes)} min" : t.TotalDays < 1 ? $"{(int)t.TotalHours} h" : $"{(int)t.TotalDays} d {t.Hours} h";
|
||||
}
|
||||
Reference in New Issue
Block a user