using System.Buffers.Binary; using System.Text.Json.Serialization; namespace WebDrop.Core; public sealed class CreateChannelRequest { [JsonPropertyName("key_package")] public string? KeyPackage { get; init; } [JsonPropertyName("join_password")] public string? JoinPassword { get; init; } [JsonPropertyName("id")] public string? Id { get; init; } [JsonPropertyName("mode")] public ChannelMode? Mode { get; init; } [JsonPropertyName("settings")] public ChannelSettings? Settings { get; init; } [JsonPropertyName("read_hash")] public string? ReadHash { get; init; } [JsonPropertyName("write_hash")] public string? WriteHash { get; init; } [JsonPropertyName("owner_hash")] public string? OwnerHash { get; init; } } public sealed class CreateShareRequest { [JsonPropertyName("id")] public string? Id { get; init; } [JsonPropertyName("read_hash")] public string? ReadHash { get; init; } [JsonPropertyName("owner_hash")] public string? OwnerHash { get; init; } [JsonPropertyName("settings")] public ShareSettings? Settings { get; init; } } public sealed class FinishShareRequest { [JsonPropertyName("manifest_item_id")] public string? ManifestItemId { get; init; } } public sealed class ShareInfo { [JsonPropertyName("id")] public required string Id { get; init; } [JsonPropertyName("state")] public required string State { get; init; } [JsonPropertyName("expires_at")] public required long ExpiresAt { get; init; } [JsonPropertyName("created_at")] public required long CreatedAt { get; init; } [JsonPropertyName("sealed_at")] public long? SealedAt { get; init; } [JsonPropertyName("manifest_item_id")] public string? ManifestItemId { get; init; } [JsonPropertyName("settings")] public required ShareSettings Settings { get; init; } [JsonPropertyName("items")] public required IReadOnlyList Items { get; init; } } public sealed class ShareItemInfo { [JsonPropertyName("complete")] public bool Complete { get; init; } [JsonPropertyName("item_id")] public required string ItemId { get; init; } [JsonPropertyName("bytes")] public required long Bytes { get; init; } [JsonPropertyName("frames")] public required int Frames { get; init; } [JsonPropertyName("max_downloads")] public required long MaxDownloads { get; init; } [JsonPropertyName("downloads_left")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public long? DownloadsLeft { get; init; } } public sealed class ChannelInfo { // ───── 调用者在这个空间里的能力。前端只读不算:规则只在后端一处 /// 调用者是空间所有者 [JsonPropertyName("is_owner")] public bool IsOwner { get; init; } [JsonPropertyName("can_upload")] public bool CanUpload { get; init; } /// 能重命名频道、管理分组(所有者,或被授予「管理频道和分组」) [JsonPropertyName("can_manage_structure")] public bool CanManageStructure { get; init; } [JsonPropertyName("can_create_channel")] public bool CanCreateChannel { get; init; } [JsonPropertyName("can_manage_folders")] public bool CanManageFolders { get; init; } [JsonPropertyName("can_invite")] public bool CanInvite { get; init; } [JsonPropertyName("can_edit_settings")] public bool CanEditSettings { get; init; } [JsonPropertyName("can_manage_roles")] public bool CanManageRoles { get; init; } /// 能移除成员、撤销邀请;具体能移除谁见成员列表里每一项的 can_remove [JsonPropertyName("can_remove_members")] public bool CanRemoveMembers { get; init; } /// 能删除任何人的消息和文件(所有者,或删除策略为 anyone) [JsonPropertyName("can_delete_any")] public bool CanDeleteAny { get; init; } /// 能删除自己发的消息和文件 [JsonPropertyName("can_delete_own")] public bool CanDeleteOwn { get; init; } [JsonPropertyName("password_required")] public bool PasswordRequired { get; init; } [JsonPropertyName("creator_account")] public string? CreatorAccount { get; init; } /// 头像版本标记,变了就要重新取头像(GET /channels/{id}/avatar);没有头像为 null。头像本体不随空间信息下发。 [JsonPropertyName("avatar_version")] public string? AvatarVersion { get; init; } [JsonPropertyName("id")] public required string Id { get; init; } [JsonPropertyName("mode")] public required ChannelMode Mode { get; init; } [JsonPropertyName("settings")] public required ChannelSettings Settings { get; init; } /// 频道成员数(账号),不统计账号设备数。 [JsonPropertyName("members_used")] public required int MembersUsed { get; init; } /// 从该 seq 起的续传有效;0 < from < oldest_seq 的续传会得到 410,需从 0 全量同步。 [JsonPropertyName("oldest_seq")] public required long OldestSeq { get; init; } [JsonPropertyName("last_seq")] public required long LastSeq { get; init; } [JsonPropertyName("created_at")] public required long CreatedAt { get; init; } [JsonPropertyName("expires_at")] public required long ExpiresAt { get; init; } } public sealed class JoinResult { [JsonPropertyName("joined_at")] public required long JoinedAt { get; init; } /// 加入后的成员人数,同一账号的其他终端不增加此值。 [JsonPropertyName("members_used")] public required int MembersUsed { get; init; } [JsonPropertyName("members_max")] public required int MembersMax { get; init; } } public sealed class AppendResult { [JsonPropertyName("seq")] public required long Seq { get; init; } /// true 为新追加(201),false 为幂等重发(200)。 [JsonIgnore] public bool Created { get; init; } } /// 一条日志记录的线格式:seq(u64) ‖ kind(u8) ‖ body。 public sealed record LogRecord(long Seq, byte[] Bytes) { public static byte[] Encode(long seq, LogKind kind, ReadOnlySpan body) { var bytes = new byte[9 + body.Length]; BinaryPrimitives.WriteInt64BigEndian(bytes, seq); bytes[8] = (byte)kind; body.CopyTo(bytes.AsSpan(9)); return bytes; } public static byte[] EncodeTombstone(long seq, Id16 itemId, TombstoneReason reason) { Span body = stackalloc byte[Id16.Size + 1]; itemId.WriteTo(body); body[Id16.Size] = (byte)reason; return Encode(seq, LogKind.Tombstone, body); } public static byte[] EncodeAnnounce(long seq, Id16 itemId, FrameFlags flags, long createdMs) { Span body = stackalloc byte[Id16.Size + 1 + 8]; itemId.WriteTo(body); body[Id16.Size] = (byte)flags; BinaryPrimitives.WriteInt64BigEndian(body[(Id16.Size + 1)..], createdMs); return Encode(seq, LogKind.Announce, body); } } public sealed record LogPage(IReadOnlyList Records, long LastSeq); /// GATED 条目拉取的一个批次:自 FromIndex 起连续的完整封装。 public sealed record ItemChunk(IReadOnlyList Frames, uint NextIndex, bool Complete); [JsonSerializable(typeof(ChannelSettings))] [JsonSerializable(typeof(ShareSettings))] public sealed partial class CoreJsonContext : JsonSerializerContext;