diff --git a/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ServerFilesController.cs b/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ServerFilesController.cs index 6ec924b..d0b5d95 100644 --- a/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ServerFilesController.cs +++ b/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ServerFilesController.cs @@ -34,12 +34,14 @@ public class ServerFilesController : ControllerBase private readonly LightlessMetrics _metricsClient; private readonly MainServerShardRegistrationService _shardRegistrationService; private readonly CDNDownloadUrlService _cdnDownloadUrlService; + private readonly CDNDownloadsService _cdnDownloadsService; public ServerFilesController(ILogger logger, CachedFileProvider cachedFileProvider, IConfigurationService configuration, IHubContext hubContext, IDbContextFactory lightlessDbContext, LightlessMetrics metricsClient, - MainServerShardRegistrationService shardRegistrationService, CDNDownloadUrlService cdnDownloadUrlService) : base(logger) + MainServerShardRegistrationService shardRegistrationService, CDNDownloadUrlService cdnDownloadUrlService, + CDNDownloadsService cdnDownloadsService) : base(logger) { _basePath = configuration.GetValueOrDefault(nameof(StaticFilesServerConfiguration.UseColdStorage), false) ? configuration.GetValue(nameof(StaticFilesServerConfiguration.ColdStorageDirectory)) @@ -51,6 +53,7 @@ public class ServerFilesController : ControllerBase _metricsClient = metricsClient; _shardRegistrationService = shardRegistrationService; _cdnDownloadUrlService = cdnDownloadUrlService; + _cdnDownloadsService = cdnDownloadsService; } [HttpPost(LightlessFiles.ServerFiles_DeleteAll)] @@ -145,24 +148,16 @@ public class ServerFilesController : ControllerBase [AllowAnonymous] public async Task DownloadFileDirect(string hash, [FromQuery] long expires, [FromQuery] string signature) { - if (!_cdnDownloadUrlService.DirectDownloadsEnabled) - { - return NotFound(); - } + var result = await _cdnDownloadsService.GetDownloadAsync(hash, expires, signature).ConfigureAwait(false); - hash = hash.ToUpperInvariant(); - if (!_cdnDownloadUrlService.TryValidateSignature(hash, expires, signature)) + return result.Status switch { - return Unauthorized(); - } - - var fileInfo = await _cachedFileProvider.DownloadAndGetLocalFileInfo(hash).ConfigureAwait(false); - if (fileInfo == null) - { - return NotFound(); - } - - return PhysicalFile(fileInfo.FullName, "application/octet-stream"); + CDNDownloadsService.ResultStatus.Disabled => NotFound(), + CDNDownloadsService.ResultStatus.Unauthorized => Unauthorized(), + CDNDownloadsService.ResultStatus.NotFound => NotFound(), + CDNDownloadsService.ResultStatus.Success => PhysicalFile(result.File!.FullName, "application/octet-stream"), + _ => NotFound() + }; } [HttpPost(LightlessFiles.ServerFiles_FilesSend)] diff --git a/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ShardServerFilesController.cs b/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ShardServerFilesController.cs new file mode 100644 index 0000000..819597e --- /dev/null +++ b/LightlessSyncServer/LightlessSyncStaticFilesServer/Controllers/ShardServerFilesController.cs @@ -0,0 +1,34 @@ +using LightlessSync.API.Routes; +using LightlessSyncStaticFilesServer.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace LightlessSyncStaticFilesServer.Controllers; + +[Route(LightlessFiles.ServerFiles)] +public class ShardServerFilesController : ControllerBase +{ + private readonly CDNDownloadsService _cdnDownloadsService; + + public ShardServerFilesController(ILogger logger, + CDNDownloadsService cdnDownloadsService) : base(logger) + { + _cdnDownloadsService = cdnDownloadsService; + } + + [HttpGet(LightlessFiles.ServerFiles_DirectDownload + "/{hash}")] + [AllowAnonymous] + public async Task DownloadFileDirect(string hash, [FromQuery] long expires, [FromQuery] string signature) + { + var result = await _cdnDownloadsService.GetDownloadAsync(hash, expires, signature).ConfigureAwait(false); + + return result.Status switch + { + CDNDownloadsService.ResultStatus.Disabled => NotFound(), + CDNDownloadsService.ResultStatus.Unauthorized => Unauthorized(), + CDNDownloadsService.ResultStatus.NotFound => NotFound(), + CDNDownloadsService.ResultStatus.Success => PhysicalFile(result.File!.FullName, "application/octet-stream"), + _ => NotFound() + }; + } +} diff --git a/LightlessSyncServer/LightlessSyncStaticFilesServer/Services/CDNDownloadsService.cs b/LightlessSyncServer/LightlessSyncStaticFilesServer/Services/CDNDownloadsService.cs new file mode 100644 index 0000000..3cbd661 --- /dev/null +++ b/LightlessSyncServer/LightlessSyncStaticFilesServer/Services/CDNDownloadsService.cs @@ -0,0 +1,56 @@ +using System.IO; +using System.Threading.Tasks; + +namespace LightlessSyncStaticFilesServer.Services; + +public class CDNDownloadsService +{ + public enum ResultStatus + { + Disabled, + Unauthorized, + NotFound, + Success + } + + public readonly record struct Result(ResultStatus Status, FileInfo? File); + + private readonly CDNDownloadUrlService _cdnDownloadUrlService; + private readonly CachedFileProvider _cachedFileProvider; + + public CDNDownloadsService(CDNDownloadUrlService cdnDownloadUrlService, CachedFileProvider cachedFileProvider) + { + _cdnDownloadUrlService = cdnDownloadUrlService; + _cachedFileProvider = cachedFileProvider; + } + + public bool DownloadsEnabled => _cdnDownloadUrlService.DirectDownloadsEnabled; + + public async Task GetDownloadAsync(string hash, long expiresUnixSeconds, string signature) + { + if (!_cdnDownloadUrlService.DirectDownloadsEnabled) + { + return new Result(ResultStatus.Disabled, null); + } + + if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(hash)) + { + return new Result(ResultStatus.Unauthorized, null); + } + + hash = hash.ToUpperInvariant(); + + if (!_cdnDownloadUrlService.TryValidateSignature(hash, expiresUnixSeconds, signature)) + { + return new Result(ResultStatus.Unauthorized, null); + } + + var fileInfo = await _cachedFileProvider.DownloadAndGetLocalFileInfo(hash).ConfigureAwait(false); + if (fileInfo == null) + { + return new Result(ResultStatus.NotFound, null); + } + + return new Result(ResultStatus.Success, fileInfo); + } +} diff --git a/LightlessSyncServer/LightlessSyncStaticFilesServer/Startup.cs b/LightlessSyncServer/LightlessSyncStaticFilesServer/Startup.cs index f4034a5..a3f2469 100644 --- a/LightlessSyncServer/LightlessSyncStaticFilesServer/Startup.cs +++ b/LightlessSyncServer/LightlessSyncStaticFilesServer/Startup.cs @@ -88,6 +88,7 @@ public class Startup services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddHostedService(p => p.GetService()); services.AddHostedService(m => m.GetService()); services.AddSingleton, LightlessConfigurationServiceClient>(); @@ -205,7 +206,8 @@ public class Startup } else if (_isDistributionNode) { - a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(CacheController), typeof(RequestController), typeof(DistributionController), typeof(SpeedTestController))); + a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(CacheController), typeof(RequestController), + typeof(DistributionController), typeof(ShardServerFilesController), typeof(SpeedTestController))); } else {