Files
WebDrop/server/src/WebDrop.Core/Abstractions.cs
T

131 lines
5.0 KiB
C#

namespace WebDrop.Core;
public interface IClock
{
long NowMs { get; }
}
public sealed class SystemClock : IClock
{
public long NowMs => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
/// <summary>大帧的字节存储。键由服务生成,实现不得解析。</summary>
public interface IBlobStore
{
ValueTask PutAsync(string key, ReadOnlyMemory<byte> data, CancellationToken ct = default);
ValueTask<byte[]?> ReadAsync(string key, CancellationToken ct = default);
void Delete(string key);
void DeletePrefix(string prefix);
IEnumerable<(string Key, DateTimeOffset LastWrite)> Enumerate();
}
/// <summary>
/// 创建频道前的人机/滥用校验(Cap 的 PoW 验证码)。验证码本身不属于本项目:
/// 校验失败抛 CaptchaRequired,验证服务不可用抛 CaptchaUnavailable(两者对用户的含义不同)。
/// </summary>
public interface ICaptchaVerifier
{
bool Enabled { get; }
Task VerifyAsync(string? token, CancellationToken ct);
}
/// <summary>不启用验证码(开发和小型自托管)。</summary>
public sealed class NullCaptchaVerifier : ICaptchaVerifier
{
public bool Enabled => false;
public Task VerifyAsync(string? token, CancellationToken ct) => Task.CompletedTask;
}
/// <summary>服务提交后发出的通知,传输层据此唤醒订阅者。</summary>
public interface IChannelEvents
{
void Advanced(Id16 channelId, long seq);
void SettingsChanged(Id16 channelId);
void SeatsChanged(Id16 channelId);
void ChannelDeleted(Id16 channelId);
}
public sealed class NullChannelEvents : IChannelEvents
{
public void Advanced(Id16 channelId, long seq) { }
public void SettingsChanged(Id16 channelId) { }
public void SeatsChanged(Id16 channelId) { }
public void ChannelDeleted(Id16 channelId) { }
}
/// <summary>频道和分享资源共用的读写事务入口。Write 串行执行,抛出异常即回滚。</summary>
public interface IResourceStore
{
T Read<T>(Func<IResourceTx, T> action);
T Write<T>(Func<IResourceTx, T> action);
}
/// <summary>单个数据库事务内的频道、分享与帧操作。</summary>
public interface IResourceTx
{
// 便捷分享
Share? GetShare(Id16 id);
void InsertShare(Share share);
void UpdateShare(Share share);
IReadOnlyList<Id16> ListExpiredShares(long now, int limit);
bool HasResourceId(Id16 id);
int CountShareVisitors(Id16 id);
bool HasShareVisitor(Id16 id, ReadOnlySpan<byte> endpointHash);
void InsertShareVisitor(Id16 id, ReadOnlySpan<byte> endpointHash, long joinedAt);
void DeleteShareRows(Id16 id);
IReadOnlyList<ShareItem> ListShareItems(Id16 id);
int CountShareItems(Id16 id);
void AddShareBytes(Id16 id, long delta);
IReadOnlyList<ShareItem> ListExpiredShareItems(long now, int limit);
ShareItem? GetShareItem(Id16 shareId, Id16 itemId);
void InsertShareItem(ShareItem item);
void UpdateShareItem(ShareItem item);
void DeleteShareItemRows(Id16 shareId, Id16 itemId);
bool HasDownloadGrant(Id16 resourceId, Id16 itemId, ReadOnlySpan<byte> endpointHash);
void AddDownloadGrant(Id16 resourceId, Id16 itemId, ReadOnlySpan<byte> endpointHash);
ShareFrame? GetShareFrame(Id16 shareId, Id16 itemId, uint index);
void InsertShareFrame(ShareFrame frame);
IReadOnlyList<ShareFrame> ListShareFrames(Id16 shareId, Id16 itemId, uint fromIndex, int limit);
// 频道
Channel? GetChannel(Id16 id);
void InsertChannel(Channel channel);
void UpdateChannel(Id16 id, ChannelSettings settings, long expiresAt);
void TouchChannel(Id16 id, long expiresAt);
void AddChannelBytes(Id16 id, long delta);
void SetCompactedThrough(Id16 id, long seq);
long NextSeq(Id16 id);
void DeleteChannelRows(Id16 id);
IReadOnlyList<Id16> ListExpiredChannels(long now, int limit);
// 席位
Seat? GetSeat(Id16 channelId, ReadOnlySpan<byte> endpointHash);
int CountSeats(Id16 channelId);
void InsertSeat(Id16 channelId, Seat seat);
bool DeleteSeat(Id16 channelId, ReadOnlySpan<byte> endpointHash);
void SetSeatLabel(Id16 channelId, ReadOnlySpan<byte> endpointHash, byte[] label);
IReadOnlyList<Seat> ListSeats(Id16 channelId);
// 条目
Item? GetItem(Id16 channelId, Id16 itemId);
void InsertItem(Item item);
void UpdateItem(Item item);
void DeleteItemRows(Id16 channelId, Id16 itemId);
int CountItems(Id16 channelId);
IReadOnlyList<Item> ListOldestItems(Id16 channelId, int limit);
Item? FindSnapshotItem(Id16 channelId, Id16 excludeItemId);
IReadOnlyList<Item> ListExpiredItems(long now, int limit);
// 帧
StoredFrame? GetFrame(Id16 channelId, Id16 itemId, uint index);
void InsertFrame(StoredFrame frame);
IReadOnlyList<StoredFrame> ListFrames(Id16 channelId, Id16 itemId, uint fromIndex, int limit);
bool BlobKeyExists(string blobKey);
// 日志
void InsertLog(Id16 channelId, LogEntry entry);
IReadOnlyList<LogEntry> ReadLog(Id16 channelId, long fromSeq, int limit);
long PruneTombstones(Id16 channelId, long olderThanMs);
}