152 lines
6.2 KiB
C#
152 lines
6.2 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 MembersChanged(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 MembersChanged(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);
|
|
WorkspaceState? GetWorkspace(Id16 id);
|
|
CryptoState GetWorkspaceCrypto(Id16 id);
|
|
void PutWorkspaceCrypto(Id16 id, CryptoState state);
|
|
IReadOnlyList<Id16> ListAccountSpaces(string username, long now);
|
|
void PutWorkspace(Id16 id, WorkspaceState state);
|
|
void UpdateChannel(Id16 id, ChannelSettings settings, long expiresAt);
|
|
void SetJoinPassword(Id16 id, byte[]? hash, byte[]? salt);
|
|
bool HasInvitation(Id16 id, string username);
|
|
void PutInvitation(ChannelInvitation invitation);
|
|
IReadOnlyList<ChannelInvitation> ListInvitations(Id16 id);
|
|
IReadOnlyList<ChannelInvitation> ListUserInvitations(string username);
|
|
void DeleteInvitation(Id16 id, string username);
|
|
void PutKeyPackage(Id16 id, string username, string payload, long createdAt);
|
|
void DeleteKeyPackage(Id16 id, string username);
|
|
IReadOnlyList<ChannelKeyPackage> ListUserKeyPackages(string username, long now);
|
|
ChannelMember? GetChannelMember(Id16 id, string username);
|
|
IReadOnlyList<ChannelMember> ListChannelMembers(Id16 id);
|
|
void PutChannelMember(Id16 id, ChannelMember member);
|
|
void DeleteChannelMember(Id16 id, string username);
|
|
bool IsJoinDenied(Id16 id, string username);
|
|
void DenyAccount(Id16 id, string username);
|
|
void AllowAccount(Id16 id, string username);
|
|
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);
|
|
/// <summary>空间头像(data URL)单独存放,不放进每次请求都要读的设置 JSON</summary>
|
|
/// <summary>thumb 为 true 取缩略图(没有就用原图),否则取原图</summary>
|
|
string? GetChannelAvatar(Id16 id, bool thumb);
|
|
void SetChannelAvatar(Id16 id, string? avatar, string? thumb);
|
|
IReadOnlyList<Id16> ListExpiredChannels(long now, int limit);
|
|
|
|
// 成员
|
|
int CountMembers(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);
|
|
int CountControlItems(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);
|
|
}
|