Fixed exoscale keys

This commit is contained in:
Noe 2023-11-20 17:29:45 +01:00
parent 6a18e56cf7
commit ac8f874255
14 changed files with 407 additions and 209 deletions

View File

@ -100,6 +100,23 @@ public class Controller : ControllerBase
.Where(error => error.InstallationId == id) .Where(error => error.InstallationId == id)
.ToList(); .ToList();
} }
[HttpGet(nameof(GetAllWarningsForInstallation))]
public ActionResult<IEnumerable<Warning>> GetAllWarningsForInstallation(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var installation = Db.GetInstallationById(id);
if (installation is null || !user.HasAccessTo(installation))
return Unauthorized();
return Db.Warnings
.Where(error => error.InstallationId == id)
.ToList();
}
[HttpGet(nameof(GetUserById))] [HttpGet(nameof(GetUserById))]
public ActionResult<User> GetUserById(Int64 id, Token authToken) public ActionResult<User> GetUserById(Int64 id, Token authToken)

View File

@ -2,13 +2,22 @@ using SQLite;
namespace InnovEnergy.App.Backend.DataTypes; namespace InnovEnergy.App.Backend.DataTypes;
public class Error public abstract class LogEntry
{ {
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public Int64 Id { get; set; } public Int64 Id { get; set; }
public Int64 InstallationId { get; set; } public Int64 InstallationId { get; set; }
public String ErrorDescription { get; set; } = null!; public String Description { get; set; } = null!;
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public String DeviceCreatedTheError { get; set; } = null!; public String DeviceCreatedTheMessage { get; set; } = null!;
public Boolean Seen { get; set; } public Boolean Seen { get; set; }
}
// Derived class for errors
public class Error : LogEntry
{
}
public class Warning : LogEntry
{
} }

View File

@ -13,11 +13,8 @@ namespace InnovEnergy.App.Backend.DataTypes.Methods;
public static class ExoCmd public static class ExoCmd
{ {
private const String Key = "EXOea18f5a82bd358896154c783";
private const String Secret = "lYtzU7R5e0L6XKOgBaLVPFr41nEBDxDdXU47zBAEI6M";
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")] [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static readonly S3Credentials? S3Creds = JsonSerializer.Deserialize<S3Credentials>(File.OpenRead("./Resources/exoscaleS3.json")); public static readonly S3Credentials S3Credentials = JsonSerializer.Deserialize<S3Credentials>(File.OpenRead("./Resources/exoscaleS3.json"))!;
private static Byte[] HmacSha256Digest(String message, String secret) private static Byte[] HmacSha256Digest(String message, String secret)
{ {
@ -47,7 +44,7 @@ public static class ExoCmd
//Console.WriteLine("Message to sign:\n" + messageToSign); //Console.WriteLine("Message to sign:\n" + messageToSign);
var hmac = HmacSha256Digest(messageToSign, Secret); var hmac = HmacSha256Digest(messageToSign, S3Credentials.Secret);
return Convert.ToBase64String(hmac); return Convert.ToBase64String(hmac);
} }
@ -76,7 +73,7 @@ public static class ExoCmd
var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
var authheader = "credential="+Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("GET", method, unixtime); var authheader = "credential="+S3Credentials.Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("GET", method, unixtime);
var client = new HttpClient(); var client = new HttpClient();
@ -92,10 +89,12 @@ public static class ExoCmd
{ {
var url = "https://api-ch-dk-2.exoscale.com/v2/api-key"; var url = "https://api-ch-dk-2.exoscale.com/v2/api-key";
var method = "api-key"; var method = "api-key";
var contentString = $$"""{"role-id": "{{roleName}}", "name":"{{installation.BucketName()}}v2"}"""; var contentString = $$"""{"role-id": "{{roleName}}", "name":"{{installation.BucketName()}}"}""";
var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60;
var authheader = "credential=" + Key + ",expires=" + unixtime + ",signature=" +
var authheader = "credential=" + S3Credentials.Key + ",expires=" + unixtime + ",signature=" +
BuildSignature("POST", method, contentString, unixtime); BuildSignature("POST", method, contentString, unixtime);
var client = new HttpClient(); var client = new HttpClient();
@ -143,7 +142,7 @@ public static class ExoCmd
var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
var authheader = "credential="+Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("POST", method, contentString, unixtime); var authheader = "credential="+S3Credentials.Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("POST", method, contentString, unixtime);
var client = new HttpClient(); var client = new HttpClient();
@ -171,7 +170,7 @@ public static class ExoCmd
var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
var authheader = "credential="+Key+",expires="+unixtime+",signature="+BuildSignature("DELETE", method, unixtime); var authheader = "credential="+S3Credentials.Key+",expires="+unixtime+",signature="+BuildSignature("DELETE", method, unixtime);
var client = new HttpClient(); var client = new HttpClient();
@ -220,7 +219,7 @@ public static class ExoCmd
var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; var unixtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
var authheader = "credential="+Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("POST", method, contentString, unixtime); var authheader = "credential="+S3Credentials.Key+",signed-query-args="+",expires="+unixtime+",signature="+BuildSignature("POST", method, contentString, unixtime);
var client = new HttpClient(); var client = new HttpClient();
@ -245,7 +244,7 @@ public static class ExoCmd
public static async Task<Boolean> CreateBucket(this Installation installation) public static async Task<Boolean> CreateBucket(this Installation installation)
{ {
var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Creds!); var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Credentials!);
return await s3Region.PutBucket(installation.BucketName()) != null; return await s3Region.PutBucket(installation.BucketName()) != null;
} }
@ -272,7 +271,7 @@ public static class ExoCmd
// return result.ExitCode == 200; // return result.ExitCode == 200;
var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Creds!); var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Credentials!);
var url = s3Region.Bucket(installation.BucketName()).Path("config.json"); var url = s3Region.Bucket(installation.BucketName()).Path("config.json");
return await url.PutObject(config); return await url.PutObject(config);

View File

@ -23,7 +23,8 @@ public static class InstallationMethods
public static async Task<Boolean> RenewS3Credentials(this Installation installation) public static async Task<Boolean> RenewS3Credentials(this Installation installation)
{ {
if(installation.S3Key != "") await installation.RevokeReadKey(); if(!installation.S3Key.IsNullOrEmpty())
await installation.RevokeReadKey();
var (key,secret) = await installation.CreateReadKey(); var (key,secret) = await installation.CreateReadKey();

View File

@ -25,6 +25,11 @@ public static partial class Db
return Insert(error); return Insert(error);
} }
public static Boolean Create(Warning warning)
{
return Insert(warning);
}
public static Boolean Create(Folder folder) public static Boolean Create(Folder folder)
{ {
return Insert(folder); return Insert(folder);
@ -80,4 +85,30 @@ public static partial class Db
Create(newError); Create(newError);
} }
} }
public static void HandleWarning(Warning newWarning,int installationId)
{
//Find the total number of warnings for this installation
var totalWarnings = Warnings.Count(warning => warning.InstallationId == installationId);
//If there are 100 warnings, remove the one with the oldest timestamp
if (totalWarnings == 100)
{
var oldestWarning =
Warnings.Where(warning => warning.InstallationId == installationId)
.OrderBy(warning => warning.CreatedAt)
.FirstOrDefault();
//Remove the old error
Delete(oldestWarning);
//Add the new error
Create(newWarning);
}
else
{
Console.WriteLine("---------------Added the new Error to the database-----------------");
Create(newWarning);
}
}
} }

View File

@ -12,21 +12,20 @@ using SQLiteConnection = SQLite.SQLiteConnection;
namespace InnovEnergy.App.Backend.Database; namespace InnovEnergy.App.Backend.Database;
public static partial class Db
public static partial class Db
{ {
private static SQLiteConnection Connection { get; } = InitConnection(); private static SQLiteConnection Connection { get; } = InitConnection();
private static SQLiteConnection InitConnection() private static SQLiteConnection InitConnection()
{ {
var latestDb = new DirectoryInfo("DbBackups") var latestDb = new DirectoryInfo("DbBackups")
.GetFiles() .GetFiles()
.OrderBy(f => f.LastWriteTime) .OrderBy(f => f.LastWriteTime)
.Last().Name; .Last().Name;
//This is the file connection from the DbBackups folder //This is the file connection from the DbBackups folder
var fileConnection = new SQLiteConnection("DbBackups/" + latestDb); var fileConnection = new SQLiteConnection("DbBackups/" + latestDb);
//Create a table if it does not exist //Create a table if it does not exist
fileConnection.CreateTable<User>(); fileConnection.CreateTable<User>();
fileConnection.CreateTable<Installation>(); fileConnection.CreateTable<Installation>();
@ -36,14 +35,15 @@ public static partial class Db
fileConnection.CreateTable<Session>(); fileConnection.CreateTable<Session>();
fileConnection.CreateTable<OrderNumber2Installation>(); fileConnection.CreateTable<OrderNumber2Installation>();
fileConnection.CreateTable<Error>(); fileConnection.CreateTable<Error>();
fileConnection.CreateTable<Warning>();
return CopyDbToMemory(fileConnection); return CopyDbToMemory(fileConnection);
} }
private static SQLiteConnection CopyDbToMemory(SQLiteConnection fileConnection) private static SQLiteConnection CopyDbToMemory(SQLiteConnection fileConnection)
{ {
var memoryConnection = new SQLiteConnection(":memory:"); var memoryConnection = new SQLiteConnection(":memory:");
//Create a table if it does not exist in main memory //Create a table if it does not exist in main memory
memoryConnection.CreateTable<User>(); memoryConnection.CreateTable<User>();
memoryConnection.CreateTable<Installation>(); memoryConnection.CreateTable<Installation>();
@ -53,7 +53,8 @@ public static partial class Db
memoryConnection.CreateTable<Session>(); memoryConnection.CreateTable<Session>();
memoryConnection.CreateTable<OrderNumber2Installation>(); memoryConnection.CreateTable<OrderNumber2Installation>();
memoryConnection.CreateTable<Error>(); memoryConnection.CreateTable<Error>();
memoryConnection.CreateTable<Warning>();
//Copy all the existing tables from the disk to main memory //Copy all the existing tables from the disk to main memory
fileConnection.Table<Session>().ForEach(memoryConnection.Insert); fileConnection.Table<Session>().ForEach(memoryConnection.Insert);
fileConnection.Table<Folder>().ForEach(memoryConnection.Insert); fileConnection.Table<Folder>().ForEach(memoryConnection.Insert);
@ -63,7 +64,8 @@ public static partial class Db
fileConnection.Table<InstallationAccess>().ForEach(memoryConnection.Insert); fileConnection.Table<InstallationAccess>().ForEach(memoryConnection.Insert);
fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert); fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert);
fileConnection.Table<Error>().ForEach(memoryConnection.Insert); fileConnection.Table<Error>().ForEach(memoryConnection.Insert);
fileConnection.Table<Warning>().ForEach(memoryConnection.Insert);
return memoryConnection; return memoryConnection;
} }
@ -72,21 +74,23 @@ public static partial class Db
var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite";
Connection.Backup("DbBackups/" + filename); 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<Session> Sessions => Connection.Table<Session>(); public static void Init()
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 void Init(){
// used to force static constructor // 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 //Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class
} }
//This is the constructor of the class //This is the constructor of the class
static Db() static Db()
{ {
@ -100,23 +104,53 @@ public static partial class Db
Connection.CreateTable<Session>(); Connection.CreateTable<Session>();
Connection.CreateTable<OrderNumber2Installation>(); Connection.CreateTable<OrderNumber2Installation>();
Connection.CreateTable<Error>(); Connection.CreateTable<Error>();
Connection.CreateTable<Warning>();
}); });
UpdateKeys().SupressAwaitWarning();
CleanupSessions().SupressAwaitWarning();
}
private static async Task CleanupSessions()
{
while (true)
{
try
{
DeleteStaleSessions();
}
catch(Exception e)
{
Console.WriteLine("An error has occured when cleaning stale sessions, exception is:\n"+e);
}
Observable.Interval(TimeSpan.FromHours(0.5)) await Task.Delay(TimeSpan.FromHours(0.5));
.StartWith(0) // Do it right away (on startup) }
.ObserveOn(TaskPoolScheduler.Default)
.SubscribeOn(TaskPoolScheduler.Default)
.SelectMany(Cleanup)
.Subscribe();
} }
private static async Task UpdateKeys()
{
while (true)
{
try
{
await UpdateS3Urls();
}
catch(Exception e)
{
Console.WriteLine("An error has occured when updating S3 keys, exception is:\n"+e);
}
await Task.Delay(TimeSpan.FromHours(24));
}
}
private static Boolean RunTransaction(Func<Boolean> func) private static Boolean RunTransaction(Func<Boolean> func)
{ {
var savepoint = Connection.SaveTransactionPoint(); var savepoint = Connection.SaveTransactionPoint();
var success = false; var success = false;
try try
{ {
success = func(); success = func();
@ -131,40 +165,33 @@ public static partial class Db
return success; return success;
} }
private static async Task<Boolean> Cleanup(Int64 _)
{
await UpdateS3Urls();
DeleteStaleSessions();
return true;
}
private static void DeleteStaleSessions() private static void DeleteStaleSessions()
{ {
var deadline = DateTime.Now.AddDays((-1)*Session.MaxAge.Days); var deadline = DateTime.Now.AddDays((-1) * Session.MaxAge.Days);
Sessions.Delete(s => s.LastSeen < deadline); Sessions.Delete(s => s.LastSeen < deadline);
} }
private static async Task UpdateS3Urls() private static async Task UpdateS3Urls()
{ {
var regions = Installations var regions = Installations
.Select(i => i.S3Region) .Select(i => i.S3Region)
.Distinct() .Distinct()
.ToList(); .ToList();
const String provider = "exo.io"; const String provider = "exo.io";
foreach (var region in regions) foreach (var region in regions)
{ {
var s3Region = new S3Region($"https://{region}.{provider}", ExoCmd.S3Creds!); var s3Region = new S3Region($"https://{region}.{provider}", ExoCmd.S3Credentials!);
var bucketList = await s3Region.ListAllBuckets(); var bucketList = await s3Region.ListAllBuckets();
var installations = from bucket in bucketList.Buckets var installations = from bucket in bucketList.Buckets
from installation in Installations from installation in Installations
where installation.BucketName() == bucket.BucketName where installation.BucketName() == bucket.BucketName
select installation; select installation;
foreach (var installation in installations) foreach (var installation in installations)
{ {
await installation.RenewS3Credentials(); await installation.RenewS3Credentials();
@ -179,12 +206,12 @@ public static partial class Db
await user.SendPasswordResetEmail(sessionToken); await user.SendPasswordResetEmail(sessionToken);
return true; return true;
} }
catch catch
{ {
return false; return false;
} }
} }
public static async Task<Boolean> SendNewUserEmail(User user) public static async Task<Boolean> SendNewUserEmail(User user)
{ {
try try
@ -192,7 +219,7 @@ public static partial class Db
await user.SendNewUserWelcomeMessage(); await user.SendNewUserWelcomeMessage();
return true; return true;
} }
catch catch
{ {
return false; return false;
} }

View File

@ -48,6 +48,20 @@ public static partial class Db
return Errors.Delete(error => error.Id == errorToDelete.Id) >0; return Errors.Delete(error => error.Id == errorToDelete.Id) >0;
} }
} }
public static Boolean Delete(Warning warningToDelete)
{
var deleteSuccess = RunTransaction(DeleteWarning);
if (deleteSuccess)
BackupDatabase();
return deleteSuccess;
Boolean DeleteWarning()
{
return Warnings.Delete(error => error.Id == warningToDelete.Id) >0;
}
}
public static Boolean Delete(Installation installation) public static Boolean Delete(Installation installation)
{ {

View File

@ -4,6 +4,6 @@ public class StatusMessage
public required int InstallationId { get; init; } public required int InstallationId { get; init; }
public required int Status { get; init; } public required int Status { get; init; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public String Error { get; init; } = null!; public String Description { get; init; } = null!;
public String DeviceCreatedTheError { get; init; } = null!; public String CreatedBy { get; init; } = null!;
} }

View File

@ -99,15 +99,30 @@ public static class WebsocketManager
Error newError = new Error Error newError = new Error
{ {
InstallationId = receivedStatusMessage.InstallationId, InstallationId = receivedStatusMessage.InstallationId,
ErrorDescription = receivedStatusMessage.Error, Description = receivedStatusMessage.Description,
CreatedAt = receivedStatusMessage.CreatedAt, CreatedAt = receivedStatusMessage.CreatedAt,
DeviceCreatedTheError = receivedStatusMessage.DeviceCreatedTheError, DeviceCreatedTheMessage = receivedStatusMessage.CreatedBy,
Seen = false Seen = false
}; };
//Create a new error and add it to the database //Create a new error and add it to the database
Db.HandleError(newError,receivedStatusMessage.InstallationId); Db.HandleError(newError,receivedStatusMessage.InstallationId);
} }
else if (receivedStatusMessage.Status==1)
{
Console.WriteLine("-----------------------New warning-----------------------");
Warning newWarning = new Warning
{
InstallationId = receivedStatusMessage.InstallationId,
Description = receivedStatusMessage.Description,
CreatedAt = receivedStatusMessage.CreatedAt,
DeviceCreatedTheMessage = receivedStatusMessage.CreatedBy,
Seen = false
};
//Create a new error and add it to the database
Db.HandleWarning(newWarning,receivedStatusMessage.InstallationId);
}
if (!InstallationConnections.ContainsKey(installationId)) if (!InstallationConnections.ContainsKey(installationId))
{ {

View File

@ -4,9 +4,9 @@ namespace InnovEnergy.App.SaliMax.MiddlewareClasses;
public class StatusMessage public class StatusMessage
{ {
public required int InstallationId { get; init; } public required int InstallationId { get; set; }
public required int Status { get; init; } public required int Status { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public String Error { get; set; } = null!; public String Description { get; set; } = null!;
public String DeviceCreatedTheError { get; set; } = null!; public String CreatedBy { get; set; } = null!;
} }

View File

@ -357,8 +357,14 @@ internal static class Program
if (status == 2) if (status == 2)
{ {
jsonObject.CreatedAt = DateTime.Now; jsonObject.CreatedAt = DateTime.Now;
jsonObject.Error = "Battery Temperature High"; jsonObject.Description = "Battery Temperature High";
jsonObject.DeviceCreatedTheError = "Battery/1"; jsonObject.CreatedBy = "Battery/1";
}
else if (status == 1)
{
jsonObject.CreatedAt = DateTime.Now;
jsonObject.Description = "Temp warning message";
jsonObject.CreatedBy = "Battery/4";
} }
var message = JsonSerializer.Serialize(jsonObject); var message = JsonSerializer.Serialize(jsonObject);

View File

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

View File

@ -17,12 +17,14 @@ import {
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import ErrorIcon from '@mui/icons-material/Error'; import ErrorIcon from '@mui/icons-material/Error';
import WarningIcon from '@mui/icons-material/Warning';
import axiosConfig from '../../../Resources/axiosConfig'; import axiosConfig from '../../../Resources/axiosConfig';
import { AxiosError, AxiosResponse } from 'axios/index'; import { AxiosError, AxiosResponse } from 'axios/index';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { TokenContext } from '../../../contexts/tokenContext'; import { TokenContext } from '../../../contexts/tokenContext';
import { ErrorMessage } from '../../../interfaces/S3Types'; import { ErrorMessage } from '../../../interfaces/S3Types';
import Button from '@mui/material/Button';
interface LogProps { interface LogProps {
errorLoadingS3Data: boolean; errorLoadingS3Data: boolean;
@ -31,8 +33,11 @@ interface LogProps {
function Log(props: LogProps) { function Log(props: LogProps) {
const theme = useTheme(); const theme = useTheme();
//const [warnings, setWarnings] = useState<Notification[]>([]); const [warnings, setWarnings] = useState<ErrorMessage[]>([]);
const [errors, setErrors] = useState<ErrorMessage[]>([]); const [errors, setErrors] = useState<ErrorMessage[]>([]);
const [errorButtonPressed, setErrorButtonPressed] = useState(false);
const [warningButtonPressed, setWarningButtonPressed] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const tokencontext = useContext(TokenContext); const tokencontext = useContext(TokenContext);
const { removeToken } = tokencontext; const { removeToken } = tokencontext;
@ -49,14 +54,41 @@ function Log(props: LogProps) {
navigate(routes.login); navigate(routes.login);
} }
}); });
axiosConfig
.get(`/GetAllWarningsForInstallation?id=${props.id}`)
.then((res: AxiosResponse<ErrorMessage[]>) => {
setWarnings(res.data);
})
.catch((err: AxiosError) => {
if (err.response && err.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
}, []); }, []);
const handleErrorButtonPressed = () => {
setErrorButtonPressed(!errorButtonPressed);
};
const handleWarningButtonPressed = () => {
setWarningButtonPressed(!warningButtonPressed);
};
return ( return (
<Container maxWidth="xl"> <Container maxWidth="xl">
<Grid container> <Grid container>
<Grid item xs={12} md={12}> <Grid item xs={12} md={12}>
{/* IT SHOULD BE {(errors.length > 0 || props.warnings.length > 0) && (*/} <Button
{errors.length > 0 && ( variant="contained"
onClick={handleErrorButtonPressed}
sx={{ marginTop: '20px' }}
>
<FormattedMessage id="Show Errors" defaultMessage="Show Errors" />
</Button>
{errorButtonPressed && errors.length > 0 && (
<Card sx={{ marginTop: '10px' }}> <Card sx={{ marginTop: '10px' }}>
<Divider /> <Divider />
<TableContainer> <TableContainer>
@ -66,7 +98,6 @@ function Log(props: LogProps) {
<TableCell> <TableCell>
<FormattedMessage id="type" defaultMessage="Type" /> <FormattedMessage id="type" defaultMessage="Type" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<FormattedMessage <FormattedMessage
id="description" id="description"
@ -85,13 +116,146 @@ function Log(props: LogProps) {
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{errors.map((error, index) => { {errors.map((error, index) => (
<TableRow hover key={index}>
<TableCell>
<ErrorIcon
sx={{
color: 'red',
width: 25,
height: 25,
marginLeft: '5px',
marginTop: '8px'
}}
/>
</TableCell>
<TableCell>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
noWrap
sx={{ marginTop: '5px', verticalAlign: 'middle' }}
>
{error.description}
</Typography>
</TableCell>
<TableCell>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
noWrap
sx={{ marginTop: '5px', verticalAlign: 'middle' }}
>
{error.deviceCreatedTheMessage}
</Typography>
</TableCell>
<TableCell>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
noWrap
sx={{ marginTop: '5px', verticalAlign: 'middle' }}
>
{error.createdAt}
</Typography>
</TableCell>
<TableCell>
<Typography
variant="body1"
fontWeight="bold"
color="text.primary"
gutterBottom
noWrap
sx={{ marginTop: '5px', verticalAlign: 'middle' }}
>
{error.seen == false ? 'No' : 'Yes'}
</Typography>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Card>
)}
{!props.errorLoadingS3Data &&
errorButtonPressed &&
errors.length == 0 && (
<Alert
severity="error"
sx={{
display: 'flex',
alignItems: 'center',
marginTop: '20px'
}}
>
<FormattedMessage
id="noerrors"
defaultMessage="There are no errors"
/>
<IconButton
color="inherit"
size="small"
sx={{ marginLeft: '4px' }}
></IconButton>
</Alert>
)}
</Grid>
<Grid item xs={12} md={12}>
<Button
variant="contained"
onClick={handleWarningButtonPressed}
sx={{ marginTop: '20px' }}
>
<FormattedMessage
id="Show Warnings"
defaultMessage="Show Warnings"
/>
</Button>
{warningButtonPressed && warnings.length > 0 && (
<Card sx={{ marginTop: '10px' }}>
<Divider />
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>
<FormattedMessage id="type" defaultMessage="Type" />
</TableCell>
<TableCell>
<FormattedMessage
id="description"
defaultMessage="Description"
/>
</TableCell>
<TableCell>
<FormattedMessage id="device" defaultMessage="Device" />
</TableCell>
<TableCell>
<FormattedMessage id="date" defaultMessage="Date" />
</TableCell>
<TableCell>
<FormattedMessage id="seen" defaultMessage="Seen" />
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{warnings.map((warning, index) => {
return ( return (
<TableRow hover key={index}> <TableRow hover key={index}>
<TableCell> <TableCell>
<ErrorIcon <WarningIcon
sx={{ sx={{
color: 'red', color: 'orange',
width: 25, width: 25,
height: 25, height: 25,
marginLeft: '5px', marginLeft: '5px',
@ -106,9 +270,9 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px', marginLeft: '-40px' }} sx={{ marginTop: '5px', verticalAlign: 'middle' }}
> >
{error.errorDescription} {warning.description}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -120,7 +284,7 @@ function Log(props: LogProps) {
noWrap noWrap
sx={{ marginTop: '5px' }} sx={{ marginTop: '5px' }}
> >
{error.deviceCreatedTheError} {warning.deviceCreatedTheMessage}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -130,9 +294,9 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px', marginLeft: '-40px' }} sx={{ marginTop: '5px', verticalAlign: 'middle' }}
> >
{error.createdAt} {warning.createdAt}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -142,106 +306,43 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px', marginLeft: '10px' }} sx={{ marginTop: '5px', verticalAlign: 'middle' }}
> >
{error.seen == false ? 'No' : 'Yes'} {warning.seen == false ? 'No' : 'Yes'}
</Typography> </Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
})} })}
{/*{props.warnings.map((warning, index) => {*/}
{/* return (*/}
{/* <TableRow hover key={index}>*/}
{/* <TableCell>*/}
{/* <WarningIcon*/}
{/* sx={{*/}
{/* color: '#ffc04d',*/}
{/* width: 25,*/}
{/* height: 25,*/}
{/* marginLeft: '5px',*/}
{/* marginTop: '8px'*/}
{/* }}*/}
{/* />*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="body1"*/}
{/* fontWeight="bold"*/}
{/* color="text.primary"*/}
{/* gutterBottom*/}
{/* noWrap*/}
{/* sx={{ marginTop: '10px' }}*/}
{/* >*/}
{/* {warning.device}*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="body1"*/}
{/* fontWeight="bold"*/}
{/* color="text.primary"*/}
{/* gutterBottom*/}
{/* noWrap*/}
{/* sx={{ marginTop: '10px' }}*/}
{/* >*/}
{/* {warning.description}*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="body1"*/}
{/* fontWeight="bold"*/}
{/* color="text.primary"*/}
{/* gutterBottom*/}
{/* noWrap*/}
{/* sx={{ marginTop: '10px' }}*/}
{/* >*/}
{/* {warning.date}*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="body1"*/}
{/* fontWeight="bold"*/}
{/* color="text.primary"*/}
{/* gutterBottom*/}
{/* noWrap*/}
{/* sx={{ marginTop: '10px' }}*/}
{/* >*/}
{/* {warning.time}*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* </TableRow>*/}
{/* );*/}
{/*})}*/}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
</Card> </Card>
)} )}
{!props.errorLoadingS3Data && errors.length == 0 && ( {!props.errorLoadingS3Data &&
<Alert warningButtonPressed &&
severity="error" warnings.length == 0 && (
sx={{ <Alert
display: 'flex', severity="error"
alignItems: 'center', sx={{
marginTop: '20px' display: 'flex',
}} alignItems: 'center',
> //marginBottom: '20px'
<FormattedMessage marginTop: '20px'
id="noerrors" }}
defaultMessage="There are no errors" >
/> <FormattedMessage
<IconButton id="nowarnings"
color="inherit" defaultMessage="There are no warnings"
size="small" />
sx={{ marginLeft: '4px' }} <IconButton
></IconButton> color="inherit"
</Alert> size="small"
)} sx={{ marginLeft: '4px' }}
></IconButton>
</Alert>
)}
</Grid> </Grid>
<Grid item xs={12} md={12} style={{ marginBottom: '20px' }}> <Grid item xs={12} md={12} style={{ marginBottom: '20px' }}>
@ -265,28 +366,6 @@ function Log(props: LogProps) {
></IconButton> ></IconButton>
</Alert> </Alert>
)} )}
{/*{!props.errorLoadingS3Data && props.warnings.length == 0 && (*/}
{/* <Alert*/}
{/* severity="error"*/}
{/* sx={{*/}
{/* display: 'flex',*/}
{/* alignItems: 'center',*/}
{/* //marginBottom: '20px'*/}
{/* marginTop: '20px'*/}
{/* }}*/}
{/* >*/}
{/* <FormattedMessage*/}
{/* id="nowarnings"*/}
{/* defaultMessage="There are no warnings"*/}
{/* />*/}
{/* <IconButton*/}
{/* color="inherit"*/}
{/* size="small"*/}
{/* sx={{ marginLeft: '4px' }}*/}
{/* ></IconButton>*/}
{/* </Alert>*/}
{/*)}*/}
</Grid> </Grid>
</Grid> </Grid>
</Container> </Container>

View File

@ -8,8 +8,8 @@ export interface I_S3Credentials {
export interface ErrorMessage { export interface ErrorMessage {
installationId: number; installationId: number;
description: string;
createdAt: Date; createdAt: Date;
errorDescription: string; deviceCreatedTheMessage: string;
deviceCreatedTheError: string;
seen: boolean; seen: boolean;
} }