cdn download support on shards + clean up
This commit is contained in:
Submodule LightlessAPI updated: 4ce70bee83...44fbe10458
@@ -10,6 +10,7 @@ using LightlessSyncShared.Services;
|
|||||||
using LightlessSyncShared.Utils.Configuration;
|
using LightlessSyncShared.Utils.Configuration;
|
||||||
using LightlessSyncStaticFilesServer.Services;
|
using LightlessSyncStaticFilesServer.Services;
|
||||||
using LightlessSyncStaticFilesServer.Utils;
|
using LightlessSyncStaticFilesServer.Utils;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -32,12 +33,15 @@ public class ServerFilesController : ControllerBase
|
|||||||
private readonly IDbContextFactory<LightlessDbContext> _lightlessDbContext;
|
private readonly IDbContextFactory<LightlessDbContext> _lightlessDbContext;
|
||||||
private readonly LightlessMetrics _metricsClient;
|
private readonly LightlessMetrics _metricsClient;
|
||||||
private readonly MainServerShardRegistrationService _shardRegistrationService;
|
private readonly MainServerShardRegistrationService _shardRegistrationService;
|
||||||
|
private readonly CDNDownloadUrlService _cdnDownloadUrlService;
|
||||||
|
private readonly CDNDownloadsService _cdnDownloadsService;
|
||||||
|
|
||||||
public ServerFilesController(ILogger<ServerFilesController> logger, CachedFileProvider cachedFileProvider,
|
public ServerFilesController(ILogger<ServerFilesController> logger, CachedFileProvider cachedFileProvider,
|
||||||
IConfigurationService<StaticFilesServerConfiguration> configuration,
|
IConfigurationService<StaticFilesServerConfiguration> configuration,
|
||||||
IHubContext<LightlessHub> hubContext,
|
IHubContext<LightlessHub> hubContext,
|
||||||
IDbContextFactory<LightlessDbContext> lightlessDbContext, LightlessMetrics metricsClient,
|
IDbContextFactory<LightlessDbContext> lightlessDbContext, LightlessMetrics metricsClient,
|
||||||
MainServerShardRegistrationService shardRegistrationService) : base(logger)
|
MainServerShardRegistrationService shardRegistrationService, CDNDownloadUrlService cdnDownloadUrlService,
|
||||||
|
CDNDownloadsService cdnDownloadsService) : base(logger)
|
||||||
{
|
{
|
||||||
_basePath = configuration.GetValueOrDefault(nameof(StaticFilesServerConfiguration.UseColdStorage), false)
|
_basePath = configuration.GetValueOrDefault(nameof(StaticFilesServerConfiguration.UseColdStorage), false)
|
||||||
? configuration.GetValue<string>(nameof(StaticFilesServerConfiguration.ColdStorageDirectory))
|
? configuration.GetValue<string>(nameof(StaticFilesServerConfiguration.ColdStorageDirectory))
|
||||||
@@ -48,6 +52,8 @@ public class ServerFilesController : ControllerBase
|
|||||||
_lightlessDbContext = lightlessDbContext;
|
_lightlessDbContext = lightlessDbContext;
|
||||||
_metricsClient = metricsClient;
|
_metricsClient = metricsClient;
|
||||||
_shardRegistrationService = shardRegistrationService;
|
_shardRegistrationService = shardRegistrationService;
|
||||||
|
_cdnDownloadUrlService = cdnDownloadUrlService;
|
||||||
|
_cdnDownloadsService = cdnDownloadsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost(LightlessFiles.ServerFiles_DeleteAll)]
|
[HttpPost(LightlessFiles.ServerFiles_DeleteAll)]
|
||||||
@@ -105,6 +111,16 @@ public class ServerFilesController : ControllerBase
|
|||||||
baseUrl = shard.Value ?? _configuration.GetValue<Uri>(nameof(StaticFilesServerConfiguration.CdnFullUrl));
|
baseUrl = shard.Value ?? _configuration.GetValue<Uri>(nameof(StaticFilesServerConfiguration.CdnFullUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cdnDownloadUrl = string.Empty;
|
||||||
|
if (forbiddenFile == null)
|
||||||
|
{
|
||||||
|
var directUri = _cdnDownloadUrlService.TryCreateDirectDownloadUri(baseUrl, file.Hash);
|
||||||
|
if (directUri != null)
|
||||||
|
{
|
||||||
|
cdnDownloadUrl = directUri.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
response.Add(new DownloadFileDto
|
response.Add(new DownloadFileDto
|
||||||
{
|
{
|
||||||
FileExists = file.Size > 0,
|
FileExists = file.Size > 0,
|
||||||
@@ -113,6 +129,7 @@ public class ServerFilesController : ControllerBase
|
|||||||
Hash = file.Hash,
|
Hash = file.Hash,
|
||||||
Size = file.Size,
|
Size = file.Size,
|
||||||
Url = baseUrl?.ToString() ?? string.Empty,
|
Url = baseUrl?.ToString() ?? string.Empty,
|
||||||
|
CDNDownloadUrl = cdnDownloadUrl,
|
||||||
RawSize = file.RawSize
|
RawSize = file.RawSize
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -127,6 +144,22 @@ public class ServerFilesController : ControllerBase
|
|||||||
return Ok(JsonSerializer.Serialize(allFileShards.SelectMany(t => t.RegionUris.Select(v => v.Value.ToString()))));
|
return Ok(JsonSerializer.Serialize(allFileShards.SelectMany(t => t.RegionUris.Select(v => v.Value.ToString()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet(LightlessFiles.ServerFiles_DirectDownload + "/{hash}")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> 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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost(LightlessFiles.ServerFiles_FilesSend)]
|
[HttpPost(LightlessFiles.ServerFiles_FilesSend)]
|
||||||
public async Task<IActionResult> FilesSend([FromBody] FilesSendDto filesSendDto)
|
public async Task<IActionResult> FilesSend([FromBody] FilesSendDto filesSendDto)
|
||||||
{
|
{
|
||||||
@@ -360,4 +393,4 @@ public class ServerFilesController : ControllerBase
|
|||||||
buffer[i] ^= 42;
|
buffer[i] ^= 42;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<ShardServerFilesController> logger,
|
||||||
|
CDNDownloadsService cdnDownloadsService) : base(logger)
|
||||||
|
{
|
||||||
|
_cdnDownloadsService = cdnDownloadsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(LightlessFiles.ServerFiles_DirectDownload + "/{hash}")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> 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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Result> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -87,6 +87,8 @@ public class Startup
|
|||||||
services.AddSingleton<RequestFileStreamResultFactory>();
|
services.AddSingleton<RequestFileStreamResultFactory>();
|
||||||
services.AddSingleton<ServerTokenGenerator>();
|
services.AddSingleton<ServerTokenGenerator>();
|
||||||
services.AddSingleton<RequestQueueService>();
|
services.AddSingleton<RequestQueueService>();
|
||||||
|
services.AddSingleton<CDNDownloadUrlService>();
|
||||||
|
services.AddSingleton<CDNDownloadsService>();
|
||||||
services.AddHostedService(p => p.GetService<RequestQueueService>());
|
services.AddHostedService(p => p.GetService<RequestQueueService>());
|
||||||
services.AddHostedService(m => m.GetService<FileStatisticsService>());
|
services.AddHostedService(m => m.GetService<FileStatisticsService>());
|
||||||
services.AddSingleton<IConfigurationService<LightlessConfigurationBase>, LightlessConfigurationServiceClient<LightlessConfigurationBase>>();
|
services.AddSingleton<IConfigurationService<LightlessConfigurationBase>, LightlessConfigurationServiceClient<LightlessConfigurationBase>>();
|
||||||
@@ -204,7 +206,8 @@ public class Startup
|
|||||||
}
|
}
|
||||||
else if (_isDistributionNode)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user