wabbajack/Wabbajack.Server/Controllers/Proxy.cs

124 lines
4.2 KiB
C#
Raw Normal View History

using System.Text;
using FluentFTP.Helpers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Wabbajack.BuildServer;
using Wabbajack.Downloaders;
using Wabbajack.Downloaders.Interfaces;
using Wabbajack.DTOs;
using Wabbajack.DTOs.DownloadStates;
using Wabbajack.Hashing.xxHash64;
using Wabbajack.Paths.IO;
using Wabbajack.VFS;
namespace Wabbajack.Server.Controllers;
[ApiController]
[Route("/proxy")]
public class Proxy : ControllerBase
{
private readonly ILogger<Proxy> _logger;
private readonly DownloadDispatcher _dispatcher;
private readonly TemporaryFileManager _tempFileManager;
private readonly AppSettings _appSettings;
private readonly FileHashCache _hashCache;
public Proxy(ILogger<Proxy> logger, DownloadDispatcher dispatcher, TemporaryFileManager tempFileManager, FileHashCache hashCache, AppSettings appSettings)
{
_logger = logger;
_dispatcher = dispatcher;
_tempFileManager = tempFileManager;
_appSettings = appSettings;
_hashCache = hashCache;
}
[HttpGet]
public async Task<IActionResult> ProxyGet(CancellationToken token, [FromQuery] Uri uri, [FromQuery] string? name, [FromQuery] string? hash)
{
var shouldMatch = hash != null ? Hash.FromHex(hash) : default;
_logger.LogInformation("Got proxy request for {Uri}", uri);
var state = _dispatcher.Parse(uri);
var cacheName = (await Encoding.UTF8.GetBytes(uri.ToString()).Hash()).ToHex();
var cacheFile = _appSettings.ProxyPath.Combine(cacheName);
if (state == null)
{
return BadRequest(new {Type = "Could not get state from Uri", Uri = uri.ToString()});
}
var archive = new Archive
{
Name = name ?? "",
State = state,
Hash = shouldMatch
};
var downloader = _dispatcher.Downloader(archive);
if (downloader is not IProxyable)
{
return BadRequest(new {Type = "Downloader is not IProxyable", Downloader = downloader.GetType().FullName});
}
if (cacheFile.FileExists() && (DateTime.Now - cacheFile.LastModified()) > TimeSpan.FromHours(4))
{
try
{
var verify = await _dispatcher.Verify(archive, token);
if (verify)
cacheFile.Touch();
}
catch (Exception ex)
{
_logger.LogInformation("When trying to verify cached file");
}
}
if (cacheFile.FileExists() && (DateTime.Now - cacheFile.LastModified()) > TimeSpan.FromHours(24))
{
try
{
cacheFile.Delete();
}
catch (Exception ex)
{
_logger.LogError(ex, "When trying to delete expired file");
}
}
if (cacheFile.FileExists())
{
if (hash != default)
{
var hashResult = await _hashCache.FileHashCachedAsync(cacheFile, token);
if (hashResult != shouldMatch)
return BadRequest(new {Type = "Unmatching Hashes", Expected = shouldMatch.ToHex(), Found = hashResult.ToHex()});
}
var ret = new PhysicalFileResult(cacheFile.ToString(), "application/octet-stream");
if (name != null)
ret.FileDownloadName = name;
return ret;
}
_logger.LogInformation("Downloading proxy request for {Uri}", uri);
var tempFile = _tempFileManager.CreateFile(deleteOnDispose:false);
var result = await _dispatcher.Download(archive, tempFile.Path, token);
if (hash != default && result != shouldMatch)
{
if (tempFile.Path.FileExists())
tempFile.Path.Delete();
return BadRequest(new {Type = "Unmatching Hashes", Expected = shouldMatch.ToHex(), Found = result.ToHex()});
}
await tempFile.Path.MoveToAsync(cacheFile, true, token);
_logger.LogInformation("Returning proxy request for {Uri} {Size}", uri, cacheFile.Size().FileSizeToString());
return new PhysicalFileResult(cacheFile.ToString(), "application/binary");
}
}