using LANCommander.Server.Data; using LANCommander.Server.Data.Models; using LANCommander.Server.Extensions; using LANCommander.Server.Models; using LANCommander.Server.Services; using LANCommander.Server.UI.Pages.Servers.Components; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.IO.Compression; using ZiggyCreatures.Caching.Fusion; namespace LANCommander.Server.Controllers.Api { [Authorize(AuthenticationSchemes = "Bearer")] [Route("api/[controller]")] [ApiController] public class ArchivesController : BaseApiController { private readonly IFusionCache Cache; private readonly ArchiveService ArchiveService; public ArchivesController( ILogger logger, IFusionCache cache, ArchiveService archiveService) : base(logger) { Cache = cache; ArchiveService = archiveService; } [HttpGet] public async Task>> GetAsync() { return Ok(await ArchiveService.GetAsync()); } [HttpGet("{id}")] public async Task> GetAsync(Guid id) { var archive = await ArchiveService.GetAsync(id); if (archive != null) return Ok(archive); else return NotFound(); } [HttpGet("Download/{id}")] public async Task DownloadAsync(Guid id) { var archive = await ArchiveService.GetAsync(id); if (archive == null) { Logger?.LogError("No archive found with ID {ArchiveId}", id); return NotFound(); } var filename = await ArchiveService.GetArchiveFileLocationAsync(archive); if (!System.IO.File.Exists(filename)) { Logger?.LogError("Archive ({ArchiveId}) file not found at {FileName}", filename); return NotFound(); } return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{archive.Game.Title.SanitizeFilename()}.zip"); } [HttpGet("Contents/{gameId}/{version}")] public async Task ByVersionAsync(Guid gameId, string version) { var archive = await ArchiveService.FirstOrDefaultAsync(a => a.GameId == gameId && a.Version == version); if (archive == null) return NotFound(); else return await ContentsAsync(archive.Id); } [HttpGet("Contents/{id}")] public async Task ContentsAsync(Guid id, string version = null) { var archive = await ArchiveService.GetAsync(id); if (archive == null) { Logger?.LogError("No archive found with ID {ArchiveId}", id); return NotFound(); } var entries = await Cache.GetOrSetAsync>($"Archive/{archive.Id}/Contents", async _ => { var filename = await ArchiveService.GetArchiveFileLocationAsync(archive); if (!System.IO.File.Exists(filename)) { Logger?.LogError("Archive ({ArchiveId}) file not found at {FileName}", filename); return new List(); } var entries = new List(); using (var zip = ZipFile.OpenRead(filename)) { foreach (var entry in zip.Entries) { entries.Add(new ArchiveEntry { FullName = entry.FullName, Name = entry.Name, Crc32 = entry.Crc32, Length = entry.Length, }); } } return entries; }, TimeSpan.MaxValue, tags: ["Archives", $"Archives/{id}", $"Games/{archive.GameId}/Archives"]); if (entries.Count() == 0) return NotFound(); else return Ok(entries); } } }