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

124 lines
4.0 KiB
C#

using Dapper;
using Npgsql;
using VoidCat.Model;
using VoidCat.Services.Abstractions;
namespace VoidCat.Services.Files;
/// <inheritdoc />
public class PostgresFileMetadataStore : IFileMetadataStore
{
private readonly NpgsqlConnection _connection;
public PostgresFileMetadataStore(NpgsqlConnection connection)
{
_connection = connection;
}
/// <inheritdoc />
public ValueTask<VoidFileMeta?> Get(Guid id)
{
return Get<VoidFileMeta>(id);
}
/// <inheritdoc />
public ValueTask<SecretVoidFileMeta?> GetPrivate(Guid id)
{
return Get<SecretVoidFileMeta>(id);
}
/// <inheritdoc />
public async ValueTask Set(Guid id, SecretVoidFileMeta obj)
{
await _connection.ExecuteAsync(
@"insert into
""Files""(""Id"", ""Name"", ""Size"", ""Uploaded"", ""Description"", ""MimeType"", ""Digest"", ""EditSecret"")
values(:id, :name, :size, :uploaded, :description, :mimeType, :digest, :editSecret)
on conflict (""Id"") do update set ""Name"" = :name, ""Description"" = :description, ""MimeType"" = :mimeType", new
{
id,
name = obj.Name,
size = (long) obj.Size,
uploaded = obj.Uploaded.ToUniversalTime(),
description = obj.Description,
mimeType = obj.MimeType,
digest = obj.Digest,
editSecret = obj.EditSecret
});
}
/// <inheritdoc />
public async ValueTask Delete(Guid id)
{
await _connection.ExecuteAsync("delete from \"Files\" where \"Id\" = :id", new {id});
}
/// <inheritdoc />
public async ValueTask<TMeta?> Get<TMeta>(Guid id) where TMeta : VoidFileMeta
{
return await _connection.QuerySingleOrDefaultAsync<TMeta?>(@"select * from ""Files"" where ""Id"" = :id",
new {id});
}
/// <inheritdoc />
public async ValueTask<IReadOnlyList<TMeta>> Get<TMeta>(Guid[] ids) where TMeta : VoidFileMeta
{
var ret = await _connection.QueryAsync<TMeta>("select * from \"Files\" where \"Id\" in :ids", new {ids});
return ret.ToList();
}
/// <inheritdoc />
public async ValueTask Update<TMeta>(Guid id, TMeta meta) where TMeta : VoidFileMeta
{
var oldMeta = await Get<SecretVoidFileMeta>(id);
if (oldMeta == default) return;
oldMeta.Description = meta.Description ?? oldMeta.Description;
oldMeta.Name = meta.Name ?? oldMeta.Name;
oldMeta.MimeType = meta.MimeType ?? oldMeta.MimeType;
await Set(id, oldMeta);
}
/// <inheritdoc />
public async ValueTask<PagedResult<TMeta>> ListFiles<TMeta>(PagedRequest request) where TMeta : VoidFileMeta
{
var count = await _connection.ExecuteScalarAsync<int>(@"select count(*) from ""Files""");
async IAsyncEnumerable<TMeta> Enumerate()
{
var orderBy = request.SortBy switch
{
PagedSortBy.Date => "Uploaded",
PagedSortBy.Name => "Name",
PagedSortBy.Size => "Size",
_ => "Id"
};
var orderDirection = request.SortOrder == PageSortOrder.Asc ? "asc" : "desc";
var results = await _connection.QueryAsync<TMeta>(
$"select * from \"Files\" order by \"{orderBy}\" {orderDirection} offset @offset limit @limit",
new {offset = request.PageSize * request.Page, limit = request.PageSize});
foreach (var meta in results)
{
yield return meta;
}
}
return new()
{
TotalResults = count,
PageSize = request.PageSize,
Page = request.Page,
Results = Enumerate()
};
}
/// <inheritdoc />
public async ValueTask<IFileMetadataStore.StoreStats> Stats()
{
var v = await _connection.QuerySingleAsync<(long Files, long Size)>(
@"select count(1) ""Files"", cast(sum(""Size"") as bigint) ""Size"" from ""Files""");
return new(v.Files, (ulong) v.Size);
}
}