Integrate middleware code inside backend

This commit is contained in:
Noe 2023-11-08 11:59:15 +01:00
parent d81ed2e36c
commit 2199f43400
12 changed files with 463 additions and 73 deletions

View File

@ -23,6 +23,7 @@
<PackageReference Include="Microsoft.Owin.Security" Version="4.2.2" />
<PackageReference Include="Microsoft.Owin.Security.Cookies" Version="4.2.2" />
<PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.2.2" />
<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
<PackageReference Include="sqlite-net-sqlcipher" Version="1.9.141-beta" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
@ -51,4 +52,11 @@
</ItemGroup>
<ItemGroup>
<Reference Include="RabbitMQ.Client">
<HintPath>..\..\..\..\..\..\.nuget\packages\rabbitmq.client\6.6.0\lib\netstandard2.0\RabbitMQ.Client.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -1,7 +1,12 @@
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.App.Backend.Websockets;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc;
@ -9,10 +14,12 @@ namespace InnovEnergy.App.Backend;
using Token = String;
[Controller]
[Route("api/")]
public class Controller : ControllerBase
{
[HttpPost(nameof(Login))]
public ActionResult<Session> Login(String username, String? password)
{
@ -47,6 +54,125 @@ public class Controller : ControllerBase
: Unauthorized();
}
[HttpGet(nameof(CreateWebSocket))]
public async Task CreateWebSocket(Token authToken)
{
var session = Db.GetSession(authToken)?.User;
if (session is null)
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
HttpContext.Abort();
return;
}
if (!HttpContext.WebSockets.IsWebSocketRequest)
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
HttpContext.Abort();
return;
}
var webSocketContext = await HttpContext.WebSockets.AcceptWebSocketAsync();
var webSocket = webSocketContext;
//Handle the WebSocket connection
//WebsocketManager.HandleWebSocketConnection(webSocket)
var buffer = new byte[4096];
try
{
while (webSocket.State == WebSocketState.Open)
{
//Listen for incoming messages on this WebSocket
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
Console.WriteLine("Received a new message from websocket");
if (result.MessageType != WebSocketMessageType.Text)
continue;
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var installationIds = JsonSerializer.Deserialize<int[]>(message);
lock (WebsocketManager.InstallationConnections)
{
//Each front-end will send the list of the installations it wants to access
//If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client
//Then, report the status of each requested installation to the front-end that created the websocket connection
if (installationIds != null)
foreach (var installationId in installationIds)
{
if (!WebsocketManager.InstallationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation id " + installationId);
WebsocketManager.InstallationConnections[installationId] = new InstallationInfo
{
Status = -2
};
}
WebsocketManager.InstallationConnections[installationId].Connections.Add(webSocket);
var jsonObject = new
{
id = installationId,
status = WebsocketManager.InstallationConnections[installationId].Status
};
var jsonString = JsonSerializer.Serialize(jsonObject);
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
webSocket.SendAsync(dataToSend,
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
Console.WriteLine("Printing installation connection list");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in WebsocketManager.InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
}
lock (WebsocketManager.InstallationConnections)
{
//When the front-end terminates the connection, the following code will be executed
Console.WriteLine("The connection has been terminated");
foreach (var installationConnection in WebsocketManager.InstallationConnections)
{
if (installationConnection.Value.Connections.Contains(webSocket))
{
installationConnection.Value.Connections.Remove(webSocket);
}
}
}
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed by server", CancellationToken.None);
lock (WebsocketManager.InstallationConnections)
{
//Print the installationConnections dictionary after deleting a websocket
Console.WriteLine("Print the installation connections list after deleting a websocket");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in WebsocketManager.InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
}
catch (Exception ex)
{
Console.WriteLine("WebSocket error: " + ex.Message);
}
}
[HttpGet(nameof(GetUserById))]
public ActionResult<User> GetUserById(Int64 id, Token authToken)

View File

@ -44,7 +44,7 @@ public static class ExoCmd
messageToSign += time;
Console.WriteLine("Message to sign:\n" + messageToSign);
//Console.WriteLine("Message to sign:\n" + messageToSign);
var hmac = HmacSha256Digest(messageToSign, Secret);
@ -108,7 +108,7 @@ public static class ExoCmd
if (response.StatusCode != HttpStatusCode.OK){
Console.WriteLine("Fuck");
}
Console.WriteLine($"Created Key for {installation.Name}");
//Console.WriteLine($"Created Key for {installation.Name}");
var responseString = await response.Content.ReadAsStringAsync();
var responseJson = JsonNode.Parse(responseString) ;
@ -155,7 +155,7 @@ public static class ExoCmd
var response = await client.PostAsync(url, content);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
//Console.WriteLine(responseString);
//Put Role ID into database
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();
@ -232,7 +232,7 @@ public static class ExoCmd
var response = await client.PostAsync(url, content);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
//Console.WriteLine(responseString);
//Put Role ID into database
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();

View File

@ -1,5 +1,6 @@
using Hellang.Middleware.ProblemDetails;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Websockets;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
@ -14,6 +15,8 @@ public static class Program
//Db.CreateFakeRelations();
Db.Init();
var builder = WebApplication.CreateBuilder(args);
WebsocketManager.InformInstallationsToSubscribeToRabbitMq();
builder.Services.AddControllers();
builder.Services.AddProblemDetails(setup =>
@ -46,8 +49,9 @@ public static class Program
await next(context);
});
app.UseWebSockets();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto

View File

@ -0,0 +1,7 @@
using System.Net.WebSockets;
public class InstallationInfo
{
public int Status { get; set; }
public List<WebSocket> Connections { get; } = new List<WebSocket>();
}

View File

@ -0,0 +1,6 @@
public class StatusMessage
{
public required int InstallationId { get; init; }
public required int Status { get; init; }
}

View File

@ -0,0 +1,238 @@
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace InnovEnergy.App.Backend.Websockets;
public static class WebsocketManager
{
public static readonly Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
private static ConnectionFactory _factory = null!;
private static IConnection _connection = null!;
private static IModel _channel = null!;
public static void InformInstallationsToSubscribeToRabbitMq()
{
var installationsIds = new List<int> { 1 };
var installationIps = new List<string> { "10.2.3.115" };
var maxRetransmissions = 2;
StartRabbitMqConsumer();
UdpClient udpClient = new UdpClient();
udpClient.Client.ReceiveTimeout = 2000;
int port = 9000;
//Send a message to each installation and tell it to subscribe to the queue
for (int i = 0; i < installationsIds.Count; i++)
{
using (udpClient)
{
//Try at most MAX_RETRANSMISSIONS times to reach an installation.
for (int j = 0; j < maxRetransmissions; j++)
{
string message = "This is a message from RabbitMQ server, you can subscribe to the RabbitMQ queue";
byte[] data = Encoding.UTF8.GetBytes(message);
udpClient.Send(data, data.Length, installationIps[i], port);
Console.WriteLine($"Sent UDP message to {installationIps[i]}:{port}: {message}");
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(installationIps[i]), port);
try
{
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
string replyMessage = Encoding.UTF8.GetString(replyData);
Console.WriteLine("Received "+replyMessage +" from installation " + installationsIds[i]);
break;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine("Timed out waiting for a response. Retry...");
}
else
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
}
}
}
public static void StartRabbitMqConsumer()
{
//string vpnServerIp = "194.182.190.208";
string vpnServerIp = "127.0.0.1";
_factory = new ConnectionFactory { HostName = vpnServerIp};
_connection = _factory.CreateConnection();
_channel = _connection.CreateModel();
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (_, ea) => CallbackReceiveMessageFromQueue(ea);
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
}
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
private static void CallbackReceiveMessageFromQueue(BasicDeliverEventArgs ea)
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (InstallationConnections)
{
// Process the received message
if (receivedStatusMessage != null)
{
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId;
if (!InstallationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation: " + installationId);
InstallationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status
};
}
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in InstallationConnections)
{
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
{
Console.WriteLine("Update all the connected websockets for installation " + installationId);
installationConnection.Value.Status = receivedStatusMessage.Status;
var jsonObject = new
{
id = installationId,
status = receivedStatusMessage.Status
};
string jsonString = JsonSerializer.Serialize(jsonObject);
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
foreach (var connection in installationConnection.Value.Connections)
{
connection.SendAsync(
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
}
}
}
}
}
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static async Task HandleWebSocketConnection(WebSocket currentWebSocket)
{
var buffer = new byte[4096];
try
{
while (currentWebSocket.State == WebSocketState.Open)
{
//Listen for incoming messages on this WebSocket
var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
Console.WriteLine("Received a new message from websocket");
if (result.MessageType != WebSocketMessageType.Text)
continue;
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var installationIds = JsonSerializer.Deserialize<int[]>(message);
lock (InstallationConnections)
{
//Each front-end will send the list of the installations it wants to access
//If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client
//Then, report the status of each requested installation to the front-end that created the websocket connection
if (installationIds != null)
foreach (var installationId in installationIds)
{
if (!InstallationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation id " + installationId);
InstallationConnections[installationId] = new InstallationInfo
{
Status = -2
};
}
InstallationConnections[installationId].Connections.Add(currentWebSocket);
var jsonObject = new
{
id = installationId,
status = InstallationConnections[installationId].Status
};
var jsonString = JsonSerializer.Serialize(jsonObject);
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(dataToSend,
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
Console.WriteLine("Printing installation connection list");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
}
lock (InstallationConnections)
{
//When the front-end terminates the connection, the following code will be executed
Console.WriteLine("The connection has been terminated");
foreach (var installationConnection in InstallationConnections)
{
if (installationConnection.Value.Connections.Contains(currentWebSocket))
{
installationConnection.Value.Connections.Remove(currentWebSocket);
}
}
}
await currentWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed by server", CancellationToken.None);
lock (InstallationConnections)
{
//Print the installationConnections dictionary after deleting a websocket
Console.WriteLine("Print the installation connections list after deleting a websocket");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in InstallationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
}
catch (Exception ex)
{
Console.WriteLine("WebSocket error: " + ex.Message);
}
}
}

View File

@ -25,11 +25,11 @@ public static class Program
ExplainNode();
ExplainExit();
Console.WriteLine("");
//Console.WriteLine("");
while (true)
{
Console.WriteLine("");
//Console.WriteLine("");
Console.Write($"node{tunnel.Node}> ");
var cmd = Console.ReadLine()?.ToUpper().Trim();

View File

@ -4,6 +4,7 @@ using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using InnovEnergy.Lib.Utils;
internal class Program
{
@ -16,6 +17,7 @@ internal class Program
var installationsIds = new List<int> {1};
var installationIps = new List<string> {"10.2.3.115"};
var MAX_RETRANSMISSIONS = 2;
RabbitMqConsumer.StartRabbitMqConsumer(installationConnections,SharedDataLock);
UdpClient udpClient = new UdpClient();
@ -58,8 +60,6 @@ internal class Program
}
}
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
await WebSocketListener.StartServerAsync(installationConnections,SharedDataLock);
}

View File

@ -15,72 +15,74 @@ public static class RabbitMqConsumer
public static void StartRabbitMqConsumer(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock)
{
string vpnServerIp = "194.182.190.208";
_factory = new ConnectionFactory { HostName = vpnServerIp };
_factory = new ConnectionFactory { HostName = "localhost" };
_connection = _factory.CreateConnection();
_channel = _connection.CreateModel();
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (sharedDataLock)
{
// Process the received message
if (receivedStatusMessage != null)
{
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId;
if (!installationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation: " + installationId);
installationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status
};
}
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in installationConnections)
{
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
{
Console.WriteLine("Update all the connected websockets for installation " + installationId);
installationConnection.Value.Status = receivedStatusMessage.Status;
var jsonObject = new
{
id = installationId,
status = receivedStatusMessage.Status
};
string jsonString = JsonSerializer.Serialize(jsonObject);
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
foreach (var connection in installationConnection.Value.Connections)
{
connection.SendAsync(
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
}
}
}
}
};
consumer.Received += (_, ea) => Callback(installationConnections, sharedDataLock, ea);
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
}
private static void Callback(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock, BasicDeliverEventArgs ea)
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (sharedDataLock)
{
// Process the received message
if (receivedStatusMessage != null)
{
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId;
if (!installationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation: " + installationId);
installationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status
};
}
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in installationConnections)
{
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
{
Console.WriteLine("Update all the connected websockets for installation " + installationId);
installationConnection.Value.Status = receivedStatusMessage.Status;
var jsonObject = new
{
id = installationId,
status = receivedStatusMessage.Status
};
string jsonString = JsonSerializer.Serialize(jsonObject);
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
foreach (var connection in installationConnection.Value.Connections)
{
connection.SendAsync(
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
}
}
}
}
}
}

View File

@ -1,11 +1,13 @@
import axios from 'axios';
export const axiosConfigWithoutToken = axios.create({
baseURL: 'https://monitor.innov.energy/api'
//baseURL: 'https://monitor.innov.energy/api'
baseURL: 'http://127.0.0.1:7087/api'
});
const axiosConfig = axios.create({
baseURL: 'https://monitor.innov.energy/api'
//baseURL: 'https://monitor.innov.energy/api'
baseURL: 'http://127.0.0.1:7087/api'
});
axiosConfig.defaults.params = {};

View File

@ -1,7 +1,6 @@
import { Box, Grid, useTheme } from '@mui/material';
import InstallationTree from './InstallationTree';
import InstallationsContextProvider from 'src/contexts/InstallationsContextProvider';
import LogContextProvider from '../../../contexts/LogContextProvider';
function TreeView() {
const theme = useTheme();
@ -10,9 +9,7 @@ function TreeView() {
<InstallationsContextProvider>
<Grid item xs={12}>
<Box p={4}>
<LogContextProvider>
<InstallationTree />
</LogContextProvider>
<InstallationTree />
</Box>
</Grid>
</InstallationsContextProvider>