Files
Relay/RelayShared/Services/WsControlMessage.cs
2026-06-06 23:38:50 -04:00

112 lines
4.4 KiB
C#

namespace RelayShared.Services;
/// <summary>
/// JSON-dispatch contract for the WebSocket "control plane" (non-encrypted,
/// non-realtime requests like auth, key registration, channel CRUD, history fetches).
///
/// The server's ChatSocketBehavior.OnMessage looks at the first JSON property of every
/// incoming text frame:
/// - "Action" present → deserialise into WsControlMessage and dispatch on WsAction.
/// - "Type" present → deserialise into SocketEncryptedMessage/SocketRtcSignalMessage
/// and dispatch on SignalType (the "data plane" — chat messages,
/// RTC signals, edit/delete requests).
///
/// Responses come back as either WsEventMessage (for acks/errors) or one of the
/// Socket*Message types (for streaming data).
/// </summary>
public enum WsAction
{
/// <summary>Verify a Core-issued user token. Fields used: Username, Token.</summary>
Authenticate,
/// <summary>Register/update the client's RSA public key. Fields used: Username, PublicKey.</summary>
RegisterKey,
/// <summary>Request the server's public RSA key for outbound encryption. No fields.</summary>
GetServerKey,
/// <summary>Request the full channel list for this user. No fields.</summary>
GetChannels,
/// <summary>Request decrypted message history for a channel. Fields used: Username, ChannelId.</summary>
GetHistory,
/// <summary>Join a voice channel (presence tracking). Fields used: Username, ChannelId.</summary>
RtcJoin,
/// <summary>Leave a voice channel. Fields used: Username, ChannelId.</summary>
RtcLeave,
/// <summary>Broadcast "user is typing" to channel peers. Fields used: ChannelId.</summary>
SendTyping,
/// <summary>Request the edit-history chain for a specific message. Fields used: Username, MessageId, ChannelId.</summary>
GetEditHistory,
/// <summary>Create a new channel (permission-gated). Fields used: ChannelName, ChannelType, ChannelGroup.</summary>
CreateChannel,
/// <summary>Soft-delete a channel (permission-gated). Fields used: ChannelId.</summary>
DeleteChannel
}
/// <summary>Server-to-client event types for acks and errors.</summary>
public enum WsEvent
{
/// <summary>Reply to Authenticate. Detail = username.</summary>
Authenticated,
/// <summary>Reply to RegisterKey. Detail = username.</summary>
KeyRegistered,
/// <summary>Generic error. Detail = human-readable reason shown to the user.</summary>
Error
}
/// <summary>
/// Control-plane envelope. All fields are nullable because each action only uses a subset
/// of them. Serialised as JSON; identified by the presence of the "Action" property.
/// </summary>
public sealed class WsControlMessage
{
/// <summary>The action to perform. Server dispatches on this.</summary>
public WsAction Action { get; set; }
/// <summary>Mixed-case username as the user typed it on sign-in. Server preserves casing for display.</summary>
public string? Username { get; set; }
/// <summary>Core-issued auth token. Only set on Authenticate.</summary>
public string? Token { get; set; }
/// <summary>Base64-encoded RSA public key. Only set on RegisterKey.</summary>
public string? PublicKey { get; set; }
/// <summary>Surreal record id of a channel (e.g. "channels:xyz"). Used by most channel-scoped actions.</summary>
public string? ChannelId { get; set; }
/// <summary>Surreal record id of a message. Used by GetEditHistory.</summary>
public string? MessageId { get; set; }
/// <summary>Channel name on create (e.g. "memes"). Server normalises to lowercase-dashes.</summary>
public string? ChannelName { get; set; }
/// <summary>Integer cast of ChannelType enum (Text=0, Voice=1, …). Used on CreateChannel.</summary>
public int ChannelType { get; set; }
/// <summary>Group/category label shown in the sidebar (e.g. "General"). Optional on CreateChannel.</summary>
public string? ChannelGroup { get; set; }
}
/// <summary>
/// Server-to-client ack envelope. Identified by the "Event" JSON property
/// (vs WsControlMessage's "Action" or Socket*Message's "Type").
/// </summary>
public sealed class WsEventMessage
{
/// <summary>Which event this is acknowledging.</summary>
public WsEvent Event { get; set; }
/// <summary>Human-readable context (username on success, error message on Error).</summary>
public string? Detail { get; set; }
}