✨ Device alternative for related device (like watch) to connect websocket
This commit is contained in:
		| @@ -17,42 +17,52 @@ public class WebSocketController( | |||||||
|     INatsConnection nats |     INatsConnection nats | ||||||
| ) : ControllerBase | ) : ControllerBase | ||||||
| { | { | ||||||
|  |     private static readonly List<string> AllowedDeviceAlternative = ["watch"]; | ||||||
|  |  | ||||||
|     [Route("/ws")] |     [Route("/ws")] | ||||||
|     [Authorize] |     [Authorize] | ||||||
|     [SwaggerIgnore] |     [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("CurrentUser", out var currentUserValue); | ||||||
|         HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue); |         HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue); | ||||||
|         if (currentUserValue is not Account currentUser || |         if ( | ||||||
|             currentSessionValue is not AuthSession currentSession) |             currentUserValue is not Account currentUser | ||||||
|  |             || currentSessionValue is not AuthSession currentSession | ||||||
|  |         ) | ||||||
|         { |         { | ||||||
|             HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; |             return Unauthorized(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var accountId = Guid.Parse(currentUser.Id!); |         var accountId = Guid.Parse(currentUser.Id!); | ||||||
|         var deviceId = currentSession.Challenge?.DeviceId ?? Guid.NewGuid().ToString(); |         var deviceId = currentSession.Challenge?.DeviceId ?? Guid.NewGuid().ToString(); | ||||||
|  |  | ||||||
|         if (string.IsNullOrEmpty(deviceId)) |         if (string.IsNullOrEmpty(deviceId)) | ||||||
|         { |             return BadRequest("Unable to get device ID from session."); | ||||||
|             HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; |         if (deviceAlt is not null) | ||||||
|             return; |             deviceId = $"{deviceId}+{deviceAlt}"; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(new WebSocketAcceptContext |         var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync( | ||||||
|             { KeepAliveInterval = TimeSpan.FromSeconds(60) }); |             new WebSocketAcceptContext { KeepAliveInterval = TimeSpan.FromSeconds(60) } | ||||||
|  |         ); | ||||||
|         var cts = new CancellationTokenSource(); |         var cts = new CancellationTokenSource(); | ||||||
|         var connectionKey = (accountId, deviceId); |         var connectionKey = (accountId, deviceId); | ||||||
|  |  | ||||||
|         if (!ws.TryAdd(connectionKey, webSocket, cts)) |         if (!ws.TryAdd(connectionKey, webSocket, cts)) | ||||||
|         { |         { | ||||||
|             await webSocket.SendAsync( |             await webSocket.SendAsync( | ||||||
|                 new ArraySegment<byte>(new WebSocketPacket |                 new ArraySegment<byte>( | ||||||
|  |                     new WebSocketPacket | ||||||
|                     { |                     { | ||||||
|                         Type = "error.dupe", |                         Type = "error.dupe", | ||||||
|                     ErrorMessage = "Too many connections from the same device and account." |                         ErrorMessage = "Too many connections from the same device and account.", | ||||||
|                 }.ToBytes()), |                     }.ToBytes() | ||||||
|  |                 ), | ||||||
|                 WebSocketMessageType.Binary, |                 WebSocketMessageType.Binary, | ||||||
|                 true, |                 true, | ||||||
|                 CancellationToken.None |                 CancellationToken.None | ||||||
| @@ -62,21 +72,26 @@ public class WebSocketController( | |||||||
|                 "Too many connections from the same device and account.", |                 "Too many connections from the same device and account.", | ||||||
|                 CancellationToken.None |                 CancellationToken.None | ||||||
|             ); |             ); | ||||||
|             return; |             return new EmptyResult(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         logger.LogDebug( |         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 |         // Broadcast WebSocket connected event | ||||||
|         await nats.PublishAsync( |         await nats.PublishAsync( | ||||||
|             WebSocketConnectedEvent.Type, |             WebSocketConnectedEvent.Type, | ||||||
|             GrpcTypeHelper.ConvertObjectToByteString(new WebSocketConnectedEvent |             GrpcTypeHelper | ||||||
|  |                 .ConvertObjectToByteString( | ||||||
|  |                     new WebSocketConnectedEvent | ||||||
|                     { |                     { | ||||||
|                         AccountId = accountId, |                         AccountId = accountId, | ||||||
|                         DeviceId = deviceId, |                         DeviceId = deviceId, | ||||||
|                 IsOffline = false |                         IsOffline = false, | ||||||
|             }).ToByteArray(), |                     } | ||||||
|  |                 ) | ||||||
|  |                 .ToByteArray(), | ||||||
|             cancellationToken: cts.Token |             cancellationToken: cts.Token | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
| @@ -84,7 +99,11 @@ public class WebSocketController( | |||||||
|         { |         { | ||||||
|             await _ConnectionEventLoop(deviceId, currentUser, webSocket, cts.Token); |             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( |             logger.LogDebug( | ||||||
|                 "WebSocket disconnected with user @{UserName}#{UserId} and device #{DeviceId} - client closed connection without proper handshake", |                 "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) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|             logger.LogError(ex, |             logger.LogError( | ||||||
|  |                 ex, | ||||||
|                 "WebSocket disconnected with user @{UserName}#{UserId} and device #{DeviceId} unexpectedly", |                 "WebSocket disconnected with user @{UserName}#{UserId} and device #{DeviceId} unexpectedly", | ||||||
|                 currentUser.Name, |                 currentUser.Name, | ||||||
|                 currentUser.Id, |                 currentUser.Id, | ||||||
| @@ -109,12 +129,16 @@ public class WebSocketController( | |||||||
|             // Broadcast WebSocket disconnected event |             // Broadcast WebSocket disconnected event | ||||||
|             await nats.PublishAsync( |             await nats.PublishAsync( | ||||||
|                 WebSocketDisconnectedEvent.Type, |                 WebSocketDisconnectedEvent.Type, | ||||||
|                 GrpcTypeHelper.ConvertObjectToByteString(new WebSocketDisconnectedEvent |                 GrpcTypeHelper | ||||||
|  |                     .ConvertObjectToByteString( | ||||||
|  |                         new WebSocketDisconnectedEvent | ||||||
|                         { |                         { | ||||||
|                             AccountId = accountId, |                             AccountId = accountId, | ||||||
|                             DeviceId = deviceId, |                             DeviceId = deviceId, | ||||||
|                     IsOffline = !WebSocketService.GetAccountIsConnected(accountId) |                             IsOffline = !WebSocketService.GetAccountIsConnected(accountId), | ||||||
|                 }).ToByteArray(), |                         } | ||||||
|  |                     ) | ||||||
|  |                     .ToByteArray(), | ||||||
|                 cancellationToken: cts.Token |                 cancellationToken: cts.Token | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
| @@ -122,6 +146,8 @@ public class WebSocketController( | |||||||
|                 $"Connection disconnected with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}" |                 $"Connection disconnected with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}" | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         return new EmptyResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private async Task _ConnectionEventLoop( |     private async Task _ConnectionEventLoop( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user