feat(server): implement sealed share lifecycle
Signed-off-by: JustMe <mr.toing@outlook.com>
This commit is contained in:
1 parent
e02e024605
commit
42b93ae2d8
45 files changed
+6327
No files matched your search
@@ -0,0 +1,128 @@
|
||||
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);
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user