Allow uploading of archive files via CLI

Implements #111
This commit is contained in:
Pat Hartl 2024-10-01 17:56:58 -05:00
parent 3d9749a9db
commit 320d4e2c5e
7 changed files with 206 additions and 5 deletions

View file

@ -86,8 +86,27 @@ namespace LANCommander.Launcher.Models
[Option("Id", HelpText = "The ID of the entity to export", Required = true)]
public Guid Id { get; set; }
[Option("Type", HelpText = "The type of archive to export", Required = true)]
public ImportArchiveType Type { get; set; }
[Option("Type", HelpText = "The type of export file", Required = true)]
public ArchiveType Type { get; set; }
}
[Verb("Upload", HelpText = "Upload an archive to the server (Admin Only)")]
public class UploadCommandLineOptions
{
[Option("Path", HelpText = "Path to the archive file")]
public string Path { get; set; }
[Option("Id", HelpText = "The ID of the entity associated with the archive")]
public Guid Id { get; set; }
[Option("Version", HelpText = "The version of the archive")]
public string Version { get; set; }
[Option("Changelog", HelpText = "Any changes introduces with the archive")]
public string Changelog { get; set; }
[Option("Type", HelpText = "The type of entity associated with the archive")]
public ArchiveType Type { get; set; }
}
[Verb("Login", HelpText = "Login to a LANCommander server")]
@ -112,4 +131,4 @@ namespace LANCommander.Launcher.Models
[Option("Alias", Required = true)]
public string Alias { get; set; }
}
}
}

View file

@ -46,6 +46,7 @@ namespace LANCommander.Launcher.Services
SyncCommandLineOptions,
ImportCommandLineOptions,
ExportCommandLineOptions,
UploadCommandLineOptions,
LoginCommandLineOptions,
LogoutCommandLineOptions,
ChangeAliasCommandLineOptions
@ -58,6 +59,7 @@ namespace LANCommander.Launcher.Services
await result.WithParsedAsync<SyncCommandLineOptions>(Sync);
await result.WithParsedAsync<ImportCommandLineOptions>(Import);
await result.WithParsedAsync<ExportCommandLineOptions>(Export);
await result.WithParsedAsync<UploadCommandLineOptions>(Upload);
await result.WithParsedAsync<LoginCommandLineOptions>(Login);
await result.WithParsedAsync<LogoutCommandLineOptions>(Logout);
await result.WithParsedAsync<ChangeAliasCommandLineOptions>(ChangeAlias);
@ -212,16 +214,52 @@ namespace LANCommander.Launcher.Services
switch (options.Type)
{
case ImportArchiveType.Game:
case ArchiveType.Game:
Logger.LogInformation("Exporting game from server...");
await Client.Games.ExportAsync(options.Path, options.Id);
break;
case ArchiveType.Redistributable:
Logger.LogInformation("Exporting redistributable from server...");
await Client.Redistributables.ExportAsync(options.Path, options.Id);
break;
case ArchiveType.Server:
Logger.LogInformation("Exporting server from server...");
await Client.Servers.ExportAsync(options.Path, options.Id);
break;
}
Logger.LogInformation($"Successfully exported game to {options.Path}");
}
private async Task Upload(UploadCommandLineOptions options)
{
if (!File.Exists(options.Path))
{
Logger.LogInformation("File not found! Check your path!");
return;
}
switch (options.Type)
{
case ArchiveType.Game:
Logger.LogInformation("Uploading game archive to server...");
await Client.Games.UploadArchiveAsync(options.Path, options.Id, options.Version, options.Changelog);
break;
case ArchiveType.Redistributable:
Logger.LogInformation("Uploading redistributable archive to server...");
await Client.Redistributables.UploadArchiveAsync(options.Path, options.Id, options.Version, options.Changelog);
break;
}
}
private async Task Login(LoginCommandLineOptions options)
{
try

View file

@ -881,6 +881,23 @@ namespace LANCommander.SDK
await Client.DownloadRequestAsync($"/Games/{gameId}/Export/Full", destinationPath);
}
public async Task UploadArchiveAsync(string archivePath, Guid gameId, string version, string changelog = "")
{
using (var fs = new FileStream(archivePath, FileMode.Open, FileAccess.Read))
{
var objectKey = await Client.ChunkedUploadRequestAsync("", fs);
if (objectKey != Guid.Empty)
await Client.PostRequestAsync<object>($"/api/Games/UploadArchive", new UploadArchiveRequest
{
Id = gameId,
ObjectKey = objectKey,
Version = version,
Changelog = changelog,
});
}
}
public static string GetMetadataDirectoryPath(string installDirectory, Guid gameId)
{
if (String.IsNullOrWhiteSpace(installDirectory))

View file

@ -0,0 +1,12 @@
using System;
namespace LANCommander.SDK.Models
{
public class UploadArchiveRequest
{
public Guid Id { get; set; }
public Guid ObjectKey { get; set; }
public string Version { get; set; }
public string Changelog { get; set; }
}
}

View file

@ -182,5 +182,27 @@ namespace LANCommander.SDK
await Client.PostRequestAsync<object>($"/api/Redistributables/Import/{objectKey}");
}
}
public async Task ExportAsync(string destinationPath, Guid redistributableId)
{
await Client.DownloadRequestAsync($"/Redistributables/{redistributableId}/Export/Full", destinationPath);
}
public async Task UploadArchiveAsync(string archivePath, Guid redistributableId, string version, string changelog = "")
{
using (var fs = new FileStream(archivePath, FileMode.Open, FileAccess.Read))
{
var objectKey = await Client.ChunkedUploadRequestAsync("", fs);
if (objectKey != Guid.Empty)
await Client.PostRequestAsync<object>($"/api/Redistributables/UploadArchive", new UploadArchiveRequest
{
Id = redistributableId,
ObjectKey = objectKey,
Version = version,
Changelog = changelog,
});
}
}
}
}

View file

@ -19,6 +19,7 @@ namespace LANCommander.Server.Controllers.Api
{
private readonly IMapper Mapper;
private readonly GameService GameService;
private readonly ArchiveService ArchiveService;
private readonly UserManager<User> UserManager;
private readonly RoleManager<Role> RoleManager;
private readonly IFusionCache Cache;
@ -28,11 +29,13 @@ namespace LANCommander.Server.Controllers.Api
IMapper mapper,
IFusionCache cache,
GameService gameService,
ArchiveService archiveService,
UserManager<User> userManager,
RoleManager<Role> roleManager) : base(logger)
{
Mapper = mapper;
GameService = gameService;
ArchiveService = archiveService;
UserManager = userManager;
RoleManager = roleManager;
Cache = cache;
@ -145,5 +148,48 @@ namespace LANCommander.Server.Controllers.Api
return BadRequest(ex.Message);
}
}
[Authorize(Roles = "Administrator")]
[HttpPost("UploadArchive")]
public async Task<IActionResult> UploadArchive(SDK.Models.UploadArchiveRequest request)
{
try
{
var archive = await ArchiveService.Get(a => a.GameId == request.Id && a.Version == request.Version).FirstOrDefaultAsync();
var archivePath = ArchiveService.GetArchiveFileLocation(archive.ObjectKey);
if (archive != null)
{
var existingArchivePath = ArchiveService.GetArchiveFileLocation(archive.ObjectKey);
System.IO.File.Delete(existingArchivePath);
archive.ObjectKey = request.ObjectKey.ToString();
archive.Changelog = request.Changelog;
archive.CompressedSize = new System.IO.FileInfo(archivePath).Length;
archive = await ArchiveService.Update(archive);
}
else
{
archive = new Archive()
{
ObjectKey = request.ObjectKey.ToString(),
Changelog = request.Changelog,
GameId = request.Id,
CompressedSize = new System.IO.FileInfo(archivePath).Length,
};
await ArchiveService.Add(archive);
}
return Ok();
}
catch (Exception ex)
{
Logger?.LogError(ex, "Could not upload game archive");
return BadRequest(ex.Message);
}
}
}
}

View file

@ -5,6 +5,7 @@ using LANCommander.Server.Models;
using LANCommander.Server.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LANCommander.Server.Controllers.Api
{
@ -15,14 +16,17 @@ namespace LANCommander.Server.Controllers.Api
{
private readonly IMapper Mapper;
private readonly RedistributableService RedistributableService;
private readonly ArchiveService ArchiveService;
public RedistributablesController(
ILogger<RedistributablesController> logger,
IMapper mapper,
RedistributableService redistributableService) : base(logger)
RedistributableService redistributableService,
ArchiveService archiveService) : base(logger)
{
Mapper = mapper;
RedistributableService = redistributableService;
ArchiveService = archiveService;
}
[HttpGet]
@ -79,5 +83,48 @@ namespace LANCommander.Server.Controllers.Api
return BadRequest(ex.Message);
}
}
[Authorize(Roles = "Administrator")]
[HttpPost("UploadArchive")]
public async Task<IActionResult> UploadArchive(SDK.Models.UploadArchiveRequest request)
{
try
{
var archive = await ArchiveService.Get(a => a.RedistributableId == request.Id && a.Version == request.Version).FirstOrDefaultAsync();
var archivePath = ArchiveService.GetArchiveFileLocation(archive.ObjectKey);
if (archive != null)
{
var existingArchivePath = ArchiveService.GetArchiveFileLocation(archive.ObjectKey);
System.IO.File.Delete(existingArchivePath);
archive.ObjectKey = request.ObjectKey.ToString();
archive.Changelog = request.Changelog;
archive.CompressedSize = new System.IO.FileInfo(archivePath).Length;
archive = await ArchiveService.Update(archive);
}
else
{
archive = new Archive()
{
ObjectKey = request.ObjectKey.ToString(),
Changelog = request.Changelog,
RedistributableId = request.Id,
CompressedSize = new System.IO.FileInfo(archivePath).Length,
};
await ArchiveService.Add(archive);
}
return Ok();
}
catch (Exception ex)
{
Logger?.LogError(ex, "Could not upload redistributable archive");
return BadRequest(ex.Message);
}
}
}
}