From 01c7bbfa8ba73fb78c13cbaa80a703025835a4e8 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Tue, 25 Feb 2025 21:22:19 -0600 Subject: [PATCH] Fix archive download endpoint Fixes #181 --- .../Controllers/DownloadController.cs | 72 ------------------ .../Endpoints/DownloadEndpoints.cs | 73 +++++++++++++++++++ LANCommander.Server/Program.cs | 4 +- 3 files changed, 76 insertions(+), 73 deletions(-) delete mode 100644 LANCommander.Server/Controllers/DownloadController.cs create mode 100644 LANCommander.Server/Endpoints/DownloadEndpoints.cs diff --git a/LANCommander.Server/Controllers/DownloadController.cs b/LANCommander.Server/Controllers/DownloadController.cs deleted file mode 100644 index e5b6a561..00000000 --- a/LANCommander.Server/Controllers/DownloadController.cs +++ /dev/null @@ -1,72 +0,0 @@ -using LANCommander.Server.Data; -using LANCommander.Server.Extensions; -using LANCommander.Server.Models; -using LANCommander.Server.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace LANCommander.Server.Controllers -{ - [Authorize] - public class DownloadController : BaseController - { - private readonly ArchiveService ArchiveService; - private readonly GameSaveService GameSaveService; - private readonly UpdateService UpdateService; - - public DownloadController( - ILogger logger, - ArchiveService archiveService, - GameSaveService gameSaveService, - UpdateService updateService) : base(logger) - { - ArchiveService = archiveService; - GameSaveService = gameSaveService; - UpdateService = updateService; - } - - [Authorize(Roles = RoleService.AdministratorRoleName)] - [HttpGet("/Download/Archive/{id}")] - public async Task ArchiveAsync(Guid id) - { - var archive = await ArchiveService.GetAsync(id); - - if (archive == null) - return NotFound(); - - var filename = await ArchiveService.GetArchiveFileLocationAsync(archive); - - if (!System.IO.File.Exists(filename)) - return NotFound(); - - string name = ""; - - if (archive.GameId != null && archive.GameId != Guid.Empty) - name = $"{archive.Game.Title.SanitizeFilename()}.zip"; - else if (archive.RedistributableId != null && archive.RedistributableId != Guid.Empty) - name = $"{archive.Redistributable.Name.SanitizeFilename()}.zip"; - - return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", name); - } - - [HttpGet("/Download/Save/{id}")] - public async Task SaveAsync(Guid id) - { - var save = await GameSaveService.GetAsync(id); - - if (User == null || User.Identity?.Name != save.User?.UserName && !User.IsInRole(RoleService.AdministratorRoleName)) - return Unauthorized(); - - if (save == null) - return NotFound(); - - var filename = GameSaveService.GetSavePath(save); - - if (!System.IO.File.Exists(filename)) - return NotFound(); - - return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/zip", $"{save.User?.UserName} - {(save.Game == null ? "Unknown" : save.Game?.Title)} - {save.CreatedOn.ToString("MM-dd-yyyy.hh-mm")}.zip"); - } - } -} diff --git a/LANCommander.Server/Endpoints/DownloadEndpoints.cs b/LANCommander.Server/Endpoints/DownloadEndpoints.cs new file mode 100644 index 00000000..a65d4a25 --- /dev/null +++ b/LANCommander.Server/Endpoints/DownloadEndpoints.cs @@ -0,0 +1,73 @@ +using System.Net.Mime; +using System.Security.Claims; +using LANCommander.SDK.Services; +using LANCommander.Server.Services; +using LANCommander.Server.Services.Extensions; +using Microsoft.AspNetCore.Mvc; + +namespace LANCommander.Server.Endpoints; + +public static class DownloadEndpoints +{ + public static void MapDownloadEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/Download"); + + group.MapGet("/Archive/{id:guid}", DownloadArchiveAsync); + group.MapGet("/Save/{id:guid}", DownloadSaveAsync); + } + + internal static async Task DownloadArchiveAsync( + Guid id, + [FromServices] ArchiveService archiveService) + { + var archive = await archiveService + .Include(a => a.Game) + .Include(a => a.Redistributable) + .GetAsync(id); + + if (archive == null) + return TypedResults.NotFound(); + + var fileName = await archiveService.GetArchiveFileLocationAsync(archive); + + if (!File.Exists(fileName)) + return TypedResults.NotFound(); + + string name = ""; + + if (archive.Game != null) + name = $"{archive.Game.Title.SanitizeFilename()}.zip"; + else if (archive.Redistributable != null) + name = $"{archive.Redistributable.Name.SanitizeFilename()}.zip"; + + return TypedResults.File(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), fileDownloadName: name, contentType: MediaTypeNames.Application.Octet); + } + + internal static async Task DownloadSaveAsync( + Guid id, + ClaimsPrincipal user, + [FromServices] GameSaveService gameSaveService) + { + var save = await gameSaveService + .Include(s => s.Game) + .Include(s => s.User) + .GetAsync(id); + + if (user == null || user.Identity?.Name != save.User?.UserName && !user.IsInRole(RoleService.AdministratorRoleName)) + return TypedResults.Unauthorized(); + + if (save == null) + return TypedResults.NotFound(); + + var fileName = gameSaveService.GetSavePath(save); + + if (!File.Exists(fileName)) + return TypedResults.NotFound(); + + var name = + $"{save.User?.UserName} - {(save.Game != null ? "Unknown" : save.Game?.Title)} - {save.CreatedOn.ToString("MM-dd-yyyy.hh-mm")}.zip"; + + return TypedResults.File(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), fileDownloadName: name, contentType: MediaTypeNames.Application.Zip); + } +} \ No newline at end of file diff --git a/LANCommander.Server/Program.cs b/LANCommander.Server/Program.cs index 022b8bb8..43ca97ef 100644 --- a/LANCommander.Server/Program.cs +++ b/LANCommander.Server/Program.cs @@ -11,6 +11,7 @@ using Serilog; using LANCommander.Server; using LANCommander.Server.Data.Enums; using LANCommander.Server.Data.Models; +using LANCommander.Server.Endpoints; using LANCommander.Server.Jobs.Background; using LANCommander.Server.Models; using LANCommander.Server.Services.Models; @@ -106,8 +107,9 @@ app.UseStaticFiles(); app.UseEndpoints(endpoints => { - endpoints.MapFallbackToPage("/_Host"); + endpoints.MapDownloadEndpoints(); endpoints.MapControllers(); + endpoints.MapFallbackToPage("/_Host"); }); app.MapRazorComponents()