152 lines
5.4 KiB
C#
152 lines
5.4 KiB
C#
using LANCommander.Server.Extensions;
|
|
using LANCommander.Server.Services;
|
|
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using System.IO.Compression;
|
|
using ZiggyCreatures.Caching.Fusion;
|
|
|
|
namespace LANCommander.Server.Endpoints;
|
|
|
|
public static class ArchivesEndpoints
|
|
{
|
|
public static void MapArchivesEndpoints(this IEndpointRouteBuilder routes)
|
|
{
|
|
var group = routes.MapGroup("/api/Archives").RequireAuthorization();
|
|
|
|
group.MapGet("/", GetAsync);
|
|
group.MapGet("/{id:guid}", GetByIdAsync);
|
|
group.MapGet("/Download/{id:guid}", DownloadAsync);
|
|
group.MapGet("/Contents/{gameId:guid}/{version}", ByVersionAsync);
|
|
group.MapGet("/Contents/{id:guid}", ContentsAsync);
|
|
}
|
|
|
|
internal static async Task<Results<Ok<ICollection<SDK.Models.Archive>>, UnauthorizedHttpResult>> GetAsync(
|
|
[FromServices] ArchiveService archiveService)
|
|
=> TypedResults.Ok(await archiveService.GetAsync<SDK.Models.Archive>());
|
|
|
|
internal static async Task<Results<Ok<SDK.Models.Archive>, NotFound, UnauthorizedHttpResult>> GetByIdAsync(
|
|
Guid id,
|
|
[FromServices] ArchiveService archiveService)
|
|
{
|
|
var archive = await archiveService.GetAsync<SDK.Models.Archive>(id);
|
|
|
|
return archive is not null ?
|
|
TypedResults.Ok(archive) :
|
|
TypedResults.NotFound();
|
|
}
|
|
|
|
internal static async Task<Results<FileStreamHttpResult, NotFound, UnauthorizedHttpResult>> DownloadAsync(
|
|
Guid id,
|
|
[FromServices] ArchiveService archiveService,
|
|
[FromServices] ILoggerFactory loggerFactory)
|
|
{
|
|
var logger = loggerFactory.CreateLogger("ArchivesApi");
|
|
|
|
var archive = await archiveService.GetAsync(id);
|
|
|
|
if (archive is null)
|
|
{
|
|
logger.LogError("No archive found with ID {ArchiveId}", id);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
var filename = await archiveService.GetArchiveFileLocationAsync(archive);
|
|
|
|
if (!File.Exists(filename))
|
|
{
|
|
logger.LogError("Archive ({ArchiveId}) file not found at {FileName}", id, filename);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
if (archive.Game is null)
|
|
{
|
|
logger.LogError("Archive ({ArchiveId}) is missing associated game data", id);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
return TypedResults.File(
|
|
new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read),
|
|
"application/octet-stream",
|
|
$"{archive.Game.Title.SanitizeFilename()}.zip");
|
|
}
|
|
|
|
internal static async Task<Results<Ok<IEnumerable<ArchiveEntry>>, NotFound, UnauthorizedHttpResult>> ByVersionAsync(
|
|
Guid gameId,
|
|
string version,
|
|
[FromServices] ArchiveService archiveService,
|
|
[FromServices] IFusionCache cache,
|
|
[FromServices] ILoggerFactory loggerFactory)
|
|
{
|
|
var archive = await archiveService.FirstOrDefaultAsync(a => a.GameId == gameId && a.Version == version);
|
|
var logger = loggerFactory.CreateLogger("ArchivesApi");
|
|
|
|
if (archive is null)
|
|
{
|
|
logger.LogInformation("No archive found for game ID {GameId} with version {Version}", gameId, version);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
return await ContentsAsync(archive.Id, archiveService, cache, loggerFactory);
|
|
}
|
|
|
|
internal static async Task<Results<Ok<IEnumerable<ArchiveEntry>>, NotFound, UnauthorizedHttpResult>> ContentsAsync(
|
|
Guid id,
|
|
[FromServices] ArchiveService archiveService,
|
|
[FromServices] IFusionCache cache,
|
|
[FromServices] ILoggerFactory loggerFactory)
|
|
{
|
|
var logger = loggerFactory.CreateLogger("ArchivesApi");
|
|
|
|
var archive = await archiveService.GetAsync(id);
|
|
|
|
if (archive is null)
|
|
{
|
|
logger.LogError("No archive found with ID {ArchiveId}", id);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
var entries = await cache.GetOrSetAsync<IEnumerable<ArchiveEntry>>(
|
|
$"Archive/{archive.Id}/Contents",
|
|
async _ =>
|
|
{
|
|
var filename = await archiveService.GetArchiveFileLocationAsync(archive);
|
|
|
|
if (!File.Exists(filename))
|
|
{
|
|
logger.LogError("Archive ({ArchiveId}) file not found at {FileName}", id, filename);
|
|
return [];
|
|
}
|
|
|
|
var items = new List<ArchiveEntry>();
|
|
|
|
using (var zip = ZipFile.OpenRead(filename))
|
|
{
|
|
foreach (var entry in zip.Entries)
|
|
{
|
|
items.Add(new ArchiveEntry
|
|
{
|
|
FullName = entry.FullName,
|
|
Name = entry.Name,
|
|
Crc32 = entry.Crc32,
|
|
Length = entry.Length,
|
|
});
|
|
}
|
|
}
|
|
|
|
return items;
|
|
},
|
|
TimeSpan.MaxValue,
|
|
tags: ["Archives", $"Archives/{id}", $"Games/{archive.GameId}/Archives"]);
|
|
|
|
if (!entries.Any())
|
|
{
|
|
logger.LogInformation("No contents found for archive ID {ArchiveId}", id);
|
|
return TypedResults.NotFound();
|
|
}
|
|
|
|
logger.LogInformation("Returning {EntryCount} entries for archive ID {ArchiveId}", entries.Count(), id);
|
|
return TypedResults.Ok(entries);
|
|
}
|
|
}
|
|
|
|
|