diff --git a/backend/Api/AMQP/AMQPPublisher.cs b/backend/Api/AMQP/AMQPPublisher.cs index c7e08db..60bb0a4 100644 --- a/backend/Api/AMQP/AMQPPublisher.cs +++ b/backend/Api/AMQP/AMQPPublisher.cs @@ -1,7 +1,9 @@ using Api.DBAccess; using Api.Models; +using Microsoft.IdentityModel.Tokens; using RabbitMQ.Client; using RabbitMQ.Client.Events; +using System.Security.Cryptography; using System.Text; using System.Text.Json; @@ -11,34 +13,27 @@ namespace Api.AMQP { private readonly IConfiguration _configuration; private readonly DbAccess _dbAccess; + private IConnection _conn; + private IChannel _channel; + private ConnectionFactory _factory; + private string _queue; public AMQPPublisher(IConfiguration configuration, DbAccess dbAccess) { _dbAccess = dbAccess; _configuration = configuration; + _factory = new ConnectionFactory(); + _queue = "temperature-limits"; + + InitFactory(); } public async Task Handle_Push_Device_Limits() { - var factory = new ConnectionFactory(); - var queue = "temperature-limits"; - - factory.UserName = _configuration["AMQP:username"]; - factory.Password = _configuration["AMQP:password"]; - factory.HostName = _configuration["AMQP:host"]; - factory.Port = Convert.ToInt32(_configuration["AMQP:port"]); - - // Connecting to our rabbitmq and after that it create's a channel where you can connect to a queue - var conn = await factory.CreateConnectionAsync(); - Console.WriteLine("AMQPClient connected"); - var channel = await conn.CreateChannelAsync(); - - // Here we connect to the queue through the channel that got created earlier - await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false); - Console.WriteLine($"{queue} connected"); - while (true) { + await Connect(); + // Publishes all devices limits var devices = _dbAccess.ReadDevices(); foreach (var device in devices) @@ -49,33 +44,21 @@ namespace Api.AMQP deviceLimit.TempLow = device.TempLow; string message = JsonSerializer.Serialize(deviceLimit); var body = Encoding.UTF8.GetBytes(message); - await channel.BasicPublishAsync(exchange: string.Empty, routingKey: queue, body: body); + await _channel.BasicPublishAsync(exchange: string.Empty, routingKey: _queue, body: body); } // Short delay before disconnecting from rabbitMQ - await Task.Delay(10000); + await Task.Delay(1000); // Disconnecting from rabbitMQ to save resources - await channel.CloseAsync(); - Console.WriteLine($"{queue} disconnected"); - await conn.CloseAsync(); - Console.WriteLine("AMQPClient disconnected"); - await channel.DisposeAsync(); - await conn.DisposeAsync(); + await Dispose(); // 1 hour delay await Task.Delay(3600000); - // Creating a new connection to rabbitMQ - conn = await factory.CreateConnectionAsync(); - Console.WriteLine("AMQPClient connected"); - channel = await conn.CreateChannelAsync(); - - // Here we connect to the queue through the channel that got created earlier - await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false); - Console.WriteLine($"{queue} connected"); + await Connect(); // Here all messages is consumed so the queue is empty - var consumer = new AsyncEventingBasicConsumer(channel); + var consumer = new AsyncEventingBasicConsumer(_channel); consumer.ReceivedAsync += (model, ea) => { Console.WriteLine("Emptying queue"); @@ -84,9 +67,45 @@ namespace Api.AMQP }; // Consumes the data in the queue - await channel.BasicConsumeAsync(queue, true, consumer); + await _channel.BasicConsumeAsync(_queue, true, consumer); + // Short delay before disconnecting from rabbitMQ + await Task.Delay(1000); + await Dispose(); } } + + // Disconnects from rabbitMQ + private async Task Dispose() + { + await _channel.CloseAsync(); + await _conn.CloseAsync(); + await _channel.DisposeAsync(); + await _conn.DisposeAsync(); + return true; + } + + // Connects to rabbitMQ + private async Task Connect() + { + // Creating a new connection to rabbitMQ + _conn = await _factory.CreateConnectionAsync(); + Console.WriteLine("AMQPClient connected"); + _channel = await _conn.CreateChannelAsync(); + + // Here we connect to the queue through the channel that got created earlier + await _channel.QueueDeclareAsync(queue: _queue, durable: false, exclusive: false, autoDelete: false); + Console.WriteLine($"{_queue} connected"); + return true; + } + + // The info for the factory + private void InitFactory() + { + _factory.UserName = _configuration["AMQP:username"]; + _factory.Password = _configuration["AMQP:password"]; + _factory.HostName = _configuration["AMQP:host"]; + _factory.Port = Convert.ToInt32(_configuration["AMQP:port"]); + } } } diff --git a/backend/Api/AMQP/AMQPReciever.cs b/backend/Api/AMQP/AMQPReciever.cs index 558d1ca..892341e 100644 --- a/backend/Api/AMQP/AMQPReciever.cs +++ b/backend/Api/AMQP/AMQPReciever.cs @@ -4,6 +4,7 @@ using RabbitMQ.Client; using RabbitMQ.Client.Events; using System.Text; using System.Text.Json; +using System.Threading.Channels; namespace Api.AMQPReciever { @@ -11,34 +12,27 @@ namespace Api.AMQPReciever { private readonly IConfiguration _configuration; private readonly DbAccess _dbAccess; + private IConnection _conn; + private IChannel _channel; + private ConnectionFactory _factory; + private string _queue; public AMQPReciever(IConfiguration configuration, DbAccess dbAccess) { _dbAccess = dbAccess; _configuration = configuration; + _factory = new ConnectionFactory(); + _queue = "temperature-logs"; + + InitFactory(); } public async Task Handle_Received_Application_Message() { - var factory = new ConnectionFactory(); - var queue = "temperature-logs"; - - factory.UserName = _configuration["AMQP:username"]; - factory.Password = _configuration["AMQP:password"]; - factory.HostName = _configuration["AMQP:host"]; - factory.Port = Convert.ToInt32(_configuration["AMQP:port"]); - - // Connecting to our rabbitmq and after that it create's a channel where you can connect to a queue - using var conn = await factory.CreateConnectionAsync(); - Console.WriteLine("AMQPClient connected"); - using var channel = await conn.CreateChannelAsync(); - - // Here we connect to the queue through the channel that got created earlier - await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false); - Console.WriteLine($"{queue} connected"); + await Connect(); // Everytime a message is recieved from the queue it goes into this consumer.ReceivedAsync - var consumer = new AsyncEventingBasicConsumer(channel); + var consumer = new AsyncEventingBasicConsumer(_channel); consumer.ReceivedAsync += (model, ea) => { Console.WriteLine("Received application message."); @@ -73,9 +67,44 @@ namespace Api.AMQPReciever }; // Consumes the data in the queue - await channel.BasicConsumeAsync(queue, true, consumer); + await _channel.BasicConsumeAsync(_queue, true, consumer); while (true); + + await Dispose(); + } + + // Disconnects from rabbitMQ + private async Task Dispose() + { + await _channel.CloseAsync(); + await _conn.CloseAsync(); + await _channel.DisposeAsync(); + await _conn.DisposeAsync(); + return true; + } + + // Connects to rabbitMQ + private async Task Connect() + { + // Creating a new connection to rabbitMQ + _conn = await _factory.CreateConnectionAsync(); + Console.WriteLine("AMQPClient connected"); + _channel = await _conn.CreateChannelAsync(); + + // Here we connect to the queue through the channel that got created earlier + await _channel.QueueDeclareAsync(queue: _queue, durable: false, exclusive: false, autoDelete: false); + Console.WriteLine($"{_queue} connected"); + return true; + } + + // The info for the factory + private void InitFactory() + { + _factory.UserName = _configuration["AMQP:username"]; + _factory.Password = _configuration["AMQP:password"]; + _factory.HostName = _configuration["AMQP:host"]; + _factory.Port = Convert.ToInt32(_configuration["AMQP:port"]); } } } diff --git a/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.Designer.cs b/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.Designer.cs new file mode 100644 index 0000000..ec38fc2 --- /dev/null +++ b/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.Designer.cs @@ -0,0 +1,138 @@ +// +using System; +using Api; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Migrations +{ + [DbContext(typeof(DBContext))] + [Migration("20250327084557_AddedRefreshTokenToUser")] + partial class AddedRefreshTokenToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); + + modelBuilder.Entity("Api.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ReferenceId") + .HasColumnType("TEXT"); + + b.Property("TempHigh") + .HasColumnType("REAL"); + + b.Property("TempLow") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("Api.Models.TemperatureLogs", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceId") + .HasColumnType("INTEGER"); + + b.Property("TempHigh") + .HasColumnType("REAL"); + + b.Property("TempLow") + .HasColumnType("REAL"); + + b.Property("Temperature") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.ToTable("TemperatureLogs"); + }); + + modelBuilder.Entity("Api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiresAt") + .HasColumnType("TEXT"); + + b.Property("Salt") + .HasColumnType("TEXT"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Api.Models.Device", b => + { + b.HasOne("Api.Models.User", null) + .WithMany("Devices") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Api.Models.TemperatureLogs", b => + { + b.HasOne("Api.Models.Device", null) + .WithMany("Logs") + .HasForeignKey("DeviceId"); + }); + + modelBuilder.Entity("Api.Models.Device", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Api.Models.User", b => + { + b.Navigation("Devices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.cs b/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.cs new file mode 100644 index 0000000..63ab30f --- /dev/null +++ b/backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Migrations +{ + /// + public partial class AddedRefreshTokenToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RefreshToken", + table: "Users", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "RefreshTokenExpiresAt", + table: "Users", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RefreshToken", + table: "Users"); + + migrationBuilder.DropColumn( + name: "RefreshTokenExpiresAt", + table: "Users"); + } + } +} diff --git a/backend/Api/Migrations/DBContextModelSnapshot.cs b/backend/Api/Migrations/DBContextModelSnapshot.cs index 69184bb..6d96bdd 100644 --- a/backend/Api/Migrations/DBContextModelSnapshot.cs +++ b/backend/Api/Migrations/DBContextModelSnapshot.cs @@ -88,6 +88,12 @@ namespace Api.Migrations .IsRequired() .HasColumnType("TEXT"); + b.Property("RefreshToken") + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiresAt") + .HasColumnType("TEXT"); + b.Property("Salt") .HasColumnType("TEXT"); diff --git a/backend/Api/Models/User.cs b/backend/Api/Models/User.cs index 4583fab..542547e 100644 --- a/backend/Api/Models/User.cs +++ b/backend/Api/Models/User.cs @@ -12,6 +12,10 @@ public string? Salt { get; set; } + public string? RefreshToken { get; set; } + + public DateTime RefreshTokenExpiresAt { get; set; } + public List? Devices { get; set; } } }