✨ Device alternative for related device (like watch) to connect websocket
This commit is contained in:
		| @@ -17,42 +17,52 @@ public class WebSocketController( | ||||
|     INatsConnection nats | ||||
| ) : ControllerBase | ||||
| { | ||||
|     private static readonly List<string> AllowedDeviceAlternative = ["watch"]; | ||||
|  | ||||
|     [Route("/ws")] | ||||
|     [Authorize] | ||||
|     [SwaggerIgnore] | ||||
|     public async Task TheGateway() | ||||
|     public async Task<ActionResult> 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<byte>(new WebSocketPacket | ||||
|                 new ArraySegment<byte>( | ||||
|                     new WebSocketPacket | ||||
|                     { | ||||
|                         Type = "error.dupe", | ||||
|                     ErrorMessage = "Too many connections from the same device and account." | ||||
|                 }.ToBytes()), | ||||
|                         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 | ||||
|             GrpcTypeHelper | ||||
|                 .ConvertObjectToByteString( | ||||
|                     new WebSocketConnectedEvent | ||||
|                     { | ||||
|                         AccountId = accountId, | ||||
|                         DeviceId = deviceId, | ||||
|                 IsOffline = false | ||||
|             }).ToByteArray(), | ||||
|                         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 | ||||
|                 GrpcTypeHelper | ||||
|                     .ConvertObjectToByteString( | ||||
|                         new WebSocketDisconnectedEvent | ||||
|                         { | ||||
|                             AccountId = accountId, | ||||
|                             DeviceId = deviceId, | ||||
|                     IsOffline = !WebSocketService.GetAccountIsConnected(accountId) | ||||
|                 }).ToByteArray(), | ||||
|                             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( | ||||
|   | ||||
		Reference in New Issue
	
	Block a user