feat: acquire legacy settings from approved network source

This commit is contained in:
codex
2026-08-06 13:56:54 +03:00
parent 0213b229a9
commit 97fbc1d746
6 changed files with 153 additions and 13 deletions
@@ -4,8 +4,11 @@ using Launcher.Next.Core;
namespace Launcher.Next.Application;
public sealed record LegacyPluginDefinition(string Id, string Renewal, string Catalog, string Install, int DefinitionOrder);
public sealed record LegacyProductPattern(string Name, string Filter, string Roster, IReadOnlyList<LegacyPluginDefinition> Plugins);
public sealed record LegacyPluginDefinition(string Id, string Renewal, string Catalog, string Install, int DefinitionOrder, IReadOnlyList<string>? Machines = null)
{
public bool IsAllowedOn(string machineName) => Machines is null || Machines.Count == 0 || Machines.Contains(machineName, StringComparer.OrdinalIgnoreCase);
}
public sealed record LegacyProductPattern(string Name, string Filter, string Roster, IReadOnlyList<LegacyPluginDefinition> Plugins, IReadOnlyList<string>? Attributes = null, IReadOnlyList<string>? Sources = null, string? Target = null, string? Loader = null, string? ObjectPath = null);
public sealed record LegacyLauncherSettings(string Help, string CareMarker, IReadOnlyList<LegacyProductPattern> Patterns);
public sealed record LegacyDownloadSelection(IReadOnlyList<string> PluginIds);
public sealed record LegacyCompatibilityMapping(string LegacyId, string PackageId, string Version, string Channel);
@@ -27,8 +30,12 @@ public sealed class LegacyConfigurationAdapter
plugin.Element("id")?.Value ?? throw new InvalidOperationException("LEGACY_PLUGIN_ID_MISSING"),
plugin.Element("renewal")?.Value ?? throw new InvalidOperationException("LEGACY_RENEWAL_MISSING"),
plugin.Element("catalog")?.Value ?? throw new InvalidOperationException("LEGACY_CATALOG_MISSING"),
plugin.Element("install")?.Value ?? throw new InvalidOperationException("LEGACY_INSTALL_MISSING"), index)).ToArray();
return new LegacyProductPattern(Value("name"), Value("filter"), Value("roster"), plugins);
plugin.Element("install")?.Value ?? throw new InvalidOperationException("LEGACY_INSTALL_MISSING"), index,
plugin.Elements("machine").Select(x => x.Value.Trim()).Where(x => x.Length > 0).ToArray())).ToArray();
return new LegacyProductPattern(Value("name"), Value("filter"), Value("roster"), plugins,
pattern.Elements("attrib").Select(x => x.Value).ToArray(),
pattern.Elements("source").Elements("course").Select(x => x.Value).ToArray(),
pattern.Element("target")?.Value, pattern.Element("loader")?.Value, pattern.Element("object")?.Value);
}).ToArray();
return new(path.Element("help")?.Value ?? throw new InvalidOperationException("LEGACY_HELP_MISSING"), path.Element("care")?.Value ?? throw new InvalidOperationException("LEGACY_CARE_MISSING"), patterns);
}
@@ -0,0 +1,61 @@
using System.Security.Cryptography;
namespace Launcher.Next.Application;
public sealed record LegacySettingsSnapshot(string SourceEndpoint, string SourceFingerprint, string Sha256, long Length, DateTimeOffset LastWriteUtc, DateTimeOffset AcquiredUtc, string LocalSnapshotPath, bool OfflineFallback);
public static class LegacyNetworkConfiguration
{
public const string ApprovedSettingsEndpoint = @"\\phimproject.com\common\BIM\save\All_Serv_Install\Settings.xml";
public const string LegacyBimAlias = @"N:\BIM";
public const string ApprovedBimEndpoint = @"\\phimproject.com\common\BIM";
public static string ResolveApprovedAlias(string legacyPath)
{
if (string.IsNullOrWhiteSpace(legacyPath)) throw new InvalidOperationException("LEGACY_NETWORK_PATH_INVALID");
var normalized = legacyPath.Replace('/', '\\');
if (string.Equals(normalized, LegacyBimAlias, StringComparison.OrdinalIgnoreCase)) return ApprovedBimEndpoint;
var prefix = LegacyBimAlias + "\\";
if (!normalized.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("LEGACY_NETWORK_ALIAS_NOT_APPROVED");
return ApprovedBimEndpoint + normalized[(prefix.Length - 1)..];
}
public static LegacySettingsSnapshot AcquireApprovedSnapshot(string endpoint, string localRoot)
{
if (!string.Equals(endpoint, ApprovedSettingsEndpoint, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("LEGACY_SETTINGS_ENDPOINT_NOT_APPROVED");
if (!Path.IsPathFullyQualified(endpoint) || !endpoint.StartsWith(@"\\", StringComparison.Ordinal) || endpoint.Contains('?') || endpoint.Contains('*')) throw new InvalidOperationException("LEGACY_SETTINGS_ENDPOINT_INVALID");
if (!File.Exists(endpoint) || File.GetAttributes(endpoint).HasFlag(FileAttributes.ReparsePoint)) return OfflineSnapshot(endpoint, localRoot);
var first = new FileInfo(endpoint);
var sourceHashBefore = Hash(endpoint);
var snapshots = Path.Combine(Path.GetFullPath(localRoot), "configuration-snapshots");
Directory.CreateDirectory(snapshots);
var temporary = Path.Combine(snapshots, Guid.NewGuid().ToString("N") + ".tmp");
File.Copy(endpoint, temporary, false);
var copiedHash = Hash(temporary);
var after = new FileInfo(endpoint);
var sourceHashAfter = Hash(endpoint);
if (first.Length != after.Length || first.LastWriteTimeUtc != after.LastWriteTimeUtc || !string.Equals(sourceHashBefore, sourceHashAfter, StringComparison.Ordinal) || !string.Equals(copiedHash, sourceHashAfter, StringComparison.Ordinal))
{
File.Delete(temporary);
throw new InvalidOperationException("LEGACY_SETTINGS_CHANGED_DURING_ACQUISITION");
}
var final = Path.Combine(snapshots, copiedHash + ".xml");
if (File.Exists(final)) File.Delete(temporary); else File.Move(temporary, final);
if (!string.Equals(Hash(final), copiedHash, StringComparison.Ordinal)) throw new InvalidOperationException("LEGACY_SETTINGS_SNAPSHOT_HASH_MISMATCH");
return new(endpoint, Fingerprint(endpoint, copiedHash), copiedHash, first.Length, new DateTimeOffset(first.LastWriteTimeUtc), DateTimeOffset.UtcNow, final, false);
}
private static LegacySettingsSnapshot OfflineSnapshot(string endpoint, string localRoot)
{
var snapshots = Path.Combine(Path.GetFullPath(localRoot), "configuration-snapshots");
if (!Directory.Exists(snapshots)) throw new InvalidOperationException("LEGACY_SETTINGS_NETWORK_UNAVAILABLE");
var candidate = Directory.GetFiles(snapshots, "*.xml").OrderByDescending(File.GetLastWriteTimeUtc).FirstOrDefault();
if (candidate is null) throw new InvalidOperationException("LEGACY_SETTINGS_NETWORK_UNAVAILABLE");
var info = new FileInfo(candidate);
var hash = Hash(candidate);
return new(endpoint, Fingerprint(endpoint, hash), hash, info.Length, new DateTimeOffset(info.LastWriteTimeUtc), DateTimeOffset.UtcNow, candidate, true);
}
private static string Hash(string path) { using var stream = File.OpenRead(path); return Convert.ToHexString(SHA256.HashData(stream)); }
private static string Fingerprint(string endpoint, string hash) => Convert.ToHexString(SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(endpoint + "|" + hash)));
}