Innovenergy_trunk/csharp/App/Backend/Controller.cs

1157 lines
39 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Diagnostics;
using System.Net;
using System.Text.RegularExpressions;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.App.Backend.Services;
using InnovEnergy.App.Backend.Websockets;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace InnovEnergy.App.Backend;
using Token = String;
// create JobStatus class to track download battery log job
public class JobStatus
{
public string JobId { get; set; }
public string Status { get; set; }
public string FileName { get; set; }
public DateTime StartTime { get; set; }
}
[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)
throw new Exceptions(400, "Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);
if (!(user.Password.IsNullOrEmpty() && user.MustResetPassword) && !user.VerifyPassword(password))
{
throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!);
}
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
return Db.Create(session)
? session
: throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!);
}
[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()
? Ok()
: Unauthorized();
}
[HttpGet(nameof(CreateWebSocket))]
public async Task CreateWebSocket(Token authToken)
{
//Everytime a user logs in, this function is called
var session = Db.GetSession(authToken)?.User;
if (session is null)
{
Console.WriteLine("------------------------------------Unauthorized user----------------------------------------------");
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
HttpContext.Abort();
return;
}
if (!HttpContext.WebSockets.IsWebSocketRequest)
{
Console.WriteLine("------------------------------------Not a websocket request ----------------------------------------------");
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
HttpContext.Abort();
return;
}
//Create a websocket and pass its descriptor to the HandleWebSocketConnection method.
//This descriptor is returned to the frontend on the background
var webSocketContext = await HttpContext.WebSockets.AcceptWebSocketAsync();
var webSocket = webSocketContext;
//Handle the WebSocket connection
await WebsocketManager.HandleWebSocketConnection(webSocket);
}
[HttpGet(nameof(GetAllErrorsForInstallation))]
public ActionResult<IEnumerable<Error>> GetAllErrorsForInstallation(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.Errors
.Where(error => error.InstallationId == id)
.OrderByDescending(error => error.Date)
.ThenByDescending(error => error.Time)
.ToList();
}
[HttpGet(nameof(GetHistoryForInstallation))]
public ActionResult<IEnumerable<UserAction>> GetHistoryForInstallation(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.UserActions
.Where(action =>action.InstallationId == id)
.OrderByDescending(action => action.Timestamp)
.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)
.OrderByDescending(error => error.Date)
.ThenByDescending(error => error.Time)
.ToList();
}
[HttpGet(nameof(GetCsvTimestampsForInstallation))]
public ActionResult<IEnumerable<Int64>> GetCsvTimestampsForInstallation(Int64 id, Int32 start, Int32 end, 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();
var sampleSize = 100;
var allTimestamps = new List<Int64>();
static string FindCommonPrefix(string str1, string str2)
{
int minLength = Math.Min(str1.Length, str2.Length);
int i = 0;
while (i < minLength && str1[i] == str2[i])
{
i++;
}
return str1.Substring(0, i);
}
Int64 startTimestamp = Int64.Parse(start.ToString().Substring(0,5));
Int64 endTimestamp = Int64.Parse(end.ToString().Substring(0,5));
if (installation.Product == (int)ProductType.Salidomo)
{
start = Int32.Parse(start.ToString().Substring(0, start.ToString().Length - 2));
end = Int32.Parse(end.ToString().Substring(0, end.ToString().Length - 2));
}
string configPath = "/home/ubuntu/.s3cfg";
while (startTimestamp <= endTimestamp)
{
string bucketPath = installation.Product==(int)ProductType.Salimax || installation.Product==(int)ProductType.SodiStoreMax?
"s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp :
"s3://"+installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/"+startTimestamp;
Console.WriteLine("Fetching data for "+startTimestamp);
try
{
// Set up process start info
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "s3cmd",
Arguments = $"--config {configPath} ls {bucketPath}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
// Start the process
Process process = new Process
{
StartInfo = startInfo
};
process.Start();
// Read the output
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
// Check for errors
if (process.ExitCode != 0)
{
Console.WriteLine("Error executing command:");
Console.WriteLine(error);
}
else
{
// Define a regex pattern to match the filenames without .csv extension
var pattern = @"/([^/]+)\.(csv|json)$";
var regex = new Regex(pattern);
// Process each line of the output
foreach (var line in output.Split('\n'))
{
var match = regex.Match(line);
if (match.Success && long.Parse(match.Groups[1].Value) >= start && long.Parse(match.Groups[1].Value) <= end)
{
allTimestamps.Add(long.Parse(match.Groups[1].Value));
//Console.WriteLine(match.Groups[1].Value);
}
}
}
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e.Message}");
}
startTimestamp++;
}
int totalRecords = allTimestamps.Count;
if (totalRecords <= sampleSize)
{
// If the total records are less than or equal to the sample size, return all records
Console.WriteLine("Start timestamp = "+start +" end timestamp = "+end);
Console.WriteLine("SampledTimestamps = " + allTimestamps.Count);
return allTimestamps;
}
int interval = totalRecords / sampleSize;
var sampledTimestamps = new List<Int64>();
for (int i = 0; i < totalRecords; i += interval)
{
sampledTimestamps.Add(allTimestamps[i]);
}
// If we haven't picked enough records (due to rounding), add the latest record to ensure completeness
if (sampledTimestamps.Count < sampleSize)
{
sampledTimestamps.Add(allTimestamps.Last());
}
Console.WriteLine("Start timestamp = "+start +" end timestamp = "+end);
Console.WriteLine("TotalRecords = "+totalRecords + " interval = "+ interval);
Console.WriteLine("SampledTimestamps = " + sampledTimestamps.Count);
return sampledTimestamps;
}
[HttpGet(nameof(GetUserById))]
public ActionResult<User> GetUserById(Int64 id, Token authToken)
{
var session = Db.GetSession(authToken)?.User;
if (session == null)
return Unauthorized();
var user = Db.GetUserById(id);
if (user is null || !session.HasAccessTo(user))
return Unauthorized();
return user
.HidePassword()
.HideParentIfUserHasNoAccessToParent(session);
}
[HttpGet(nameof(GetInstallationById))]
public ActionResult<Installation> GetInstallationById(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 installation
.FillOrderNumbers()
.HideParentIfUserHasNoAccessToParent(user)
.HideWriteKeyIfUserIsNotAdmin(user.UserType);
}
[HttpGet(nameof(GetUsersWithDirectAccessToInstallation))]
public ActionResult<IEnumerable<Object>> GetUsersWithDirectAccessToInstallation(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 installation
.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => u.HidePassword())
.ToList();
}
[HttpGet(nameof(GetInstallationsTheUserHasAccess))]
public ActionResult<IEnumerable<Object>> GetInstallationsTheUserHasAccess(Int64 userId, Token authToken)
{
var user = Db.GetUserById(userId);
if (user == null)
return Unauthorized();
return user.AccessibleInstallations().ToList();
}
[HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))]
public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToInstallation(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 installation
.Ancestors()
.SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() }))
.ToList();
}
[HttpGet(nameof(GetUsersWithDirectAccessToFolder))]
public ActionResult<IEnumerable<Object>> GetUsersWithDirectAccessToFolder(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder))
return Unauthorized();
return folder
.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => u.HidePassword())
.ToList();
}
[HttpGet(nameof(GetUsersWithInheritedAccessToFolder))]
public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToFolder(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder))
return Unauthorized();
return folder
.Ancestors()
.SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() }))
.ToList();
}
[HttpGet(nameof(GetFolderById))]
public ActionResult<Folder> GetFolderById(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder))
return Unauthorized();
return folder.HideParentIfUserHasNoAccessToParent(user);
}
[HttpGet(nameof(GetAllDirectChildUsers))]
public ActionResult<IEnumerable<User>> GetAllDirectChildUsers(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
return user.ChildUsers().Select(u => u.HidePassword()).ToList();
}
[HttpGet(nameof(GetAllChildUsers))]
public ActionResult<IEnumerable<User>> GetAllChildUsers(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
return user
.DescendantUsers()
.Select(u => u.HidePassword())
.ToList();
}
[HttpGet(nameof(GetAllInstallationsFromProduct))]
public ActionResult<IEnumerable<Installation>> GetAllInstallationsFromProduct(int product,Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return user
.AccessibleInstallations(product)
.Select(i => i.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(user).HideWriteKeyIfUserIsNotAdmin(user.UserType))
.ToList();
}
[HttpGet(nameof(GetAllInstallations))]
public ActionResult<IEnumerable<Installation>> GetAllInstallations(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return user
.AccessibleInstallations(product:(int)ProductType.Salimax)
.ToList();
}
[HttpGet(nameof(GetAllSalidomoInstallations))]
public ActionResult<IEnumerable<Installation>> GetAllSalidomoInstallations(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return user
.AccessibleInstallations(product:(int)ProductType.Salidomo)
.ToList();
}
[HttpGet(nameof(GetAllSodioHomeInstallations))]
public ActionResult<IEnumerable<Installation>> GetAllSodioHomeInstallations(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return user
.AccessibleInstallations(product:(int)ProductType.SodioHome)
.ToList();
}
[HttpGet(nameof(GetAllFolders))]
public ActionResult<IEnumerable<Folder>> GetAllFolders(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return new(user.AccessibleFolders().HideParentIfUserHasNoAccessToParent(user));
}
[HttpGet(nameof(GetAllFoldersAndInstallations))]
public ActionResult<IEnumerable<Object>> GetAllFoldersAndInstallations(int productId, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
var foldersAndInstallations = user
.AccessibleFoldersAndInstallations()
.Do(o => o.FillOrderNumbers())
.Select(o => o.HideParentIfUserHasNoAccessToParent(user))
.OfType<Object>(); // Important! JSON serializer must see Objects otherwise
// it will just serialize the members of TreeNode %&@#!!!
// TODO Filter out write keys
return new (foldersAndInstallations);
}
[HttpPost(nameof(CreateUser))]
public async Task<ActionResult<User>> CreateUser([FromBody] User newUser, Token authToken)
{
var create = Db.GetSession(authToken).Create(newUser);
if (create)
{
var mail_success= await Db.SendNewUserEmail(newUser);
if (!mail_success)
{
Db.GetSession(authToken).Delete(newUser);
return StatusCode(500, "Welcome email failed to send");
}
return mail_success ? newUser.HidePassword():Unauthorized();
}
return Unauthorized() ;
}
[HttpPost(nameof(CreateInstallation))]
public async Task<ActionResult<Installation>> CreateInstallation([FromBody] Installation installation, Token authToken)
{
var session = Db.GetSession(authToken);
if (! await session.Create(installation))
return Unauthorized();
return installation;
}
[HttpPost(nameof(CreateFolder))]
public ActionResult<Folder> CreateFolder([FromBody] Folder folder, Token authToken)
{
var session = Db.GetSession(authToken);
if (!session.Create(folder))
return Unauthorized();
return folder.HideParentIfUserHasNoAccessToParent(session!.User);
}
[HttpPost(nameof(GrantUserAccessToFolder))]
public ActionResult GrantUserAccessToFolder(FolderAccess folderAccess, Token authToken)
{
var session = Db.GetSession(authToken);
// TODO: automatic BadRequest when properties are null during deserialization
var folder = Db.GetFolderById(folderAccess.FolderId);
var user = Db.GetUserById(folderAccess.UserId);
// Check if user already has access - treat as idempotent (success)
if (user is not null && folder is not null && user.HasAccessTo(folder))
{
Console.WriteLine($"GrantUserAccessToFolder: User {user.Id} ({user.Name}) already has access to folder {folder.Id} ({folder.Name}) - returning success");
return Ok();
}
return session.GrantUserAccessTo(user, folder)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(RevokeUserAccessToFolder))]
public ActionResult RevokeUserAccessToFolder(FolderAccess folderAccess, Token authToken)
{
var session = Db.GetSession(authToken);
// TODO: automatic BadRequest when properties are null during deserialization
var folder = Db.GetFolderById(folderAccess.FolderId);
var user = Db.GetUserById(folderAccess.UserId);
return session.RevokeUserAccessTo(user, folder)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(GrantUserAccessToInstallation))]
public ActionResult GrantUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
{
var session = Db.GetSession(authToken);
// TODO: automatic BadRequest when properties are null during deserialization
var installation = Db.GetInstallationById(installationAccess.InstallationId);
var user = Db.GetUserById(installationAccess.UserId);
// Check if user already has access - treat as idempotent (success)
if (user is not null && installation is not null && user.HasAccessTo(installation))
{
Console.WriteLine($"GrantUserAccessToInstallation: User {user.Id} ({user.Name}) already has access to installation {installation.Id} ({installation.Name}) - returning success");
return Ok();
}
return session.GrantUserAccessTo(user, installation)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(RevokeUserAccessToInstallation))]
public ActionResult RevokeUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
{
var session = Db.GetSession(authToken);
// TODO: automatic BadRequest when properties are null during deserialization
var installation = Db.GetInstallationById(installationAccess.InstallationId);
var user = Db.GetUserById(installationAccess.UserId);
return session.RevokeUserAccessTo(user, installation)
? Ok()
: Unauthorized();
}
[HttpPut(nameof(UpdateUser))]
public ActionResult<User> UpdateUser([FromBody] User updatedUser, Token authToken)
{
var session = Db.GetSession(authToken);
if (!session.Update(updatedUser))
return Unauthorized();
return updatedUser.HidePassword();
}
[HttpPut(nameof(UpdatePassword))]
public ActionResult<User> UpdatePassword(String newPassword, Token authToken)
{
var session = Db.GetSession(authToken);
return session.UpdatePassword(newPassword)
? Ok()
: Unauthorized();
}
[HttpPut(nameof(UpdateInstallation))]
public ActionResult<Installation> UpdateInstallation([FromBody] Installation installation, Token authToken)
{
var session = Db.GetSession(authToken);
if (!session.Update(installation))
return Unauthorized();
if (installation.Product == (int)ProductType.Salimax)
{
return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User).HideWriteKeyIfUserIsNotAdmin(session.User.UserType);
}
return installation.HideParentIfUserHasNoAccessToParent(session!.User);
}
[HttpPost(nameof(AcknowledgeError))]
public ActionResult AcknowledgeError(Int64 id, Token authToken)
{
var session = Db.GetSession(authToken);
if (session == null)
return Unauthorized();
var error=Db.Errors
.FirstOrDefault(error => error.Id == id);
error.Seen = true;
return Db.Update(error)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(AcknowledgeWarning))]
public ActionResult AcknowledgeWarning(Int64 id, Token authToken)
{
var session = Db.GetSession(authToken);
if (session == null)
return Unauthorized();
var warning=Db.Warnings
.FirstOrDefault(warning => warning.Id == id);
warning.Seen = true;
return Db.Update(warning)
? Ok()
: Unauthorized();
}
/// <summary>
/// Returns an AI-generated diagnosis for a single error/alarm description.
/// Responses are cached in memory — repeated calls for the same error code
/// do not hit Mistral again.
/// </summary>
[HttpGet(nameof(DiagnoseError))]
public async Task<ActionResult<DiagnosticResponse>> DiagnoseError(Int64 installationId, string errorDescription, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var installation = Db.GetInstallationById(installationId);
if (installation is null || !user.HasAccessTo(installation))
return Unauthorized();
// AI diagnostics are scoped to SodistoreHome and SodiStoreMax only
if (installation.Product != (int)ProductType.SodioHome &&
installation.Product != (int)ProductType.SodiStoreMax)
return BadRequest("AI diagnostics not available for this product.");
if (!DiagnosticService.IsEnabled)
return StatusCode(503, "AI diagnostics not configured.");
var result = await DiagnosticService.DiagnoseAsync(installationId, errorDescription);
if (result is null)
return StatusCode(500, "Diagnosis failed please try again later.");
return result;
}
/// <summary>
/// Test endpoint for AlarmKnowledgeBase - no authentication required.
/// Tests multiple Sinexcel and Growatt alarms to verify the knowledge base works.
/// Remove this endpoint in production if not needed.
/// </summary>
[HttpGet(nameof(TestAlarmKnowledgeBase))]
public ActionResult TestAlarmKnowledgeBase()
{
var testCases = new[]
{
// Sinexcel alarms
"Fan fault",
"Abnormal grid voltage",
"Battery 1not connected",
"Inverter power tube fault",
"Island protection",
// Growatt alarms
"Warning 300",
"Warning 500",
"Error 408",
"AFCI Fault",
// Unknown alarm (should return null - would call Mistral)
"Some unknown alarm XYZ123"
};
var results = new List<object>();
foreach (var alarm in testCases)
{
var diagnosis = AlarmKnowledgeBase.TryGetDiagnosis(alarm);
results.Add(new
{
Alarm = alarm,
FoundInKnowledgeBase = diagnosis != null,
Explanation = diagnosis?.Explanation ?? "NOT FOUND - Would call Mistral API",
CausesCount = diagnosis?.Causes.Count ?? 0,
NextStepsCount = diagnosis?.NextSteps.Count ?? 0
});
}
return Ok(new
{
TestTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
TotalTests = testCases.Length,
FoundInKnowledgeBase = results.Count(r => ((dynamic)r).FoundInKnowledgeBase),
WouldCallMistral = results.Count(r => !((dynamic)r).FoundInKnowledgeBase),
Results = results
});
}
[HttpPut(nameof(UpdateFolder))]
public ActionResult<Folder> UpdateFolder([FromBody] Folder folder, Token authToken)
{
var session = Db.GetSession(authToken);
if (!session.Update(folder))
return Unauthorized();
return folder.HideParentIfUserHasNoAccessToParent(session!.User);
}
[HttpPut(nameof(MoveInstallation))]
public ActionResult MoveInstallation(Int64 installationId,Int64 parentId, Token authToken)
{
var session = Db.GetSession(authToken);
return session.MoveInstallation(installationId, parentId)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(UpdateFirmware))]
public async Task<ActionResult> UpdateFirmware(Int64 batteryNode, Int64 installationId,String version,Token authToken)
{
var session = Db.GetSession(authToken);
var installationToUpdate = Db.GetInstallationById(installationId);
if (installationToUpdate != null)
{
_ = session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode,version,installationToUpdate.Product);
}
return Ok();
}
private static Dictionary<string, JobStatus> JobStatuses = new Dictionary<string, JobStatus>();
[HttpPost("StartDownloadBatteryLog")]
public async Task<ActionResult<string>> StartDownloadBatteryLog(long batteryNode, long installationId, Token authToken)
{
var session = Db.GetSession(authToken);
var installationToDownload = Db.GetInstallationById(installationId);
if (installationToDownload != null)
{
string jobId = Guid.NewGuid().ToString();
_ = Task.Run(async () =>
{
await session.RunDownloadLogScript(installationToDownload.VpnIp, batteryNode, installationToDownload.Product);
string fileName = $"{installationToDownload.VpnIp}-node{batteryNode}-{DateTime.Now:dd-MM-yyyy}.bin";
string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";
if (System.IO.File.Exists(filePath))
{
SaveJobStatus(jobId, "Completed", fileName:fileName);
}
else
{
SaveJobStatus(jobId, "Failed");
}
});
// Store initial job status in in-memory storage
SaveJobStatus(jobId, "Processing");
return Ok(jobId);
}
return NotFound();
}
[HttpGet("DownloadBatteryLog")]
public async Task<ActionResult> DownloadBatteryLog(string jobId)
{
Console.WriteLine("-----------------------------------Start uploading battery log-----------------------------------");
var jobStatus = JobStatuses.TryGetValue(jobId, out var status) ? status : null;
if (jobStatus == null || jobStatus.Status != "Completed" || string.IsNullOrEmpty(jobStatus.FileName))
{
return NotFound();
}
string fileName = jobStatus.FileName;
string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";
if (!System.IO.File.Exists(filePath))
{
return NotFound();
}
string contentType = "application/octet-stream";
var memory = new MemoryStream();
await using (var stream = new FileStream(filePath, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var fileContentResult = new FileContentResult(memory.ToArray(), contentType)
{
//FileDownloadName = Path.GetFileName(filePath)
FileDownloadName = fileName
};
Console.WriteLine("-----------------------------------Stop uploading battery log-----------------------------------");
return fileContentResult;
}
[HttpDelete("DeleteBatteryLog")]
public IActionResult DeleteBatteryLog(string fileName)
{
Console.WriteLine("-----------------------------------Start deleting downloaded battery log-----------------------------------");
string filePath = $"/home/ubuntu/backend/downloadBatteryLog/{fileName}";
try
{
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
Console.WriteLine("-----------------------------------Stop deleting downloaded battery log-----------------------------------");
return Ok();
}
else
{
return NotFound("File not found.");
}
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
private void SaveJobStatus(string jobId, string status, string fileName = null)
{
JobStatuses[jobId] = new JobStatus
{
JobId = jobId,
Status = status,
FileName = fileName,
StartTime = DateTime.UtcNow // Initialize StartTime when saving
};
}
[HttpGet("GetJobResult")]
public ActionResult GetJobResult(string jobId)
{
if (string.IsNullOrEmpty(jobId))
{
return BadRequest(new { status = "Error", message = "Job ID is required." });
}
if (!JobStatuses.TryGetValue(jobId, out var jobStatus))
{
return NotFound();
}
if (jobStatus.Status == "Completed")
{
return Ok(new { status = "Completed", fileName = jobStatus.FileName });
}
else if (jobStatus.Status == "Failed")
{
return StatusCode(500, new { status = "Failed", message = "Job processing failed." });
}
else if (jobStatus.Status == "Processing")
{
// Check for timeout
var startTime = jobStatus.StartTime;
var currentTime = DateTime.UtcNow;
if ((currentTime - startTime).TotalMinutes > 60)//60 minutes as timeout => Running multiple tasks in parallel on a crowded backend server will increase the time each task takes to complete
{
return StatusCode(500, new { status = "Failed", message = "Job in back end timeout exceeded." });
}
return Ok(new { status = "Processing" });
}
else
{
return BadRequest(new { status = "Unknown", message = "Unknown job status." });
}
}
[HttpPost(nameof(InsertNewAction))]
public async Task<ActionResult<IEnumerable<Object>>> InsertNewAction([FromBody] UserAction action, Token authToken)
{
var session = Db.GetSession(authToken);
var actionSuccess = await session.InsertUserAction(action);
return actionSuccess ? Ok() : Unauthorized();
}
[HttpPost(nameof(UpdateAction))]
public async Task<ActionResult<IEnumerable<Object>>> UpdateAction([FromBody] UserAction action, Token authToken)
{
var session = Db.GetSession(authToken);
var actionSuccess = await session.UpdateUserAction(action);
return actionSuccess ? Ok() : Unauthorized();
}
[HttpPost(nameof(DeleteAction))]
public async Task<ActionResult<IEnumerable<Object>>> DeleteAction(Int64 actionId, Token authToken)
{
var session = Db.GetSession(authToken);
var actionSuccess = await session.DeleteUserAction(actionId);
return actionSuccess ? Ok() : Unauthorized();
}
[HttpPost(nameof(EditInstallationConfig))]
public async Task<ActionResult<IEnumerable<Object>>> EditInstallationConfig([FromBody] Configuration config, Int64 installationId,int product,Token authToken)
{
var session = Db.GetSession(authToken);
string configString = product switch
{
0 => config.GetConfigurationSalimax(), // Salimax
3 => config.GetConfigurationSodistoreMax(), // SodiStoreMax
2 => config.GetConfigurationSodistoreHome(), // SodiStoreHome
_ => config.GetConfigurationString() // fallback
};
Console.WriteLine("CONFIG IS " + configString);
//Send configuration changes
var success = await session.SendInstallationConfig(installationId, config);
// Record configuration change
if (success)
{
// // Update Configuration colum in Installation table
// var installation = Db.GetInstallationById(installationId);
//
// installation.Configuration = JsonConvert.SerializeObject(config);
//
// if (!installation.Apply(Db.Update))
// return StatusCode(500, "Failed to update installation configuration in database");
var action = new UserAction
{
InstallationId = installationId,
Timestamp = DateTime.UtcNow,
Description = configString
};
var actionSuccess = await session.InsertUserAction(action);
return actionSuccess ? Ok() : StatusCode(500, "Failed to record Configuration changes in History of Action");
}
return Unauthorized();
}
[HttpPut(nameof(MoveFolder))]
public ActionResult MoveFolder(Int64 folderId,Int64 parentId, Token authToken)
{
var session = Db.GetSession(authToken);
return session.MoveFolder(folderId, parentId)
? Ok()
: Unauthorized();
}
[HttpDelete(nameof(DeleteUser))]
public ActionResult DeleteUser(Int64 userId, Token authToken)
{
var session = Db.GetSession(authToken);
var user = Db.GetUserById(userId);
return session.Delete(user)
? Ok()
: Unauthorized();
}
[HttpDelete(nameof(DeleteInstallation))]
public async Task<ActionResult> DeleteInstallation(Int64 installationId, Token authToken)
{
var session = Db.GetSession(authToken);
var installation = Db.GetInstallationById(installationId);
return await session.Delete(installation)
? Ok()
: Unauthorized();
}
[HttpDelete(nameof(DeleteFolder))]
public ActionResult DeleteFolder(Int64 folderId, Token authToken)
{
var session = Db.GetSession(authToken);
var folder = Db.GetFolderById(folderId);
return session.Delete(folder)
? Ok()
: Unauthorized();
}
[HttpPost(nameof(ResetPasswordRequest))]
public async Task<ActionResult<IEnumerable<Object>>> ResetPasswordRequest(String username)
{
var user = Db.GetUserByEmail(username);
if (user is null)
return Unauthorized();
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
var success = Db.Create(session);
return success && await Db.SendPasswordResetEmail(user, session.Token)
? Ok()
: Unauthorized();
}
[HttpGet(nameof(ResetPassword))]
public ActionResult<Object> ResetPassword(Token token)
{
var user = Db.GetSession(token)?.User;
if (user is null)
return Unauthorized();
Db.DeleteUserPassword(user);
return Redirect($"https://monitor.inesco.energy/?username={user.Email}&reset=true"); // TODO: move to settings file
}
}