using System.IO; using System.Threading; 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, Stream? Stream, long? ContentLength); 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, CancellationToken ct) { if (!_cdnDownloadUrlService.DirectDownloadsEnabled) { return new Result(ResultStatus.Disabled, null, null, null); } if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(hash)) { return new Result(ResultStatus.Unauthorized, null, null, null); } hash = hash.ToUpperInvariant(); if (!_cdnDownloadUrlService.TryValidateSignature(hash, expiresUnixSeconds, signature)) { return new Result(ResultStatus.Unauthorized, null, null, null); } var fileResult = await _cachedFileProvider.GetFileStreamForDirectDownloadAsync(hash, ct).ConfigureAwait(false); if (fileResult == null) { return new Result(ResultStatus.NotFound, null, null, null); } return new Result(ResultStatus.Success, fileResult.Value.File, fileResult.Value.Stream, fileResult.Value.ContentLength); } }