diff --git a/.gitignore b/.gitignore index e87d38f..bb1d98d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ out/ sw.js .DS_Store .idea/ -appsettings.*.json +appsettings.*.json \ No newline at end of file diff --git a/VoidCat/Controllers/DownloadController.cs b/VoidCat/Controllers/DownloadController.cs index ecc8799..9fdabd2 100644 --- a/VoidCat/Controllers/DownloadController.cs +++ b/VoidCat/Controllers/DownloadController.cs @@ -15,7 +15,8 @@ public class DownloadController : Controller private readonly IPaywallStore _paywall; private readonly ILogger _logger; - public DownloadController(IFileStore storage, ILogger logger, IFileInfoManager fileInfo, IPaywallStore paywall) + public DownloadController(IFileStore storage, ILogger logger, IFileInfoManager fileInfo, + IPaywallStore paywall) { _storage = storage; _logger = logger; @@ -40,7 +41,7 @@ public class DownloadController : Controller var voidFile = await SetupDownload(gid); if (voidFile == default) return; - var egressReq = new EgressRequest(gid, GetRanges(Request, (long)voidFile!.Metadata!.Size)); + var egressReq = new EgressRequest(gid, GetRanges(Request, (long) voidFile!.Metadata!.Size)); if (egressReq.Ranges.Count() > 1) { _logger.LogWarning("Multi-range request not supported!"); @@ -52,10 +53,10 @@ public class DownloadController : Controller } else if (egressReq.Ranges.Count() == 1) { - Response.StatusCode = (int)HttpStatusCode.PartialContent; + Response.StatusCode = (int) HttpStatusCode.PartialContent; if (egressReq.Ranges.Sum(a => a.Size) == 0) { - Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable; + Response.StatusCode = (int) HttpStatusCode.RequestedRangeNotSatisfiable; return; } } @@ -91,7 +92,7 @@ public class DownloadController : Controller var orderId = Request.Headers.GetHeader("V-OrderId") ?? Request.Query["orderId"]; if (!await IsOrderPaid(orderId)) { - Response.StatusCode = (int)HttpStatusCode.PaymentRequired; + Response.StatusCode = (int) HttpStatusCode.PaymentRequired; return default; } } @@ -126,20 +127,10 @@ public class DownloadController : Controller continue; } - var ranges = rangeHeader.Replace("bytes=", string.Empty).Split(","); - foreach (var range in ranges) + foreach (var h in RangeRequest.Parse(rangeHeader, totalSize)) { - var rangeValues = range.Split("-"); - - long? endByte = null, startByte = 0; - if (long.TryParse(rangeValues[1], out var endParsed)) - endByte = endParsed; - - if (long.TryParse(rangeValues[0], out var startParsed)) - startByte = startParsed; - - yield return new(totalSize, startByte, endByte); + yield return h; } } } -} +} \ No newline at end of file diff --git a/VoidCat/Controllers/UploadController.cs b/VoidCat/Controllers/UploadController.cs index f1d8ec4..31d2c4b 100644 --- a/VoidCat/Controllers/UploadController.cs +++ b/VoidCat/Controllers/UploadController.cs @@ -41,6 +41,7 @@ namespace VoidCat.Controllers Name = Request.Headers.GetHeader("V-Filename"), Description = Request.Headers.GetHeader("V-Description"), Digest = Request.Headers.GetHeader("V-Full-Digest"), + Size = (ulong?)Request.ContentLength ?? 0UL, Uploader = uid }; @@ -84,7 +85,8 @@ namespace VoidCat.Controllers { Hash = digest, EditSecret = editSecret?.FromBase58Guid() ?? Guid.Empty, - Id = gid + Id = gid, + IsAppend = true }, HttpContext.RequestAborted); return UploadResult.Success(vf); diff --git a/VoidCat/Model/Extensions.cs b/VoidCat/Model/Extensions.cs index fc80c92..8eda24b 100644 --- a/VoidCat/Model/Extensions.cs +++ b/VoidCat/Model/Extensions.cs @@ -2,11 +2,26 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; +using Amazon; +using Amazon.Runtime; +using Amazon.S3; namespace VoidCat.Model; public static class Extensions { + public static AmazonS3Client CreateClient(this S3BlobConfig c) + { + return new AmazonS3Client(new BasicAWSCredentials(c.AccessKey, c.SecretKey), + new AmazonS3Config + { + RegionEndpoint = !string.IsNullOrEmpty(c.Region) ? RegionEndpoint.GetBySystemName(c.Region) : null, + ServiceURL = c.ServiceUrl?.ToString(), + UseHttp = c.ServiceUrl?.Scheme == "http", + ForcePathStyle = true + }); + } + public static Guid? GetUserId(this HttpContext context) { var claimSub = context?.User?.Claims?.FirstOrDefault(a => a.Type == ClaimTypes.NameIdentifier)?.Value; @@ -99,7 +114,7 @@ public static class Extensions } }; - private static byte[] FromHex(this string input) + public static byte[] FromHex(this string input) { var result = new byte[(input.Length + 1) >> 1]; var lastCell = result.Length - 1; @@ -160,4 +175,4 @@ public static class Extensions var hashParts = vu.PasswordHash.Split(":"); return vu.PasswordHash == password.HashPassword(hashParts[0], hashParts.Length == 3 ? hashParts[1] : null); } -} +} \ No newline at end of file diff --git a/VoidCat/Model/IngressPayload.cs b/VoidCat/Model/IngressPayload.cs index 0399577..67a9021 100644 --- a/VoidCat/Model/IngressPayload.cs +++ b/VoidCat/Model/IngressPayload.cs @@ -6,5 +6,5 @@ public sealed record IngressPayload(Stream InStream, SecretVoidFileMeta Meta) public Guid? EditSecret { get; init; } public string? Hash { get; init; } - public bool IsAppend => EditSecret.HasValue; + public bool IsAppend { get; init; } } \ No newline at end of file diff --git a/VoidCat/Model/RangeRequest.cs b/VoidCat/Model/RangeRequest.cs index 5134090..064bf02 100644 --- a/VoidCat/Model/RangeRequest.cs +++ b/VoidCat/Model/RangeRequest.cs @@ -4,6 +4,8 @@ public sealed record RangeRequest(long? TotalSize, long? Start, long? End) { private const long DefaultBufferSize = 1024L * 512L; + public string OriginalString { get; private init; } + public long? Size => Start.HasValue ? (End ?? Math.Min(TotalSize!.Value, Start.Value + DefaultBufferSize)) - Start.Value : End; @@ -16,4 +18,25 @@ public sealed record RangeRequest(long? TotalSize, long? Start, long? End) /// public string ToContentRange() => $"bytes {Start}-{End ?? (Start + Size - 1L)}/{TotalSize?.ToString() ?? "*"}"; + + public static IEnumerable Parse(string header, long totalSize) + { + var ranges = header.Replace("bytes=", string.Empty).Split(","); + foreach (var range in ranges) + { + var rangeValues = range.Split("-"); + + long? endByte = null, startByte = 0; + if (long.TryParse(rangeValues[1], out var endParsed)) + endByte = endParsed; + + if (long.TryParse(rangeValues[0], out var startParsed)) + startByte = startParsed; + + yield return new(totalSize, startByte, endByte) + { + OriginalString = range + }; + } + } } \ No newline at end of file diff --git a/VoidCat/Model/VoidSettings.cs b/VoidCat/Model/VoidSettings.cs index 3f2b925..570be89 100644 --- a/VoidCat/Model/VoidSettings.cs +++ b/VoidCat/Model/VoidSettings.cs @@ -17,11 +17,32 @@ namespace VoidCat.Model public SmtpSettings? Smtp { get; init; } public List CorsOrigins { get; init; } = new(); + + public CloudStorageSettings? CloudStorage { get; init; } } public sealed record TorSettings(Uri TorControl, string PrivateKey, string ControlPassword); public sealed record JwtSettings(string Issuer, string Key); - public sealed record SmtpSettings(string Address, string Username, string Password); + public sealed record SmtpSettings + { + public Uri? Server { get; init; } + public string? Username { get; init; } + public string? Password { get; init; } + } + + public sealed record CloudStorageSettings + { + public S3BlobConfig? S3 { get; set; } + } + + public sealed record S3BlobConfig + { + public string? AccessKey { get; init; } + public string? SecretKey { get; init; } + public Uri? ServiceUrl { get; init; } + public string? Region { get; init; } + public string? BucketName { get; init; } = "void-cat"; + } } diff --git a/VoidCat/Program.cs b/VoidCat/Program.cs index f2dd0ff..2541493 100644 --- a/VoidCat/Program.cs +++ b/VoidCat/Program.cs @@ -78,10 +78,7 @@ services.AddAuthorization((opt) => { opt.AddPolicy(Policies.RequireAdmin, (auth) services.AddVoidMigrations(); // file storage -services.AddTransient(); -services.AddTransient(); -services.AddTransient(); -services.AddTransient(); +services.AddStorage(voidSettings); // stats services.AddTransient(); diff --git a/VoidCat/Properties/launchSettings.json b/VoidCat/Properties/launchSettings.json index 7ec56fb..152fc9e 100644 --- a/VoidCat/Properties/launchSettings.json +++ b/VoidCat/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "VoidCat": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/VoidCat/Services/Files/FileStorageStartup.cs b/VoidCat/Services/Files/FileStorageStartup.cs new file mode 100644 index 0000000..713f090 --- /dev/null +++ b/VoidCat/Services/Files/FileStorageStartup.cs @@ -0,0 +1,26 @@ +using VoidCat.Model; +using VoidCat.Services.Abstractions; +using VoidCat.Services.Users; + +namespace VoidCat.Services.Files; + +public static class FileStorageStartup +{ + public static void AddStorage(this IServiceCollection services, VoidSettings settings) + { + services.AddTransient(); + services.AddTransient(); + + if (settings.CloudStorage != default) + { + // cloud storage + services.AddSingleton(); + services.AddSingleton(); + } + else + { + services.AddTransient(); + services.AddTransient(); + } + } +} \ No newline at end of file diff --git a/VoidCat/Services/Files/LocalDiskFileStorage.cs b/VoidCat/Services/Files/LocalDiskFileStorage.cs index c8b6f60..5bff80e 100644 --- a/VoidCat/Services/Files/LocalDiskFileStorage.cs +++ b/VoidCat/Services/Files/LocalDiskFileStorage.cs @@ -6,6 +6,7 @@ namespace VoidCat.Services.Files; public class LocalDiskFileStore : StreamFileStore, IFileStore { + private const string FilesDir = "files-v1"; private readonly ILogger _logger; private readonly VoidSettings _settings; private readonly IFileMetadataStore _metadataStore; @@ -20,9 +21,10 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore _fileInfo = fileInfo; _logger = logger; - if (!Directory.Exists(_settings.DataDirectory)) + var dir = Path.Combine(_settings.DataDirectory, FilesDir); + if (!Directory.Exists(dir)) { - Directory.CreateDirectory(_settings.DataDirectory); + Directory.CreateDirectory(dir); } } @@ -99,5 +101,5 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore } private string MapPath(Guid id) => - Path.Join(_settings.DataDirectory, id.ToString()); + Path.Join(_settings.DataDirectory, FilesDir, id.ToString()); } \ No newline at end of file diff --git a/VoidCat/Services/Files/S3FileMetadataStore.cs b/VoidCat/Services/Files/S3FileMetadataStore.cs new file mode 100644 index 0000000..601497a --- /dev/null +++ b/VoidCat/Services/Files/S3FileMetadataStore.cs @@ -0,0 +1,62 @@ +using Amazon.S3; +using Newtonsoft.Json; +using VoidCat.Model; +using VoidCat.Services.Abstractions; + +namespace VoidCat.Services.Files; + +// ReSharper disable once InconsistentNaming +public class S3FileMetadataStore : IFileMetadataStore +{ + private readonly ILogger _logger; + private readonly AmazonS3Client _client; + private readonly S3BlobConfig _config; + + public S3FileMetadataStore(VoidSettings settings, ILogger logger) + { + _logger = logger; + _config = settings.CloudStorage!.S3!; + _client = _config.CreateClient(); + } + + public async ValueTask Get(Guid id) where TMeta : VoidFileMeta + { + try + { + var obj = await _client.GetObjectAsync(_config.BucketName, ToKey(id)); + + using var sr = new StreamReader(obj.ResponseStream); + var json = await sr.ReadToEndAsync(); + return JsonConvert.DeserializeObject(json); + } + catch (AmazonS3Exception aex) + { + _logger.LogError(aex, "Failed to get metadata for {Id}, {Error}", id, aex.Message); + } + + return default; + } + + public async ValueTask Set(Guid id, SecretVoidFileMeta meta) + { + await _client.PutObjectAsync(new() + { + BucketName = _config.BucketName, + Key = ToKey(id), + ContentBody = JsonConvert.SerializeObject(meta), + ContentType = "application/json" + }); + } + + public ValueTask Update(Guid id, SecretVoidFileMeta patch) + { + throw new NotImplementedException(); + } + + public async ValueTask Delete(Guid id) + { + await _client.DeleteObjectAsync(_config.BucketName, ToKey(id)); + } + + private static string ToKey(Guid id) => $"{id}-metadata"; +} \ No newline at end of file diff --git a/VoidCat/Services/Files/S3FileStore.cs b/VoidCat/Services/Files/S3FileStore.cs new file mode 100644 index 0000000..318aac1 --- /dev/null +++ b/VoidCat/Services/Files/S3FileStore.cs @@ -0,0 +1,128 @@ +using Amazon.S3; +using Amazon.S3.Model; +using VoidCat.Model; +using VoidCat.Services.Abstractions; + +namespace VoidCat.Services.Files; + +// ReSharper disable once InconsistentNaming +public class S3FileStore : StreamFileStore, IFileStore +{ + private readonly IFileInfoManager _fileInfo; + private readonly AmazonS3Client _client; + private readonly S3BlobConfig _config; + private readonly IAggregateStatsCollector _statsCollector; + + public S3FileStore(VoidSettings settings, IAggregateStatsCollector stats, IFileMetadataStore metadataStore, + IUserUploadsStore userUploads, IFileInfoManager fileInfo) : base(stats, metadataStore, userUploads) + { + _fileInfo = fileInfo; + _statsCollector = stats; + _config = settings.CloudStorage!.S3!; + _client = _config.CreateClient(); + } + + public async ValueTask Ingress(IngressPayload payload, CancellationToken cts) + { + var req = new PutObjectRequest + { + BucketName = _config.BucketName, + Key = payload.Id.ToString(), + InputStream = payload.InStream, + ContentType = "application/octet-stream", + AutoResetStreamPosition = false, + AutoCloseStream = false, + ChecksumAlgorithm = ChecksumAlgorithm.SHA256, + ChecksumSHA256 = payload.Hash != default ? Convert.ToBase64String(payload.Hash!.FromHex()) : null, + StreamTransferProgress = (s, e) => + { + _statsCollector.TrackIngress(payload.Id, (ulong) e.IncrementTransferred) + .GetAwaiter().GetResult(); + }, + Headers = + { + ContentLength = (long)payload.Meta.Size + } + }; + + var r = await _client.PutObjectAsync(req, cts); + return await HandleCompletedUpload(payload, r.ChecksumSHA256, payload.Meta.Size); + } + + public async ValueTask Egress(EgressRequest request, Stream outStream, CancellationToken cts) + { + var req = new GetObjectRequest() + { + BucketName = _config.BucketName, + Key = request.Id.ToString() + }; + if (request.Ranges.Any()) + { + var r = request.Ranges.First(); + req.ByteRange = new ByteRange(r.OriginalString); + } + + var obj = await _client.GetObjectAsync(req, cts); + await EgressFull(request.Id, obj.ResponseStream, outStream, cts); + } + + public async ValueTask> ListFiles(PagedRequest request) + { + try + { + var objs = await _client.ListObjectsV2Async(new ListObjectsV2Request() + { + BucketName = _config.BucketName, + }); + + var files = (request.SortBy, request.SortOrder) switch + { + (PagedSortBy.Date, PageSortOrder.Asc) => objs.S3Objects.OrderBy(a => a.LastModified), + (PagedSortBy.Date, PageSortOrder.Dsc) => objs.S3Objects.OrderByDescending(a => a.LastModified), + (PagedSortBy.Name, PageSortOrder.Asc) => objs.S3Objects.OrderBy(a => a.Key), + (PagedSortBy.Name, PageSortOrder.Dsc) => objs.S3Objects.OrderByDescending(a => a.Key), + (PagedSortBy.Size, PageSortOrder.Asc) => objs.S3Objects.OrderBy(a => a.Size), + (PagedSortBy.Size, PageSortOrder.Dsc) => objs.S3Objects.OrderByDescending(a => a.Size), + _ => objs.S3Objects.AsEnumerable() + }; + + async IAsyncEnumerable EnumerateFiles(IEnumerable page) + { + foreach (var item in page) + { + if (!Guid.TryParse(item.Key, out var gid)) continue; + + var obj = await _fileInfo.Get(gid); + if (obj != default) + { + yield return obj; + } + } + } + + return new() + { + Page = request.Page, + PageSize = request.PageSize, + TotalResults = files.Count(), + Results = EnumerateFiles(files.Skip(request.PageSize * request.Page).Take(request.PageSize)) + }; + } + catch (AmazonS3Exception aex) + { + // ignore + return new() + { + Page = request.Page, + PageSize = request.PageSize, + TotalResults = 0, + Results = AsyncEnumerable.Empty() + }; + } + } + + public async ValueTask DeleteFile(Guid id) + { + await _client.DeleteObjectAsync(_config.BucketName, id.ToString()); + } +} \ No newline at end of file diff --git a/VoidCat/Services/Files/StreamFileStore.cs b/VoidCat/Services/Files/StreamFileStore.cs index a6badf9..2377626 100644 --- a/VoidCat/Services/Files/StreamFileStore.cs +++ b/VoidCat/Services/Files/StreamFileStore.cs @@ -34,7 +34,7 @@ public abstract class StreamFileStore } } - protected async ValueTask IngressToStream(Stream stream, IngressPayload payload, + protected async ValueTask IngressToStream(Stream outStream, IngressPayload payload, CancellationToken cts) { var id = payload.Id; @@ -47,17 +47,23 @@ public abstract class StreamFileStore } } - var (total, hash) = await IngressInternal(id, payload.InStream, stream, cts); + var (total, hash) = await IngressInternal(id, payload.InStream, outStream, cts); if (payload.Hash != null && !hash.Equals(payload.Hash, StringComparison.InvariantCultureIgnoreCase)) { throw new CryptographicException("Invalid file hash"); } + return await HandleCompletedUpload(payload, hash, total); + } + + protected async Task HandleCompletedUpload(IngressPayload payload, string hash, ulong totalSize) + { + var meta = payload.Meta; if (payload.IsAppend) { meta = meta! with { - Size = meta.Size + total + Size = meta.Size + totalSize }; } else @@ -67,14 +73,14 @@ public abstract class StreamFileStore Digest = hash, Uploaded = DateTimeOffset.UtcNow, EditSecret = Guid.NewGuid(), - Size = total + Size = totalSize }; } - await _metadataStore.Set(id, meta); + await _metadataStore.Set(payload.Id, meta); var vf = new PrivateVoidFile() { - Id = id, + Id = payload.Id, Metadata = meta }; @@ -85,8 +91,8 @@ public abstract class StreamFileStore return vf; } - - private async Task<(ulong, string)> IngressInternal(Guid id, Stream ingress, Stream fs, CancellationToken cts) + + private async Task<(ulong, string)> IngressInternal(Guid id, Stream ingress, Stream outStream, CancellationToken cts) { using var buffer = MemoryPool.Shared.Rent(BufferSize); var total = 0UL; @@ -103,7 +109,7 @@ public abstract class StreamFileStore var totalRead = readLength + offset; var buf = buffer.Memory[..totalRead]; - await fs.WriteAsync(buf, cts); + await outStream.WriteAsync(buf, cts); await _stats.TrackIngress(id, (ulong) buf.Length); sha.TransformBlock(buf.ToArray(), 0, buf.Length, null, 0); total += (ulong) buf.Length; @@ -111,10 +117,10 @@ public abstract class StreamFileStore } sha.TransformFinalBlock(Array.Empty(), 0, 0); - return (total, BitConverter.ToString(sha.Hash!).Replace("-", string.Empty)); + return (total, sha.Hash!.ToHex()); } - private async Task EgressFull(Guid id, Stream inStream, Stream outStream, + protected async Task EgressFull(Guid id, Stream inStream, Stream outStream, CancellationToken cts) { using var buffer = MemoryPool.Shared.Rent(BufferSize); diff --git a/VoidCat/Services/Migrations/20220217_MigrateMetadata.cs b/VoidCat/Services/Migrations/20220217_MigrateMetadata.cs deleted file mode 100644 index d442007..0000000 --- a/VoidCat/Services/Migrations/20220217_MigrateMetadata.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Newtonsoft.Json; -using VoidCat.Model; - -namespace VoidCat.Services.Migrations; - -public class MigrateMetadata_20220217 : IMigration -{ - private const string MetadataDir = "metadata"; - private const string MetadataV2Dir = "metadata-v2"; - private readonly ILogger _logger; - private readonly VoidSettings _settings; - - public MigrateMetadata_20220217(VoidSettings settings, ILogger log) - { - _settings = settings; - _logger = log; - } - - public async ValueTask Migrate() - { - var newMeta = Path.Combine(_settings.DataDirectory, MetadataV2Dir); - if (!Directory.Exists(newMeta)) - { - Directory.CreateDirectory(newMeta); - } - - foreach (var fe in Directory.EnumerateFiles(_settings.DataDirectory)) - { - var filename = Path.GetFileNameWithoutExtension(fe); - if (!Guid.TryParse(filename, out var id)) continue; - - var fp = MapMeta(id); - if (File.Exists(fp)) - { - _logger.LogInformation("Migrating metadata for {file}", fp); - try - { - var oldJson = await File.ReadAllTextAsync(fp); - if(!oldJson.Contains("\"Metadata\":")) continue; // old format should contain "Metadata": - - var old = JsonConvert.DeserializeObject(oldJson); - var newObj = new PrivateVoidFile() - { - Id = old!.Id, - Metadata = new() - { - Name = old.Metadata!.Name, - Description = old.Metadata.Description, - Uploaded = old.Uploaded, - MimeType = old.Metadata.MimeType, - EditSecret = old.EditSecret, - Size = old.Size - } - }; - - await File.WriteAllTextAsync(MapV2Meta(id), JsonConvert.SerializeObject(newObj)); - - // delete old metadata - File.Delete(fp); - } - catch (Exception ex) - { - _logger.LogError(ex, ex.Message); - } - } - } - } - - private string MapMeta(Guid id) => - Path.ChangeExtension(Path.Join(_settings.DataDirectory, MetadataDir, id.ToString()), ".json"); - private string MapV2Meta(Guid id) => - Path.ChangeExtension(Path.Join(_settings.DataDirectory, MetadataV2Dir, id.ToString()), ".json"); - - private record VoidFile - { - [JsonConverter(typeof(Base58GuidConverter))] - public Guid Id { get; init; } - - public VoidFileMeta? Metadata { get; set; } - - public ulong Size { get; init; } - - public DateTimeOffset Uploaded { get; init; } - } - - private record InternalVoidFile : VoidFile - { - [JsonConverter(typeof(Base58GuidConverter))] - public Guid EditSecret { get; init; } - } - - private record VoidFileMeta - { - public string? Name { get; init; } - - public string? Description { get; init; } - - public string? MimeType { get; init; } - } - - private record NewVoidFileMeta - { - public string? Name { get; init; } - public ulong Size { get; init; } - public DateTimeOffset Uploaded { get; init; } = DateTimeOffset.UtcNow; - public string? Description { get; init; } - public string? MimeType { get; init; } - public string? Digest { get; init; } - } - - private record NewSecretVoidFileMeta : NewVoidFileMeta - { - [JsonConverter(typeof(Base58GuidConverter))] - public Guid EditSecret { get; init; } - } -} \ No newline at end of file diff --git a/VoidCat/Services/Migrations/20220218_FixMigration.cs b/VoidCat/Services/Migrations/20220218_FixMigration.cs deleted file mode 100644 index f79cc2e..0000000 --- a/VoidCat/Services/Migrations/20220218_FixMigration.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Newtonsoft.Json; -using VoidCat.Model; - -namespace VoidCat.Services.Migrations; - -public class FixMigration_20220218 : MetadataMigrator -{ - public FixMigration_20220218(VoidSettings settings, ILogger> logger) : base(settings, logger) - { - } - - protected override string OldPath => "metadata-v2"; - protected override string NewPath => "metadata-v3"; - - protected override bool ShouldMigrate(string json) - { - var metaBase = JsonConvert.DeserializeObject(json); - return metaBase?.Metadata?.Version == 2 && metaBase?.Metadata?.Size > 0; - } - - protected override WrongMeta MigrateModel(WrongFile old) - { - return old.Metadata!; - } -} - -public class WrongFile -{ - [JsonConverter(typeof(Base58GuidConverter))] - public Guid Id { get; init; } - public WrongMeta? Metadata { get; init; } -} - -public class WrongMeta -{ - public int Version { get; init; } = 2; - public string? Name { get; init; } - public ulong Size { get; init; } - public DateTimeOffset Uploaded { get; init; } = DateTimeOffset.UtcNow; - public string? Description { get; init; } - public string? MimeType { get; init; } - public string? Digest { get; init; } - [JsonConverter(typeof(Base58GuidConverter))] - public Guid EditSecret { get; init; } -} \ No newline at end of file diff --git a/VoidCat/Services/Migrations/IMigration.cs b/VoidCat/Services/Migrations/IMigration.cs index 98a5ff4..a97c497 100644 --- a/VoidCat/Services/Migrations/IMigration.cs +++ b/VoidCat/Services/Migrations/IMigration.cs @@ -7,10 +7,7 @@ public interface IMigration public static class Migrations { - public static IServiceCollection AddVoidMigrations(this IServiceCollection svc) + public static void AddVoidMigrations(this IServiceCollection svc) { - svc.AddTransient(); - svc.AddTransient(); - return svc; } } \ No newline at end of file diff --git a/VoidCat/VoidCat.csproj b/VoidCat/VoidCat.csproj index b72132c..ae35c62 100644 --- a/VoidCat/VoidCat.csproj +++ b/VoidCat/VoidCat.csproj @@ -12,6 +12,7 @@ +