void.cat/VoidCat/Services/Files/S3FileMetadataStore.cs

154 lines
4.2 KiB
C#
Raw Normal View History

2022-03-01 16:48:42 +00:00
using Amazon.S3;
using Newtonsoft.Json;
using VoidCat.Model;
using VoidCat.Services.Abstractions;
namespace VoidCat.Services.Files;
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-03-01 16:48:42 +00:00
public class S3FileMetadataStore : IFileMetadataStore
{
private readonly ILogger<S3FileMetadataStore> _logger;
private readonly AmazonS3Client _client;
private readonly S3BlobConfig _config;
public S3FileMetadataStore(S3BlobConfig settings, ILogger<S3FileMetadataStore> logger)
2022-03-01 16:48:42 +00:00
{
_logger = logger;
_config = settings;
2022-03-01 16:48:42 +00:00
_client = _config.CreateClient();
}
/// <inheritdoc />
public string? Key => _config.Name;
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public ValueTask<TMeta?> Get<TMeta>(Guid id) where TMeta : FileMeta
2022-03-07 13:38:53 +00:00
{
return GetMeta<TMeta>(id);
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public async ValueTask<IReadOnlyList<TMeta>> Get<TMeta>(Guid[] ids) where TMeta : FileMeta
2022-06-06 21:51:25 +00:00
{
var ret = new List<TMeta>();
foreach (var id in ids)
{
var r = await GetMeta<TMeta>(id);
if (r != null)
{
ret.Add(r);
}
}
return ret;
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public async ValueTask Update<TMeta>(Guid id, TMeta meta) where TMeta : FileMeta
2022-03-15 10:39:36 +00:00
{
2022-09-11 19:07:38 +00:00
var oldMeta = await Get<SecretFileMeta>(id);
2022-03-15 10:39:36 +00:00
if (oldMeta == default) return;
2022-06-06 21:51:25 +00:00
2022-09-11 19:07:38 +00:00
oldMeta.Patch(meta);
2022-03-15 10:39:36 +00:00
await Set(id, oldMeta);
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public ValueTask<PagedResult<TMeta>> ListFiles<TMeta>(PagedRequest request) where TMeta : FileMeta
2022-06-06 21:51:25 +00:00
{
2022-06-08 16:17:53 +00:00
async IAsyncEnumerable<TMeta> Enumerate()
2022-06-06 21:51:25 +00:00
{
var obj = await _client.ListObjectsV2Async(new()
{
BucketName = _config.BucketName,
2022-06-06 22:06:02 +00:00
Prefix = "metadata_",
MaxKeys = 5_000
2022-06-06 21:51:25 +00:00
});
foreach (var file in obj.S3Objects)
{
2022-06-06 22:06:02 +00:00
if (Guid.TryParse(file.Key.Split("metadata_")[1], out var id))
2022-06-06 21:51:25 +00:00
{
2022-06-08 16:17:53 +00:00
var meta = await GetMeta<TMeta>(id);
2022-06-06 21:51:25 +00:00
if (meta != default)
{
2022-06-08 16:17:53 +00:00
yield return meta;
2022-06-06 21:51:25 +00:00
}
}
}
}
2022-06-08 16:17:53 +00:00
return ValueTask.FromResult(new PagedResult<TMeta>
2022-06-06 21:51:25 +00:00
{
2022-06-08 16:17:53 +00:00
Page = request.Page,
PageSize = request.PageSize,
Results = Enumerate().Skip(request.PageSize * request.Page).Take(request.PageSize)
});
}
2022-06-06 21:51:25 +00:00
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
public async ValueTask<IFileMetadataStore.StoreStats> Stats()
{
2022-09-11 19:07:38 +00:00
var files = await ListFiles<FileMeta>(new(0, Int32.MaxValue));
2022-06-08 16:17:53 +00:00
var count = await files.Results.CountAsync();
var size = await files.Results.SumAsync(a => (long) a.Size);
return new(count, (ulong) size);
2022-06-06 21:51:25 +00:00
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public ValueTask<FileMeta?> Get(Guid id)
2022-03-07 13:38:53 +00:00
{
2022-09-11 19:07:38 +00:00
return GetMeta<FileMeta>(id);
2022-03-07 13:38:53 +00:00
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public ValueTask<SecretFileMeta?> GetPrivate(Guid id)
2022-06-08 16:17:53 +00:00
{
2022-09-11 19:07:38 +00:00
return GetMeta<SecretFileMeta>(id);
2022-06-08 16:17:53 +00:00
}
/// <inheritdoc />
2022-09-11 19:07:38 +00:00
public async ValueTask Set(Guid id, SecretFileMeta meta)
2022-03-07 13:38:53 +00:00
{
await _client.PutObjectAsync(new()
{
BucketName = _config.BucketName,
Key = ToKey(id),
ContentBody = JsonConvert.SerializeObject(meta),
ContentType = "application/json"
});
}
2022-06-08 16:17:53 +00:00
/// <inheritdoc />
2022-03-07 13:38:53 +00:00
public async ValueTask Delete(Guid id)
{
await _client.DeleteObjectAsync(_config.BucketName, ToKey(id));
}
2022-09-11 19:07:38 +00:00
private async ValueTask<TMeta?> GetMeta<TMeta>(Guid id) where TMeta : FileMeta
2022-03-01 16:48:42 +00:00
{
try
{
var obj = await _client.GetObjectAsync(_config.BucketName, ToKey(id));
using var sr = new StreamReader(obj.ResponseStream);
var json = await sr.ReadToEndAsync();
var ret = JsonConvert.DeserializeObject<TMeta>(json);
2022-06-08 16:17:53 +00:00
if (ret != default)
{
2022-06-08 16:17:53 +00:00
ret.Id = id;
}
return ret;
2022-03-01 16:48:42 +00:00
}
catch (AmazonS3Exception aex)
{
_logger.LogError(aex, "Failed to get metadata for {Id}, {Error}", id, aex.Message);
}
return default;
}
2022-06-06 21:51:25 +00:00
2022-06-06 22:06:02 +00:00
private static string ToKey(Guid id) => $"metadata_{id}";
2022-06-06 21:51:25 +00:00
}