From ab23f87a665e0c443982bd17e4ace046dc10b9ff Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 30 Oct 2025 21:26:58 +0800 Subject: [PATCH] :sparkles: Device alternative for related device (like watch) to connect websocket --- .../Connection/WebSocketController.cs | 90 ++++++++++++------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/DysonNetwork.Ring/Connection/WebSocketController.cs b/DysonNetwork.Ring/Connection/WebSocketController.cs index 5fc710c..c533e1f 100644 --- a/DysonNetwork.Ring/Connection/WebSocketController.cs +++ b/DysonNetwork.Ring/Connection/WebSocketController.cs @@ -17,42 +17,52 @@ public class WebSocketController( INatsConnection nats ) : ControllerBase { + private static readonly List AllowedDeviceAlternative = ["watch"]; + [Route("/ws")] [Authorize] [SwaggerIgnore] - public async Task TheGateway() + public async Task TheGateway([FromQuery] string? deviceAlt) { + if (string.IsNullOrWhiteSpace(deviceAlt)) + deviceAlt = null; + if (deviceAlt is not null && !AllowedDeviceAlternative.Contains(deviceAlt)) + return BadRequest("Unsupported device alternative: " + deviceAlt); + HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue); - if (currentUserValue is not Account currentUser || - currentSessionValue is not AuthSession currentSession) + if ( + currentUserValue is not Account currentUser + || currentSessionValue is not AuthSession currentSession + ) { - HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; - return; + return Unauthorized(); } var accountId = Guid.Parse(currentUser.Id!); var deviceId = currentSession.Challenge?.DeviceId ?? Guid.NewGuid().ToString(); if (string.IsNullOrEmpty(deviceId)) - { - HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; - return; - } + return BadRequest("Unable to get device ID from session."); + if (deviceAlt is not null) + deviceId = $"{deviceId}+{deviceAlt}"; - var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(new WebSocketAcceptContext - { KeepAliveInterval = TimeSpan.FromSeconds(60) }); + var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync( + new WebSocketAcceptContext { KeepAliveInterval = TimeSpan.FromSeconds(60) } + ); var cts = new CancellationTokenSource(); var connectionKey = (accountId, deviceId); if (!ws.TryAdd(connectionKey, webSocket, cts)) { await webSocket.SendAsync( - new ArraySegment(new WebSocketPacket - { - Type = "error.dupe", - ErrorMessage = "Too many connections from the same device and account." - }.ToBytes()), + new ArraySegment( + new WebSocketPacket + { + Type = "error.dupe", + ErrorMessage = "Too many connections from the same device and account.", + }.ToBytes() + ), WebSocketMessageType.Binary, true, CancellationToken.None @@ -62,21 +72,26 @@ public class WebSocketController( "Too many connections from the same device and account.", CancellationToken.None ); - return; + return new EmptyResult(); } logger.LogDebug( - $"Connection established with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}"); + $"Connection established with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}" + ); // Broadcast WebSocket connected event await nats.PublishAsync( WebSocketConnectedEvent.Type, - GrpcTypeHelper.ConvertObjectToByteString(new WebSocketConnectedEvent - { - AccountId = accountId, - DeviceId = deviceId, - IsOffline = false - }).ToByteArray(), + GrpcTypeHelper + .ConvertObjectToByteString( + new WebSocketConnectedEvent + { + AccountId = accountId, + DeviceId = deviceId, + IsOffline = false, + } + ) + .ToByteArray(), cancellationToken: cts.Token ); @@ -84,7 +99,11 @@ public class WebSocketController( { await _ConnectionEventLoop(deviceId, currentUser, webSocket, cts.Token); } - catch (WebSocketException ex) when (ex.Message.Contains("The remote party closed the WebSocket connection without completing the close handshake")) + catch (WebSocketException ex) + when (ex.Message.Contains( + "The remote party closed the WebSocket connection without completing the close handshake" + ) + ) { logger.LogDebug( "WebSocket disconnected with user @{UserName}#{UserId} and device #{DeviceId} - client closed connection without proper handshake", @@ -95,7 +114,8 @@ public class WebSocketController( } catch (Exception ex) { - logger.LogError(ex, + logger.LogError( + ex, "WebSocket disconnected with user @{UserName}#{UserId} and device #{DeviceId} unexpectedly", currentUser.Name, currentUser.Id, @@ -109,12 +129,16 @@ public class WebSocketController( // Broadcast WebSocket disconnected event await nats.PublishAsync( WebSocketDisconnectedEvent.Type, - GrpcTypeHelper.ConvertObjectToByteString(new WebSocketDisconnectedEvent - { - AccountId = accountId, - DeviceId = deviceId, - IsOffline = !WebSocketService.GetAccountIsConnected(accountId) - }).ToByteArray(), + GrpcTypeHelper + .ConvertObjectToByteString( + new WebSocketDisconnectedEvent + { + AccountId = accountId, + DeviceId = deviceId, + IsOffline = !WebSocketService.GetAccountIsConnected(accountId), + } + ) + .ToByteArray(), cancellationToken: cts.Token ); @@ -122,6 +146,8 @@ public class WebSocketController( $"Connection disconnected with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}" ); } + + return new EmptyResult(); } private async Task _ConnectionEventLoop(