diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 77a727c3c..7e3df592c 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -24,12 +24,15 @@ public class JobStatus [Controller] [Route("api/")] +//All the http requests from the frontend that contain "/api" will be forwarded to this controller from the nginx reverse proxy. public class Controller : ControllerBase { - [HttpPost(nameof(Login))] public ActionResult Login(String username, String? password) { + //Find the user to the database, verify its password and create a new session. + //Store the new session to the database and return it to the frontend. + //If the user log out, the session will be deleted. Each session is valid for 24 hours. The db deletes all invalid/expired sessions every 30 minutes. var user = Db.GetUserByEmail(username); if (user is null) @@ -37,18 +40,11 @@ public class Controller : ControllerBase if (!(user.Password.IsNullOrEmpty() && user.MustResetPassword) && !user.VerifyPassword(password)) { - //return Unauthorized("No Password set"); throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!); } - - var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user)); - //TODO The Frontend should check for the MustResetPassword Flag - - - return Db.Create(session) ? session : throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!); @@ -58,6 +54,7 @@ public class Controller : ControllerBase [HttpPost(nameof(Logout))] public ActionResult Logout(Token authToken) { + //Find the session and delete it from the database. var session = Db.GetSession(authToken); return session.Logout() diff --git a/csharp/App/Backend/DataTypes/Methods/ExoCmd.cs b/csharp/App/Backend/DataTypes/Methods/ExoCmd.cs index fde540ac2..86f22ed03 100644 --- a/csharp/App/Backend/DataTypes/Methods/ExoCmd.cs +++ b/csharp/App/Backend/DataTypes/Methods/ExoCmd.cs @@ -188,12 +188,83 @@ public static class ExoCmd return id; } - public static async Task RevokeReadKey(String S3Key) + public static async Task RemoveReadRole(this Installation installation) + { + var roleId = installation.ReadRoleId; + var url = $"https://api-ch-dk-2.exoscale.com/v2/iam-role/{roleId}"; + var method = $"iam-role/{roleId}"; + + var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60; + var authheader = "credential=" + S3Credentials.Key + ",expires=" + unixtime + ",signature=" + BuildSignature("DELETE", method, unixtime); + + + var client = new HttpClient(); + + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authheader); + + try + { + var response = await client.DeleteAsync(url); + + if (response.IsSuccessStatusCode) + { + Console.WriteLine($"Successfully deleted read role with ID {roleId}."); + return true; + } + + Console.WriteLine($"Failed to delete read role. HTTP Status: {response.StatusCode}. Response: {await response.Content.ReadAsStringAsync()}"); + return false; + } + catch (Exception ex) + { + Console.WriteLine($"Error occurred while deleting read role: {ex.Message}"); + return false; + } + } + + public static async Task RemoveWriteRole(this Installation installation) + { + var roleId = installation.WriteRoleId; + var url = $"https://api-ch-dk-2.exoscale.com/v2/iam-role/{roleId}"; + var method = $"iam-role/{roleId}"; + + var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60; + var authheader = "credential=" + S3Credentials.Key + ",expires=" + unixtime + ",signature=" + BuildSignature("DELETE", method, unixtime); + + + var client = new HttpClient(); + + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authheader); + + try + { + var response = await client.DeleteAsync(url); + + if (response.IsSuccessStatusCode) + { + Console.WriteLine($"Successfully deleted write role with ID {roleId}."); + return true; + } + + Console.WriteLine($"Failed to delete write role. HTTP Status: {response.StatusCode}. Response: {await response.Content.ReadAsStringAsync()}"); + return false; + } + catch (Exception ex) + { + Console.WriteLine($"Error occurred while deleting write role: {ex.Message}"); + return false; + } + } + + + public static async Task RevokeReadKey(this Installation installation) { //Check exoscale documentation https://openapi-v2.exoscale.com/topic/topic-api-request-signature - var url = $"https://api-ch-dk-2.exoscale.com/v2/access-key/{S3Key}"; - var method = $"access-key/{S3Key}"; + var url = $"https://api-ch-dk-2.exoscale.com/v2/access-key/{installation.S3Key}"; + var method = $"access-key/{installation.S3Key}"; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; @@ -208,12 +279,12 @@ public static class ExoCmd return response.IsSuccessStatusCode; } - public static async Task RevokeReadKey(this Installation installation) + public static async Task RevokeWriteKey(this Installation installation) { //Check exoscale documentation https://openapi-v2.exoscale.com/topic/topic-api-request-signature - var url = $"https://api-ch-dk-2.exoscale.com/v2/access-key/{installation.S3Key}"; - var method = $"access-key/{installation.S3Key}"; + var url = $"https://api-ch-dk-2.exoscale.com/v2/access-key/{installation.S3WriteKey}"; + var method = $"access-key/{installation.S3WriteKey}"; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; @@ -296,6 +367,12 @@ public static class ExoCmd return await s3Region.PutBucket(installation.BucketName()) != null; } + public static async Task DeleteBucket(this Installation installation) + { + var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Credentials!); + return await s3Region.DeleteBucket(installation.BucketName()) ; + } + public static async Task SendConfig(this Installation installation, Configuration config) { diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs index f1c275386..be2e714ac 100644 --- a/csharp/App/Backend/DataTypes/Methods/Installation.cs +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -46,12 +46,6 @@ public static class InstallationMethods return Db.Update(installation); } - - public static async Task DeleteBucket(this Installation installation) - { - // TODO We dont do this here - return true; - } public static IEnumerable UsersWithAccess(this Installation installation) { diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 330738d75..70fdd5f0f 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -274,15 +274,18 @@ public static class SessionMethods { var user = session?.User; - - return user is not null - && installation is not null - && user.UserType != 0 - && user.HasAccessTo(installation) - && Db.Delete(installation) - && await installation.DeleteBucket(); - - + + return user is not null + && installation is not null + && user.UserType != 0 + && user.HasAccessTo(installation) + && Db.Delete(installation) + && await installation.RevokeReadKey() + && await installation.RevokeWriteKey() + && await installation.DeleteBucket() + && await installation.RemoveReadRole() + && await installation.RemoveWriteRole(); + } public static Boolean Create(this Session? session, User newUser) diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index 888cb11be..7e7fecf29 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -212,7 +212,7 @@ public static class UserMethods public static Task SendPasswordResetEmail(this User user, String token) { - const String subject = "Reset the password of your InnovEnergy-Account"; + const String subject = "Reset the password of your Inesco Energy Account"; const String resetLink = "https://monitor.innov.energy/api/ResetPassword"; // TODO: move to settings file var encodedToken = HttpUtility.UrlEncode(token); @@ -225,13 +225,13 @@ public static class UserMethods public static Task SendNewUserWelcomeMessage(this User user) { - const String subject = "Your new InnovEnergy-Account"; + const String subject = "Your new Inesco Energy Account"; var resetLink = $"https://monitor.innov.energy/?username={user.Email}"; // TODO: move to settings file var body = $"Dear {user.Name}\n" + $"To set your password and log in to your " + - $"Innovenergy-Account open this link:{resetLink}"; + $"Inesco Energy Account open this link:{resetLink}"; return user.SendEmail(subject, body); } diff --git a/csharp/App/Backend/DataTypes/TreeNode.cs b/csharp/App/Backend/DataTypes/TreeNode.cs index a19d05598..87b2e24e9 100644 --- a/csharp/App/Backend/DataTypes/TreeNode.cs +++ b/csharp/App/Backend/DataTypes/TreeNode.cs @@ -1,16 +1,17 @@ using SQLite; - namespace InnovEnergy.App.Backend.DataTypes; public abstract partial class TreeNode { + //This is the parent class of each relation. It has an autoincrement Id, name, information, parent Id and Type. + //Ignore means: "Do not map this property to a database column." [PrimaryKey, AutoIncrement] - public Int64 Id { get; set; } - public virtual String Name { get; set; } = ""; // overridden by User (unique) - public String Information { get; set; } = ""; // unstructured random info + public Int64 Id { get; set; } + public virtual String Name { get; set; } = ""; // overridden by User (unique) + public String Information { get; set; } = ""; // unstructured random info - [Indexed] // parent/child relation - public Int64 ParentId { get; set; } + [Indexed] + public Int64 ParentId { get; set; } [Ignore] public String Type => GetType().Name; diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index 78fedf21f..4738c7f45 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -9,7 +9,6 @@ public class User : TreeNode public String Email { get; set; } = null!; public int UserType { get; set; } = 0; public Boolean MustResetPassword { get; set; } = false; - public String Language { get; set; } = null!; public String? Password { get; set; } = null!; [Unique] diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 6ccb53332..97074f4af 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -7,13 +7,55 @@ using InnovEnergy.Lib.Utils; using SQLite; using SQLiteConnection = SQLite.SQLiteConnection; - namespace InnovEnergy.App.Backend.Database; public static partial class Db { private static SQLiteConnection Connection { get; } = InitConnection(); + public static TableQuery Sessions => Connection.Table(); + public static TableQuery Folders => Connection.Table(); + public static TableQuery Installations => Connection.Table(); + public static TableQuery Users => Connection.Table(); + public static TableQuery FolderAccess => Connection.Table(); + public static TableQuery InstallationAccess => Connection.Table(); + public static TableQuery OrderNumber2Installation => Connection.Table(); + public static TableQuery Errors => Connection.Table(); + public static TableQuery Warnings => Connection.Table(); + public static TableQuery UserActions => Connection.Table(); + + public static void Init() + { + //Used to force static constructor + //Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class + //When a class is loaded, the fields are initialized before the constructor's code is executed. + //The TableQuery fields are lazy meaning that they will be initialized when they get accessed + //The connection searches for the latest backup and it binds all the tables to it. + } + + //This is the constructor of the class + static Db() + { + Connection.RunInTransaction(() => + { + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + Connection.CreateTable(); + }); + + //UpdateKeys(); + CleanupSessions().SupressAwaitWarning(); + DeleteSnapshots().SupressAwaitWarning(); + } + + private static SQLiteConnection InitConnection() { var latestDb = new DirectoryInfo("DbBackups") @@ -41,90 +83,60 @@ public static partial class Db return fileConnection; //return CopyDbToMemory(fileConnection); } - - private static SQLiteConnection CopyDbToMemory(SQLiteConnection fileConnection) - { - var memoryConnection = new SQLiteConnection(":memory:"); - - //Create a table if it does not exist in main memory - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - memoryConnection.CreateTable(); - fileConnection.CreateTable(); - - //Copy all the existing tables from the disk to main memory - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - fileConnection.Table().ForEach(memoryConnection.Insert); - - return memoryConnection; - } - + public static void BackupDatabase() { var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; Connection.Backup("DbBackups/" + filename); } - - public static TableQuery Sessions => Connection.Table(); - public static TableQuery Folders => Connection.Table(); - public static TableQuery Installations => Connection.Table(); - public static TableQuery Users => Connection.Table(); - public static TableQuery FolderAccess => Connection.Table(); - public static TableQuery InstallationAccess => Connection.Table(); - public static TableQuery OrderNumber2Installation => Connection.Table(); - public static TableQuery Errors => Connection.Table(); - public static TableQuery Warnings => Connection.Table(); - public static TableQuery UserActions => Connection.Table(); - public static void Init() + //Delete all by 10 snapshots every 24 hours. + private static async Task DeleteSnapshots() { - // used to force static constructor - //Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class - } - - //This is the constructor of the class - static Db() - { - Connection.RunInTransaction(() => + while (true) { - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - Connection.CreateTable(); - }); - - //UpdateKeys(); - CleanupSessions().SupressAwaitWarning(); - + try + { + var files = new DirectoryInfo("DbBackups") + .GetFiles() + .OrderByDescending(f => f.LastWriteTime); + + var filesToDelete = files.Skip(10); + + foreach (var file in filesToDelete) + { + Console.WriteLine("File to delete is " + file.Name); + file.Delete(); + } + + } + catch(Exception e) + { + Console.WriteLine("An error has occured when cleaning database snapshots, exception is:\n"+e); + } + + await Task.Delay(TimeSpan.FromHours(24)); + } } - + + //Delete all expired sessions every half an hour. An expired session is a session remained for more than 1 day. private static async Task CleanupSessions() { while (true) { try { - DeleteStaleSessions(); + var deadline = DateTime.Now.AddDays(-Session.MaxAge.Days); + foreach (var session in Sessions) + { + if (session.LastSeen < deadline) + { + Console.WriteLine("Need to remove session of user id " + session.User.Name + "last time is "+session.LastSeen); + } + + } + + Sessions.Delete(s => s.LastSeen < deadline); } catch(Exception e) { @@ -232,12 +244,6 @@ public static partial class Db } - private static void DeleteStaleSessions() - { - var deadline = DateTime.Now.AddDays((-1) * Session.MaxAge.Days); - Sessions.Delete(s => s.LastSeen < deadline); - } - private static async Task UpdateS3Urls() { var regions = Installations diff --git a/csharp/App/Backend/Database/Fake.cs b/csharp/App/Backend/Database/Fake.cs deleted file mode 100644 index 89461a9e1..000000000 --- a/csharp/App/Backend/Database/Fake.cs +++ /dev/null @@ -1,101 +0,0 @@ -using InnovEnergy.App.Backend.Relations; - -namespace InnovEnergy.App.Backend.Database; - -public static partial class Db -{ - public static void CreateFakeRelations() - { - Connection.RunInTransaction(() => - { - CreateFakeUserTree(); - CreateFakeFolderTree(); - LinkFakeInstallationsToFolders(); - GiveFakeUsersAccessToFolders(); - GiveFakeUsersAccessToInstallations(); - }); - } - - private static void CreateFakeUserTree() - { - foreach (var userId in Enumerable.Range(1, Users.Count())) - { - var user = GetUserById(userId); - if (user is null) - continue; - - user.ParentId = userId > 1 - ? Random.Shared.Next(userId - 1) + 1 - : 0; // root has parentId 0 - - Update(user); - } - } - - private static void CreateFakeFolderTree() - { - foreach (var folderId in Enumerable.Range(1, Folders.Count())) - { - var folder = GetFolderById(folderId); - if (folder is null) - continue; - - folder.ParentId = folderId > 1 - ? Random.Shared.Next(folderId - 1) + 1 - : 0; // root has parentId 0 - - Update(folder); - } - } - - private static void LinkFakeInstallationsToFolders() - { - var nFolders = Folders.Count(); - - foreach (var installation in Installations) - { - installation.ParentId = Random.Shared.Next(nFolders) + 1; - Update(installation); - } - } - - private static void GiveFakeUsersAccessToFolders() - { - foreach (var uf in FolderAccess) // remove existing relations - Connection.Delete(uf); - - var nFolders = Folders.Count(); - var nUsers = Users.Count(); - - foreach (var user in Users) - while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0) - { - var relation = new FolderAccess - { - UserId = user.Id, - FolderId = Random.Shared.Next(nFolders) + 1 - }; - Connection.Insert(relation); - } - } - - private static void GiveFakeUsersAccessToInstallations() - { - foreach (var ui in InstallationAccess) // remove existing relations - Connection.Delete(ui); - - var nbInstallations = Installations.Count(); - - foreach (var user in Users) - while (Random.Shared.Next(5) != 0) - { - var relation = new InstallationAccess - { - UserId = user.Id, - InstallationId = Random.Shared.Next(nbInstallations) + 1 - }; - Connection.Insert(relation); - } - } - //TODO fake OrderNumbers -} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Read.cs b/csharp/App/Backend/Database/Read.cs index c98465155..f47298cb3 100644 --- a/csharp/App/Backend/Database/Read.cs +++ b/csharp/App/Backend/Database/Read.cs @@ -1,12 +1,11 @@ using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.Relations; - - namespace InnovEnergy.App.Backend.Database; public static partial class Db { + //In this file, we provide all the methods that can be used in order to retrieve information from the database (read) public static Folder? GetFolderById(Int64? id) { return Folders @@ -39,14 +38,15 @@ public static partial class Db public static Session? GetSession(String token) { + //This method is called in almost every controller function. + //After logging in, the frontend receives a session object which contains a token. For all the future REST API calls, this token is used for session authentication. var session = Sessions .FirstOrDefault(s => s.Token == token); - - // cannot use session.Valid in the DB query above. - // It does not exist in the db (IgnoreAttribute) if (session is null) + { return null; + } if (!session.Valid) { diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs index 70def9fd1..1528530ff 100644 --- a/csharp/App/Backend/Database/Update.cs +++ b/csharp/App/Backend/Database/Update.cs @@ -27,11 +27,6 @@ public static partial class Db { return Update(obj: warning); } - - // public static Boolean Update(UserAction action) - // { - // return Update(obj: action); - // } public static Boolean Update(Installation installation) { diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index ec927a158..036c41d12 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Models; using InnovEnergy.Lib.Utils; -using Org.BouncyCastle.Math.EC; namespace InnovEnergy.App.Backend; @@ -15,6 +14,13 @@ public static class Program { public static async Task Main(String[] args) { + //First, we initialize the database. This is an empty constructor of the Db class that will be called. + //In addition, we initialize WatchDog in order to restart the backend service in case of failure. + //Finally, we start all the backend services. We call the InitializeEnvironment function of RabbitMqManager to create the queue (factory/connection) + //Then, we generate a consumer that binds to the queue. This is a separate async Task so it must not be awaited (it acts as a separate thread). + //Finally, we call the MonitorSalimaxInstallationTable and MonitorSalidomoInstallationTable from the WebsocketManager class. + //Those methods will build in-memory data structures to track the connected frontends and update them regarding the offline installations. + Watchdog.NotifyReady(); Db.Init(); var builder = WebApplication.CreateBuilder(args); @@ -80,7 +86,7 @@ public static class Program private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo { - Title = "InnovEnergy Backend API", + Title = "Innesco Backend API", Version = "v1" }; diff --git a/csharp/App/Backend/Relations/Session.cs b/csharp/App/Backend/Relations/Session.cs index bb0555396..d4535d5d5 100644 --- a/csharp/App/Backend/Relations/Session.cs +++ b/csharp/App/Backend/Relations/Session.cs @@ -7,41 +7,48 @@ namespace InnovEnergy.App.Backend.Relations; public class Session : Relation { - public static TimeSpan MaxAge { get; } = TimeSpan.FromDays(7); + public static TimeSpan MaxAge { get; } = TimeSpan.FromDays(1); [Unique ] public String Token { get => Left ; init => Left = value;} [Indexed] public Int64 UserId { get => Right; init => Right = value;} [Indexed] public DateTime LastSeen { get; set; } public Boolean AccessToSalimax { get; set; } = false; public Boolean AccessToSalidomo { get; set; } = false; - [Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge - && (User) is not null; - - [Ignore] public User User => _User ??= Db.GetUserById(UserId)!; - - + [Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ; + + // Private backing field private User? _User; + [Ignore] public User User + { + get => _User ??= Db.GetUserById(UserId)!; + set => _User =value; + } + + [Obsolete("To be used only by deserializer")] public Session() {} + //We need to return a session object to the frontend. Only the public fields can be included. + //For this reason, we use the public User User. It is a public field but ignored, so it can be included to the object returned + //to the frontend but it will not get inserted to the database. + //When we initialize it like that: User = Db.GetUserById(user.Id)!, the set will be called and the private member will be initialized as well. + //What if the getSession method is called from another function of the controller? + //GetSession will retrieve a session object from the database, but this does not have the metadata included (the private fields and the ignored public fields) + //Thus, the get will be called and the private field _User will be initialized on the fly. public Session(User user) { - _User = user; + User = Db.GetUserById(user.Id)!; Token = CreateToken(); UserId = user.Id; LastSeen = DateTime.Now; AccessToSalimax = user.AccessibleInstallations(product: 0).ToList().Count > 0; AccessToSalidomo = user.AccessibleInstallations(product: 1).ToList().Count > 0; - } private static String CreateToken() { - //var token = new Byte[24]; - //Random.Shared.NextBytes(token); - //return Convert.ToBase64String(token).Replace("/",""); return Guid.NewGuid().ToString("N"); } diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs index 3f911db7e..e60b90596 100644 --- a/csharp/App/Backend/Websockets/WebsockerManager.cs +++ b/csharp/App/Backend/Websockets/WebsockerManager.cs @@ -5,6 +5,7 @@ using System.Text; using System.Text.Json; using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.Backend.Websockets; @@ -21,6 +22,9 @@ public static class WebsocketManager foreach (var installationConnection in InstallationConnections){ if (installationConnection.Value.Product==0 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){ installationConnection.Value.Status = -1; + Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 0 && f.S3BucketId == installationConnection.Key); + installation.Status = -1; + installation.Apply(Db.Update); if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} } } @@ -40,12 +44,16 @@ public static class WebsocketManager // Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp); // Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp)); + Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 1 && f.S3BucketId == installationConnection.Key); + installation.Status = -1; + installation.Apply(Db.Update); + installationConnection.Value.Status = -1; if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} } } } - await Task.Delay(TimeSpan.FromMinutes(1)); + await Task.Delay(TimeSpan.FromMinutes(10)); } } diff --git a/csharp/Lib/S3Utils/S3.cs b/csharp/Lib/S3Utils/S3.cs index 0bd7eb99f..ac07cbc97 100644 --- a/csharp/Lib/S3Utils/S3.cs +++ b/csharp/Lib/S3Utils/S3.cs @@ -184,22 +184,7 @@ public static class S3 return null; } } - - // public static async Task PutBucket(this S3Region region, String name) - // { - // var request = new PutBucketRequest { BucketName = name }; - // - // var response = await region - // .GetS3Client() - // .PutBucketAsync(request); - // - // return response.HttpStatusCode switch - // { - // HttpStatusCode.OK => region.Bucket(name), - // _ => null - // }; - // } - + public static async Task PutCors(this S3Bucket bucket, CORSConfiguration corsConfiguration) { @@ -216,14 +201,15 @@ public static class S3 return response.HttpStatusCode == HttpStatusCode.OK; } - public static async Task DeleteBucket(this S3Bucket bucket) + public static async Task DeleteBucket(this S3Region region, String bucketName) { - var request = new DeleteBucketRequest { BucketName = bucket.Name }; - var response = await bucket + var request = new DeleteBucketRequest { BucketName = bucketName }; + + var response = await region .GetS3Client() .DeleteBucketAsync(request); - return response.HttpStatusCode == HttpStatusCode.OK; + return response.HttpStatusCode == HttpStatusCode.NoContent; } private static AmazonS3Client GetS3Client(this S3Url url ) => url.Bucket.GetS3Client(); diff --git a/firmware/Cerbo_Release/CerboReleaseFiles/dbus-fzsonick-48tl/config.py b/firmware/Cerbo_Release/CerboReleaseFiles/dbus-fzsonick-48tl/config.py index 53465e229..6e3d9f703 100644 --- a/firmware/Cerbo_Release/CerboReleaseFiles/dbus-fzsonick-48tl/config.py +++ b/firmware/Cerbo_Release/CerboReleaseFiles/dbus-fzsonick-48tl/config.py @@ -13,9 +13,9 @@ DEVICE_INSTANCE = 1 SERVICE_NAME_PREFIX = 'com.victronenergy.battery.' #s3 configuration -S3BUCKET = "637-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" -S3KEY = "EXOe9a2f9b47c34cf9f1b615b09" -S3SECRET = "S8MuM7k3KGAVw2iPiociaCfYVrJ5RXvozL1wY_f_i90" +S3BUCKET = "673-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" +S3KEY = "EXO270612dc3f57a61870220eea" +S3SECRET = "4fPVVN8JGnD9IY1k5RrrNUzo2L1IpR6gdSuGRB9pMWg" # driver configuration diff --git a/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/config.py b/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/config.py index 612759d55..595ed1bcb 100755 --- a/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/config.py +++ b/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/config.py @@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3' # S3 Credentials -S3BUCKET = "425-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" -S3KEY = "EXO3fb358076b23f6daebe779ac" -S3SECRET = "HnspAdjwfRjtB_6vm0aH2BUYPsPOvZW6Hya_OU0gSLU" +S3BUCKET = "140-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" +S3KEY = "EXOa947c7fc5990a7a6f6c40860" +S3SECRET = "J1yOTLbYEO6cMxQ2wgIwe__ru9-_RH5BBtKzx_2JJHk" diff --git a/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/dbus-fzsonick-48tl.py b/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/dbus-fzsonick-48tl.py index f08209798..f34de575a 100755 --- a/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/dbus-fzsonick-48tl.py +++ b/firmware/Venus_Release/VenusReleaseFiles/dbus-fzsonick-48tl/dbus-fzsonick-48tl.py @@ -549,7 +549,8 @@ def create_update_task(modbus, service, batteries): elapsed_time = time.time() - start_time create_csv_files(csv_signals, statuses, node_numbers, alarms_number_list, warnings_number_list) - + print("11111111111111111111111111111111111111111111 elapsed time is ",elapsed_time) + # keep at most 1900 files at CSV_DIR for logging and aggregation manage_csv_files(CSV_DIR, 1900) @@ -561,7 +562,7 @@ def create_update_task(modbus, service, batteries): upload_status_to_innovenergy(_socket, statuses) - logging.debug('finished update cycle\n') +# logging.debug('finished update cycleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n') alive = True diff --git a/firmware/Venus_Release/update_all_venus_installations.sh b/firmware/Venus_Release/update_all_venus_installations.sh index 3b8c6b5ab..b9dc7cc5b 100755 --- a/firmware/Venus_Release/update_all_venus_installations.sh +++ b/firmware/Venus_Release/update_all_venus_installations.sh @@ -15,7 +15,7 @@ echo -e "\n============================ Deploy ============================\n" # Steiger, Rheinau 10.2.0.188 failed with ssh -ip_addresses=("10.2.1.115" "10.2.0.238" "10.2.0.115" "10.2.0.160" "10.2.0.149") +ip_addresses=("10.2.0.219") # #ip_addresses=( #"10.2.1.70" diff --git a/typescript/frontend-marios2/package-lock.json b/typescript/frontend-marios2/package-lock.json index b8a7c6960..55246c23d 100644 --- a/typescript/frontend-marios2/package-lock.json +++ b/typescript/frontend-marios2/package-lock.json @@ -1,11 +1,11 @@ { - "name": "InnovEnergy", + "name": "Inesco Energy", "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "InnovEnergy", + "name": "Inesco Energy", "version": "2.0.0", "dependencies": { "@emotion/react": "11.9.0", diff --git a/typescript/frontend-marios2/package.json b/typescript/frontend-marios2/package.json index fb96cf903..fe0944d81 100644 --- a/typescript/frontend-marios2/package.json +++ b/typescript/frontend-marios2/package.json @@ -1,7 +1,7 @@ { - "name": "InnovEnergy", + "name": "c", "version": "2.0.0", - "title": "InnovEnergy", + "title": "Inesco Energy", "private": false, "dependencies": { "@emotion/react": "11.9.0", diff --git a/typescript/frontend-marios2/public/Logo.png b/typescript/frontend-marios2/public/Logo.png new file mode 100644 index 000000000..38b67c0f4 Binary files /dev/null and b/typescript/frontend-marios2/public/Logo.png differ diff --git a/typescript/frontend-marios2/public/index.html b/typescript/frontend-marios2/public/index.html index ee76d7b15..44d58e831 100644 --- a/typescript/frontend-marios2/public/index.html +++ b/typescript/frontend-marios2/public/index.html @@ -13,7 +13,7 @@ href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400&display=swap" rel="stylesheet" /> - InnovEnergy + Inesco Energy diff --git a/typescript/frontend-marios2/src/App.tsx b/typescript/frontend-marios2/src/App.tsx index 3ac216888..2233a4448 100644 --- a/typescript/frontend-marios2/src/App.tsx +++ b/typescript/frontend-marios2/src/App.tsx @@ -18,7 +18,6 @@ import ForgotPassword from './components/ForgotPassword'; import { axiosConfigWithoutToken } from './Resources/axiosConfig'; import InstallationsContextProvider from './contexts/InstallationsContextProvider'; import AccessContextProvider from './contexts/AccessContextProvider'; -import WebSocketContextProvider from './contexts/WebSocketContextProvider'; import SalidomoInstallationTabs from './content/dashboards/SalidomoInstallations'; import { ProductIdContext } from './contexts/ProductIdContextProvider'; @@ -125,7 +124,7 @@ function App() { locale={language} defaultLocale="en" > - + - - - + } /> @@ -160,9 +157,7 @@ function App() { path={routes.salidomo_installations + '*'} element={ - - - + } /> @@ -175,7 +170,7 @@ function App() { }> - + ); diff --git a/typescript/frontend-marios2/src/Resources/images/Logo.png b/typescript/frontend-marios2/src/Resources/images/Logo.png new file mode 100644 index 000000000..38b67c0f4 Binary files /dev/null and b/typescript/frontend-marios2/src/Resources/images/Logo.png differ diff --git a/typescript/frontend-marios2/src/Resources/images/Logo.svg b/typescript/frontend-marios2/src/Resources/images/Logo.svg new file mode 100644 index 000000000..08220ee5f --- /dev/null +++ b/typescript/frontend-marios2/src/Resources/images/Logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.png b/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.png new file mode 100644 index 000000000..acfffcd03 Binary files /dev/null and b/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.png differ diff --git a/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.svg b/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.svg new file mode 100644 index 000000000..c58778608 --- /dev/null +++ b/typescript/frontend-marios2/src/Resources/images/Logo_for_dark_bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/frontend-marios2/src/Resources/images/innovenergy-Logo_Speichern-mit-Salz_R_color.svg b/typescript/frontend-marios2/src/Resources/images/innovenergy-Logo_Speichern-mit-Salz_R_color.svg deleted file mode 100644 index 6b5ebc1a2..000000000 --- a/typescript/frontend-marios2/src/Resources/images/innovenergy-Logo_Speichern-mit-Salz_R_color.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - diff --git a/typescript/frontend-marios2/src/components/Footer/index.tsx b/typescript/frontend-marios2/src/components/Footer/index.tsx index 239c9a31e..c6567c78d 100644 --- a/typescript/frontend-marios2/src/components/Footer/index.tsx +++ b/typescript/frontend-marios2/src/components/Footer/index.tsx @@ -18,7 +18,7 @@ function Footer() { > - © 2024 - InnovEnergy AG + © 2024 - Inesco Energy Solutions AG - InnovEnergy AG + Inesco Energy Solutions AG diff --git a/typescript/frontend-marios2/src/components/ForgotPassword.tsx b/typescript/frontend-marios2/src/components/ForgotPassword.tsx index c44a4325c..4e61b6fa1 100644 --- a/typescript/frontend-marios2/src/components/ForgotPassword.tsx +++ b/typescript/frontend-marios2/src/components/ForgotPassword.tsx @@ -3,14 +3,11 @@ import { Box, Button, CircularProgress, - Container, - Grid, Modal, TextField, Typography, useTheme } from '@mui/material'; -import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png'; import { UserContext } from 'src/contexts/userContext'; import { TokenContext } from 'src/contexts/tokenContext'; import Avatar from '@mui/material/Avatar'; @@ -18,6 +15,7 @@ import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import axiosConfig from 'src/Resources/axiosConfig'; import { useNavigate } from 'react-router-dom'; import routes from 'src/Resources/routes.json'; +import inescologo from '../Resources/images/Logo.svg'; interface ForgotPasswordPromps { resetPassword: () => void; @@ -73,16 +71,6 @@ function ForgotPassword() { return ( <> - - - - - innovenergy logo - - - - - - + + + inescologo + + + @@ -134,15 +127,15 @@ function ForgotPassword() { }} /> - {loading && } + {loading && }