Update logos in frontend
Fixed delete installation method (delete read/write keys, read/write roles, buckets) Fixed bug in not-connected installation in frontend
This commit is contained in:
parent
e342cb18b1
commit
f03325a6a2
|
@ -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<Session> 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()
|
||||
|
|
|
@ -188,12 +188,83 @@ public static class ExoCmd
|
|||
return id;
|
||||
}
|
||||
|
||||
public static async Task<Boolean> RevokeReadKey(String S3Key)
|
||||
public static async Task<bool> 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<bool> 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<Boolean> 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<Boolean> RevokeReadKey(this Installation installation)
|
||||
public static async Task<Boolean> 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<Boolean> DeleteBucket(this Installation installation)
|
||||
{
|
||||
var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Credentials!);
|
||||
return await s3Region.DeleteBucket(installation.BucketName()) ;
|
||||
}
|
||||
|
||||
|
||||
public static async Task<Boolean> SendConfig(this Installation installation, Configuration config)
|
||||
{
|
||||
|
|
|
@ -47,12 +47,6 @@ public static class InstallationMethods
|
|||
}
|
||||
|
||||
|
||||
public static async Task<Boolean> DeleteBucket(this Installation installation)
|
||||
{
|
||||
// TODO We dont do this here
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<User> UsersWithAccess(this Installation installation)
|
||||
{
|
||||
return installation
|
||||
|
|
|
@ -280,8 +280,11 @@ public static class SessionMethods
|
|||
&& user.UserType != 0
|
||||
&& user.HasAccessTo(installation)
|
||||
&& Db.Delete(installation)
|
||||
&& await installation.DeleteBucket();
|
||||
|
||||
&& await installation.RevokeReadKey()
|
||||
&& await installation.RevokeWriteKey()
|
||||
&& await installation.DeleteBucket()
|
||||
&& await installation.RemoveReadRole()
|
||||
&& await installation.RemoveWriteRole();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
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
|
||||
|
||||
[Indexed] // parent/child relation
|
||||
[Indexed]
|
||||
public Int64 ParentId { get; set; }
|
||||
|
||||
[Ignore]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -7,12 +7,54 @@ 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<Session> Sessions => Connection.Table<Session>();
|
||||
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
||||
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
|
||||
public static TableQuery<User> Users => Connection.Table<User>();
|
||||
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
|
||||
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
|
||||
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
|
||||
public static TableQuery<Error> Errors => Connection.Table<Error>();
|
||||
public static TableQuery<Warning> Warnings => Connection.Table<Warning>();
|
||||
public static TableQuery<UserAction> UserActions => Connection.Table<UserAction>();
|
||||
|
||||
|
||||
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<User>();
|
||||
Connection.CreateTable<Installation>();
|
||||
Connection.CreateTable<Folder>();
|
||||
Connection.CreateTable<FolderAccess>();
|
||||
Connection.CreateTable<InstallationAccess>();
|
||||
Connection.CreateTable<Session>();
|
||||
Connection.CreateTable<OrderNumber2Installation>();
|
||||
Connection.CreateTable<Error>();
|
||||
Connection.CreateTable<Warning>();
|
||||
Connection.CreateTable<UserAction>();
|
||||
});
|
||||
|
||||
//UpdateKeys();
|
||||
CleanupSessions().SupressAwaitWarning();
|
||||
DeleteSnapshots().SupressAwaitWarning();
|
||||
}
|
||||
|
||||
|
||||
private static SQLiteConnection InitConnection()
|
||||
{
|
||||
|
@ -42,89 +84,59 @@ public static partial class Db
|
|||
//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<User>();
|
||||
memoryConnection.CreateTable<Installation>();
|
||||
memoryConnection.CreateTable<Folder>();
|
||||
memoryConnection.CreateTable<FolderAccess>();
|
||||
memoryConnection.CreateTable<InstallationAccess>();
|
||||
memoryConnection.CreateTable<Session>();
|
||||
memoryConnection.CreateTable<OrderNumber2Installation>();
|
||||
memoryConnection.CreateTable<Error>();
|
||||
memoryConnection.CreateTable<Warning>();
|
||||
fileConnection.CreateTable<UserAction>();
|
||||
|
||||
//Copy all the existing tables from the disk to main memory
|
||||
fileConnection.Table<Session>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<Folder>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<Installation>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<User>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<FolderAccess>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<InstallationAccess>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<Error>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<Warning>().ForEach(memoryConnection.Insert);
|
||||
fileConnection.Table<UserAction>().ForEach(memoryConnection.Insert);
|
||||
|
||||
return memoryConnection;
|
||||
}
|
||||
|
||||
public static void BackupDatabase()
|
||||
{
|
||||
var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite";
|
||||
Connection.Backup("DbBackups/" + filename);
|
||||
}
|
||||
|
||||
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
||||
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
||||
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
|
||||
public static TableQuery<User> Users => Connection.Table<User>();
|
||||
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
|
||||
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
|
||||
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
|
||||
public static TableQuery<Error> Errors => Connection.Table<Error>();
|
||||
public static TableQuery<Warning> Warnings => Connection.Table<Warning>();
|
||||
public static TableQuery<UserAction> UserActions => Connection.Table<UserAction>();
|
||||
|
||||
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
|
||||
while (true)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
//This is the constructor of the class
|
||||
static Db()
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
{
|
||||
Connection.CreateTable<User>();
|
||||
Connection.CreateTable<Installation>();
|
||||
Connection.CreateTable<Folder>();
|
||||
Connection.CreateTable<FolderAccess>();
|
||||
Connection.CreateTable<InstallationAccess>();
|
||||
Connection.CreateTable<Session>();
|
||||
Connection.CreateTable<OrderNumber2Installation>();
|
||||
Connection.CreateTable<Error>();
|
||||
Connection.CreateTable<Warning>();
|
||||
Connection.CreateTable<UserAction>();
|
||||
});
|
||||
|
||||
//UpdateKeys();
|
||||
CleanupSessions().SupressAwaitWarning();
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -28,11 +28,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)
|
||||
{
|
||||
return Update(obj: installation);
|
||||
|
|
|
@ -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"
|
||||
};
|
||||
|
||||
|
|
|
@ -7,41 +7,48 @@ namespace InnovEnergy.App.Backend.Relations;
|
|||
|
||||
public class Session : Relation<String, Int64>
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,21 +185,6 @@ public static class S3
|
|||
}
|
||||
}
|
||||
|
||||
// public static async Task<S3Bucket?> 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<Boolean> PutCors(this S3Bucket bucket, CORSConfiguration corsConfiguration)
|
||||
{
|
||||
|
@ -216,14 +201,15 @@ public static class S3
|
|||
return response.HttpStatusCode == HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
public static async Task<Boolean> DeleteBucket(this S3Bucket bucket)
|
||||
public static async Task<Boolean> 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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -549,6 +549,7 @@ 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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
|
@ -13,7 +13,7 @@
|
|||
href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>InnovEnergy</title>
|
||||
<title>Inesco Energy</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
<WebSocketContextProvider>
|
||||
<InstallationsContextProvider>
|
||||
<CssBaseline />
|
||||
<Routes>
|
||||
<Route
|
||||
|
@ -149,9 +148,7 @@ function App() {
|
|||
path={routes.installations + '*'}
|
||||
element={
|
||||
<AccessContextProvider>
|
||||
<InstallationsContextProvider>
|
||||
<InstallationTabs />
|
||||
</InstallationsContextProvider>
|
||||
</AccessContextProvider>
|
||||
}
|
||||
/>
|
||||
|
@ -160,9 +157,7 @@ function App() {
|
|||
path={routes.salidomo_installations + '*'}
|
||||
element={
|
||||
<AccessContextProvider>
|
||||
<InstallationsContextProvider>
|
||||
<SalidomoInstallationTabs />
|
||||
</InstallationsContextProvider>
|
||||
</AccessContextProvider>
|
||||
}
|
||||
/>
|
||||
|
@ -175,7 +170,7 @@ function App() {
|
|||
<Route path="ResetPassword" element={<ResetPassword />}></Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
</WebSocketContextProvider>
|
||||
</InstallationsContextProvider>
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.2 KiB |
|
@ -1,88 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 841.89 595.28" style="enable-background:new 0 0 841.89 595.28;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#646363;}
|
||||
.st1{fill:#F39200;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M114.05,430.69c2.55,1.07,6.32,1.94,10.5,1.94c6.47,0,12.13-2.7,12.13-9.69c0-7.03-4.99-9.02-11.06-11.01
|
||||
c-4.79-1.53-7.55-2.91-7.55-7.04c0-3.82,2.55-5.91,7.9-5.91c2.45,0,5.15,0.61,7.54,1.38l0.76-3.11c-2.24-0.87-5.71-1.53-8.56-1.53
|
||||
c-6.98,0-11.42,3.21-11.42,9.23c0,5.71,3.52,8.21,9.53,10.14c5.45,1.68,9.07,3.11,9.07,7.85c0,4.38-3.26,6.42-8.46,6.42
|
||||
c-3.98,0-7.29-1.07-9.68-1.94L114.05,430.69L114.05,430.69z M165.59,418.56c0-7.54-3.98-12.85-11.98-12.85
|
||||
c-2.86,0-6.42,0.71-9.64,2.39v33.34h3.57v-10.14c1.38,0.66,3.26,1.27,5.86,1.27C161.76,432.58,165.59,426.72,165.59,418.56
|
||||
L165.59,418.56z M147.54,428.09V410.3c1.53-0.87,3.57-1.48,5.91-1.48c5.91,0,8.41,4.38,8.41,9.79c0,6.98-2.96,10.91-8.82,10.91
|
||||
C150.6,429.52,148.92,428.81,147.54,428.09L147.54,428.09z M192.96,427.84c-2.4,1.02-5.1,1.68-7.7,1.68
|
||||
c-5.81,0-9.48-3.11-9.79-9.63h18.35c0.1-0.92,0.15-1.89,0.15-3.01c0-6.22-3.62-11.16-10.4-11.16c-7.34,0-11.77,5.61-11.77,13.41
|
||||
c0,8.77,5.2,13.51,13.2,13.51c3.21,0,6.12-0.61,8.61-1.78L192.96,427.84L192.96,427.84z M183.58,408.77c4.89,0,6.88,3.72,6.88,7.6
|
||||
c0,0.41,0,0.61-0.05,0.97h-14.88C175.99,412.24,178.79,408.77,183.58,408.77L183.58,408.77z M205.55,406.33h-3.57v25.69h3.57
|
||||
V406.33L205.55,406.33z M203.77,400.98c1.53,0,2.4-1.07,2.4-2.45c0-1.33-0.87-2.35-2.4-2.35c-1.53,0-2.4,1.02-2.4,2.35
|
||||
C201.37,399.9,202.24,400.98,203.77,400.98L203.77,400.98z M225.89,405.72c-7.85,0-12.29,5.51-12.29,13.66
|
||||
c0,8.41,4.64,13.25,12.9,13.25c2.55,0,5.15-0.66,6.98-1.63l-0.71-2.96c-1.89,0.92-4.03,1.48-6.12,1.48
|
||||
c-6.17,0-9.38-3.57-9.38-10.19c0-6.37,3.01-10.5,8.82-10.5c2.14,0,4.43,0.56,6.37,1.32l0.76-2.9
|
||||
C231.4,406.38,228.8,405.72,225.89,405.72L225.89,405.72z M244.24,432.02v-21.26c2.04-1.22,4.38-1.94,7.14-1.94
|
||||
c4.54,0,6.73,2.5,6.73,6.52v16.67h3.57V415.1c0-5.81-3.57-9.38-9.94-9.38c-3.21,0-5.45,0.82-7.49,1.89v-12.49l-3.57,0.25v36.65
|
||||
H244.24L244.24,432.02z M290.42,427.84c-2.4,1.02-5.1,1.68-7.7,1.68c-5.81,0-9.48-3.11-9.79-9.63h18.35
|
||||
c0.1-0.92,0.15-1.89,0.15-3.01c0-6.22-3.62-11.16-10.4-11.16c-7.34,0-11.78,5.61-11.78,13.41c0,8.77,5.2,13.51,13.2,13.51
|
||||
c3.21,0,6.12-0.61,8.61-1.78L290.42,427.84L290.42,427.84z M281.05,408.77c4.89,0,6.88,3.72,6.88,7.6c0,0.41,0,0.61-0.05,0.97
|
||||
h-14.88C273.45,412.24,276.25,408.77,281.05,408.77L281.05,408.77z M299.45,408.32v23.7h3.57v-21.46c1.78-1.02,4.23-1.73,7.34-1.73
|
||||
c0.61,0,1.48,0.05,2.14,0.25l0.51-3.11c-0.87-0.15-1.73-0.25-2.85-0.25C306.02,405.72,302.15,406.79,299.45,408.32L299.45,408.32z
|
||||
M322.44,432.02v-21.46c2.19-1.17,4.69-1.73,7.24-1.73c4.59,0,6.63,2.55,6.63,6.47v16.72h3.57V415.1c0-5.81-3.31-9.38-10.2-9.38
|
||||
c-3.98,0-7.7,1.02-10.81,2.6v23.7H322.44L322.44,432.02z M402.21,432.02V415.1c0-5.56-3.11-9.38-9.89-9.38
|
||||
c-3.62,0-6.63,1.17-9.12,2.65c-1.63-1.68-4.03-2.65-7.29-2.65c-3.82,0-7.39,1.02-10.45,2.6v23.7h3.57v-21.46
|
||||
c2.09-1.17,4.43-1.73,6.78-1.73c4.64,0,6.27,2.75,6.27,6.47v16.72h3.52v-16.46c0-1.68-0.26-3.16-0.71-4.49
|
||||
c1.89-1.27,4.64-2.24,7.34-2.24c4.59,0,6.42,2.75,6.42,6.47v16.72H402.21L402.21,432.02z M415.21,406.33h-3.57v25.69h3.57V406.33
|
||||
L415.21,406.33z M413.42,400.98c1.53,0,2.4-1.07,2.4-2.45c0-1.33-0.87-2.35-2.4-2.35c-1.53,0-2.4,1.02-2.4,2.35
|
||||
C411.03,399.9,411.89,400.98,413.42,400.98L413.42,400.98z M436.67,432.12l-0.26-3.01c-0.87,0.25-2.09,0.41-3.11,0.41
|
||||
c-2.75,0-4.38-1.27-4.38-5.05v-15.14h6.93v-3.01h-6.93v-6.32l-3.57,0.26v6.07h-3.87v3.01h3.87v15.14c0,5.96,3.01,8.16,7.49,8.16
|
||||
C434.32,432.63,435.49,432.43,436.67,432.12L436.67,432.12z M457.31,430.69c2.55,1.07,6.32,1.94,10.5,1.94
|
||||
c6.47,0,12.13-2.7,12.13-9.69c0-7.03-5-9.02-11.06-11.01c-4.79-1.53-7.54-2.91-7.54-7.04c0-3.82,2.55-5.91,7.9-5.91
|
||||
c2.45,0,5.15,0.61,7.54,1.38l0.76-3.11c-2.24-0.87-5.71-1.53-8.56-1.53c-6.98,0-11.42,3.21-11.42,9.23c0,5.71,3.52,8.21,9.53,10.14
|
||||
c5.45,1.68,9.07,3.11,9.07,7.85c0,4.38-3.26,6.42-8.46,6.42c-3.98,0-7.29-1.07-9.69-1.94L457.31,430.69L457.31,430.69z
|
||||
M495.28,429.78c-3.98,0-6.37-1.58-6.37-4.79c0-3.82,2.85-5.1,6.68-5.1c2.29,0,4.13,0.36,5.81,0.71v7.75
|
||||
C500.28,429.06,498.34,429.78,495.28,429.78L495.28,429.78z M504.92,430.24V415.2c0-6.12-3.31-9.48-9.94-9.48
|
||||
c-2.9,0-5.96,0.61-7.95,1.48l0.82,2.96c1.89-0.81,4.38-1.32,6.88-1.32c4.64,0,6.68,2.29,6.68,6.22V418
|
||||
c-1.84-0.41-4.08-0.71-6.17-0.71c-5.45,0.05-9.89,2.29-9.89,7.8c0,4.43,3.31,7.55,9.63,7.55
|
||||
C499.51,432.63,502.67,431.56,504.92,430.24L504.92,430.24z M517.66,395.11l-3.57,0.25v29.41c0,5.15,2.09,7.85,6.47,7.85
|
||||
c0.71,0,1.63-0.15,2.29-0.31l-0.31-2.91c-0.36,0.05-0.87,0.15-1.48,0.15c-2.6,0-3.41-1.63-3.41-4.84V395.11L517.66,395.11z
|
||||
M527.4,406.33v3.01h14.53c-3.62,6.47-9.18,13.41-15.39,20.14v2.55h20.14v-3.01h-15.65c6.07-6.63,11.01-13.15,14.89-20.14v-2.55
|
||||
H527.4L527.4,406.33z M558.08,396.34h-4.03v12.13c0,3.31,0.2,7.39,0.61,11.98h2.8c0.41-4.64,0.61-8.61,0.61-11.93V396.34
|
||||
L558.08,396.34z M558.44,430.03c0-1.33-0.87-2.29-2.29-2.29c-1.53,0-2.4,0.97-2.4,2.29s0.87,2.35,2.4,2.35
|
||||
C557.57,432.38,558.44,431.36,558.44,430.03L558.44,430.03z"/>
|
||||
<path class="st0" d="M305.72,269.82v-65.28c5.44-2.72,12-4.16,18.72-4.16c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16
|
||||
c0-19.52-11.84-30.88-34.24-30.88c-12.32,0-24.96,2.88-34.88,7.52v75.52H305.72L305.72,269.82z M399,269.82v-65.28
|
||||
c5.44-2.72,12-4.16,18.72-4.16c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16c0-19.52-11.84-30.88-34.24-30.88
|
||||
c-12.32,0-24.96,2.88-34.88,7.52v75.52H399L399,269.82z M547.32,229.18c0-25.28-14.08-42.4-38.08-42.4
|
||||
c-23.68,0-37.92,17.12-37.92,42.4c0,25.44,14.24,42.56,37.92,42.56C533.24,271.74,547.32,254.62,547.32,229.18L547.32,229.18z
|
||||
M487.8,229.18c0-17.44,7.36-28.8,21.44-28.8c14.56,0,21.44,11.36,21.44,28.8c0,17.6-6.88,28.96-21.44,28.96
|
||||
C495.16,258.14,487.8,246.78,487.8,229.18L487.8,229.18z M575.8,188.7h-16.32c3.2,31.2,11.84,53.12,28.16,81.12h17.92
|
||||
c16-28,24.32-49.92,28.16-81.12h-16.16c-2.56,27.36-9.92,47.2-20.96,67.68C585.88,235.9,578.68,216.06,575.8,188.7L575.8,188.7z"/>
|
||||
<path class="st1" d="M307.68,360.51c-7.2,3.04-15.36,4.96-23.36,4.96c-15.68,0-25.44-7.84-27.04-24.96h54.56
|
||||
c0.32-3.04,0.64-6.56,0.64-10.72c0-20.32-12.48-35.52-33.6-35.52c-23.36,0-38.24,17.44-38.24,42.4c0,27.04,16.8,42.56,42.24,42.56
|
||||
c10.4,0,20-2.08,27.52-5.6L307.68,360.51L307.68,360.51z M278.88,307.55c12.32,0,17.92,9.28,17.92,20c0,0.96,0,1.6-0.16,2.56h-39.2
|
||||
C258.88,316.35,266.4,307.55,278.88,307.55L278.88,307.55z M348.07,377.31v-65.28c5.44-2.72,12-4.16,18.72-4.16
|
||||
c12.16,0,18.08,6.72,18.08,18.4v51.04h16.16v-52.16c0-19.52-11.84-30.88-34.24-30.88c-12.32,0-24.96,2.88-34.88,7.52v75.52H348.07
|
||||
L348.07,377.31z M487.43,360.51c-7.2,3.04-15.36,4.96-23.36,4.96c-15.68,0-25.44-7.84-27.04-24.96h54.56
|
||||
c0.32-3.04,0.64-6.56,0.64-10.72c0-20.32-12.48-35.52-33.6-35.52c-23.36,0-38.24,17.44-38.24,42.4c0,27.04,16.8,42.56,42.24,42.56
|
||||
c10.4,0,20-2.08,27.52-5.6L487.43,360.51L487.43,360.51z M458.63,307.55c12.32,0,17.92,9.28,17.92,20c0,0.96,0,1.6-0.16,2.56h-39.2
|
||||
C438.63,316.35,446.15,307.55,458.63,307.55L458.63,307.55z M512.71,301.95v75.36h16.16v-65.28c4.48-2.4,10.56-4.16,18.56-4.16
|
||||
c2.56,0,5.6,0.32,8,1.12l2.24-13.6c-2.88-0.64-6.88-1.12-11.36-1.12C533.99,294.27,521.03,297.31,512.71,301.95L512.71,301.95z
|
||||
M636.05,375.07v-74.4c-7.04-3.36-17.92-6.4-29.92-6.4c-24.8,0-41.12,15.84-41.12,41.44c0,24.32,14.4,38.24,37.28,38.24
|
||||
c6.72,0,12.64-1.6,17.6-4.16v6.72c0,11.04-7.52,17.6-21.76,17.6c-8.48,0-15.36-1.28-22.4-4l-3.36,13.12
|
||||
c8.16,3.2,15.2,4.64,26.72,4.64C621.81,407.87,636.05,396.35,636.05,375.07L636.05,375.07z M581.49,334.43
|
||||
c0-16.48,9.44-26.56,24.64-26.56c5.12,0,10.24,1.28,13.76,2.56v45.6c-4,2.56-8.96,4.16-15.36,4.16
|
||||
C589.97,360.19,581.49,350.43,581.49,334.43L581.49,334.43z M700.69,372.83c12.8-22.88,22.56-47.68,25.76-76.64h-16.16
|
||||
c-2.4,27.36-8.48,45.12-18.56,65.28c-11.04-18.56-19.36-37.92-21.92-65.28h-16.32c3.36,30.72,12.64,53.44,29.76,77.76
|
||||
c-6.24,10.4-16.96,16.96-27.36,19.04l2.72,13.92C674.45,403.71,688.53,394.11,700.69,372.83L700.69,372.83z"/>
|
||||
<polygon class="st0" points="263.6,188.59 247.44,188.59 247.44,269.71 263.6,269.71 263.6,188.59 "/>
|
||||
<path class="st1" d="M255.44,174.51c6.24,0,9.92-4,9.92-9.92c0-5.6-3.68-9.6-9.92-9.6c-6.4,0-10.08,4-10.08,9.6
|
||||
C245.36,170.51,249.04,174.51,255.44,174.51L255.44,174.51z"/>
|
||||
<path class="st0" d="M666.38,255.96c0,8.32-4.87,12.94-12.92,12.94c-8.12,0-12.85-4.62-12.85-12.94c0-8.25,4.73-12.94,12.85-12.94
|
||||
C661.51,243.03,666.38,247.71,666.38,255.96L666.38,255.96z M637.36,255.96c0,9.51,6.29,15.78,16.1,15.78
|
||||
c9.61,0,16.17-6.27,16.17-15.78s-6.49-15.78-16.17-15.78C643.72,240.19,637.36,246.46,637.36,255.96L637.36,255.96z M647.78,263.69
|
||||
h3.11v-5.81h2.37c1.02,1.98,2.23,3.96,3.79,5.81h3.79c-1.83-2.18-3.31-4.23-4.4-6.27c2.23-0.73,3.25-2.38,3.25-4.62
|
||||
c0-3.1-2.03-5.15-6.49-5.15h-5.41V263.69L647.78,263.69z M653.19,250.15c2.3,0,3.52,0.79,3.52,2.64c0,1.85-1.22,2.64-3.52,2.64
|
||||
h-2.3v-5.28H653.19L653.19,250.15z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 9.0 KiB |
|
@ -18,7 +18,7 @@ function Footer() {
|
|||
>
|
||||
<Box>
|
||||
<Typography variant="subtitle1">
|
||||
© 2024 - InnovEnergy AG
|
||||
© 2024 - Inesco Energy Solutions AG
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
|
@ -33,7 +33,7 @@ function Footer() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
InnovEnergy AG
|
||||
Inesco Energy Solutions AG
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
|
@ -100,7 +88,12 @@ function ForgotPassword() {
|
|||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={inescologo} alt="inescologo" height="100" />
|
||||
</a>
|
||||
</Box>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
|
@ -134,15 +127,15 @@ function ForgotPassword() {
|
|||
}}
|
||||
/>
|
||||
|
||||
{loading && <CircularProgress sx={{ color: '#ffc04d' }} />}
|
||||
{loading && <CircularProgress sx={{ color: '#00b33c' }} />}
|
||||
|
||||
<Button
|
||||
sx={{
|
||||
mt: 3,
|
||||
mb: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
bgcolor: '#00b33c',
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
variant="contained"
|
||||
fullWidth={true}
|
||||
|
@ -181,9 +174,9 @@ function ForgotPassword() {
|
|||
sx={{
|
||||
marginTop: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
bgcolor: '#00b33c',
|
||||
color: '#111111',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
onClick={() => setErrorModalOpen(false)}
|
||||
>
|
||||
|
@ -221,9 +214,9 @@ function ForgotPassword() {
|
|||
sx={{
|
||||
marginTop: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
bgcolor: '#00b33c',
|
||||
color: '#111111',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
onClick={handleReturn}
|
||||
>
|
||||
|
|
|
@ -3,20 +3,18 @@ import {
|
|||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Grid,
|
||||
Modal,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
|
||||
import axiosConfig from 'src/Resources/axiosConfig';
|
||||
import { UserContext } from 'src/contexts/userContext';
|
||||
import { TokenContext } from 'src/contexts/tokenContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||
import inescologo from '../Resources/images/Logo.svg';
|
||||
|
||||
function ResetPassword() {
|
||||
const [username, setUsername] = useState('');
|
||||
|
@ -70,16 +68,6 @@ function ResetPassword() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
|
@ -97,7 +85,12 @@ function ResetPassword() {
|
|||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={inescologo} alt="inescologo" height="100" />
|
||||
</a>
|
||||
</Box>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
|
@ -137,7 +130,7 @@ function ResetPassword() {
|
|||
/>
|
||||
|
||||
{loading && (
|
||||
<CircularProgress sx={{ color: '#ffc04d', marginLeft: '170px' }} />
|
||||
<CircularProgress sx={{ color: '#00b33c', marginLeft: '170px' }} />
|
||||
)}
|
||||
|
||||
{password != verifypassword && (
|
||||
|
@ -155,8 +148,8 @@ function ResetPassword() {
|
|||
mt: 3,
|
||||
mb: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
bgcolor: '#00b33c',
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
variant="contained"
|
||||
fullWidth={true}
|
||||
|
@ -195,9 +188,9 @@ function ResetPassword() {
|
|||
sx={{
|
||||
marginTop: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
bgcolor: '#00b33c',
|
||||
color: '#111111',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
|
|
|
@ -3,20 +3,18 @@ import {
|
|||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Grid,
|
||||
Modal,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
|
||||
import axiosConfig from 'src/Resources/axiosConfig';
|
||||
import { UserContext } from 'src/contexts/userContext';
|
||||
import { TokenContext } from 'src/contexts/tokenContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||
import inescologo from '../Resources/images/Logo.svg';
|
||||
|
||||
function SetNewPassword() {
|
||||
const [username, setUsername] = useState('');
|
||||
|
@ -71,16 +69,6 @@ function SetNewPassword() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
|
@ -98,7 +86,12 @@ function SetNewPassword() {
|
|||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={inescologo} alt="inescologo" height="100" />
|
||||
</a>
|
||||
</Box>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
|
@ -138,7 +131,7 @@ function SetNewPassword() {
|
|||
/>
|
||||
|
||||
{loading && (
|
||||
<CircularProgress sx={{ color: '#ffc04d', marginLeft: '0px' }} />
|
||||
<CircularProgress sx={{ color: '#00b33c', marginLeft: '0px' }} />
|
||||
)}
|
||||
|
||||
{password != verifypassword && (
|
||||
|
@ -156,8 +149,8 @@ function SetNewPassword() {
|
|||
mt: 3,
|
||||
mb: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
bgcolor: '#00b33c',
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
variant="contained"
|
||||
fullWidth={true}
|
||||
|
@ -196,9 +189,9 @@ function SetNewPassword() {
|
|||
sx={{
|
||||
marginTop: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
bgcolor: '#00b33c',
|
||||
color: '#111111',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
Button,
|
||||
Checkbox,
|
||||
CircularProgress,
|
||||
Container,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
Modal,
|
||||
|
@ -15,7 +14,7 @@ import {
|
|||
import Avatar from '@mui/material/Avatar';
|
||||
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||
import Link from '@mui/material/Link';
|
||||
import innovenergyLogo from 'src/Resources/innoveng_logo_on_orange.png';
|
||||
import inescologo from 'src/Resources/images/Logo.svg';
|
||||
import { axiosConfigWithoutToken } from 'src/Resources/axiosConfig';
|
||||
import Cookies from 'universal-cookie';
|
||||
import { UserContext } from 'src/contexts/userContext';
|
||||
|
@ -102,16 +101,6 @@ function Login() {
|
|||
|
||||
return (
|
||||
<div style={{ userSelect: 'none' }}>
|
||||
<Container maxWidth="xl" sx={{ pt: 2 }} className="login">
|
||||
<Grid container>
|
||||
<Grid item xs={3} container justifyContent="flex-start" mb={2}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={innovenergyLogo} alt="innovenergy logo" height="100" />
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
|
@ -129,7 +118,12 @@ function Login() {
|
|||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#ffc04d' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
|
||||
<a href="https://monitor.innov.energy/">
|
||||
<img src={inescologo} alt="inescologo" height="100" />
|
||||
</a>
|
||||
</Box>
|
||||
<Avatar sx={{ m: 1, bgcolor: '#00b33c' }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
|
@ -185,7 +179,7 @@ function Login() {
|
|||
checked={rememberMe}
|
||||
onChange={handleRememberMeChange}
|
||||
icon={<CheckBoxOutlineBlankIcon style={{ color: 'grey' }} />}
|
||||
checkedIcon={<CheckBoxIcon style={{ color: '#ffc04d' }} />}
|
||||
checkedIcon={<CheckBoxIcon style={{ color: '#00b33c' }} />}
|
||||
style={{ marginLeft: -175 }}
|
||||
/>
|
||||
}
|
||||
|
@ -196,8 +190,8 @@ function Login() {
|
|||
sx={{
|
||||
mb: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
bgcolor: '#00b33c',
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
variant="contained"
|
||||
fullWidth={true}
|
||||
|
@ -210,7 +204,7 @@ function Login() {
|
|||
{loading && (
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: '#ffc04d',
|
||||
color: '#009933',
|
||||
marginLeft: '20px'
|
||||
}}
|
||||
/>
|
||||
|
@ -245,9 +239,9 @@ function Login() {
|
|||
sx={{
|
||||
marginTop: 2,
|
||||
textTransform: 'none',
|
||||
bgcolor: '#ffc04d',
|
||||
bgcolor: '#00b33c',
|
||||
color: '#111111',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
'&:hover': { bgcolor: '#009933' }
|
||||
}}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
|
|
|
@ -200,7 +200,7 @@ function InformationSalidomo(props: InformationSalidomoProps) {
|
|||
defaultMessage="Installation Name"
|
||||
/>
|
||||
}
|
||||
name="installationName"
|
||||
name="name"
|
||||
value={formValues.name}
|
||||
onChange={handleChange}
|
||||
variant="outlined"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CircularProgress,
|
||||
|
@ -15,7 +15,6 @@ import {
|
|||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
|
@ -26,8 +25,6 @@ interface FlatInstallationViewProps {
|
|||
|
||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus, getTestingMode } = webSocketContext;
|
||||
const navigate = useNavigate();
|
||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||
const currentLocation = useLocation();
|
||||
|
@ -35,8 +32,8 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
//Installations with alarms go first
|
||||
let a_status = getStatus(a.id);
|
||||
let b_status = getStatus(b.id);
|
||||
let a_status = a.status;
|
||||
let b_status = b.status;
|
||||
|
||||
if (a_status > b_status) {
|
||||
return -1;
|
||||
|
@ -120,7 +117,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
const isInstallationSelected =
|
||||
installation.s3BucketId === selectedInstallation;
|
||||
|
||||
const status = getStatus(installation.id);
|
||||
const status = installation.status;
|
||||
const rowStyles =
|
||||
isRowHovered === installation.s3BucketId
|
||||
? {
|
||||
|
@ -244,7 +241,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
}}
|
||||
/>
|
||||
|
||||
{getTestingMode(installation.id) && (
|
||||
{installation.testingMode && (
|
||||
<BuildIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
extractValues,
|
||||
TopologyValues
|
||||
} from 'src/content/dashboards/Log/graph.util';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import Topology from '../Topology/Topology';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Overview from '../Overview/overview';
|
||||
|
@ -44,11 +43,9 @@ function Installation(props: singleInstallationProps) {
|
|||
const location = useLocation().pathname;
|
||||
const [fetchFunctionCalled, setFetchFunctionCalled] = useState(false);
|
||||
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { getStatus, getTestingMode } = webSocketsContext;
|
||||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
const status = getStatus(props.current_installation.id);
|
||||
const status = props.current_installation.status;
|
||||
const [connected, setConnected] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
@ -138,14 +135,13 @@ function Installation(props: singleInstallationProps) {
|
|||
}
|
||||
}
|
||||
|
||||
if (i <= 0) {
|
||||
if (i >= timeperiodToSearch) {
|
||||
setConnected(false);
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
setConnected(true);
|
||||
setLoading(false);
|
||||
console.log('NUMBER OF FILES=' + Object.keys(res).length);
|
||||
|
||||
while (continueFetching.current) {
|
||||
for (const timestamp of Object.keys(res)) {
|
||||
|
@ -360,7 +356,7 @@ function Installation(props: singleInstallationProps) {
|
|||
}}
|
||||
/>
|
||||
|
||||
{getTestingMode(props.current_installation.id) && (
|
||||
{props.current_installation.testingMode && (
|
||||
<BuildIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
|
@ -456,7 +452,11 @@ function Installation(props: singleInstallationProps) {
|
|||
<Route
|
||||
path={routes.live}
|
||||
element={
|
||||
<Topology values={values} connected={connected}></Topology>
|
||||
<Topology
|
||||
values={values}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
></Topology>
|
||||
}
|
||||
/>
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { UserContext } from '../../../contexts/userContext';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import Installation from './Installation';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
||||
|
@ -33,14 +32,16 @@ function InstallationTabs() {
|
|||
];
|
||||
|
||||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const { salimaxInstallations, fetchAllInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const {
|
||||
salimaxInstallations,
|
||||
fetchAllInstallations,
|
||||
currentProduct,
|
||||
socket,
|
||||
openSocket,
|
||||
closeSocket
|
||||
} = useContext(InstallationsContext);
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
||||
|
@ -54,15 +55,15 @@ function InstallationTabs() {
|
|||
}
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (salimaxInstallations && salimaxInstallations.length > 0) {
|
||||
if (socket) {
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
openSocket(salimaxInstallations);
|
||||
}
|
||||
}, [salimaxInstallations]);
|
||||
// useEffect(() => {
|
||||
// if (salimaxInstallations && salimaxInstallations.length > 0) {
|
||||
// if (socket) {
|
||||
// closeSocket();
|
||||
// }
|
||||
//
|
||||
// openSocket(salimaxInstallations);
|
||||
// }
|
||||
// }, [salimaxInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (salimaxInstallations.length === 0) {
|
||||
|
@ -70,6 +71,17 @@ function InstallationTabs() {
|
|||
}
|
||||
}, [salimaxInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (salimaxInstallations && salimaxInstallations.length > 0) {
|
||||
if (!socket) {
|
||||
openSocket(0);
|
||||
} else if (currentProduct == 1) {
|
||||
closeSocket();
|
||||
openSocket(0);
|
||||
}
|
||||
}
|
||||
}, [salimaxInstallations, currentProduct]);
|
||||
|
||||
useEffect(() => {
|
||||
setProduct(0);
|
||||
}, []);
|
||||
|
@ -378,7 +390,11 @@ function InstallationTabs() {
|
|||
|
||||
return salimaxInstallations.length > 1 ? (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{ marginLeft: '40px', marginTop: '20px' }}
|
||||
className="mainframe"
|
||||
>
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
|
@ -440,7 +456,11 @@ function InstallationTabs() {
|
|||
</>
|
||||
) : salimaxInstallations.length === 1 ? (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{ marginLeft: '40px', marginTop: '20px' }}
|
||||
className="mainframe"
|
||||
>
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CircularProgress,
|
||||
|
@ -14,7 +14,6 @@ import {
|
|||
useTheme
|
||||
} from '@mui/material';
|
||||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
|
@ -26,17 +25,17 @@ interface FlatInstallationViewProps {
|
|||
}
|
||||
|
||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus, getTestingMode } = webSocketContext;
|
||||
// const webSocketContext = useContext(WebSocketContext);
|
||||
// const { getSortedInstallations } = webSocketContext;
|
||||
const navigate = useNavigate();
|
||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||
const currentLocation = useLocation();
|
||||
|
||||
//
|
||||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
//Installations with alarms go first
|
||||
let a_status = getStatus(a.id);
|
||||
let b_status = getStatus(b.id);
|
||||
let a_status = a.status;
|
||||
let b_status = b.status;
|
||||
|
||||
if (a_status > b_status) {
|
||||
return -1;
|
||||
|
@ -47,6 +46,16 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
return 0;
|
||||
});
|
||||
|
||||
// const sortedInstallations = useMemo(() => {
|
||||
// return [...props.installations].sort((a, b) => {
|
||||
// const a_status = getStatus(a.id) || 0;
|
||||
// const b_status = getStatus(b.id) || 0;
|
||||
// return b_status - a_status;
|
||||
// });
|
||||
// }, [props.installations, getStatus]);
|
||||
|
||||
// const sortedInstallations = getSortedInstallations();
|
||||
|
||||
const handleSelectOneInstallation = (installationID: number): void => {
|
||||
if (selectedInstallation != installationID) {
|
||||
setSelectedInstallation(installationID);
|
||||
|
@ -124,7 +133,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
const isInstallationSelected =
|
||||
installation.s3BucketId === selectedInstallation;
|
||||
|
||||
const status = getStatus(installation.id);
|
||||
const status = installation.status;
|
||||
|
||||
return (
|
||||
<HoverableTableRow
|
||||
|
@ -306,7 +315,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
: 'green'
|
||||
}}
|
||||
/>
|
||||
{getTestingMode(installation.id) && (
|
||||
{installation.testingMode && (
|
||||
<BuildIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
|
@ -315,7 +324,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
borderRadius: '50%',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
marginLeft: '15px'
|
||||
marginLeft: status != -1 ? '25px' : '0px'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
extractValues,
|
||||
TopologyValues
|
||||
} from 'src/content/dashboards/Log/graph.util';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||
|
@ -40,11 +39,9 @@ function SalidomoInstallation(props: singleInstallationProps) {
|
|||
const { currentUser } = context;
|
||||
const location = useLocation().pathname;
|
||||
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { getStatus, getTestingMode } = webSocketsContext;
|
||||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
const status = getStatus(props.current_installation.id);
|
||||
const status = props.current_installation.status;
|
||||
const [
|
||||
failedToCommunicateWithInstallation,
|
||||
setFailedToCommunicateWithInstallation
|
||||
|
@ -181,7 +178,7 @@ function SalidomoInstallation(props: singleInstallationProps) {
|
|||
}, [currentTab, location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === -1) {
|
||||
if (status === null) {
|
||||
setConnected(false);
|
||||
}
|
||||
}, [status]);
|
||||
|
@ -281,7 +278,7 @@ function SalidomoInstallation(props: singleInstallationProps) {
|
|||
}}
|
||||
/>
|
||||
|
||||
{getTestingMode(props.current_installation.id) && (
|
||||
{props.current_installation.testingMode && (
|
||||
<BuildIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { FormControl, Grid, InputAdornment, TextField } from '@mui/material';
|
||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||
import FlatInstallationView from './FlatInstallationView';
|
||||
|
@ -14,17 +14,25 @@ interface installationSearchProps {
|
|||
function InstallationSearch(props: installationSearchProps) {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const currentLocation = useLocation();
|
||||
const [filteredData, setFilteredData] = useState(props.installations);
|
||||
// const [filteredData, setFilteredData] = useState(props.installations);
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = props.installations.filter(
|
||||
const indexedData = useMemo(() => {
|
||||
return props.installations.map((item) => ({
|
||||
...item,
|
||||
nameLower: item.name.toLowerCase(),
|
||||
locationLower: item.location.toLowerCase(),
|
||||
regionLower: item.region.toLowerCase()
|
||||
}));
|
||||
}, [props.installations]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
return indexedData.filter(
|
||||
(item) =>
|
||||
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.location.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.region.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
item.nameLower.includes(searchTerm.toLowerCase()) ||
|
||||
item.locationLower.includes(searchTerm.toLowerCase()) ||
|
||||
item.regionLower.includes(searchTerm.toLowerCase())
|
||||
);
|
||||
setFilteredData(filtered);
|
||||
}, [searchTerm, props.installations]);
|
||||
}, [searchTerm, indexedData]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -7,7 +7,6 @@ import InstallationSearch from './InstallationSearch';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { UserContext } from '../../../contexts/userContext';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
import ListIcon from '@mui/icons-material/List';
|
||||
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
||||
import TreeView from '../Tree/treeView';
|
||||
|
@ -31,13 +30,18 @@ function SalidomoInstallationTabs() {
|
|||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const [fetchedInstallations, setFetchedInstallations] =
|
||||
useState<boolean>(false);
|
||||
const { salidomoInstallations, fetchAllSalidomoInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const {
|
||||
salidomoInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
currentProduct,
|
||||
socket,
|
||||
openSocket,
|
||||
closeSocket
|
||||
} = useContext(InstallationsContext);
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
// const webSocketsContext = useContext(WebSocketContext);
|
||||
// const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
@ -61,10 +65,12 @@ function SalidomoInstallationTabs() {
|
|||
|
||||
useEffect(() => {
|
||||
if (salidomoInstallations && salidomoInstallations.length > 0) {
|
||||
if (socket) {
|
||||
if (!socket) {
|
||||
openSocket(1);
|
||||
} else if (currentProduct == 0) {
|
||||
closeSocket();
|
||||
openSocket(1);
|
||||
}
|
||||
openSocket(salidomoInstallations);
|
||||
}
|
||||
}, [salidomoInstallations]);
|
||||
|
||||
|
@ -270,7 +276,11 @@ function SalidomoInstallationTabs() {
|
|||
|
||||
return salidomoInstallations.length > 1 ? (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{ marginLeft: '40px', marginTop: '20px' }}
|
||||
className="mainframe"
|
||||
>
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
|
@ -336,7 +346,11 @@ function SalidomoInstallationTabs() {
|
|||
) : salidomoInstallations.length === 1 ? (
|
||||
<>
|
||||
{' '}
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{ marginLeft: '40px', marginTop: '20px' }}
|
||||
className="mainframe"
|
||||
>
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
interface TopologyProps {
|
||||
values: TopologyValues;
|
||||
connected: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
function Topology(props: TopologyProps) {
|
||||
|
@ -35,7 +36,7 @@ function Topology(props: TopologyProps) {
|
|||
return (
|
||||
<Container maxWidth="xl" style={{ backgroundColor: 'white' }}>
|
||||
<Grid container>
|
||||
{!props.connected && (
|
||||
{!props.connected && !props.loading && (
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{
|
||||
|
|
|
@ -7,7 +7,6 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
|||
import Typography from '@mui/material/Typography';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
@ -39,9 +38,7 @@ const useTreeItemStyles = makeStyles((theme) => ({
|
|||
function CustomTreeItem(props: CustomTreeItemProps) {
|
||||
const theme = useTheme();
|
||||
const classes = useTreeItemStyles();
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketContext;
|
||||
const status = getStatus(props.node.id);
|
||||
const status = props.node.status;
|
||||
const navigate = useNavigate();
|
||||
const [selected, setSelected] = useState(false);
|
||||
const currentLocation = useLocation();
|
||||
|
|
|
@ -8,7 +8,6 @@ import Installation from '../Installations/Installation';
|
|||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
import SalidomoInstallation from '../SalidomoInstallations/Installation';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
import Folder from './Folder';
|
||||
|
@ -16,12 +15,8 @@ import Folder from './Folder';
|
|||
function InstallationTree() {
|
||||
const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketContext;
|
||||
|
||||
const sortedInstallations = [...foldersAndInstallations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
//Installations with alarms go first
|
||||
|
@ -29,8 +24,8 @@ function InstallationTree() {
|
|||
if (a.type == 'Folder') {
|
||||
return -1;
|
||||
}
|
||||
let a_status = getStatus(a.id);
|
||||
let b_status = getStatus(b.id);
|
||||
let a_status = a.status;
|
||||
let b_status = b.status;
|
||||
|
||||
if (a_status > b_status) {
|
||||
return -1;
|
||||
|
|
|
@ -4,6 +4,9 @@ import {
|
|||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import axiosConfig from 'src/Resources/axiosConfig';
|
||||
|
@ -12,48 +15,7 @@ import { TokenContext } from './tokenContext';
|
|||
import routes from '../Resources/routes.json';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface I_InstallationContextProviderProps {
|
||||
salimaxInstallations: I_Installation[];
|
||||
salidomoInstallations: I_Installation[];
|
||||
foldersAndInstallations: I_Installation[];
|
||||
fetchAllInstallations: () => Promise<void>;
|
||||
fetchAllSalidomoInstallations: () => Promise<void>;
|
||||
fetchAllFoldersAndInstallations: (product: number) => Promise<void>;
|
||||
createInstallation: (value: Partial<I_Installation>) => Promise<void>;
|
||||
updateInstallation: (value: I_Installation, view: string) => Promise<void>;
|
||||
loading: boolean;
|
||||
setLoading: (value: boolean) => void;
|
||||
error: boolean;
|
||||
setError: (value: boolean) => void;
|
||||
updated: boolean;
|
||||
setUpdated: (value: boolean) => void;
|
||||
deleteInstallation: (value: I_Installation, view: string) => Promise<void>;
|
||||
createFolder: (value: Partial<I_Folder>, product: number) => Promise<void>;
|
||||
updateFolder: (value: I_Folder, product: number) => Promise<void>;
|
||||
deleteFolder: (value: I_Folder, product: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export const InstallationsContext =
|
||||
createContext<I_InstallationContextProviderProps>({
|
||||
salimaxInstallations: [],
|
||||
salidomoInstallations: [],
|
||||
foldersAndInstallations: [],
|
||||
fetchAllInstallations: () => Promise.resolve(),
|
||||
fetchAllSalidomoInstallations: () => Promise.resolve(),
|
||||
fetchAllFoldersAndInstallations: (product: number) => Promise.resolve(),
|
||||
createInstallation: () => Promise.resolve(),
|
||||
updateInstallation: () => Promise.resolve(),
|
||||
loading: false,
|
||||
setLoading: () => {},
|
||||
error: false,
|
||||
setError: () => {},
|
||||
updated: false,
|
||||
setUpdated: () => {},
|
||||
deleteInstallation: () => Promise.resolve(),
|
||||
createFolder: () => Promise.resolve(),
|
||||
updateFolder: () => Promise.resolve(),
|
||||
deleteFolder: () => Promise.resolve()
|
||||
});
|
||||
export const InstallationsContext = createContext(null);
|
||||
|
||||
const InstallationsContextProvider = ({
|
||||
children
|
||||
|
@ -73,224 +35,286 @@ const InstallationsContextProvider = ({
|
|||
const navigate = useNavigate();
|
||||
const tokencontext = useContext(TokenContext);
|
||||
const { removeToken } = tokencontext;
|
||||
const [socket, setSocket] = useState<WebSocket>(null);
|
||||
const [currentProduct, setcurrentProduct] = useState<number>(0);
|
||||
|
||||
const pendingUpdates = useRef<
|
||||
Record<number, { status: number; testingMode: boolean }>
|
||||
>({}); // To store pending updates
|
||||
|
||||
const updateInstallationStatus = useCallback(
|
||||
(product, id, status, testingMode) => {
|
||||
// Buffer updates instead of applying them immediately
|
||||
pendingUpdates.current[id] = { status: Number(status), testingMode }; // Ensure status is a number
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const applyBatchUpdates = useCallback(() => {
|
||||
if (Object.keys(pendingUpdates.current).length === 0) return; // No updates to apply
|
||||
|
||||
const updatedSalidomo = salidomoInstallations.map((installation) => {
|
||||
const update = pendingUpdates.current[installation.id];
|
||||
return update
|
||||
? {
|
||||
...installation,
|
||||
status: update.status,
|
||||
testingMode: update.testingMode
|
||||
}
|
||||
: installation;
|
||||
});
|
||||
|
||||
const updatedSalimax = salimaxInstallations.map((installation) => {
|
||||
const update = pendingUpdates.current[installation.id];
|
||||
return update
|
||||
? {
|
||||
...installation,
|
||||
status: update.status,
|
||||
testingMode: update.testingMode
|
||||
}
|
||||
: installation;
|
||||
});
|
||||
|
||||
setSalidomoInstallations(updatedSalidomo);
|
||||
setSalimaxInstallations(updatedSalimax);
|
||||
|
||||
// Clear the pending updates after applying
|
||||
pendingUpdates.current = {};
|
||||
}, [salidomoInstallations, salimaxInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
applyBatchUpdates();
|
||||
}, 60000);
|
||||
|
||||
return () => clearInterval(timer); // Cleanup timer on component unmount
|
||||
}, [applyBatchUpdates]);
|
||||
|
||||
const openSocket = (product) => {
|
||||
setcurrentProduct(product);
|
||||
const tokenString = localStorage.getItem('token');
|
||||
const token = tokenString !== null ? tokenString : '';
|
||||
const urlWithToken = `wss://monitor.innov.energy/api/CreateWebSocket?authToken=${token}`;
|
||||
|
||||
const socket = new WebSocket(urlWithToken);
|
||||
|
||||
// Connection opened
|
||||
socket.addEventListener('open', () => {
|
||||
socket.send(
|
||||
JSON.stringify(
|
||||
product === 1
|
||||
? salidomoInstallations.map((installation) => installation.id)
|
||||
: salimaxInstallations.map((installation) => installation.id)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Periodically send ping messages to keep the connection alive
|
||||
const pingInterval = setInterval(() => {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify([-1]));
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
socket.addEventListener('message', (event) => {
|
||||
const message = JSON.parse(event.data); // Parse the JSON data
|
||||
if (message.id !== -1) {
|
||||
updateInstallationStatus(
|
||||
product,
|
||||
message.id,
|
||||
message.status,
|
||||
message.testingMode
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener('close', () => {
|
||||
clearInterval(pingInterval); // Cleanup ping interval on socket close
|
||||
setSocket(null);
|
||||
});
|
||||
|
||||
setSocket(socket);
|
||||
};
|
||||
|
||||
const closeSocket = () => socket?.close();
|
||||
|
||||
const fetchAllInstallations = useCallback(async () => {
|
||||
let isMounted = true;
|
||||
axiosConfig
|
||||
.get('/GetAllInstallations', {})
|
||||
.then((res: AxiosResponse<I_Installation[]>) => {
|
||||
setSalimaxInstallations(res.data);
|
||||
})
|
||||
.get('/GetAllInstallations')
|
||||
.then((res: AxiosResponse<I_Installation[]>) =>
|
||||
setSalimaxInstallations(res.data)
|
||||
)
|
||||
.catch((err: AxiosError) => {
|
||||
if (err.response && err.response.status == 401) {
|
||||
if (err.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, [navigate, removeToken]);
|
||||
|
||||
const fetchAllSalidomoInstallations = useCallback(async () => {
|
||||
let isMounted = true;
|
||||
axiosConfig
|
||||
.get('/GetAllSalidomoInstallations', {})
|
||||
.then((res: AxiosResponse<I_Installation[]>) => {
|
||||
setSalidomoInstallations(res.data);
|
||||
})
|
||||
.get('/GetAllSalidomoInstallations')
|
||||
.then((res: AxiosResponse<I_Installation[]>) =>
|
||||
setSalidomoInstallations(res.data)
|
||||
)
|
||||
.catch((err: AxiosError) => {
|
||||
if (err.response && err.response.status == 401) {
|
||||
if (err.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, [navigate, removeToken]);
|
||||
|
||||
const fetchAllFoldersAndInstallations = useCallback(
|
||||
async (product: number) => {
|
||||
return axiosConfig
|
||||
axiosConfig
|
||||
.get(`/GetAllFoldersAndInstallations?productId=${product}`)
|
||||
.then((res) => {
|
||||
setFoldersAndInstallations(res.data);
|
||||
})
|
||||
.then((res) => setFoldersAndInstallations(res.data))
|
||||
.catch((err) => {
|
||||
if (err.response && err.response.status == 401) {
|
||||
if (err.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[navigate, removeToken]
|
||||
);
|
||||
|
||||
const createInstallation = useCallback(
|
||||
async (formValues: Partial<I_Installation>) => {
|
||||
axiosConfig
|
||||
.post('/CreateInstallation', formValues)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
// if (formValues.product == 0) {
|
||||
// fetchAllFoldersAndInstallations();
|
||||
// } else {
|
||||
// fetchAllSalidomoInstallations();
|
||||
// }
|
||||
})
|
||||
|
||||
.then(() => fetchAllFoldersAndInstallations(formValues.product))
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[fetchAllFoldersAndInstallations, navigate, removeToken]
|
||||
);
|
||||
|
||||
const updateInstallation = useCallback(
|
||||
async (formValues: I_Installation, view: string) => {
|
||||
axiosConfig
|
||||
.put('/UpdateInstallation', formValues)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setLoading(false);
|
||||
.then(() => {
|
||||
setUpdated(true);
|
||||
if (formValues.product == 0 && view == 'installation') {
|
||||
if (formValues.product === 0 && view === 'installation')
|
||||
fetchAllInstallations();
|
||||
} else if (formValues.product == 1 && view == 'installation') {
|
||||
else if (formValues.product === 1 && view === 'installation')
|
||||
fetchAllSalidomoInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setUpdated(false);
|
||||
}, 3000);
|
||||
}
|
||||
else fetchAllFoldersAndInstallations(formValues.product);
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[
|
||||
fetchAllFoldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
navigate,
|
||||
removeToken
|
||||
]
|
||||
);
|
||||
|
||||
const deleteInstallation = useCallback(
|
||||
async (formValues: I_Installation, view: string) => {
|
||||
axiosConfig
|
||||
.delete(`/DeleteInstallation?installationId=${formValues.id}`)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setLoading(false);
|
||||
.then(() => {
|
||||
setUpdated(true);
|
||||
if (formValues.product == 0 && view == 'installation') {
|
||||
if (formValues.product === 0 && view === 'installation')
|
||||
fetchAllInstallations();
|
||||
} else if (formValues.product == 1 && view == 'installation') {
|
||||
else if (formValues.product === 1 && view === 'installation')
|
||||
fetchAllSalidomoInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setUpdated(false);
|
||||
}, 3000);
|
||||
}
|
||||
else fetchAllFoldersAndInstallations(formValues.product);
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[
|
||||
fetchAllFoldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
navigate,
|
||||
removeToken
|
||||
]
|
||||
);
|
||||
|
||||
const createFolder = useCallback(
|
||||
async (formValues: Partial<I_Folder>, product: number) => {
|
||||
axiosConfig
|
||||
.post('/CreateFolder', formValues)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
})
|
||||
|
||||
.then(() => fetchAllFoldersAndInstallations(product))
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[fetchAllFoldersAndInstallations, navigate, removeToken]
|
||||
);
|
||||
|
||||
const updateFolder = useCallback(
|
||||
async (formValues: I_Folder, product: number) => {
|
||||
axiosConfig
|
||||
.put('/UpdateFolder', formValues)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setLoading(false);
|
||||
.then(() => {
|
||||
setUpdated(true);
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
|
||||
setTimeout(() => {
|
||||
setUpdated(false);
|
||||
}, 3000);
|
||||
}
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[fetchAllFoldersAndInstallations, navigate, removeToken]
|
||||
);
|
||||
|
||||
const deleteFolder = useCallback(
|
||||
async (formValues: I_Folder, product: number) => {
|
||||
axiosConfig
|
||||
.delete(`/DeleteFolder?folderId=${formValues.id}`)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setLoading(false);
|
||||
.then(() => {
|
||||
setUpdated(true);
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
|
||||
setTimeout(() => {
|
||||
setUpdated(false);
|
||||
}, 3000);
|
||||
}
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
if (error.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[fetchAllFoldersAndInstallations, navigate, removeToken]
|
||||
);
|
||||
|
||||
return (
|
||||
<InstallationsContext.Provider
|
||||
value={{
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
salimaxInstallations,
|
||||
salidomoInstallations,
|
||||
foldersAndInstallations,
|
||||
|
@ -308,9 +332,26 @@ const InstallationsContextProvider = ({
|
|||
deleteInstallation,
|
||||
createFolder,
|
||||
updateFolder,
|
||||
deleteFolder
|
||||
}}
|
||||
>
|
||||
deleteFolder,
|
||||
currentProduct,
|
||||
socket,
|
||||
openSocket,
|
||||
closeSocket
|
||||
}),
|
||||
[
|
||||
salimaxInstallations,
|
||||
salidomoInstallations,
|
||||
foldersAndInstallations,
|
||||
loading,
|
||||
error,
|
||||
updated,
|
||||
socket,
|
||||
currentProduct
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<InstallationsContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</InstallationsContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { createContext, ReactNode, useEffect, useState } from 'react';
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState
|
||||
} from 'react';
|
||||
import { I_Installation } from '../interfaces/InstallationTypes';
|
||||
|
||||
// Define the shape of the context
|
||||
|
@ -6,8 +12,7 @@ interface WebSocketContextProviderProps {
|
|||
socket: WebSocket;
|
||||
openSocket: (installations: I_Installation[]) => void;
|
||||
closeSocket: () => void;
|
||||
getStatus: (installationId: number) => number;
|
||||
getTestingMode: (installationId: number) => boolean;
|
||||
getSortedInstallations: () => I_Installation[];
|
||||
}
|
||||
|
||||
// Create the context.
|
||||
|
@ -17,118 +22,165 @@ export const WebSocketContext = createContext<
|
|||
|
||||
const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [socket, setSocket] = useState<WebSocket>(null);
|
||||
const [installations, setInstallations] = useState<I_Installation[]>(null);
|
||||
const [installationStatus, setInstallationStatus] = useState(new Map());
|
||||
const [installationMode, setInstallationMode] = useState(new Map());
|
||||
// const [installations, setInstallations] = useState<I_Installation[]>(null);
|
||||
const [sortedInstallations, setSortedInstallations] = useState<
|
||||
I_Installation[]
|
||||
>([]);
|
||||
// const [installationStatus, setInstallationStatus] = useState(new Map());
|
||||
// const [installationMode, setInstallationMode] = useState(new Map());
|
||||
const BUFFER_LENGTH = 5;
|
||||
|
||||
const updateInstallationStatus = useCallback(
|
||||
(id, newStatus, newTestingMode) => {
|
||||
setSortedInstallations((prevInstallations) => {
|
||||
const installationIndex = prevInstallations.findIndex(
|
||||
(inst) => inst.id === id
|
||||
);
|
||||
|
||||
if (installationIndex === -1) return prevInstallations; // Installation not found
|
||||
|
||||
const updatedInstallation = {
|
||||
...prevInstallations[installationIndex],
|
||||
status: newStatus,
|
||||
testingMode: newTestingMode
|
||||
};
|
||||
|
||||
const newInstallations = [...prevInstallations];
|
||||
newInstallations[installationIndex] = updatedInstallation;
|
||||
|
||||
// Sort the installations based on the new status
|
||||
return newInstallations.sort((a, b) => b.status - a.status);
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (installations) {
|
||||
const tokenString = localStorage.getItem('token');
|
||||
const token = tokenString !== null ? tokenString : '';
|
||||
// if (sortedInstallations) {
|
||||
// const tokenString = localStorage.getItem('token');
|
||||
// const token = tokenString !== null ? tokenString : '';
|
||||
// const urlWithToken = `wss://monitor.innov.energy/api/CreateWebSocket?authToken=${token}`;
|
||||
//
|
||||
// const socket = new WebSocket(urlWithToken);
|
||||
// // Connection opened
|
||||
// socket.addEventListener('open', (event) => {
|
||||
// socket.send(
|
||||
// JSON.stringify(
|
||||
// sortedInstallations.map((installation) => installation.id)
|
||||
// )
|
||||
// );
|
||||
// });
|
||||
//
|
||||
// // Periodically send ping messages to keep the connection alive
|
||||
// const pingInterval = setInterval(() => {
|
||||
// if (socket.readyState === WebSocket.OPEN) {
|
||||
// socket.send(JSON.stringify([-1]));
|
||||
// }
|
||||
// }, 10000); // Send a ping every 10 seconds
|
||||
//
|
||||
// let messageBuffer = [];
|
||||
// let isProcessing = false;
|
||||
//
|
||||
// socket.addEventListener('message', (event) => {
|
||||
// const message = JSON.parse(event.data); // Parse the JSON data
|
||||
//
|
||||
// if (Array.isArray(message)) {
|
||||
// message.forEach((item) => {
|
||||
// console.log('status is ' + item.status);
|
||||
// // Update status and testingMode for each installation received
|
||||
// // updateInstallationStatus(item.id, item.status, item.testingMode);
|
||||
// });
|
||||
// }
|
||||
// // } else if (message.id !== -1) {
|
||||
// // // Handle individual messages for installations
|
||||
// // updateInstallationStatus(
|
||||
// // message.id,
|
||||
// // message.status,
|
||||
// // message.testingMode
|
||||
// // );
|
||||
// // }
|
||||
//
|
||||
// // if (Array.isArray(message)) {
|
||||
// // // Existing code for handling arrays, if necessary
|
||||
// // setInstallationMode((prevMode) => {
|
||||
// // const newMode = new Map(prevMode);
|
||||
// // message.forEach((item) => {
|
||||
// // newMode.set(item.id, item.testingMode);
|
||||
// // });
|
||||
// // return newMode;
|
||||
// // });
|
||||
// //
|
||||
// // setInstallationStatus((prevStatus) => {
|
||||
// // const newStatus = new Map(prevStatus);
|
||||
// // message.forEach((item) => {
|
||||
// // newStatus.set(item.id, item.status);
|
||||
// // });
|
||||
// // return newStatus;
|
||||
// // });
|
||||
// // } else if (message.id != -1) {
|
||||
// // // Accumulate messages in the buffer
|
||||
// // messageBuffer.push(message);
|
||||
// //
|
||||
// // // Process the buffer if not already processing
|
||||
// // if (!isProcessing) {
|
||||
// // isProcessing = true;
|
||||
// //
|
||||
// // // Use setTimeout to process the buffer periodically
|
||||
// // setTimeout(() => {
|
||||
// // const newInstallationMode = new Map();
|
||||
// // const newInstallationStatus = new Map();
|
||||
// //
|
||||
// // // Process all accumulated messages
|
||||
// // messageBuffer.forEach((msg) => {
|
||||
// // newInstallationMode.set(msg.id, msg.testingMode);
|
||||
// // newInstallationStatus.set(msg.id, msg.status);
|
||||
// // });
|
||||
// //
|
||||
// // // Update the state with the accumulated messages
|
||||
// // setInstallationMode(
|
||||
// // (prevMode) => new Map([...prevMode, ...newInstallationMode])
|
||||
// // );
|
||||
// // setInstallationStatus(
|
||||
// // (prevStatus) =>
|
||||
// // new Map([...prevStatus, ...newInstallationStatus])
|
||||
// // );
|
||||
// //
|
||||
// // // Clear the buffer after processing
|
||||
// // messageBuffer = [];
|
||||
// // isProcessing = false; // Reset processing flag
|
||||
// // }, 100); // Adjust the delay as needed to control processing frequency
|
||||
// // }
|
||||
// // }
|
||||
// });
|
||||
//
|
||||
// setSocket(socket);
|
||||
// }
|
||||
}, [sortedInstallations]);
|
||||
|
||||
//const urlWithToken = `ws://localhost:7087/api/CreateWebSocket?authToken=${token}`;
|
||||
const urlWithToken = `wss://monitor.innov.energy/api/CreateWebSocket?authToken=${token}`;
|
||||
//const socket = new WebSocket('wss://monitor.innov.energy/websocket');
|
||||
// const openSocket = (installations: I_Installation[]) => {
|
||||
// setInstallations(installations);
|
||||
// };
|
||||
|
||||
const socket = new WebSocket(urlWithToken);
|
||||
// Connection opened
|
||||
socket.addEventListener('open', (event) => {
|
||||
socket.send(
|
||||
JSON.stringify(installations.map((installation) => installation.id))
|
||||
);
|
||||
});
|
||||
|
||||
// Periodically send ping messages to keep the connection alive
|
||||
const pingInterval = setInterval(() => {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify([-1]));
|
||||
}
|
||||
}, 10000); // Send a ping every 10 seconds
|
||||
|
||||
let messageBuffer = [];
|
||||
let isProcessing = false;
|
||||
|
||||
socket.addEventListener('message', (event) => {
|
||||
const message = JSON.parse(event.data); // Parse the JSON data
|
||||
|
||||
if (Array.isArray(message)) {
|
||||
// Existing code for handling arrays, if necessary
|
||||
setInstallationMode((prevMode) => {
|
||||
const newMode = new Map(prevMode);
|
||||
message.forEach((item) => {
|
||||
newMode.set(item.id, item.testingMode);
|
||||
});
|
||||
return newMode;
|
||||
});
|
||||
|
||||
setInstallationStatus((prevStatus) => {
|
||||
const newStatus = new Map(prevStatus);
|
||||
message.forEach((item) => {
|
||||
newStatus.set(item.id, item.status);
|
||||
});
|
||||
return newStatus;
|
||||
});
|
||||
} else if (message.id != -1) {
|
||||
// Accumulate messages in the buffer
|
||||
messageBuffer.push(message);
|
||||
|
||||
// Process the buffer if not already processing
|
||||
if (!isProcessing) {
|
||||
isProcessing = true;
|
||||
|
||||
// Use setTimeout to process the buffer periodically
|
||||
setTimeout(() => {
|
||||
const newInstallationMode = new Map();
|
||||
const newInstallationStatus = new Map();
|
||||
|
||||
// Process all accumulated messages
|
||||
messageBuffer.forEach((msg) => {
|
||||
newInstallationMode.set(msg.id, msg.testingMode);
|
||||
newInstallationStatus.set(msg.id, msg.status);
|
||||
});
|
||||
|
||||
// Update the state with the accumulated messages
|
||||
setInstallationMode(
|
||||
(prevMode) => new Map([...prevMode, ...newInstallationMode])
|
||||
);
|
||||
setInstallationStatus(
|
||||
(prevStatus) =>
|
||||
new Map([...prevStatus, ...newInstallationStatus])
|
||||
);
|
||||
|
||||
// Clear the buffer after processing
|
||||
messageBuffer = [];
|
||||
isProcessing = false; // Reset processing flag
|
||||
}, 100); // Adjust the delay as needed to control processing frequency
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setSocket(socket);
|
||||
}
|
||||
}, [installations]);
|
||||
|
||||
const openSocket = (installations: I_Installation[]) => {
|
||||
setInstallations(installations);
|
||||
const openSocket = (installations) => {
|
||||
// setSortedInstallations(installations.sort((a, b) => b.status - a.status)); // Sort installations by status
|
||||
};
|
||||
|
||||
const closeSocket = () => {
|
||||
socket.close();
|
||||
// socket.close();
|
||||
};
|
||||
|
||||
const getStatus = (installationId: number) => {
|
||||
return installationStatus.get(installationId);
|
||||
// if (installationStatus.has(installationId)) {
|
||||
// installationStatus.get(installationId);
|
||||
// } else {
|
||||
// return -2;
|
||||
// }
|
||||
};
|
||||
// const getStatus = (installationId: number) => {
|
||||
// return installationStatus.get(installationId);
|
||||
// // if (installationStatus.has(installationId)) {
|
||||
// // installationStatus.get(installationId);
|
||||
// // } else {
|
||||
// // return -2;
|
||||
// // }
|
||||
// };
|
||||
|
||||
const getTestingMode = (installationId: number) => {
|
||||
return installationMode.get(installationId);
|
||||
};
|
||||
// const getTestingMode = (installationId: number) => {
|
||||
// return installationMode.get(installationId);
|
||||
// };
|
||||
|
||||
return (
|
||||
<WebSocketContext.Provider
|
||||
|
@ -136,8 +188,7 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
socket: socket,
|
||||
openSocket: openSocket,
|
||||
closeSocket: closeSocket,
|
||||
getStatus: getStatus,
|
||||
getTestingMode: getTestingMode
|
||||
getSortedInstallations: () => sortedInstallations
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -18,7 +18,8 @@ export interface I_Installation extends I_S3Credentials {
|
|||
s3WriteSecret: string;
|
||||
product: number;
|
||||
device: number;
|
||||
testingMode: boolean;
|
||||
testingMode?: boolean;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export interface I_Folder {
|
||||
|
@ -28,5 +29,6 @@ export interface I_Folder {
|
|||
parentId: number;
|
||||
type: string;
|
||||
s3BucketId: number;
|
||||
status?: number;
|
||||
children?: (I_Installation | I_Folder)[];
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { TokenContext } from 'src/contexts/tokenContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { WebSocketContext } from '../../../../contexts/WebSocketContextProvider';
|
||||
|
||||
import '../../../../App.css';
|
||||
import { InstallationsContext } from '../../../../contexts/InstallationsContextProvider';
|
||||
|
||||
const UserBoxButton = styled(Button)(
|
||||
({ theme }) => `
|
||||
|
@ -65,8 +66,7 @@ function HeaderUserbox() {
|
|||
const tokencontext = useContext(TokenContext);
|
||||
const { token, setNewToken, removeToken } = tokencontext;
|
||||
const navigate = useNavigate();
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { closeSocket } = webSocketsContext;
|
||||
const { closeSocket } = useContext(InstallationsContext);
|
||||
|
||||
const handleSubmit = () => {
|
||||
closeSocket();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useContext } from 'react';
|
||||
import Scrollbar from 'src/components/Scrollbar';
|
||||
import { SidebarContext } from 'src/contexts/SidebarContext';
|
||||
import innovenergyLogo from 'src/Resources/images/innovenergy-Logo_Speichern-mit-Salz_R_color.svg';
|
||||
import {
|
||||
alpha,
|
||||
Box,
|
||||
|
@ -14,10 +13,11 @@ import {
|
|||
} from '@mui/material';
|
||||
|
||||
import SidebarMenu from './SidebarMenu';
|
||||
import Logo from 'src/Resources/images/Logo_for_dark_bg.svg';
|
||||
|
||||
const SidebarWrapper = styled(Box)(
|
||||
({ theme }) => `
|
||||
width: ${theme.sidebar.width};
|
||||
width : 250px;
|
||||
min-width: ${theme.sidebar.width};
|
||||
color: ${theme.colors.alpha.trueWhite[70]};
|
||||
position: relative;
|
||||
|
@ -60,10 +60,11 @@ function Sidebar() {
|
|||
}}
|
||||
>
|
||||
<img
|
||||
src={innovenergyLogo}
|
||||
alt="innovenergy logo"
|
||||
src={Logo}
|
||||
alt="inesco logo"
|
||||
style={{
|
||||
width: '150px' // Width of the image
|
||||
marginLeft: '30px',
|
||||
width: '150px'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -105,8 +106,8 @@ function Sidebar() {
|
|||
}}
|
||||
>
|
||||
<img
|
||||
src={innovenergyLogo}
|
||||
alt="innovenergy logo"
|
||||
src={Logo}
|
||||
alt="innesco logo"
|
||||
style={{
|
||||
width: '150px' // Width of the image
|
||||
}}
|
||||
|
|
Loading…
Reference in New Issue