From 4965d2019bf8bfbaa75ea8d774ba3d96e85c7ae4 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Sun, 24 May 2026 18:51:58 -0500 Subject: [PATCH] Fix redistributable/tool importing --- LANCommander.SDK/Enums/ManifestType.cs | 9 +++ .../ImportContext.cs | 68 +++++++++++-------- .../Importers/ArchiveImporter.cs | 3 +- .../Importers/RedistributableImporter.cs | 19 +++++- .../Importers/ToolImporter.cs | 18 ++++- .../Models/ImportDialogOptions.cs | 3 + .../UI/Components/ImportUploadDialog.razor | 6 +- .../UI/Pages/Games/Index.razor | 3 +- .../UI/Pages/Redistributables/Index.razor | 4 +- .../UI/Pages/Servers/Index.razor | 4 +- .../UI/Pages/Tools/Index.razor | 4 +- 11 files changed, 98 insertions(+), 43 deletions(-) create mode 100644 LANCommander.SDK/Enums/ManifestType.cs diff --git a/LANCommander.SDK/Enums/ManifestType.cs b/LANCommander.SDK/Enums/ManifestType.cs new file mode 100644 index 00000000..2b60fb34 --- /dev/null +++ b/LANCommander.SDK/Enums/ManifestType.cs @@ -0,0 +1,9 @@ +namespace LANCommander.SDK.Enums; + +public enum ManifestType +{ + Game, + Redistributable, + Server, + Tool, +} diff --git a/LANCommander.Server.ImportExport/ImportContext.cs b/LANCommander.Server.ImportExport/ImportContext.cs index e1e46d94..c8fc870f 100644 --- a/LANCommander.Server.ImportExport/ImportContext.cs +++ b/LANCommander.Server.ImportExport/ImportContext.cs @@ -38,8 +38,6 @@ public class ImportContext : IDisposable private readonly StorageLocationService _storageLocationService; private readonly ILogger _logger; - private string? _uploadedArchivePath; - #region Importers private readonly ActionImporter _actions; private readonly ArchiveImporter _archives; @@ -103,10 +101,8 @@ public class ImportContext : IDisposable public void SetId(Guid id) => Id = id; - public void TrackUploadedArchive(string path) => _uploadedArchivePath = path; - #region Initialize Import - public async Task> InitializeImportAsync(string archivePath) + public async Task> InitializeImportAsync(string archivePath, ManifestType? manifestType = null) { _actions.UseContext(this); _archives.UseContext(this); @@ -131,18 +127,18 @@ public class ImportContext : IDisposable _servers.UseContext(this); _tags.UseContext(this); _tools.UseContext(this); - + Archive = ZipArchive.OpenArchive(archivePath, new ReaderOptions()); var manifestEntry = Archive.Entries.FirstOrDefault(e => e.Key == ManifestHelper.ManifestFilename); - + // Legacy purposes if (manifestEntry == null) manifestEntry = Archive.Entries.FirstOrDefault(e => e.Key == "_manifest.yml"); if (manifestEntry == null) throw new InvalidOperationException("Invalid import file, cannot load manifest"); - + using (var reader = new StreamReader(manifestEntry.OpenEntryStream())) { var manifestContents = await reader.ReadToEndAsync(); @@ -155,22 +151,50 @@ public class ImportContext : IDisposable return await InitializeLegacyGameImportAsync(legacyGameManifest); } - if (ManifestHelper.TryDeserialize(manifestContents, out var gameManifest)) + if (manifestType.HasValue) + return await InitializeByTypeAsync(manifestType.Value, manifestContents); + + if (ManifestHelper.TryDeserialize(manifestContents, out var gameManifest) && !String.IsNullOrWhiteSpace(gameManifest.Title)) return await InitializeGameImportAsync(gameManifest); - - if (ManifestHelper.TryDeserialize(manifestContents, out var redistributableManifest)) + + if (ManifestHelper.TryDeserialize(manifestContents, out var redistributableManifest) && !String.IsNullOrWhiteSpace(redistributableManifest.Name)) return await InitializeRedistributableImportAsync(redistributableManifest); - - if (ManifestHelper.TryDeserialize(manifestContents, out var serverManifest)) + + if (ManifestHelper.TryDeserialize(manifestContents, out var serverManifest) && !String.IsNullOrWhiteSpace(serverManifest.Name)) return await InitializeServerImportAsync(serverManifest); - - if (ManifestHelper.TryDeserialize(manifestContents, out var toolManifest)) + + if (ManifestHelper.TryDeserialize(manifestContents, out var toolManifest) && !String.IsNullOrWhiteSpace(toolManifest.Name)) return await InitializeToolImportAsync(toolManifest); - + throw new InvalidOperationException("Unknown manifest file"); } } + private async Task> InitializeByTypeAsync(ManifestType manifestType, string manifestContents) + { + switch (manifestType) + { + case ManifestType.Game: + var gameManifest = ManifestHelper.Deserialize(manifestContents); + return await InitializeGameImportAsync(gameManifest); + + case ManifestType.Redistributable: + var redistributableManifest = ManifestHelper.Deserialize(manifestContents); + return await InitializeRedistributableImportAsync(redistributableManifest); + + case ManifestType.Server: + var serverManifest = ManifestHelper.Deserialize(manifestContents); + return await InitializeServerImportAsync(serverManifest); + + case ManifestType.Tool: + var toolManifest = ManifestHelper.Deserialize(manifestContents); + return await InitializeToolImportAsync(toolManifest); + + default: + throw new InvalidOperationException($"Unsupported manifest type: {manifestType}"); + } + } + public async Task InitializeMetadataUpdateAsync(SDK.Models.Manifest.Game gameManifest) { _developers.UseContext(this); @@ -553,17 +577,5 @@ public class ImportContext : IDisposable { if (Archive != null) Archive.Dispose(); - - if (_uploadedArchivePath != null && File.Exists(_uploadedArchivePath)) - { - try - { - File.Delete(_uploadedArchivePath); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Could not delete uploaded import archive at {Path}", _uploadedArchivePath); - } - } } } \ No newline at end of file diff --git a/LANCommander.Server.ImportExport/Importers/ArchiveImporter.cs b/LANCommander.Server.ImportExport/Importers/ArchiveImporter.cs index 2e4c5166..44556623 100644 --- a/LANCommander.Server.ImportExport/Importers/ArchiveImporter.cs +++ b/LANCommander.Server.ImportExport/Importers/ArchiveImporter.cs @@ -173,7 +173,8 @@ public class ArchiveImporter( if (archiveEntry == null) return false; - await archiveService.WriteToFileAsync(archive, archiveEntry.OpenEntryStream()); + using var entryStream = archiveEntry.OpenEntryStream(); + await archiveService.WriteToFileAsync(archive, entryStream); return true; } diff --git a/LANCommander.Server.ImportExport/Importers/RedistributableImporter.cs b/LANCommander.Server.ImportExport/Importers/RedistributableImporter.cs index c022f042..99638a8a 100644 --- a/LANCommander.Server.ImportExport/Importers/RedistributableImporter.cs +++ b/LANCommander.Server.ImportExport/Importers/RedistributableImporter.cs @@ -1,4 +1,3 @@ -using AutoMapper; using LANCommander.SDK.Enums; using LANCommander.SDK.Models.Manifest; using LANCommander.Server.ImportExport.Models; @@ -9,7 +8,6 @@ namespace LANCommander.Server.ImportExport.Importers; public class RedistributableImporter( ILogger logger, - IMapper mapper, RedistributableService redistributableService, UserService userService) : BaseImporter { @@ -28,7 +26,22 @@ public class RedistributableImporter( public override async Task AddAsync(Redistributable record) { - var redistributable = mapper.Map(record); + var redistributable = new Data.Models.Redistributable + { + Id = record.Id, + Name = record.Name, + Description = record.Description, + Notes = record.Notes, + OptionSchema = record.OptionSchema, + CreatedOn = record.CreatedOn, + UpdatedOn = record.UpdatedOn, + }; + + if (!String.IsNullOrWhiteSpace(record.CreatedBy)) + redistributable.CreatedBy = await userService.GetAsync(record.CreatedBy); + + if (!String.IsNullOrWhiteSpace(record.UpdatedBy)) + redistributable.UpdatedBy = await userService.GetAsync(record.UpdatedBy); try { diff --git a/LANCommander.Server.ImportExport/Importers/ToolImporter.cs b/LANCommander.Server.ImportExport/Importers/ToolImporter.cs index 716bb706..a3d2145b 100644 --- a/LANCommander.Server.ImportExport/Importers/ToolImporter.cs +++ b/LANCommander.Server.ImportExport/Importers/ToolImporter.cs @@ -1,4 +1,3 @@ -using AutoMapper; using LANCommander.SDK.Enums; using LANCommander.SDK.Models.Manifest; using LANCommander.Server.ImportExport.Models; @@ -9,7 +8,6 @@ namespace LANCommander.Server.ImportExport.Importers; public class ToolImporter( ILogger logger, - IMapper mapper, ToolService toolService, UserService userService) : BaseImporter { @@ -28,7 +26,21 @@ public class ToolImporter( public override async Task AddAsync(Tool record) { - var tool = mapper.Map(record); + var tool = new Data.Models.Tool + { + Id = record.Id, + Name = record.Name, + Description = record.Description, + Notes = record.Notes, + CreatedOn = record.CreatedOn, + UpdatedOn = record.UpdatedOn, + }; + + if (!String.IsNullOrWhiteSpace(record.CreatedBy)) + tool.CreatedBy = await userService.GetAsync(record.CreatedBy); + + if (!String.IsNullOrWhiteSpace(record.UpdatedBy)) + tool.UpdatedBy = await userService.GetAsync(record.UpdatedBy); try { diff --git a/LANCommander.Server/Models/ImportDialogOptions.cs b/LANCommander.Server/Models/ImportDialogOptions.cs index 9a57b21e..35d1d5a7 100644 --- a/LANCommander.Server/Models/ImportDialogOptions.cs +++ b/LANCommander.Server/Models/ImportDialogOptions.cs @@ -1,6 +1,9 @@ +using LANCommander.SDK.Enums; + namespace LANCommander.Server.Models; public class ImportDialogOptions { public string Hint { get; set; } + public ManifestType? ManifestType { get; set; } } \ No newline at end of file diff --git a/LANCommander.Server/UI/Components/ImportUploadDialog.razor b/LANCommander.Server/UI/Components/ImportUploadDialog.razor index 471ebfb8..a63da4db 100644 --- a/LANCommander.Server/UI/Components/ImportUploadDialog.razor +++ b/LANCommander.Server/UI/Components/ImportUploadDialog.razor @@ -302,9 +302,7 @@ else if (_stage == ImportStage.Failed) var archivePath = await ArchiveService.GetArchiveFileLocationAsync(_uploadObjectKey); - ImportContext.TrackUploadedArchive(archivePath); - - ImportItems = await ImportContext.InitializeImportAsync(archivePath); + ImportItems = await ImportContext.InitializeImportAsync(archivePath, Options.ManifestType); _selectedKeys = ImportItems.Select(i => i.Key).ToArray(); @@ -366,7 +364,7 @@ else if (_stage == ImportStage.Failed) { try { - ImportItems = await ImportContext.InitializeImportAsync(path); + ImportItems = await ImportContext.InitializeImportAsync(path, Options.ManifestType); _selectedKeys = ImportItems.Select(i => i.Key).ToArray(); diff --git a/LANCommander.Server/UI/Pages/Games/Index.razor b/LANCommander.Server/UI/Pages/Games/Index.razor index f578dfbd..802e2c9d 100644 --- a/LANCommander.Server/UI/Pages/Games/Index.razor +++ b/LANCommander.Server/UI/Pages/Games/Index.razor @@ -205,7 +205,8 @@ { var options = new ImportDialogOptions { - Hint = "Only LCX files are supported for importing games" + Hint = "Only LCX files are supported for importing games", + ManifestType = ManifestType.Game, }; var modalOptions = new ModalOptions diff --git a/LANCommander.Server/UI/Pages/Redistributables/Index.razor b/LANCommander.Server/UI/Pages/Redistributables/Index.razor index cf763ace..fb50c227 100644 --- a/LANCommander.Server/UI/Pages/Redistributables/Index.razor +++ b/LANCommander.Server/UI/Pages/Redistributables/Index.razor @@ -1,4 +1,5 @@ @page "/Redistributables" +@using ManifestType = LANCommander.SDK.Enums.ManifestType @using LANCommander.Server.UI.Pages.Redistributables.Components @attribute [Authorize(Roles = RoleService.AdministratorRoleName)] @inject RedistributableService RedistributableService @@ -98,7 +99,8 @@ { var options = new ImportDialogOptions { - Hint = "Only LCX files are supported for importing redistributables" + Hint = "Only LCX files are supported for importing redistributables", + ManifestType = ManifestType.Redistributable, }; var modalOptions = new ModalOptions diff --git a/LANCommander.Server/UI/Pages/Servers/Index.razor b/LANCommander.Server/UI/Pages/Servers/Index.razor index 98598667..05a36020 100644 --- a/LANCommander.Server/UI/Pages/Servers/Index.razor +++ b/LANCommander.Server/UI/Pages/Servers/Index.razor @@ -1,4 +1,5 @@ @page "/Servers" +@using ManifestType = LANCommander.SDK.Enums.ManifestType @using LANCommander.Server.Services.Abstractions @using LANCommander.Server.UI.Pages.Servers.Components @attribute [Authorize(Roles = RoleService.AdministratorRoleName)] @@ -152,7 +153,8 @@ { var options = new ImportDialogOptions { - Hint = "Only LCX files are supported for importing games" + Hint = "Only LCX files are supported for importing servers", + ManifestType = ManifestType.Server, }; var modalOptions = new ModalOptions diff --git a/LANCommander.Server/UI/Pages/Tools/Index.razor b/LANCommander.Server/UI/Pages/Tools/Index.razor index 64958e82..c3d25832 100644 --- a/LANCommander.Server/UI/Pages/Tools/Index.razor +++ b/LANCommander.Server/UI/Pages/Tools/Index.razor @@ -1,4 +1,5 @@ @page "/Tools" +@using ManifestType = LANCommander.SDK.Enums.ManifestType @using LANCommander.Server.UI.Pages.Tools.Components @attribute [Authorize(Roles = RoleService.AdministratorRoleName)] @inject ToolService ToolService @@ -98,7 +99,8 @@ { var options = new ImportDialogOptions { - Hint = "Only LCX files are supported for importing tools" + Hint = "Only LCX files are supported for importing tools", + ManifestType = ManifestType.Tool, }; var modalOptions = new ModalOptions