Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
313e5f4a81
|
@ -24,8 +24,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3", "lib/S3/S3.csproj", "{
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", "{46DE03C4-52D1-47AA-8E60-8BB15361D723}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", "{46DE03C4-52D1-47AA-8E60-8BB15361D723}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsController", "app/CsController/CsController.csproj", "{72DBBE42-A09F-43C0-9613-331039857056}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaliMax", "app/SaliMax/SaliMax.csproj", "{25073794-D859-4824-9984-194C7E928496}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaliMax", "app/SaliMax/SaliMax.csproj", "{25073794-D859-4824-9984-194C7E928496}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusApi", "lib/StatusApi/StatusApi.csproj", "{9D17E78C-8A70-43DB-A619-DC12D20D023D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusApi", "lib/StatusApi/StatusApi.csproj", "{9D17E78C-8A70-43DB-A619-DC12D20D023D}"
|
||||||
|
@ -112,10 +110,6 @@ Global
|
||||||
{C3639841-13F4-4F24-99C6-7D965593BF89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C3639841-13F4-4F24-99C6-7D965593BF89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C3639841-13F4-4F24-99C6-7D965593BF89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C3639841-13F4-4F24-99C6-7D965593BF89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C3639841-13F4-4F24-99C6-7D965593BF89}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C3639841-13F4-4F24-99C6-7D965593BF89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{72DBBE42-A09F-43C0-9613-331039857056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{72DBBE42-A09F-43C0-9613-331039857056}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{72DBBE42-A09F-43C0-9613-331039857056}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{72DBBE42-A09F-43C0-9613-331039857056}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{25073794-D859-4824-9984-194C7E928496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{25073794-D859-4824-9984-194C7E928496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{25073794-D859-4824-9984-194C7E928496}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{25073794-D859-4824-9984-194C7E928496}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{25073794-D859-4824-9984-194C7E928496}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{25073794-D859-4824-9984-194C7E928496}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
@ -195,7 +189,6 @@ Global
|
||||||
{E3A5F3A3-72A5-47CC-85C6-2D8E962A0EC1} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
{E3A5F3A3-72A5-47CC-85C6-2D8E962A0EC1} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
||||||
{46DE03C4-52D1-47AA-8E60-8BB15361D723} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
{46DE03C4-52D1-47AA-8E60-8BB15361D723} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
||||||
{4A67D79F-F0C9-4BBC-9601-D5948E6C05D3} = {46DE03C4-52D1-47AA-8E60-8BB15361D723}
|
{4A67D79F-F0C9-4BBC-9601-D5948E6C05D3} = {46DE03C4-52D1-47AA-8E60-8BB15361D723}
|
||||||
{72DBBE42-A09F-43C0-9613-331039857056} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
|
||||||
{25073794-D859-4824-9984-194C7E928496} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
{25073794-D859-4824-9984-194C7E928496} = {145597B4-3E30-45E6-9F72-4DD43194539A}
|
||||||
{9D17E78C-8A70-43DB-A619-DC12D20D023D} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
{9D17E78C-8A70-43DB-A619-DC12D20D023D} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
|
||||||
{C3639841-13F4-4F24-99C6-7D965593BF89} = {46DE03C4-52D1-47AA-8E60-8BB15361D723}
|
{C3639841-13F4-4F24-99C6-7D965593BF89} = {46DE03C4-52D1-47AA-8E60-8BB15361D723}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>preview</LangVersion>
|
||||||
|
<RootNamespace>Innovenergy.Backend</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using Innovenergy.Backend.Database;
|
||||||
using Backend.Database;
|
using Innovenergy.Backend.Model;
|
||||||
using Backend.Model;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
using Backend.Model.Relations;
|
using Innovenergy.Backend.Utils;
|
||||||
using Backend.Utils;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
||||||
|
|
||||||
namespace Backend.Controllers;
|
namespace Innovenergy.Backend.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/")]
|
[Route("api/")]
|
||||||
public class Controller
|
public class Controller
|
||||||
{
|
{
|
||||||
[ProducesResponseType(200)]
|
[Returns<String>]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
|
[Returns(HttpStatusCode.BadRequest)]
|
||||||
[HttpPost($"{nameof(Login)}")]
|
[HttpPost($"{nameof(Login)}")]
|
||||||
public Object Login(Credentials credentials)
|
public Object Login(Credentials credentials)
|
||||||
{
|
{
|
||||||
|
@ -29,181 +29,175 @@ public class Controller
|
||||||
if (user is null)
|
if (user is null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
// if (!VerifyPassword(password, user))
|
#if !DEBUG
|
||||||
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
if (!VerifyPassword(credentials.Password, user))
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
#endif
|
||||||
|
|
||||||
var ses = new Session(user);
|
var ses = new Session(user);
|
||||||
db.NewSession(ses);
|
db.NewSession(ses);
|
||||||
return ses.Token;
|
return ses.Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Boolean VerifyPassword(String password, User user)
|
|
||||||
{
|
|
||||||
var pwdBytes = Encoding.UTF8.GetBytes(password);
|
|
||||||
var saltBytes = Encoding.UTF8.GetBytes(user.Salt + "innovEnergy");
|
|
||||||
var pwdHash = Crypto.ComputeHash(pwdBytes, saltBytes);
|
|
||||||
|
|
||||||
return user.Password == pwdHash;
|
[Returns(HttpStatusCode.OK)]
|
||||||
}
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
|
||||||
[ProducesResponseType(401)]
|
|
||||||
[HttpPost($"{nameof(Logout)}")]
|
[HttpPost($"{nameof(Logout)}")]
|
||||||
public Object Logout()
|
public Object Logout()
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
using var db = Db.Connect();
|
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
if (currentUser is null)
|
if (caller is null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Conflict);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
return db.DeleteSession(currentUser.Id);
|
using var db = Db.Connect();
|
||||||
|
return db.DeleteSession(caller.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
[HttpPost($"{nameof(UpdateS3Creds)}")]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
public Object UpdateS3Creds()
|
[HttpPost($"{nameof(UpdateS3Credentials)}")]
|
||||||
|
public Object UpdateS3Credentials()
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
// TODO: S3Credentials should be per session, not per user
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
using var db = Db.Connect();
|
|
||||||
var currentUser = (User)ctx!.Items["User"]!;
|
|
||||||
|
|
||||||
return db.CreateAndSaveUserS3ApiKey(currentUser);
|
var caller = GetCaller();
|
||||||
|
if (caller is null)
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
|
using var db = Db.Connect();
|
||||||
|
|
||||||
|
return db.CreateAndSaveUserS3ApiKey(caller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ProducesResponseType(typeof(User), 200)]
|
[Returns<User>]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpGet($"{nameof(GetUserById)}")]
|
[HttpGet($"{nameof(GetUserById)}")]
|
||||||
public Object GetUserById(Int64 id)
|
public Object GetUserById(Int64 id)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller is null)
|
||||||
using var db = Db.Connect();
|
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
var viewedUser = db.GetUserById(id);
|
|
||||||
|
|
||||||
//using the same error to prevent fishing for ids
|
|
||||||
if (currentUser == null || viewedUser == null || !db.IsParentOfChild(currentUser, viewedUser))
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
|
using var db = Db.Connect();
|
||||||
|
|
||||||
return viewedUser;
|
var user = db
|
||||||
|
.GetDescendantUsers(caller)
|
||||||
|
.FirstOrDefault(u => u.Id == id);
|
||||||
|
|
||||||
|
return user as Object ?? new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(typeof(Installation), 200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns<Installation>]
|
||||||
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpGet($"{nameof(GetInstallationById)}")]
|
[HttpGet($"{nameof(GetInstallationById)}")]
|
||||||
public Object GetInstallationById(Int64 id)
|
public Object GetInstallationById(Int64 id)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller == null)
|
||||||
using var db = Db.Connect();
|
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
if (currentUser == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
|
using var db = Db.Connect();
|
||||||
|
|
||||||
var installation = db
|
var installation = db
|
||||||
.GetAllAccessibleInstallations(currentUser)
|
.GetAllAccessibleInstallations(caller)
|
||||||
.FirstOrDefault(i => i.Id == id);
|
.FirstOrDefault(i => i.Id == id);
|
||||||
|
|
||||||
if (installation is null)
|
return installation as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||||
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
|
||||||
|
|
||||||
return installation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(typeof(Folder), 200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns<Folder>]
|
||||||
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpGet($"{nameof(GetFolderById)}")]
|
[HttpGet($"{nameof(GetFolderById)}")]
|
||||||
public Object GetFolderById(Int64 id)
|
public Object GetFolderById(Int64 id)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller == null)
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
var folder = db
|
var folder = db
|
||||||
.GetAllAccessibleFolders(currentUser!)
|
.GetAllAccessibleFolders(caller)
|
||||||
.FirstOrDefault(f => f.Id == id);
|
.FirstOrDefault(f => f.Id == id);
|
||||||
|
|
||||||
if(folder is null)
|
return folder as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||||
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
|
||||||
|
|
||||||
return folder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpGet($"{nameof(GetAllInstallations)}/")]
|
[HttpGet($"{nameof(GetAllInstallations)}/")]
|
||||||
public Object GetAllInstallations()
|
public Object GetAllInstallations()
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller == null)
|
||||||
using var db = Db.Connect();
|
|
||||||
var user = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
|
using var db = Db.Connect();
|
||||||
|
|
||||||
return db.GetAllAccessibleInstallations(user).ToList();
|
return db
|
||||||
|
.GetAllAccessibleInstallations(caller)
|
||||||
|
.ToList(); // important!
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpGet($"{nameof(GetAllFolders)}/")]
|
[HttpGet($"{nameof(GetAllFolders)}/")]
|
||||||
public Object GetAllFolders()
|
public Object GetAllFolders()
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller == null)
|
||||||
var user = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
return db.GetAllAccessibleFolders(user).ToList();
|
using var db = Db.Connect();
|
||||||
|
return db
|
||||||
|
.GetAllAccessibleFolders(caller)
|
||||||
|
.ToList(); // important!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpPut($"{nameof(UpdateUser)}/")]
|
[HttpPut($"{nameof(UpdateUser)}/")]
|
||||||
public Object UpdateUser(User updatedUser)
|
public Object UpdateUser(User updatedUser)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
// TODO: distinguish between create and update
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
using var db = Db.Connect();
|
var caller = GetCaller();
|
||||||
var currentUser = (User)ctx.Items["User"];
|
if (caller == null)
|
||||||
|
|
||||||
if (currentUser == null || !currentUser.HasWriteAccess || !db.IsParentOfChild(currentUser, updatedUser))
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
return db.GetUserById(updatedUser.Id) != null ? db.UpdateUser(updatedUser) : db.CreateUser(updatedUser);
|
using var db = Db.Connect();
|
||||||
|
|
||||||
|
return db.GetUserById(updatedUser.Id) != null
|
||||||
|
? db.UpdateUser(updatedUser)
|
||||||
|
: db.CreateUser(updatedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpPut($"{nameof(UpdateInstallation)}/")]
|
[HttpPut($"{nameof(UpdateInstallation)}/")]
|
||||||
public Object UpdateInstallation(Installation updatedInstallation)
|
public Object UpdateInstallation(Installation updatedInstallation)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
|
|
||||||
var currentUser = (User)ctx.Items["User"];
|
if (caller is null || !caller.HasWriteAccess)
|
||||||
|
|
||||||
if (currentUser == null || !currentUser.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
|
|
||||||
var hasAccess = db.GetAllAccessibleInstallations(currentUser)
|
var hasAccessToInstallation = db
|
||||||
.Any(i => i.Id == updatedInstallation.Id);
|
.GetAllAccessibleInstallations(caller)
|
||||||
if (!hasAccess)
|
.Any(i => i.Id == updatedInstallation.Id);
|
||||||
|
|
||||||
|
if (!hasAccessToInstallation)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
// TODO: accessibility by other users etc
|
// TODO: accessibility by other users etc
|
||||||
|
@ -213,64 +207,68 @@ public class Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpPut($"{nameof(UpdateFolder)}/")]
|
[HttpPut($"{nameof(UpdateFolder)}/")]
|
||||||
public Object UpdateFolder(Folder updatedFolder)
|
public Object UpdateFolder(Folder folder)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
using var db = Db.Connect();
|
if (caller is null || !caller.HasWriteAccess)
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
if (currentUser == null || !currentUser.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
var hasAccess = db.GetAllAccessibleFolders(currentUser)
|
using var db = Db.Connect();
|
||||||
.Any(f => f.Id == updatedFolder.Id);
|
|
||||||
|
var hasAccessToFolder = db
|
||||||
|
.GetAllAccessibleFolders(caller)
|
||||||
|
.Any(f => f.Id == folder.Id);
|
||||||
|
|
||||||
if (!hasAccess)
|
if (!hasAccessToFolder)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
// TODO: accessibility by other users etc
|
// TODO: accessibility by other users etc
|
||||||
// TODO: sanity check changes
|
// TODO: sanity check changes
|
||||||
|
|
||||||
return db.UpdateFolder(updatedFolder);
|
return db.UpdateFolder(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpDelete($"{nameof(DeleteUser)}/")]
|
[HttpDelete($"{nameof(DeleteUser)}/")]
|
||||||
public Object DeleteUser(Int64 userId)
|
public Object DeleteUser(Int64 userId)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
using var db = Db.Connect();
|
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
var userToBeDeleted = db.GetUserById(userId);
|
|
||||||
|
|
||||||
if (currentUser == null
|
if (caller is null || !caller.HasWriteAccess)
|
||||||
|| userToBeDeleted == null
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|| !currentUser.HasWriteAccess
|
|
||||||
|| !db.IsParentOfChild(currentUser,userToBeDeleted))
|
using var db = Db.Connect();
|
||||||
|
|
||||||
|
var userToBeDeleted = db
|
||||||
|
.GetDescendantUsers(caller)
|
||||||
|
.FirstOrDefault(u => u.Id == userId);
|
||||||
|
|
||||||
|
if (userToBeDeleted is null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
return db.DeleteUser(userToBeDeleted);
|
return db.DeleteUser(userToBeDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[Returns(HttpStatusCode.OK)]
|
||||||
[ProducesResponseType(401)]
|
[Returns(HttpStatusCode.Unauthorized)]
|
||||||
[HttpDelete($"{nameof(DeleteInstallation)}/")]
|
[HttpDelete($"{nameof(DeleteInstallation)}/")]
|
||||||
public Object DeleteInstallation(Int64 idOfInstallationToBeDeleted)
|
public Object DeleteInstallation(Int64 installationId)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
|
||||||
|
if (caller is null || !caller.HasWriteAccess)
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
var installationToBeDeleted = db
|
var installationToBeDeleted = db
|
||||||
.GetAllAccessibleInstallations(currentUser!)
|
.GetAllAccessibleInstallations(caller)
|
||||||
.FirstOrDefault(i => i.Id == idOfInstallationToBeDeleted);
|
.FirstOrDefault(i => i.Id == installationId);
|
||||||
|
|
||||||
if (installationToBeDeleted is null)
|
if (installationToBeDeleted is null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
@ -278,18 +276,20 @@ public class Controller
|
||||||
return db.DeleteInstallation(installationToBeDeleted);
|
return db.DeleteInstallation(installationToBeDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
[ProducesResponseType(401)]
|
[ProducesResponseType(401)]
|
||||||
[HttpDelete($"{nameof(DeleteFolder)}/")]
|
[HttpDelete($"{nameof(DeleteFolder)}/")]
|
||||||
public Object DeleteFolder(Int64 folderId)
|
public Object DeleteFolder(Int64 folderId)
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var caller = GetCaller();
|
||||||
var ctx = ctxAccessor.HttpContext;
|
if (caller == null)
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
var currentUser = (User)ctx.Items["User"];
|
|
||||||
|
|
||||||
var folderToDelete = db
|
var folderToDelete = db
|
||||||
.GetAllAccessibleFolders(currentUser!)
|
.GetAllAccessibleFolders(caller)
|
||||||
.FirstOrDefault(f => f.Id == folderId);
|
.FirstOrDefault(f => f.Id == folderId);
|
||||||
|
|
||||||
if (folderToDelete is null)
|
if (folderToDelete is null)
|
||||||
|
@ -299,5 +299,22 @@ public class Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static User? GetCaller()
|
||||||
|
{
|
||||||
|
var ctxAccessor = new HttpContextAccessor();
|
||||||
|
return ctxAccessor.HttpContext?.Items["User"] as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean VerifyPassword(String password, User user)
|
||||||
|
{
|
||||||
|
var pwdBytes = Encoding.UTF8.GetBytes(password);
|
||||||
|
var saltBytes = Encoding.UTF8.GetBytes(user.Salt + "innovEnergy");
|
||||||
|
var pwdHash = Crypto.ComputeHash(pwdBytes, saltBytes);
|
||||||
|
|
||||||
|
return user.Password == pwdHash;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
namespace Backend.Controllers;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Innovenergy.Backend.Controllers;
|
||||||
|
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||||
public record Credentials(String Username, String Password);
|
public record Credentials(String Username, String Password);
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Innovenergy.Backend.Controllers;
|
||||||
|
|
||||||
|
public class ReturnsAttribute : ProducesResponseTypeAttribute
|
||||||
|
{
|
||||||
|
public ReturnsAttribute(HttpStatusCode statusCode) : base((Int32)statusCode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReturnsAttribute<T> : ProducesResponseTypeAttribute
|
||||||
|
{
|
||||||
|
public ReturnsAttribute(HttpStatusCode statusCode) : base(typeof(T), (Int32)statusCode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReturnsAttribute() : base(typeof(T), (Int32)HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Backend.Model;
|
using Innovenergy.Backend.Model;
|
||||||
using Backend.Model.Relations;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
using Backend.Utils;
|
using Innovenergy.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db : IDisposable
|
public partial class Db : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -97,6 +97,8 @@ public partial class Db : IDisposable
|
||||||
return direct.Concat(fromFolders);
|
return direct.Concat(fromFolders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Backend.Model.Relations;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using Backend.Model;
|
using Innovenergy.Backend.Model;
|
||||||
using Backend.Utils;
|
using Innovenergy.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,15 @@ public partial class Db
|
||||||
return Installations.Where(f => f.ParentId == parent.Id);
|
return Installations.Where(f => f.ParentId == parent.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<User> GetChildUsers(User parent)
|
||||||
|
{
|
||||||
|
return Users.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<User> GetDescendantUsers(User parent)
|
||||||
|
{
|
||||||
|
return parent.Traverse(GetChildUsers);
|
||||||
|
}
|
||||||
|
|
||||||
public Result CreateFolder(Folder folder)
|
public Result CreateFolder(Folder folder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using Backend.Model;
|
using Innovenergy.Backend.Model;
|
||||||
using Backend.Utils;
|
using Innovenergy.Backend.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
using System.Net;
|
|
||||||
using System.Net.Mail;
|
using System.Net.Mail;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using Backend.Model;
|
|
||||||
using Backend.Utils;
|
|
||||||
using Flurl.Http;
|
using Flurl.Http;
|
||||||
|
using Innovenergy.Backend.Model;
|
||||||
|
using Innovenergy.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
#pragma warning disable CS0472
|
#pragma warning disable CS0472
|
||||||
#pragma warning disable CS8602
|
#pragma warning disable CS8602
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
@ -22,16 +20,16 @@ public partial class Db
|
||||||
|
|
||||||
public User? GetUserById(Int64 id)
|
public User? GetUserById(Int64 id)
|
||||||
{
|
{
|
||||||
return Users.FirstOrDefault(u => u.Id == id);
|
return Users.FirstOrDefault(u => u.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean IsParentOfChild(User parent, User child)
|
public Boolean IsParentOfChild(User parent, User child)
|
||||||
{
|
{
|
||||||
var parentPointer = child.ParentId;
|
var parentPointer = child.ParentId;
|
||||||
|
|
||||||
if (parent.Id == child.Id)
|
if (parent.Id == child.Id)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
while (parentPointer != null && parentPointer != parent.Id)
|
while (parentPointer != null && parentPointer != parent.Id)
|
||||||
{
|
{
|
||||||
parentPointer = GetUserById(parentPointer).ParentId;
|
parentPointer = GetUserById(parentPointer).ParentId;
|
||||||
|
@ -46,10 +44,10 @@ public partial class Db
|
||||||
{
|
{
|
||||||
if (GetUserByEmail(user.Email) is not null)
|
if (GetUserByEmail(user.Email) is not null)
|
||||||
return Result.Error("User with that email already exists");
|
return Result.Error("User with that email already exists");
|
||||||
|
|
||||||
//Salting and Hashing password
|
//Salting and Hashing password
|
||||||
var salt = Crypto.GenerateSalt();
|
var salt = Crypto.GenerateSalt();
|
||||||
var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password),
|
var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password),
|
||||||
Encoding.UTF8.GetBytes(salt + "innovEnergy"));
|
Encoding.UTF8.GetBytes(salt + "innovEnergy"));
|
||||||
|
|
||||||
user.Salt = salt;
|
user.Salt = salt;
|
||||||
|
@ -57,49 +55,51 @@ public partial class Db
|
||||||
|
|
||||||
return Create(user);
|
return Create(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object CreateAndSaveUserS3ApiKey(User user)
|
public Object CreateAndSaveUserS3ApiKey(User user)
|
||||||
{
|
{
|
||||||
//EXOSCALE API URL
|
//EXOSCALE API URL
|
||||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/access-key";
|
const String url = "https://api-ch-dk-2.exoscale.com/v2/access-key";
|
||||||
const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
||||||
const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
||||||
var payload = new
|
var payload = new
|
||||||
{ name = user.Email,
|
{
|
||||||
operations = new List<String> {"getObject", "listBucket"},
|
name = user.Email,
|
||||||
content = new List<Object>{}};
|
operations = new List<String> { "getObject", "listBucket" },
|
||||||
|
content = new List<Object> { }
|
||||||
|
};
|
||||||
|
|
||||||
var installationIdList = User2Installation
|
var installationIdList = User2Installation
|
||||||
.Where(i => i.UserId == user.Id)
|
.Where(i => i.UserId == user.Id)
|
||||||
.SelectMany(i => Installations.Where(f => i.InstallationId == f.Id))
|
.SelectMany(i => Installations.Where(f => i.InstallationId == f.Id))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var installation in installationIdList)
|
foreach (var installation in installationIdList)
|
||||||
{
|
{
|
||||||
payload.content.Add(new {domain = "sos", resource_type = "bucket", resource_name = installation.Name}); //TODO CHANGE NAME TO S3BUCKET
|
payload.content.Add(new { domain = "sos", resource_type = "bucket", resource_name = installation.Name }); //TODO CHANGE NAME TO S3BUCKET
|
||||||
}
|
}
|
||||||
|
|
||||||
using var hmacSha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
|
using var hmacSha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
|
||||||
var signature = Encoding.UTF8
|
var signature = Encoding.UTF8
|
||||||
.GetBytes(payload.ToString())
|
.GetBytes(payload.ToString())
|
||||||
.Apply(hmacSha1.ComputeHash)
|
.Apply(hmacSha1.ComputeHash)
|
||||||
.Apply(Convert.ToBase64String);
|
.Apply(Convert.ToBase64String);
|
||||||
|
|
||||||
var keyJson = url
|
var keyJson = url
|
||||||
.WithHeader("Authorization", $"POST {apiKey};{signature}")
|
.WithHeader("Authorization", $"POST {apiKey};{signature}")
|
||||||
.PostJsonAsync(payload)
|
.PostJsonAsync(payload)
|
||||||
.ReceiveJson()
|
.ReceiveJson()
|
||||||
.Result;
|
.Result;
|
||||||
|
|
||||||
return SetUserS3ApiKey(user, keyJson.GetValue("key"));
|
return SetUserS3ApiKey(user, keyJson.GetValue("key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetUserS3ApiKey(User user,String key)
|
public Result SetUserS3ApiKey(User user, String key)
|
||||||
{
|
{
|
||||||
user.S3Key = key;
|
user.S3Key = key;
|
||||||
return Update(user);
|
return Update(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result UpdateUser(User user)
|
public Result UpdateUser(User user)
|
||||||
{
|
{
|
||||||
var oldUser = GetUserById(user.Id);
|
var oldUser = GetUserById(user.Id);
|
||||||
|
@ -108,42 +108,39 @@ public partial class Db
|
||||||
|
|
||||||
//Checking for unchangeable things
|
//Checking for unchangeable things
|
||||||
// TODO: depends on privileges of caller
|
// TODO: depends on privileges of caller
|
||||||
|
|
||||||
user.Id = oldUser.Id;
|
user.Id = oldUser.Id;
|
||||||
user.ParentId = oldUser.ParentId;
|
user.ParentId = oldUser.ParentId;
|
||||||
user.Email = oldUser.Email;
|
user.Email = oldUser.Email;
|
||||||
|
|
||||||
return Update(user);
|
return Update(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result DeleteUser(User user)
|
public Result DeleteUser(User user)
|
||||||
{
|
{
|
||||||
User2Folder .Delete(u => u.UserId == user.Id);
|
User2Folder.Delete(u => u.UserId == user.Id);
|
||||||
User2Installation.Delete(u => u.UserId == user.Id);
|
User2Installation.Delete(u => u.UserId == user.Id);
|
||||||
|
|
||||||
//Todo check for orphaned Installations/Folders
|
//Todo check for orphaned Installations/Folders
|
||||||
|
|
||||||
// GetChildUsers()
|
// GetChildUsers()
|
||||||
|
|
||||||
return Delete(user);
|
return Delete(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
private static Boolean IsValidEmail(String email)
|
private static Boolean IsValidEmail(String email)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var emailAddress = new MailAddress(email);
|
var emailAddress = new MailAddress(email);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Backend.Model.Relations;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Backend.Model.Relations;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace Innovenergy.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace Innovenergy.Backend;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is for convenient testing! Todo throw me out?
|
||||||
|
/// Operation filter to add the requirement of the custom header
|
||||||
|
/// </summary>
|
||||||
|
public class HeaderFilter : IOperationFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
|
{
|
||||||
|
operation.Parameters ??= new List<OpenApiParameter>();
|
||||||
|
|
||||||
|
operation.Parameters.Add(new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = "auth",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Content = new Dictionary<String, OpenApiMediaType>(),
|
||||||
|
Required = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using SQLite;
|
namespace Innovenergy.Backend.Model;
|
||||||
|
|
||||||
namespace Backend.Model;
|
|
||||||
|
|
||||||
public class Folder : TreeNode
|
public class Folder : TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using SQLite;
|
namespace Innovenergy.Backend.Model;
|
||||||
|
|
||||||
namespace Backend.Model;
|
|
||||||
|
|
||||||
|
|
||||||
public class Installation : TreeNode
|
public class Installation : TreeNode
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model.Relations;
|
namespace Innovenergy.Backend.Model.Relations;
|
||||||
|
|
||||||
public abstract class Relation<L,R>
|
public abstract class Relation<L,R>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model.Relations;
|
namespace Innovenergy.Backend.Model.Relations;
|
||||||
|
|
||||||
public class Session : Relation<String, Int64>
|
public class Session : Relation<String, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model.Relations;
|
namespace Innovenergy.Backend.Model.Relations;
|
||||||
|
|
||||||
internal class User2Folder : Relation<Int64, Int64>
|
internal class User2Folder : Relation<Int64, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model.Relations;
|
namespace Innovenergy.Backend.Model.Relations;
|
||||||
|
|
||||||
internal class User2Installation : Relation<Int64, Int64>
|
internal class User2Installation : Relation<Int64, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
|
namespace Innovenergy.Backend.Model;
|
||||||
namespace Backend.Model;
|
|
||||||
|
|
||||||
public abstract class TreeNode
|
public abstract class TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model;
|
namespace Innovenergy.Backend.Model;
|
||||||
|
|
||||||
public class User : TreeNode
|
public class User : TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Backend.Utils;
|
namespace Innovenergy.Backend.Utils;
|
||||||
|
|
||||||
public static class Crypto
|
public static class Crypto
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Backend.Utils;
|
namespace Innovenergy.Backend.Utils;
|
||||||
|
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
|
|
Binary file not shown.
|
@ -1,104 +1,59 @@
|
||||||
using Backend.Controllers;
|
using Innovenergy.Backend.Database;
|
||||||
using Backend.Database;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
|
|
||||||
|
namespace Innovenergy.Backend;
|
||||||
|
|
||||||
using (var db = Db.Connect())
|
public class Program
|
||||||
{
|
{
|
||||||
db.CreateFakeRelations();
|
public static void Main(string[] args)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
|
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|
||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
|
||||||
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
|
|
||||||
builder.Services.AddSwaggerGen(config =>
|
|
||||||
{
|
|
||||||
config.SwaggerDoc("v1", new OpenApiInfo{ Title = "My API", Version = "V1" });
|
|
||||||
config.OperationFilter<MyHeaderFilter>(); //Todo testing throw me out
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseSwagger();
|
|
||||||
app.UseSwaggerUI(cfg => cfg.EnableFilter());
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseCors();
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseAuthorization();
|
|
||||||
app.Use(SetSessionUser);
|
|
||||||
app.MapControllers();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================= Functions for above ===================
|
|
||||||
|
|
||||||
//Setting User for current Session
|
|
||||||
async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
|
|
||||||
{
|
|
||||||
var headers = ctx.Request.Headers;
|
|
||||||
var hasToken = headers.TryGetValue("auth", out var token);
|
|
||||||
|
|
||||||
if (!ctx.Request.Path.ToString().Contains(nameof(Controller.Login)))
|
|
||||||
{
|
{
|
||||||
if (!hasToken)
|
using (var db = Db.Connect())
|
||||||
|
db.CreateFakeRelations();
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
|
||||||
|
builder.Services.AddSwaggerGen(config =>
|
||||||
{
|
{
|
||||||
ctx.Response.StatusCode = 403;
|
config.SwaggerDoc("v1", new OpenApiInfo{ Title = "My API", Version = "V1" });
|
||||||
return;
|
config.OperationFilter<HeaderFilter>(); //Todo testing throw me out
|
||||||
}
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
var user = db.GetUserByToken(token.ToString());
|
|
||||||
|
|
||||||
if (user is null)
|
|
||||||
{
|
|
||||||
ctx.Response.StatusCode = 403;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Items["User"] = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
await next(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is for convenient testing! Todo throw me out?
|
|
||||||
/// Operation filter to add the requirement of the custom header
|
|
||||||
/// </summary>
|
|
||||||
public class MyHeaderFilter : IOperationFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
|
||||||
{
|
|
||||||
operation.Parameters ??= new List<OpenApiParameter>();
|
|
||||||
|
|
||||||
operation.Parameters.Add(new OpenApiParameter
|
|
||||||
{
|
|
||||||
Name = "auth",
|
|
||||||
In = ParameterLocation.Header,
|
|
||||||
Content = new Dictionary<String, OpenApiMediaType>(),
|
|
||||||
Required = false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(cfg => cfg.EnableFilter());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseCors();
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.Use(SetSessionUser);
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
|
||||||
|
{
|
||||||
|
var headers = ctx.Request.Headers;
|
||||||
|
var hasToken = headers.TryGetValue("auth", out var token);
|
||||||
|
|
||||||
|
if (hasToken)
|
||||||
|
{
|
||||||
|
using var db = Db.Connect();
|
||||||
|
ctx.Items["User"] = db.GetUserByToken(token.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
await next(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<Import Project="../InnovEnergy.app.props" />
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<RootNamespace>InnovEnergy.CsController</RootNamespace>
|
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="../../lib/Victron/VeDBus/VeDBus.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,62 +0,0 @@
|
||||||
using System.Reactive.Linq;
|
|
||||||
using InnovEnergy.Lib.Protocols.DBus;
|
|
||||||
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes;
|
|
||||||
using InnovEnergy.Lib.Victron.VeDBus;
|
|
||||||
|
|
||||||
|
|
||||||
// dotnet publish EmuMeter.csproj -c Release -r linux-arm -p:PublishSingleFile=true --self-contained true && \
|
|
||||||
// rsync -av bin/Release/net6.0/linux-arm/publish/ root@10.2.1.6:/home/root/emu && clear && \
|
|
||||||
// ssh root@10.2.1.6 /home/root/emu/EmuMeter
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Starting CsController ");
|
|
||||||
|
|
||||||
//Enable out InnovEnergy GUI Page through DBusService.DBUS_SERVICE_UNSUPPORTED
|
|
||||||
// var service = new DBusService("com.victronenergy.unsupported");
|
|
||||||
var veProperties = new VeProperties();
|
|
||||||
veProperties.Set("/CustomName", "InnovEnergy");
|
|
||||||
veProperties.Set("/ProductName", "InnovEnergySW");
|
|
||||||
|
|
||||||
|
|
||||||
var dbus = new DBusConnection(Bus.System);
|
|
||||||
// var ep = new UnixDomainSocketEndPoint("/home/kim/graber_dbus.sock");
|
|
||||||
// var auth = AuthenticationMethod.ExternalAsRoot();
|
|
||||||
// var dbusAddress = new Bus(ep, auth);
|
|
||||||
// var dbus = new DBusConnection(dbusAddress);
|
|
||||||
|
|
||||||
|
|
||||||
await veProperties.PublishOnDBus(Bus.System, "com.victronenergy.unsupported");
|
|
||||||
|
|
||||||
var battery = "com.victronenergy.battery.ttyUSB0";
|
|
||||||
var soc = 21.0;
|
|
||||||
|
|
||||||
//TODO change ttyUSB0 for generic Battery on dbus
|
|
||||||
var names = await dbus.ListNames();
|
|
||||||
foreach (var name in names)
|
|
||||||
{
|
|
||||||
if(name.Contains("com.victronenergy.battery."))
|
|
||||||
{
|
|
||||||
battery = name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mustCharge = dbus
|
|
||||||
.ObserveSignalMessages(sender: battery, objectPath: "/Soc")
|
|
||||||
.Select(m => m.Payload)!
|
|
||||||
.OfType<IDictionary<String, Variant>>()
|
|
||||||
.Where(d => d.ContainsKey("Value"))
|
|
||||||
.Select(d => d["Value"].Value)
|
|
||||||
.OfType<Double>()
|
|
||||||
.Do(s => Console.WriteLine($"soc = {s}"))
|
|
||||||
.Select(d => d < 67)
|
|
||||||
.DistinctUntilChanged();
|
|
||||||
|
|
||||||
|
|
||||||
mustCharge.Subscribe(b =>
|
|
||||||
{
|
|
||||||
dbus.SetValue("com.victronenergy.settings", "/Settings/CGwacs/BatteryLife/State", b ? 9 : 10); // 9 = charge battery, 10 = optimized (no batterylife)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Console.ReadLine();
|
|
|
@ -1,21 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
csproj="CsController.csproj"
|
|
||||||
exe="CsController"
|
|
||||||
remote="10.2.1.6"
|
|
||||||
#remote="10.2.2.152"
|
|
||||||
|
|
||||||
netVersion="net6.0"
|
|
||||||
platform="linux-arm"
|
|
||||||
config="Release"
|
|
||||||
host="root@$remote"
|
|
||||||
dir="/data/innovenergy/$exe"
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
dotnet publish "$csproj" -c $config -r $platform -p:SuppressTrimmAnalysisWarnings=true -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false --self-contained true
|
|
||||||
rsync -av "bin/$config/$netVersion/$platform/publish/" "$host:$dir"
|
|
||||||
|
|
||||||
clear
|
|
||||||
ssh "$host" "$dir/$exe"
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
exec 2>&1
|
|
||||||
exec multilog t s25000 n4 /var/log/CsController
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
exec 2>&1
|
|
||||||
exec softlimit -d 100000000 -s 1000000 -a 100000000 /opt/innovenergy/CsController/CsController
|
|
|
@ -1,4 +1,4 @@
|
||||||
#undef BatteriesAllowed
|
|
||||||
|
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -11,13 +11,11 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
||||||
using InnovEnergy.Lib.Devices.Ampt;
|
using InnovEnergy.Lib.Devices.Ampt;
|
||||||
using InnovEnergy.Lib.Devices.Battery48TL;
|
using InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
using InnovEnergy.SaliMax.Controller;
|
using InnovEnergy.SaliMax.Controller;
|
||||||
using InnovEnergy.SaliMax.Log;
|
using InnovEnergy.SaliMax.Log;
|
||||||
using InnovEnergy.SaliMax.SaliMaxRelays;
|
using InnovEnergy.SaliMax.SaliMaxRelays;
|
||||||
using InnovEnergy.SaliMax.SystemConfig;
|
using InnovEnergy.SaliMax.SystemConfig;
|
||||||
using InnovEnergy.Time.Unix;
|
using InnovEnergy.Time.Unix;
|
||||||
using Utils = InnovEnergy.Lib.StatusApi.Utils;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma warning disable IL2026
|
#pragma warning disable IL2026
|
||||||
|
@ -148,7 +146,7 @@ internal static class Program
|
||||||
|
|
||||||
//JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue);
|
//JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue);
|
||||||
#endif
|
#endif
|
||||||
PrintTopology(status);
|
Topology.Print(status);
|
||||||
|
|
||||||
while (UnixTime.Now == t)
|
while (UnixTime.Now == t)
|
||||||
await Task.Delay(delayTime);
|
await Task.Delay(delayTime);
|
||||||
|
@ -157,181 +155,8 @@ internal static class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void PrintTopology(StatusRecord s)
|
|
||||||
{
|
|
||||||
const String chargingSeparator = ">>>>>>>>>>";
|
|
||||||
const String dischargingSeparator = "<<<<<<<<<";
|
|
||||||
const Int32 height = 25;
|
|
||||||
|
|
||||||
|
|
||||||
var pwr = s.InverterStatus!.Ac.ActivePower;
|
|
||||||
var pvPower = (s.AmptStatus!.Devices[0].Dc.Voltage * s.AmptStatus.Devices[0].Dc.Current + s.AmptStatus!.Devices[1].Dc.Voltage * s.AmptStatus.Devices[1].Dc.Current).Round0(); // TODO using one Ampt
|
|
||||||
var loadPower = Utils.Round3((s.GridMeterStatus!.ActivePowerL123 + pwr)); // it's a + because the pwr is inverted
|
|
||||||
|
|
||||||
var gridSeparator = s.GridMeterStatus!.ActivePowerL123 > 0 ? chargingSeparator : dischargingSeparator;
|
|
||||||
var inverterSeparator = -pwr > 0 ? chargingSeparator : dischargingSeparator;
|
|
||||||
var dcSeparator = -s.DcDcStatus!.Dc.Power > 0 ? chargingSeparator : dischargingSeparator;
|
|
||||||
#if BatteriesAllowed
|
|
||||||
var battery1Separator = s.BatteriesStatus[0]!.Power > 0 ? chargingSeparator : dischargingSeparator;
|
|
||||||
var battery2Separator = s.BatteriesStatus[1]!.Power > 0 ? chargingSeparator : dischargingSeparator;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////// Grid //////////////////////
|
|
||||||
var boxGrid = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"Grid",
|
|
||||||
s.GridMeterStatus.Ac.L1.Voltage.V(),
|
|
||||||
s.GridMeterStatus.Ac.L2.Voltage.V(),
|
|
||||||
s.GridMeterStatus.Ac.L3.Voltage.V()
|
|
||||||
).AlignCenterVertical(height);
|
|
||||||
|
|
||||||
var gridAcBusArrow = AsciiArt.CreateHorizontalArrow(s.GridMeterStatus!.ActivePowerL123.Round0(), gridSeparator)
|
|
||||||
.AlignCenterVertical(height);
|
|
||||||
|
|
||||||
|
|
||||||
////////////////// Ac Bus //////////////////////
|
|
||||||
var boxAcBus = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"AC Bus",
|
|
||||||
s.InverterStatus.Ac.L1.Voltage.V(),
|
|
||||||
s.InverterStatus.Ac.L2.Voltage.V(),
|
|
||||||
s.InverterStatus.Ac.L3.Voltage.V()
|
|
||||||
);
|
|
||||||
|
|
||||||
var boxLoad = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"",
|
|
||||||
"LOAD",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
var loadRect = CreateRect(boxAcBus, boxLoad, loadPower).AlignBottom(height);
|
|
||||||
|
|
||||||
var acBusInvertArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
|
|
||||||
.AlignCenterVertical(height);
|
|
||||||
|
|
||||||
//////////////////// Inverter /////////////////////////
|
|
||||||
var inverterBox = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"",
|
|
||||||
"Inverter",
|
|
||||||
""
|
|
||||||
).AlignCenterVertical(height);
|
|
||||||
|
|
||||||
var inverterArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
|
|
||||||
.AlignCenterVertical(height);
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////// DC Bus /////////////////////////
|
|
||||||
var dcBusBox = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"DC Bus",
|
|
||||||
(s.InverterStatus.ActualDcLinkVoltageLowerHalfExt + s.InverterStatus.ActualDcLinkVoltageUpperHalfExt).V(),
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
var pvBox = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"MPPT",
|
|
||||||
((s.AmptStatus!.Devices[0].Dc.Voltage + s.AmptStatus!.Devices[1].Dc.Voltage) / 2).Round0().V(),
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
var pvRect = CreateRect(pvBox, dcBusBox, pvPower).AlignTop(height);
|
|
||||||
|
|
||||||
var dcBusArrow = AsciiArt.CreateHorizontalArrow(-s.DcDcStatus!.Dc.Power, dcSeparator)
|
|
||||||
.AlignCenterVertical(height);
|
|
||||||
|
|
||||||
//////////////////// Dc/Dc /////////////////////////
|
|
||||||
var dcBox = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"Dc/Dc",
|
|
||||||
s.DcDcStatus.BatteryVoltage.V(),
|
|
||||||
""
|
|
||||||
).AlignCenterVertical(height);
|
|
||||||
#if BatteriesAllowed
|
|
||||||
var dcArrow1 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[0]!.Power.Round0(), battery1Separator);
|
|
||||||
var dcArrow2 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[1]!.Power.Round0(), battery2Separator);
|
|
||||||
#else
|
|
||||||
var dcArrow1 ="";
|
|
||||||
var dcArrow2 = "";
|
|
||||||
var dcArrowRect = CreateRect(dcArrow1, dcArrow2).AlignCenterVertical(height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if BatteriesAllowed
|
|
||||||
|
|
||||||
//////////////////// Batteries /////////////////////////
|
|
||||||
var battery1Box = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"Battery 1",
|
|
||||||
s.BatteriesStatus[0].Voltage.V(),
|
|
||||||
s.BatteriesStatus[0].Soc.Percent(),
|
|
||||||
s.BatteriesStatus[0].Temperature.Celsius()
|
|
||||||
);
|
|
||||||
|
|
||||||
var battery2Box = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"Battery 2",
|
|
||||||
s.BatteriesStatus[1].Voltage.V(),
|
|
||||||
s.BatteriesStatus[1].Soc.Percent(),
|
|
||||||
s.BatteriesStatus[1].Temperature.Celsius()
|
|
||||||
);
|
|
||||||
|
|
||||||
var batteryRect = CreateRect(battery1Box, battery2Box).AlignCenterVertical(height);
|
|
||||||
|
|
||||||
var avgBatteryBox = AsciiArt.CreateBox
|
|
||||||
(
|
|
||||||
"Batteries",
|
|
||||||
s.AvgBatteriesStatus!.Voltage.V(),
|
|
||||||
s.AvgBatteriesStatus.Soc.Percent(),
|
|
||||||
s.AvgBatteriesStatus.Temperature.Celsius()
|
|
||||||
).AlignCenterVertical(height);
|
|
||||||
|
|
||||||
|
|
||||||
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
|
|
||||||
.SideBySideWith(loadRect, "")
|
|
||||||
.SideBySideWith(acBusInvertArrow, "")
|
|
||||||
.SideBySideWith(inverterBox, "")
|
|
||||||
.SideBySideWith(inverterArrow, "")
|
|
||||||
.SideBySideWith(pvRect, "")
|
|
||||||
.SideBySideWith(dcBusArrow, "")
|
|
||||||
.SideBySideWith(dcBox, "")
|
|
||||||
.SideBySideWith(dcArrowRect, "")
|
|
||||||
.SideBySideWith(batteryRect, "")
|
|
||||||
.SideBySideWith(avgBatteryBox, "")+ "\n";
|
|
||||||
#else
|
|
||||||
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
|
|
||||||
.SideBySideWith(loadRect, "")
|
|
||||||
.SideBySideWith(acBusInvertArrow, "")
|
|
||||||
.SideBySideWith(inverterBox, "")
|
|
||||||
.SideBySideWith(inverterArrow, "")
|
|
||||||
.SideBySideWith(pvRect, "")
|
|
||||||
.SideBySideWith(dcBusArrow, "")
|
|
||||||
.SideBySideWith(dcBox, "")+ "\n";
|
|
||||||
#endif
|
|
||||||
Console.WriteLine(topology);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String CreateRect(String boxTop, String boxBottom, Decimal power)
|
|
||||||
{
|
|
||||||
var powerArrow = AsciiArt.CreateVerticalArrow(power);
|
|
||||||
var boxes = new[] { boxTop, powerArrow, boxBottom };
|
|
||||||
var maxWidth = boxes.Max(l => l.Width());
|
|
||||||
|
|
||||||
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String CreateRect(String boxTop, String boxBottom)
|
|
||||||
{
|
|
||||||
var boxes = new[] { boxTop, boxBottom };
|
|
||||||
var maxWidth = boxes.Max(l => l.Width());
|
|
||||||
|
|
||||||
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// to delete not used anymore
|
// to delete not used anymore
|
||||||
[Conditional("RELEASE")]
|
[Conditional("RELEASE")]
|
||||||
private static void ReleaseWriteLog(JsonObject jsonLog, UnixTime timestamp)
|
private static void ReleaseWriteLog(JsonObject jsonLog, UnixTime timestamp)
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
#undef BatteriesAllowed
|
||||||
|
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using InnovEnergy.SaliMax.Controller;
|
||||||
|
using InnovEnergy.SaliMax.Log;
|
||||||
|
|
||||||
|
namespace InnovEnergy.SaliMax;
|
||||||
|
|
||||||
|
public static class Topology
|
||||||
|
{
|
||||||
|
public static void Print(StatusRecord s)
|
||||||
|
{
|
||||||
|
const String chargingSeparator = ">>>>>>>>>>";
|
||||||
|
const String dischargingSeparator = "<<<<<<<<<";
|
||||||
|
const Int32 height = 25;
|
||||||
|
|
||||||
|
|
||||||
|
var pwr = s.InverterStatus!.Ac.ActivePower;
|
||||||
|
var pvPower = (s.AmptStatus!.Devices[0].Dc.Voltage * s.AmptStatus.Devices[0].Dc.Current + s.AmptStatus!.Devices[1].Dc.Voltage * s.AmptStatus.Devices[1].Dc.Current).Round0(); // TODO using one Ampt
|
||||||
|
var loadPower = Utils.Round3((s.GridMeterStatus!.ActivePowerL123 + pwr)); // it's a + because the pwr is inverted
|
||||||
|
|
||||||
|
var gridSeparator = s.GridMeterStatus!.ActivePowerL123 > 0 ? chargingSeparator : dischargingSeparator;
|
||||||
|
var inverterSeparator = -pwr > 0 ? chargingSeparator : dischargingSeparator;
|
||||||
|
var dcSeparator = -s.DcDcStatus!.Dc.Power > 0 ? chargingSeparator : dischargingSeparator;
|
||||||
|
#if BatteriesAllowed
|
||||||
|
var battery1Separator = s.BatteriesStatus[0]!.Power > 0 ? chargingSeparator : dischargingSeparator;
|
||||||
|
var battery2Separator = s.BatteriesStatus[1]!.Power > 0 ? chargingSeparator : dischargingSeparator;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////// Grid //////////////////////
|
||||||
|
var boxGrid = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"Grid",
|
||||||
|
s.GridMeterStatus.Ac.L1.Voltage.V(),
|
||||||
|
s.GridMeterStatus.Ac.L2.Voltage.V(),
|
||||||
|
s.GridMeterStatus.Ac.L3.Voltage.V()
|
||||||
|
).AlignCenterVertical(height);
|
||||||
|
|
||||||
|
var gridAcBusArrow = AsciiArt.CreateHorizontalArrow(s.GridMeterStatus!.ActivePowerL123.Round0(), gridSeparator)
|
||||||
|
.AlignCenterVertical(height);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////// Ac Bus //////////////////////
|
||||||
|
var boxAcBus = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"AC Bus",
|
||||||
|
s.InverterStatus.Ac.L1.Voltage.V(),
|
||||||
|
s.InverterStatus.Ac.L2.Voltage.V(),
|
||||||
|
s.InverterStatus.Ac.L3.Voltage.V()
|
||||||
|
);
|
||||||
|
|
||||||
|
var boxLoad = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"",
|
||||||
|
"LOAD",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
var loadRect = StringUtils.AlignBottom(CreateRect(boxAcBus, boxLoad, loadPower), height);
|
||||||
|
|
||||||
|
var acBusInvertArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
|
||||||
|
.AlignCenterVertical(height);
|
||||||
|
|
||||||
|
//////////////////// Inverter /////////////////////////
|
||||||
|
var inverterBox = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"",
|
||||||
|
"Inverter",
|
||||||
|
""
|
||||||
|
).AlignCenterVertical(height);
|
||||||
|
|
||||||
|
var inverterArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
|
||||||
|
.AlignCenterVertical(height);
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////// DC Bus /////////////////////////
|
||||||
|
var dcBusBox = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"DC Bus",
|
||||||
|
(s.InverterStatus.ActualDcLinkVoltageLowerHalfExt + s.InverterStatus.ActualDcLinkVoltageUpperHalfExt).V(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
var pvBox = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"MPPT",
|
||||||
|
((s.AmptStatus!.Devices[0].Dc.Voltage + s.AmptStatus!.Devices[1].Dc.Voltage) / 2).Round0().V(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
var pvRect = StringUtils.AlignTop(CreateRect(pvBox, dcBusBox, pvPower), height);
|
||||||
|
|
||||||
|
var dcBusArrow = AsciiArt.CreateHorizontalArrow(-s.DcDcStatus!.Dc.Power, dcSeparator)
|
||||||
|
.AlignCenterVertical(height);
|
||||||
|
|
||||||
|
//////////////////// Dc/Dc /////////////////////////
|
||||||
|
var dcBox = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"Dc/Dc",
|
||||||
|
s.DcDcStatus.BatteryVoltage.V(),
|
||||||
|
""
|
||||||
|
).AlignCenterVertical(height);
|
||||||
|
#if BatteriesAllowed
|
||||||
|
var dcArrow1 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[0]!.Power.Round0(), battery1Separator);
|
||||||
|
var dcArrow2 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[1]!.Power.Round0(), battery2Separator);
|
||||||
|
#else
|
||||||
|
var dcArrow1 ="";
|
||||||
|
var dcArrow2 = "";
|
||||||
|
var dcArrowRect = StringUtils.AlignCenterVertical(CreateRect(dcArrow1, dcArrow2), height);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if BatteriesAllowed
|
||||||
|
|
||||||
|
//////////////////// Batteries /////////////////////////
|
||||||
|
var battery1Box = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"Battery 1",
|
||||||
|
s.BatteriesStatus[0].Voltage.V(),
|
||||||
|
s.BatteriesStatus[0].Soc.Percent(),
|
||||||
|
s.BatteriesStatus[0].Temperature.Celsius()
|
||||||
|
);
|
||||||
|
|
||||||
|
var battery2Box = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"Battery 2",
|
||||||
|
s.BatteriesStatus[1].Voltage.V(),
|
||||||
|
s.BatteriesStatus[1].Soc.Percent(),
|
||||||
|
s.BatteriesStatus[1].Temperature.Celsius()
|
||||||
|
);
|
||||||
|
|
||||||
|
var batteryRect = CreateRect(battery1Box, battery2Box).AlignCenterVertical(height);
|
||||||
|
|
||||||
|
var avgBatteryBox = AsciiArt.CreateBox
|
||||||
|
(
|
||||||
|
"Batteries",
|
||||||
|
s.AvgBatteriesStatus!.Voltage.V(),
|
||||||
|
s.AvgBatteriesStatus.Soc.Percent(),
|
||||||
|
s.AvgBatteriesStatus.Temperature.Celsius()
|
||||||
|
).AlignCenterVertical(height);
|
||||||
|
|
||||||
|
|
||||||
|
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
|
||||||
|
.SideBySideWith(loadRect, "")
|
||||||
|
.SideBySideWith(acBusInvertArrow, "")
|
||||||
|
.SideBySideWith(inverterBox, "")
|
||||||
|
.SideBySideWith(inverterArrow, "")
|
||||||
|
.SideBySideWith(pvRect, "")
|
||||||
|
.SideBySideWith(dcBusArrow, "")
|
||||||
|
.SideBySideWith(dcBox, "")
|
||||||
|
.SideBySideWith(dcArrowRect, "")
|
||||||
|
.SideBySideWith(batteryRect, "")
|
||||||
|
.SideBySideWith(avgBatteryBox, "")+ "\n";
|
||||||
|
#else
|
||||||
|
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
|
||||||
|
.SideBySideWith(loadRect, "")
|
||||||
|
.SideBySideWith(acBusInvertArrow, "")
|
||||||
|
.SideBySideWith(inverterBox, "")
|
||||||
|
.SideBySideWith(inverterArrow, "")
|
||||||
|
.SideBySideWith(pvRect, "")
|
||||||
|
.SideBySideWith(dcBusArrow, "")
|
||||||
|
.SideBySideWith(dcBox, "")+ "\n";
|
||||||
|
#endif
|
||||||
|
Console.WriteLine(topology);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String CreateRect(String boxTop, String boxBottom, Decimal power)
|
||||||
|
{
|
||||||
|
var powerArrow = AsciiArt.CreateVerticalArrow(power);
|
||||||
|
var boxes = new[] { boxTop, powerArrow, boxBottom };
|
||||||
|
var maxWidth = boxes.Max(l => l.Width());
|
||||||
|
|
||||||
|
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String CreateRect(String boxTop, String boxBottom)
|
||||||
|
{
|
||||||
|
var boxes = new[] { boxTop, boxBottom };
|
||||||
|
var maxWidth = boxes.Max(l => l.Width());
|
||||||
|
|
||||||
|
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue