Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
c52501f715
|
@ -1,10 +1,5 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||||
|
@ -26,17 +21,24 @@
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters.Abstractions" Version="7.0.6" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters.Abstractions" Version="7.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="AWSSDK.Core">
|
|
||||||
<HintPath>..\..\..\..\..\..\.nuget\packages\awssdk.core\3.7.8.10\lib\netcoreapp3.1\AWSSDK.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SQLite-net">
|
|
||||||
<HintPath>..\..\..\..\..\.nuget\packages\sqlite-net-pcl\1.8.116\lib\netstandard2.0\SQLite-net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\lib\WebServer\WebServer.csproj" />
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Resources" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Resources\s3cmd.py">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,390 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using static System.Net.HttpStatusCode;
|
||||||
|
using Folder = InnovEnergy.App.Backend.DataTypes.Folder;
|
||||||
|
using Installation = InnovEnergy.App.Backend.DataTypes.Installation;
|
||||||
|
using Object = System.Object;
|
||||||
|
using User = InnovEnergy.App.Backend.DataTypes.User;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/")]
|
||||||
|
public class Controller
|
||||||
|
{
|
||||||
|
private static readonly HttpResponseMessage _Unauthorized = new HttpResponseMessage(Unauthorized);
|
||||||
|
private static readonly HttpResponseMessage _Ok = new HttpResponseMessage(OK);
|
||||||
|
private static readonly HttpResponseMessage _BadRequest = new HttpResponseMessage(BadRequest);
|
||||||
|
|
||||||
|
[Returns<String>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[Returns(BadRequest)]
|
||||||
|
[HttpPost($"{nameof(Login)}")]
|
||||||
|
public Object Login(Credentials credentials)
|
||||||
|
{
|
||||||
|
var session = credentials.Login();
|
||||||
|
|
||||||
|
return session is null
|
||||||
|
? _Unauthorized
|
||||||
|
: session;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(Logout)}")]
|
||||||
|
public Object Logout()
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Logout()
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns<User>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetUserById)}")]
|
||||||
|
public Object GetUserById(Int64 id)
|
||||||
|
{
|
||||||
|
var caller = GetSession()?.User;
|
||||||
|
if (caller == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var user = Db.GetUserById(id);
|
||||||
|
|
||||||
|
if (user is null || !caller.HasAccessTo(user))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
user.Password = "";
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns<Installation>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetInstallationById)}")]
|
||||||
|
public Object GetInstallationById(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(id);
|
||||||
|
|
||||||
|
if (installation is null || !user.HasAccessTo(installation))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
return installation;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns<Installation>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetUsersWithAccessToInstallation)}")]
|
||||||
|
public Object GetUsersWithAccessToInstallation(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(id);
|
||||||
|
|
||||||
|
if (installation is null || !user.HasAccessTo(installation))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var usersWithInheritedAccess = installation
|
||||||
|
.Ancestors()
|
||||||
|
.SelectMany(f => f.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { folderId = f.Id, user = u }))
|
||||||
|
.OfType<Object>();
|
||||||
|
|
||||||
|
var usersWithDirectAccess = installation.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { installationId = installation.Id, user = u })
|
||||||
|
.OfType<Object>();
|
||||||
|
|
||||||
|
return usersWithInheritedAccess.Concat(usersWithDirectAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns<Installation>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetUsersWithAccessToFolder)}")]
|
||||||
|
public Object GetUsersWithAccessToFolder(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var folder = Db.GetFolderById(id);
|
||||||
|
|
||||||
|
if (folder is null || !user.HasAccessTo(folder))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
return folder
|
||||||
|
.Ancestors()
|
||||||
|
.Append(folder)
|
||||||
|
.SelectMany(f => f.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { folderId = f.Id, user = u }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns<Folder>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetFolderById)}")]
|
||||||
|
public Object GetFolderById(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var folder = Db.GetFolderById(id);
|
||||||
|
|
||||||
|
if (folder is null || !user.HasAccessTo(folder))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetAllInstallations)}/")]
|
||||||
|
public Object GetAllInstallations()
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
|
||||||
|
return user is null
|
||||||
|
? _Unauthorized
|
||||||
|
: user.AccessibleInstallations();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetAllFolders)}/")]
|
||||||
|
public Object GetAllFolders()
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
|
||||||
|
return user is null
|
||||||
|
? _Unauthorized
|
||||||
|
: user.AccessibleFolders();
|
||||||
|
}
|
||||||
|
|
||||||
|
// [Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
// [Returns(Unauthorized)]
|
||||||
|
// [HttpGet($"{nameof(GetUsersOfFolder)}/")]
|
||||||
|
// public Object GetUsersOfFolder(Int64 folderId)
|
||||||
|
// {
|
||||||
|
// var caller = GetCaller();
|
||||||
|
// if (caller == null)
|
||||||
|
// return new HttpResponseMessage(Unauthorized);
|
||||||
|
//
|
||||||
|
// var folder = Db.GetFolderById(folderId);
|
||||||
|
//
|
||||||
|
// if (folder is null || !caller.HasAccessTo(folder))
|
||||||
|
// return new HttpResponseMessage(Unauthorized);
|
||||||
|
//
|
||||||
|
// return descendantUsers;
|
||||||
|
// }
|
||||||
|
|
||||||
|
[Returns<TreeNode[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")]
|
||||||
|
public Object GetAllFoldersAndInstallations()
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
|
||||||
|
return user is null
|
||||||
|
? _Unauthorized
|
||||||
|
: user.AccessibleFoldersAndInstallations();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(CreateUser)}/")]
|
||||||
|
public Object CreateUser(User newUser)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Create(newUser)
|
||||||
|
? newUser
|
||||||
|
: _Unauthorized ;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(CreateInstallation)}/")]
|
||||||
|
public Object CreateInstallation(Installation installation)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Create(installation)
|
||||||
|
? installation
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[Returns(InternalServerError)]
|
||||||
|
[HttpPost($"{nameof(CreateFolder)}/")]
|
||||||
|
public Object CreateFolder(Folder folder)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Create(folder)
|
||||||
|
? folder
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(GrantUserAccessToFolder)}/")]
|
||||||
|
public Object GrantUserAccessToFolder([FromQuery] Int64 folderId, [FromQuery] Int64? id)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
var user = id is not null ? Db.GetUserById(id) : session?.User;
|
||||||
|
|
||||||
|
return session.GrantUserAccessTo(user, Db.GetFolderById(folderId))
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(GrantUserAccessToInstallation)}/")]
|
||||||
|
public Object GrantUserAccessToInstallation([FromQuery] Int64 installationId, [FromQuery] Int64? id)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
var user = id is not null ? Db.GetUserById(id) : session?.User;
|
||||||
|
|
||||||
|
return session.GrantUserAccessTo(user, Db.GetInstallationById(installationId))
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(RevokeUserAccessToInstallation)}/")]
|
||||||
|
public Object RevokeUserAccessToInstallation([FromQuery] Int64 installationId, [FromQuery] Int64? id)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
var user = id is not null ? Db.GetUserById(id) : session?.User;
|
||||||
|
|
||||||
|
|
||||||
|
return session.RevokeAccessTo(user, Db.GetInstallationById(installationId))
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(RevokeUserAccessToFolder)}/")]
|
||||||
|
public Object RevokeUserAccessToFolder([FromQuery] Int64 folderId, [FromQuery] Int64? id)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
var user = id is not null ? Db.GetUserById(id) : session?.User;
|
||||||
|
|
||||||
|
|
||||||
|
return session.RevokeAccessTo(user, Db.GetFolderById(folderId))
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPut($"{nameof(UpdateUser)}/")]
|
||||||
|
public Object UpdateUser(User updatedUser)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
if (!session.Update(updatedUser)) return _Unauthorized;
|
||||||
|
updatedUser.Password = "";
|
||||||
|
return updatedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPut($"{nameof(UpdateInstallation)}/")]
|
||||||
|
public Object UpdateInstallation(Installation installation)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Update(installation)
|
||||||
|
? installation
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPut($"{nameof(UpdateFolder)}/")]
|
||||||
|
public Object UpdateFolder(Folder folder)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
return session.Update(folder)
|
||||||
|
? folder
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpDelete($"{nameof(DeleteUser)}/")]
|
||||||
|
public Object DeleteUser(Int64 userId)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
var user = Db.GetUserById(userId);
|
||||||
|
|
||||||
|
return session.Delete(user)
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns(OK)]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpDelete($"{nameof(DeleteInstallation)}/")]
|
||||||
|
public Object DeleteInstallation(Int64 installationId)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
|
|
||||||
|
return session.Delete(installation)
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(401)]
|
||||||
|
[HttpDelete($"{nameof(DeleteFolder)}/")]
|
||||||
|
public Object DeleteFolder(Int64 folderId)
|
||||||
|
{
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
var folder = Db.GetFolderById(folderId);
|
||||||
|
|
||||||
|
return session.Delete(folder)
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Session? GetSession()
|
||||||
|
{
|
||||||
|
var ctxAccessor = new HttpContextAccessor();
|
||||||
|
return ctxAccessor.HttpContext?.Items["Session"] as Session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||||
|
public record Credentials(String Username, String Password);
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public class Folder : TreeNode {}
|
|
@ -1,6 +1,4 @@
|
||||||
using SQLite;
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
namespace Backend.Model;
|
|
||||||
|
|
||||||
|
|
||||||
public class Installation : TreeNode
|
public class Installation : TreeNode
|
||||||
|
@ -16,6 +14,6 @@ public class Installation : TreeNode
|
||||||
public Double Long { get; set; }
|
public Double Long { get; set; }
|
||||||
|
|
||||||
public String S3Bucket { get; set; } = "";
|
public String S3Bucket { get; set; } = "";
|
||||||
|
public String S3Url { get; set; } = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class CredentialsMethods
|
||||||
|
{
|
||||||
|
public static Session? Login(this Credentials credentials)
|
||||||
|
{
|
||||||
|
var (username, password) = credentials;
|
||||||
|
|
||||||
|
if (username.IsNullOrEmpty() || password.IsNullOrEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var user = Db.GetUserByEmail(username);
|
||||||
|
|
||||||
|
if (user is null || !user.VerifyPassword(password))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var session = new Session(user);
|
||||||
|
|
||||||
|
return Db.Create(session)
|
||||||
|
? session
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
using System.Collections;
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class FolderMethods
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return UsersWithDirectAccess(folder).Concat(UsersWithInheritedAccess(folder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithDirectAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return Db.FolderAccess
|
||||||
|
.Where(access => access.FolderId == folder.Id)
|
||||||
|
.Select(access => Db.GetUserById(access.UserId))
|
||||||
|
.NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithInheritedAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> ChildFolders(this Folder parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Folders
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Installation> ChildInstallations(this Folder parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Installations
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
||||||
|
{
|
||||||
|
return parent
|
||||||
|
.TraverseDepthFirstPreOrder(ChildFolders)
|
||||||
|
.Skip(1); // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
||||||
|
{
|
||||||
|
return folder
|
||||||
|
.Ancestors()
|
||||||
|
.Any(u => u.Id == ancestor.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> Ancestors(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder
|
||||||
|
.Unfold(Parent)
|
||||||
|
.Skip(1); // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Folder? Parent(this Folder folder)
|
||||||
|
{
|
||||||
|
return IsAbsoluteRoot(folder)
|
||||||
|
? null
|
||||||
|
: Db.GetFolderById(folder.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsAbsoluteRoot(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.ParentId == 0; // root has ParentId 0 by definition
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean WasMoved(this Folder folder)
|
||||||
|
{
|
||||||
|
if (folder.IsRelativeRoot())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var existingFolder = Db.GetFolderById(folder.Id);
|
||||||
|
|
||||||
|
return existingFolder is not null
|
||||||
|
&& existingFolder.ParentId != folder.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Exists(this Folder folder)
|
||||||
|
{
|
||||||
|
return Db.Folders.Any(f => f.Id == folder.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
using CliWrap;
|
||||||
|
using CliWrap.Buffered;
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
|
||||||
|
public static class InstallationMethods
|
||||||
|
{
|
||||||
|
public static async Task RenewS3BucketUrl(this Installation installation)
|
||||||
|
{
|
||||||
|
await RenewS3BucketUrl(installation, TimeSpan.FromDays(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RenewS3BucketUrl(this Installation installation, TimeSpan validity)
|
||||||
|
{
|
||||||
|
const String secret = "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU";
|
||||||
|
const String apiKey = "EXO44d2979c8e570eae81ead564";
|
||||||
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
var cmd = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}", validity.TotalSeconds.ToString(), "--access_key",
|
||||||
|
apiKey, "--secret_key", secret
|
||||||
|
});
|
||||||
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
|
installation.S3Url = x.StandardOutput.Replace("\n", "").Replace(" ", "");
|
||||||
|
|
||||||
|
Db.Update(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<Boolean> CreateBucket(this Installation installation)
|
||||||
|
{
|
||||||
|
const String secret = "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU";
|
||||||
|
|
||||||
|
const String apiKey = "EXO87ca85e29dd412f1238f1cf0";
|
||||||
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
|
||||||
|
var cmd = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "mb", $"s3://{installation.Id}-{salt}", "--access_key",
|
||||||
|
apiKey, "--secret_key", secret
|
||||||
|
});
|
||||||
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
|
|
||||||
|
//Updating the url in the db as not wait until the next bi-daily update
|
||||||
|
var cmd2 = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}",
|
||||||
|
TimeSpan.FromDays(1).TotalSeconds.ToString(), "--access_key",
|
||||||
|
apiKey, "--secret_key", secret
|
||||||
|
});
|
||||||
|
|
||||||
|
var y = await cmd2.ExecuteBufferedAsync();
|
||||||
|
installation.S3Url = y.StandardOutput.Replace("\n", "").Replace(" ", "");
|
||||||
|
|
||||||
|
Db.Update(installation);
|
||||||
|
|
||||||
|
return x.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Boolean> DeleteBucket(this Installation installation)
|
||||||
|
{
|
||||||
|
const String secret = "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU";
|
||||||
|
const String apiKey = "EXO87ca85e29dd412f1238f1cf0";
|
||||||
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
var cmd = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "rb", $"s3://{installation.Id}-{salt}", "--access_key",
|
||||||
|
apiKey, "--secret_key", secret
|
||||||
|
});
|
||||||
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
|
return x.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return UsersWithDirectAccess(installation).Concat(UsersWithInheritedAccess(installation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithDirectAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return Db.InstallationAccess
|
||||||
|
.Where(access => access.InstallationId == installation.Id)
|
||||||
|
.Select(access => Db.GetUserById(access.UserId))
|
||||||
|
.NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithInheritedAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return installation.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> Ancestors(this Installation installation)
|
||||||
|
{
|
||||||
|
var parentFolder = Parent(installation);
|
||||||
|
|
||||||
|
if (parentFolder is null)
|
||||||
|
return Enumerable.Empty<Folder>();
|
||||||
|
|
||||||
|
return parentFolder
|
||||||
|
.Ancestors()
|
||||||
|
.Prepend(parentFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Folder? Parent(this Installation installation)
|
||||||
|
{
|
||||||
|
return installation.IsRelativeRoot()
|
||||||
|
? null
|
||||||
|
: Db.GetFolderById(installation.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this Installation i)
|
||||||
|
{
|
||||||
|
return i.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean WasMoved(this Installation installation)
|
||||||
|
{
|
||||||
|
if (installation.IsRelativeRoot())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var existingInstallation = Db.GetInstallationById(installation.Id);
|
||||||
|
|
||||||
|
return existingInstallation is not null
|
||||||
|
&& existingInstallation.ParentId != installation.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Exists(this Installation installation)
|
||||||
|
{
|
||||||
|
return Db.Installations.Any(i => i.Id == installation.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class SessionMethods
|
||||||
|
{
|
||||||
|
public static Boolean Create(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder.Parent())
|
||||||
|
&& Db.Create(folder)
|
||||||
|
&& Db.Create(new FolderAccess() { UserId = user.Id, FolderId = folder.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder)
|
||||||
|
&& (folder.IsRelativeRoot() || user.HasAccessTo(folder.Parent()))
|
||||||
|
&& Db.Update(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder)
|
||||||
|
&& Db.Delete(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean Create(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
//Note: keep generation of access _after_ generation of object to prevent "zombie" access-rights.
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(installation.Parent())
|
||||||
|
&& Db.Create(installation)
|
||||||
|
&& installation.CreateBucket().Result
|
||||||
|
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& installation.Exists()
|
||||||
|
&& user.HasAccessTo(installation)
|
||||||
|
&& (installation.IsRelativeRoot() || user.HasAccessTo(installation.Parent())) // TODO: triple check this
|
||||||
|
&& Db.Update(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(installation)
|
||||||
|
&& Db.Delete(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(this Session? session, User? newUser)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
if (sessionUser is null || newUser is null || !sessionUser.HasWriteAccess)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
newUser.ParentId = sessionUser.Id; // Important!
|
||||||
|
|
||||||
|
return Db.Create(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, User? editedUser)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
if (editedUser == null || sessionUser == null) return false;
|
||||||
|
|
||||||
|
|
||||||
|
//Password change is only allowed for oneself
|
||||||
|
if ( editedUser.Id != sessionUser.Id) editedUser.Password = sessionUser.Password;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editedUser.Password = sessionUser.SaltAndHashPassword(editedUser.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionUser.HasWriteAccess
|
||||||
|
&& sessionUser.HasAccessTo(editedUser)
|
||||||
|
&& (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent()) || editedUser.Id == sessionUser.Id) // TODO: triple check this
|
||||||
|
&& Db.Update(editedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, User? userToDelete)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& userToDelete is not null
|
||||||
|
&& sessionUser.HasWriteAccess
|
||||||
|
&& sessionUser.HasAccessTo(userToDelete) // TODO: && user.HasAccessTo(installation.Parent()) ???
|
||||||
|
&& Db.Delete(userToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean GrantUserAccessTo(this Session? session, User? user, Installation? installation)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.IsDescendantOf(sessionUser)
|
||||||
|
&& sessionUser.HasAccessTo(installation)
|
||||||
|
&& !user.HasAccessTo(installation)
|
||||||
|
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean GrantUserAccessTo(this Session? session, User? user, Folder? folder)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.IsDescendantOf(sessionUser)
|
||||||
|
&& sessionUser.HasAccessTo(folder)
|
||||||
|
&& !user.HasAccessTo(folder)
|
||||||
|
&& Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean RevokeAccessTo(this Session? session, User? user, Installation? installation)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.IsDescendantOf(sessionUser)
|
||||||
|
&& sessionUser.HasAccessTo(installation)
|
||||||
|
&& user.HasAccessTo(installation)
|
||||||
|
&& Db.InstallationAccess.Delete(access =>
|
||||||
|
access.UserId == user.Id && access.InstallationId == installation.Id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean RevokeAccessTo(this Session? session, User? user, Folder? folder)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.IsDescendantOf(sessionUser)
|
||||||
|
&& sessionUser.HasAccessTo(folder)
|
||||||
|
&& user.HasAccessTo(folder)
|
||||||
|
&& Db.FolderAccess.Delete(access =>
|
||||||
|
access.UserId == user.Id && access.FolderId == folder.Id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Logout(this Session? session)
|
||||||
|
{
|
||||||
|
return session is not null
|
||||||
|
&& Db.Sessions.Delete(s => s.Token == session.Token) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
using System.Net.Mail;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using Convert = System.Convert;
|
||||||
|
using static System.Text.Encoding;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
|
||||||
|
public static class UserMethods
|
||||||
|
{
|
||||||
|
public static IEnumerable<Installation> AccessibleInstallations(this User user)
|
||||||
|
{
|
||||||
|
var direct = user.DirectlyAccessibleInstallations();
|
||||||
|
var fromFolders = user
|
||||||
|
.AccessibleFolders()
|
||||||
|
.SelectMany(u => u.ChildInstallations());
|
||||||
|
|
||||||
|
return direct
|
||||||
|
.Concat(fromFolders)
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> AccessibleFolders(this User user)
|
||||||
|
{
|
||||||
|
return user
|
||||||
|
.DirectlyAccessibleFolders()
|
||||||
|
.SelectMany(f => f.DescendantFolders().Prepend(f))
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
// Distinct because the user might have direct access
|
||||||
|
// to a child folder of a folder he has already access to
|
||||||
|
// ---TODO shouldn't we prevent doubling permissions? -K"
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user)
|
||||||
|
{
|
||||||
|
var folders = user.AccessibleFolders() as IEnumerable<TreeNode>;
|
||||||
|
var installations = user.AccessibleInstallations();
|
||||||
|
|
||||||
|
return folders.Concat(installations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Installation> DirectlyAccessibleInstallations(this User user)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.InstallationAccess
|
||||||
|
.Where(r => r.UserId == user.Id)
|
||||||
|
.Select(r => r.InstallationId)
|
||||||
|
.Select(Db.GetInstallationById)
|
||||||
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.FolderAccess
|
||||||
|
.Where(r => r.UserId == user.Id)
|
||||||
|
.Select(r => r.FolderId)
|
||||||
|
.Select(Db.GetFolderById)
|
||||||
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> ChildUsers(this User parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Users
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> DescendantUsers(this User parent)
|
||||||
|
{
|
||||||
|
return parent
|
||||||
|
.TraverseDepthFirstPreOrder(ChildUsers)
|
||||||
|
.Skip(1); // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsDescendantOf(this User user, User ancestor)
|
||||||
|
{
|
||||||
|
// if (user.Id == ancestor.Id) return true;
|
||||||
|
return user
|
||||||
|
.Ancestors()
|
||||||
|
.Any(u => u.Id == ancestor.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<User> Ancestors(this User user)
|
||||||
|
{
|
||||||
|
return user
|
||||||
|
.Unfold(Parent)
|
||||||
|
.Skip(1); // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean VerifyPassword(this User user, String password)
|
||||||
|
{
|
||||||
|
return user.Password == user.SaltAndHashPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String SaltAndHashPassword(this User user, String password)
|
||||||
|
{
|
||||||
|
var dataToHash = $"{password}{user.Salt()}";
|
||||||
|
|
||||||
|
return dataToHash
|
||||||
|
.Apply(UTF8.GetBytes)
|
||||||
|
.Apply(SHA256.HashData)
|
||||||
|
.Apply(Convert.ToBase64String);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? Parent(this User u)
|
||||||
|
{
|
||||||
|
return u.IsAbsoluteRoot()
|
||||||
|
? null
|
||||||
|
: Db.GetUserById(u.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsAbsoluteRoot(this User u)
|
||||||
|
{
|
||||||
|
return u.ParentId == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this User u)
|
||||||
|
{
|
||||||
|
return u.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasDirectAccessTo(this User user, Folder folder)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.FolderAccess
|
||||||
|
.Any(r => r.FolderId == folder.Id && r.UserId == user.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, Folder? folder)
|
||||||
|
{
|
||||||
|
if (folder is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return user.HasDirectAccessTo(folder)
|
||||||
|
|| folder
|
||||||
|
.Ancestors()
|
||||||
|
.Any(user.HasDirectAccessTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasDirectAccessTo(this User user, Installation installation)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.InstallationAccess
|
||||||
|
.Any(r => r.UserId == user.Id && r.InstallationId == installation.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, Installation? installation)
|
||||||
|
{
|
||||||
|
if (installation is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return user.HasDirectAccessTo(installation) ||
|
||||||
|
installation.Ancestors().Any(user.HasDirectAccessTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, User? other)
|
||||||
|
{
|
||||||
|
if (other is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (other.Id == user.Id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return other
|
||||||
|
.Ancestors()
|
||||||
|
.Contains(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this User user, Installation i)
|
||||||
|
{
|
||||||
|
// TODO: determine not by id but by accessibility
|
||||||
|
return i.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String Salt(this User user)
|
||||||
|
{
|
||||||
|
// + id => salt unique per user
|
||||||
|
// + InnovEnergy => globally unique
|
||||||
|
|
||||||
|
return $"{user.Id}InnovEnergy";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO?
|
||||||
|
private static Boolean IsValidEmail(String email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var emailAddress = new MailAddress(email);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public abstract partial class TreeNode
|
||||||
|
{
|
||||||
|
public override Boolean Equals(Object? obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != GetType()) return false;
|
||||||
|
|
||||||
|
return Equals((TreeNode)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean Equals(TreeNode other) => Id == other.Id;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
||||||
|
public override Int32 GetHashCode() => Id.GetHashCode();
|
||||||
|
|
||||||
|
public static Boolean operator ==(TreeNode? left, TreeNode? right) => Equals(left, right);
|
||||||
|
public static Boolean operator !=(TreeNode? left, TreeNode? right) => !Equals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public abstract partial class TreeNode
|
||||||
|
{
|
||||||
|
[PrimaryKey, AutoIncrement]
|
||||||
|
public Int64 Id { get; set; }
|
||||||
|
public String Name { get; set; } = "";
|
||||||
|
public String Information { get; set; } = ""; // unstructured random info
|
||||||
|
|
||||||
|
[Indexed] // parent/child relation
|
||||||
|
public Int64 ParentId { get; set; }
|
||||||
|
|
||||||
|
[Ignore]
|
||||||
|
public String Type => GetType().Name;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public class User : TreeNode
|
||||||
|
{
|
||||||
|
[Indexed]
|
||||||
|
public String Email { get; set; } = null!;
|
||||||
|
public Boolean HasWriteAccess { get; set; } = false;
|
||||||
|
public String Language { get; set; } = null!;
|
||||||
|
public String Password { get; set; } = null!;
|
||||||
|
|
||||||
|
// TODO: must reset pwd
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Create(Installation installation)
|
||||||
|
{
|
||||||
|
// SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion
|
||||||
|
return Connection.Insert(installation) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(Folder folder)
|
||||||
|
{
|
||||||
|
return Connection.Insert(folder) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(User user)
|
||||||
|
{
|
||||||
|
if (GetUserByEmail(user.Email) is not null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
user.Password = user.SaltAndHashPassword(user.Password);
|
||||||
|
|
||||||
|
return Connection.Insert(user) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(Session session)
|
||||||
|
{
|
||||||
|
return Connection.Insert(session) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(InstallationAccess installationAccess)
|
||||||
|
{
|
||||||
|
return Connection.Insert(installationAccess) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(FolderAccess folderAccess)
|
||||||
|
{
|
||||||
|
return Connection.Insert(folderAccess) > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
internal const String DbPath = "./db.sqlite";
|
||||||
|
|
||||||
|
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
|
||||||
|
|
||||||
|
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
||||||
|
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
||||||
|
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
|
||||||
|
public static TableQuery<User> Users => Connection.Table<User>();
|
||||||
|
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
|
||||||
|
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
|
||||||
|
|
||||||
|
|
||||||
|
static Db()
|
||||||
|
{
|
||||||
|
// on startup create/migrate tables
|
||||||
|
|
||||||
|
Connection.RunInTransaction(() =>
|
||||||
|
{
|
||||||
|
Connection.CreateTable<User>();
|
||||||
|
Connection.CreateTable<Installation>();
|
||||||
|
Connection.CreateTable<Folder>();
|
||||||
|
Connection.CreateTable<FolderAccess>();
|
||||||
|
Connection.CreateTable<InstallationAccess>();
|
||||||
|
Connection.CreateTable<Session>();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Observable.Interval(TimeSpan.FromDays(0.5))
|
||||||
|
.StartWith(0) // Do it right away (on startup)
|
||||||
|
.SelectMany(Cleanup)
|
||||||
|
.Subscribe(); // and then daily
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static Boolean RunTransaction(Func<Boolean> func)
|
||||||
|
{
|
||||||
|
var savepoint = Connection.SaveTransactionPoint();
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
success = func();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (success)
|
||||||
|
Connection.Release(savepoint);
|
||||||
|
else
|
||||||
|
Connection.RollbackTo(savepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task<Boolean> Cleanup(Int64 _)
|
||||||
|
{
|
||||||
|
await UpdateS3Urls();
|
||||||
|
DeleteStaleSessions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeleteStaleSessions()
|
||||||
|
{
|
||||||
|
var deadline = DateTime.Now - Session.MaxAge;
|
||||||
|
Sessions.Delete(s => s.LastSeen < deadline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task UpdateS3Urls()
|
||||||
|
{
|
||||||
|
var renewTasks = Installations.Select(i => i.RenewS3BucketUrl()).ToArray();
|
||||||
|
return Task.WhenAll(renewTasks);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Delete(Folder folder)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteFolderAndAllItsDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteFolderAndAllItsDependencies()
|
||||||
|
{
|
||||||
|
return folder
|
||||||
|
.DescendantFolders()
|
||||||
|
.All(DeleteDescendantFolderAndItsDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean DeleteDescendantFolderAndItsDependencies(Folder f)
|
||||||
|
{
|
||||||
|
FolderAccess .Delete(r => r.FolderId == f.Id);
|
||||||
|
Installations.Delete(r => r.ParentId == f.Id);
|
||||||
|
|
||||||
|
return Folders.Delete(r => r.Id == f.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(Installation installation)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteInstallationAndItsDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteInstallationAndItsDependencies()
|
||||||
|
{
|
||||||
|
InstallationAccess.Delete(i => i.InstallationId == installation.Id);
|
||||||
|
return Installations.Delete(i => i.Id == installation.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(User user)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteUserAndHisDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteUserAndHisDependencies()
|
||||||
|
{
|
||||||
|
FolderAccess .Delete(u => u.UserId == user.Id);
|
||||||
|
InstallationAccess.Delete(u => u.UserId == user.Id);
|
||||||
|
|
||||||
|
return Users.Delete(u => u.Id == user.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
// private!!
|
||||||
|
private static Boolean Delete(Session session)
|
||||||
|
{
|
||||||
|
return Sessions.Delete(s => s.Id == session.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
using Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
namespace Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public static partial class Db
|
||||||
{
|
{
|
||||||
public void CreateFakeRelations()
|
public static void CreateFakeRelations()
|
||||||
{
|
{
|
||||||
_Db.RunInTransaction(() =>
|
Connection.RunInTransaction(() =>
|
||||||
{
|
{
|
||||||
CreateFakeUserTree();
|
CreateFakeUserTree();
|
||||||
CreateFakeFolderTree();
|
CreateFakeFolderTree();
|
||||||
|
@ -16,9 +16,9 @@ public partial class Db
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateFakeUserTree()
|
private static void CreateFakeUserTree()
|
||||||
{
|
{
|
||||||
foreach (var userId in Enumerable.Range(1, NbUsers))
|
foreach (var userId in Enumerable.Range(1, Users.Count()))
|
||||||
{
|
{
|
||||||
var user = GetUserById(userId);
|
var user = GetUserById(userId);
|
||||||
if (user is null)
|
if (user is null)
|
||||||
|
@ -32,9 +32,9 @@ public partial class Db
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateFakeFolderTree()
|
private static void CreateFakeFolderTree()
|
||||||
{
|
{
|
||||||
foreach (var folderId in Enumerable.Range(1, NbFolders))
|
foreach (var folderId in Enumerable.Range(1, Folders.Count()))
|
||||||
{
|
{
|
||||||
var folder = GetFolderById(folderId);
|
var folder = GetFolderById(folderId);
|
||||||
if (folder is null)
|
if (folder is null)
|
||||||
|
@ -48,9 +48,9 @@ public partial class Db
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LinkFakeInstallationsToFolders()
|
private static void LinkFakeInstallationsToFolders()
|
||||||
{
|
{
|
||||||
var nFolders = NbFolders;
|
var nFolders = Folders.Count();
|
||||||
|
|
||||||
foreach (var installation in Installations)
|
foreach (var installation in Installations)
|
||||||
{
|
{
|
||||||
|
@ -59,42 +59,42 @@ public partial class Db
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GiveFakeUsersAccessToFolders()
|
private static void GiveFakeUsersAccessToFolders()
|
||||||
{
|
{
|
||||||
foreach (var uf in User2Folder) // remove existing relations
|
foreach (var uf in FolderAccess) // remove existing relations
|
||||||
_Db.Delete(uf);
|
Connection.Delete(uf);
|
||||||
|
|
||||||
var nFolders = NbFolders;
|
var nFolders = Folders.Count();
|
||||||
var nUsers = NbUsers;
|
var nUsers = Users.Count();
|
||||||
|
|
||||||
foreach (var user in Users)
|
foreach (var user in Users)
|
||||||
while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0)
|
while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0)
|
||||||
{
|
{
|
||||||
var relation = new User2Folder
|
var relation = new FolderAccess
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
FolderId = Random.Shared.Next(nFolders) + 1
|
FolderId = Random.Shared.Next(nFolders) + 1
|
||||||
};
|
};
|
||||||
_Db.Insert(relation);
|
Connection.Insert(relation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GiveFakeUsersAccessToInstallations()
|
private static void GiveFakeUsersAccessToInstallations()
|
||||||
{
|
{
|
||||||
foreach (var ui in User2Installation) // remove existing relations
|
foreach (var ui in InstallationAccess) // remove existing relations
|
||||||
_Db.Delete(ui);
|
Connection.Delete(ui);
|
||||||
|
|
||||||
var nbInstallations = NbInstallations;
|
var nbInstallations = Installations.Count();
|
||||||
|
|
||||||
foreach (var user in Users)
|
foreach (var user in Users)
|
||||||
while (Random.Shared.Next(5) != 0)
|
while (Random.Shared.Next(5) != 0)
|
||||||
{
|
{
|
||||||
var relation = new User2Installation
|
var relation = new InstallationAccess
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
InstallationId = Random.Shared.Next(nbInstallations) + 1
|
InstallationId = Random.Shared.Next(nbInstallations) + 1
|
||||||
};
|
};
|
||||||
_Db.Insert(relation);
|
Connection.Insert(relation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Folder? GetFolderById(Int64 id)
|
||||||
|
{
|
||||||
|
return Folders
|
||||||
|
.FirstOrDefault(f => f.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Installation? GetInstallationById(Int64 id)
|
||||||
|
{
|
||||||
|
return Installations
|
||||||
|
.FirstOrDefault(i => i.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? GetUserById(Int64? id)
|
||||||
|
{
|
||||||
|
return Users
|
||||||
|
.FirstOrDefault(u => u.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private!!
|
||||||
|
private static Session? GetSessionById(Int64 id)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
return Sessions
|
||||||
|
.FirstOrDefault(u => u.Id == id);
|
||||||
|
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static User? GetUserByEmail(String email)
|
||||||
|
{
|
||||||
|
return Users
|
||||||
|
.FirstOrDefault(u => u.Email == email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Session? GetSession(String token)
|
||||||
|
{
|
||||||
|
var session = Sessions
|
||||||
|
.FirstOrDefault(s => s.Token == token);
|
||||||
|
|
||||||
|
// cannot use session.Valid in the DB query above.
|
||||||
|
// It does not exist in the db (IgnoreAttribute)
|
||||||
|
|
||||||
|
if (session is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!session.Valid)
|
||||||
|
{
|
||||||
|
Delete(session);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? GetUserBySessionToken(String token)
|
||||||
|
{
|
||||||
|
var session = Sessions
|
||||||
|
.FirstOrDefault(s => s.Token == token);
|
||||||
|
|
||||||
|
// cannot user session.Expired in the DB query above.
|
||||||
|
// It does not exist in the db (IgnoreAttribute)
|
||||||
|
|
||||||
|
if (session is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!session.Valid)
|
||||||
|
{
|
||||||
|
Delete(session);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetUserById(session.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Update(Folder folder)
|
||||||
|
{
|
||||||
|
if (folder.IsRelativeRoot()) // TODO: triple check
|
||||||
|
{
|
||||||
|
var original = GetFolderById(folder.Id);
|
||||||
|
if (original is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
folder.ParentId = original.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Connection.InsertOrReplace(folder) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(Installation installation)
|
||||||
|
{
|
||||||
|
if (installation.IsRelativeRoot()) // TODO: triple check
|
||||||
|
{
|
||||||
|
var original = GetInstallationById(installation.Id);
|
||||||
|
if (original is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
installation.ParentId = original.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Connection.InsertOrReplace(installation) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean Update(User user)
|
||||||
|
{
|
||||||
|
var originalUser = GetUserById(user.Id);
|
||||||
|
|
||||||
|
return originalUser is not null
|
||||||
|
&& user.Id == originalUser.Id // these columns must not be modified!
|
||||||
|
&& user.ParentId == originalUser.ParentId
|
||||||
|
&& user.Email == originalUser.Email
|
||||||
|
&& Connection.InsertOrReplace(user) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session session)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
var originalSession = GetSessionById(session.Id);
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
return originalSession is not null
|
||||||
|
&& session.Token == originalSession.Token // these columns must not be modified!
|
||||||
|
&& session.UserId == originalSession.UserId
|
||||||
|
&& Connection.InsertOrReplace(session) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static void Main(String[] args)
|
||||||
|
{
|
||||||
|
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(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "InnovEnergy Backend API", Version = "v1" });
|
||||||
|
c.UseAllOfToExtendReferenceSchemas();
|
||||||
|
c.OperationFilter<HeaderFilter>(); //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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
|
||||||
|
{
|
||||||
|
var headers = ctx.Request.Headers;
|
||||||
|
var hasToken = headers.TryGetValue("auth", out var token) ;
|
||||||
|
|
||||||
|
if (hasToken)
|
||||||
|
{
|
||||||
|
var session = Db.GetSession(token);
|
||||||
|
|
||||||
|
if (session is not null)
|
||||||
|
ctx.Items["Session"] = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
await next(ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class FolderAccess : Relation<Int64, Int64>
|
||||||
|
{
|
||||||
|
[Indexed] public Int64 UserId { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 FolderId { get => Right; init => Right = value;}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class InstallationAccess : Relation<Int64, Int64>
|
||||||
|
{
|
||||||
|
[Indexed] public Int64 UserId { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 InstallationId { get => Right; init => Right = value;}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
public abstract class Relation<L,R>
|
public abstract class Relation<L,R>
|
||||||
{
|
{
|
|
@ -0,0 +1,43 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class Session : Relation<String, Int64>
|
||||||
|
{
|
||||||
|
public static TimeSpan MaxAge { get; } = TimeSpan.FromDays(7);
|
||||||
|
|
||||||
|
[Unique ] public String Token { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 UserId { get => Right; init => Right = value;}
|
||||||
|
[Indexed] public DateTime LastSeen { get; set; }
|
||||||
|
|
||||||
|
[Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge
|
||||||
|
&& !User.Email.IsNullOrEmpty();
|
||||||
|
|
||||||
|
[Ignore] public User User => _User ??= Db.GetUserById(UserId)!;
|
||||||
|
|
||||||
|
|
||||||
|
private User? _User;
|
||||||
|
|
||||||
|
[Obsolete("To be used only by deserializer")]
|
||||||
|
public Session()
|
||||||
|
{}
|
||||||
|
|
||||||
|
public Session(User user)
|
||||||
|
{
|
||||||
|
_User = user;
|
||||||
|
Token = CreateToken();
|
||||||
|
UserId = user.Id;
|
||||||
|
LastSeen = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String CreateToken()
|
||||||
|
{
|
||||||
|
var token = new Byte[24];
|
||||||
|
Random.Shared.NextBytes(token);
|
||||||
|
return Convert.ToBase64String(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -1,9 +1,9 @@
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static InnovEnergy.BmsTunnel.CliPrograms;
|
using static InnovEnergy.App.BmsTunnel.CliPrograms;
|
||||||
|
|
||||||
namespace InnovEnergy.BmsTunnel;
|
namespace InnovEnergy.App.BmsTunnel;
|
||||||
|
|
||||||
using Nodes = IReadOnlyList<Byte>;
|
using Nodes = IReadOnlyList<Byte>;
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Text;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.BmsTunnel;
|
namespace InnovEnergy.App.BmsTunnel;
|
||||||
|
|
||||||
|
|
||||||
public class BmsTunnel : IDisposable
|
public class BmsTunnel : IDisposable
|
|
@ -1,9 +1,5 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="../InnovEnergy.app.props" />
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RootNamespace>InnovEnergy.BmsTunnel</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliWrap" Version="3.6.0" />
|
<PackageReference Include="CliWrap" Version="3.6.0" />
|
||||||
|
@ -11,7 +7,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../lib/Utils/Utils.csproj" />
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -1,6 +1,6 @@
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
|
|
||||||
namespace InnovEnergy.BmsTunnel;
|
namespace InnovEnergy.App.BmsTunnel;
|
||||||
|
|
||||||
public static class CliPrograms
|
public static class CliPrograms
|
||||||
{
|
{
|
|
@ -5,7 +5,7 @@
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static System.String;
|
using static System.String;
|
||||||
|
|
||||||
namespace InnovEnergy.BmsTunnel;
|
namespace InnovEnergy.App.BmsTunnel;
|
||||||
|
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/WebServer/WebServer.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,14 +1,14 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
using InnovEnergy.Collector.Records;
|
using InnovEnergy.App.Collector.Records;
|
||||||
using InnovEnergy.Collector.Utils;
|
using InnovEnergy.App.Collector.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Utils.Net;
|
using InnovEnergy.Lib.Utils.Net;
|
||||||
using Convert = System.Convert;
|
using Convert = System.Convert;
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.Collector;
|
namespace InnovEnergy.App.Collector;
|
||||||
|
|
||||||
using Data = IReadOnlyList<String>;
|
using Data = IReadOnlyList<String>;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
using InnovEnergy.Collector.Records;
|
using InnovEnergy.App.Collector.Records;
|
||||||
using InnovEnergy.Collector.Utils;
|
using InnovEnergy.App.Collector.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using Convert = System.Convert;
|
using Convert = System.Convert;
|
||||||
|
|
||||||
|
|
||||||
// NOT (YET) USED
|
// NOT (YET) USED
|
||||||
|
|
||||||
namespace InnovEnergy.Collector;
|
namespace InnovEnergy.App.Collector;
|
||||||
|
|
||||||
using Data = IReadOnlyList<String>;
|
using Data = IReadOnlyList<String>;
|
||||||
|
|
|
@ -2,7 +2,7 @@ using static System.AttributeTargets;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Influx;
|
namespace InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
[AttributeUsage(Property)]
|
[AttributeUsage(Property)]
|
||||||
public class FieldAttribute : Attribute
|
public class FieldAttribute : Attribute
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using InnovEnergy.Collector.Utils;
|
using InnovEnergy.App.Collector.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static System.Globalization.CultureInfo;
|
using static System.Globalization.CultureInfo;
|
||||||
using static InnovEnergy.Collector.Influx.LineProtocolSyntax;
|
using static InnovEnergy.App.Collector.Influx.LineProtocolSyntax;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Influx;
|
namespace InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
public static class InfluxRecord
|
public static class InfluxRecord
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using static System.Globalization.CultureInfo;
|
using static System.Globalization.CultureInfo;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Influx;
|
namespace InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
internal static class LineProtocolSyntax
|
internal static class LineProtocolSyntax
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using static System.AttributeTargets;
|
using static System.AttributeTargets;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Influx;
|
namespace InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
[AttributeUsage(Property)]
|
[AttributeUsage(Property)]
|
||||||
public class TagAttribute : Attribute
|
public class TagAttribute : Attribute
|
|
@ -2,17 +2,17 @@
|
||||||
using System.Reactive.Concurrency;
|
using System.Reactive.Concurrency;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using InnovEnergy.Collector.Influx;
|
|
||||||
using InnovEnergy.Collector.Records;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
using static System.Text.Encoding;
|
|
||||||
using static InnovEnergy.Lib.Utils.ExceptionHandling;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
using InnovEnergy.App.Collector.Records;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Utils.Net;
|
using InnovEnergy.Lib.Utils.Net;
|
||||||
using InnovEnergy.Lib.WebServer;
|
using InnovEnergy.Lib.WebServer;
|
||||||
|
using static System.Text.Encoding;
|
||||||
|
using static InnovEnergy.Lib.Utils.ExceptionHandling;
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.Collector;
|
namespace InnovEnergy.App.Collector;
|
||||||
|
|
||||||
// TODO: net6
|
// TODO: net6
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
// ReSharper disable IdentifierTypo
|
// ReSharper disable IdentifierTypo
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class Alarms : BatteryRecord
|
public class Alarms : BatteryRecord
|
||||||
{
|
{
|
|
@ -0,0 +1,4 @@
|
||||||
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
|
public abstract class BatteryRecord
|
||||||
|
{}
|
|
@ -1,9 +1,9 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class BatteryStatus : BatteryRecord
|
public class BatteryStatus : BatteryRecord
|
||||||
{
|
{
|
|
@ -1,8 +1,8 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class Error : BatteryRecord
|
public class Error : BatteryRecord
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class InstallationStatus : BatteryRecord
|
public class InstallationStatus : BatteryRecord
|
||||||
{
|
{
|
|
@ -1,8 +1,8 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class IoStatus : BatteryRecord
|
public class IoStatus : BatteryRecord
|
||||||
{
|
{
|
|
@ -1,9 +1,9 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Records;
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
|
|
||||||
public class Leds : BatteryRecord
|
public class Leds : BatteryRecord
|
||||||
{
|
{
|
|
@ -1,7 +1,6 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
namespace InnovEnergy.Collector.Records;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
|
@ -1,7 +1,6 @@
|
||||||
using InnovEnergy.Collector.Influx;
|
using InnovEnergy.App.Collector.Influx;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Collector.Records;
|
||||||
namespace InnovEnergy.Collector.Records;
|
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector;
|
namespace InnovEnergy.App.Collector;
|
||||||
|
|
||||||
public static class Settings
|
public static class Settings
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Utils;
|
namespace InnovEnergy.App.Collector.Utils;
|
||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.Collector.Utils;
|
namespace InnovEnergy.App.Collector.Utils;
|
||||||
|
|
||||||
internal static class Log
|
internal static class Log
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Collector.Utils;
|
namespace InnovEnergy.App.Collector.Utils;
|
||||||
|
|
||||||
public readonly struct Property
|
public readonly struct Property
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.Collector.Utils;
|
namespace InnovEnergy.App.Collector.Utils;
|
||||||
|
|
||||||
public static class ReadOnlyListExtensions
|
public static class ReadOnlyListExtensions
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.Collector.Utils;
|
namespace InnovEnergy.App.Collector.Utils;
|
||||||
|
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
|
@ -0,0 +1,65 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using InnovEnergy.Lib.Victron.VeDBus;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.EmuMeterDriver;
|
||||||
|
|
||||||
|
public static class Config
|
||||||
|
{
|
||||||
|
public const String Version = "1.0";
|
||||||
|
public const String BusName = "com.victronenergy.grid.emu";
|
||||||
|
public const Byte ModbusNodeId = 1;
|
||||||
|
public const String OwnAddress = "10.0.0.1";
|
||||||
|
public const String PeerAddress = "10.0.0.2";
|
||||||
|
//public const String PeerAddress = "127.0.0.1";
|
||||||
|
public const UInt16 PeerPort = 502;
|
||||||
|
|
||||||
|
public static TimeSpan TcpTimeout { get; } = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly TimeSpan UpdatePeriod = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
public static readonly IReadOnlyList<Signal> Signals = new Signal[]
|
||||||
|
{
|
||||||
|
new(s => s.Ac.L1.Current, "/Ac/L1/Current", "0.0 A"),
|
||||||
|
new(s => s.Ac.L2.Current, "/Ac/L2/Current", "0.0 A"),
|
||||||
|
new(s => s.Ac.L3.Current, "/Ac/L3/Current", "0.0 A"),
|
||||||
|
new(s => s.Ac.L1.Current + s.Ac.L2.Current + s.Ac.L3.Current, "/Ac/Current", "0.0 A"),
|
||||||
|
|
||||||
|
new(s => s.Ac.L1.Voltage, "/Ac/L1/Voltage", "0.0 A"),
|
||||||
|
new(s => s.Ac.L2.Voltage, "/Ac/L2/Voltage", "0.0 A"),
|
||||||
|
new(s => s.Ac.L3.Voltage, "/Ac/L3/Voltage", "0.0 A"),
|
||||||
|
new(s => (s.Ac.L1.Voltage + s.Ac.L2.Voltage + s.Ac.L3.Voltage) / 3.0m, "/Ac/Voltage", "0.0 A"),
|
||||||
|
|
||||||
|
new(s => s.Ac.L1.ActivePower, "/Ac/L1/Power", "0 W"),
|
||||||
|
new(s => s.Ac.L2.ActivePower, "/Ac/L2/Power", "0 W"),
|
||||||
|
new(s => s.Ac.L3.ActivePower, "/Ac/L3/Power", "0 W"),
|
||||||
|
new(s => s.Ac.ActivePower, "/Ac/Power", "0 W"),
|
||||||
|
|
||||||
|
// new(s => s.EnergyImportL123, "Ac/Energy/Forward", "0.00 kWh"),
|
||||||
|
// new(s => s.EnergyExportL123, "Ac/Energy/Reverse", "0.00 kWh"),
|
||||||
|
//
|
||||||
|
// new(s => s.EnergyImportL1, "Ac/L1/Energy/Forward", "0.00 kWh"),
|
||||||
|
// new(s => s.EnergyExportL1, "Ac/L1/Energy/Reverse", "0.00 kWh"),
|
||||||
|
//
|
||||||
|
// new(s => s.EnergyImportL2, "Ac/L2/Energy/Forward", "0.00 kWh"),
|
||||||
|
// new(s => s.EnergyExportL2, "Ac/L2/Energy/Reverse", "0.00 kWh"),
|
||||||
|
//
|
||||||
|
// new(s => s.EnergyImportL3, "Ac/L3/Energy/Forward", "0.00 kWh"),
|
||||||
|
// new(s => s.EnergyExportL3, "Ac/L3/Energy/Reverse", "0.00 kWh"),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static VeProperties DefaultProperties => new VeProperties
|
||||||
|
{
|
||||||
|
new("/ProductName" , "Grid meter" ),
|
||||||
|
new("/CustomName" , "EMU Professional II"),
|
||||||
|
new("/DeviceInstance" , 30),
|
||||||
|
new("/DeviceType" , 72),
|
||||||
|
new("/Mgmt/Connection" , "Modbus TCP"),
|
||||||
|
new("/Mgmt/ProcessName" , Assembly.GetEntryAssembly()?.Location ?? "unknown"),
|
||||||
|
new("/Mgmt/ProcessVersion", Version),
|
||||||
|
new("/Connected" , 1),
|
||||||
|
new("/ProductId" , 45058, "b002"),
|
||||||
|
new("/Role" , "grid"),
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -5,8 +5,7 @@ using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Victron.VeDBus;
|
using InnovEnergy.Lib.Victron.VeDBus;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.EmuMeterDriver;
|
||||||
namespace InnovEnergy.EmuMeter;
|
|
||||||
|
|
||||||
public static class EmuMeterDriver
|
public static class EmuMeterDriver
|
||||||
{
|
{
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/EmuMeter/EmuMeter.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Protocols/DBus/DBus.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Protocols/Modbus/Modbus.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Victron/VeDBus/VeDBus.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CliWrap" Version="3.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -2,7 +2,7 @@ using System.Text.Json.Nodes;
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
|
|
||||||
namespace InnovEnergy.EmuMeter;
|
namespace InnovEnergy.App.EmuMeterDriver;
|
||||||
|
|
||||||
public readonly struct Nic
|
public readonly struct Nic
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
using InnovEnergy.EmuMeter;
|
using InnovEnergy.App.EmuMeterDriver;
|
||||||
using InnovEnergy.Lib.Protocols.DBus;
|
using InnovEnergy.Lib.Protocols.DBus;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Utils.Net;
|
using InnovEnergy.Lib.Utils.Net;
|
|
@ -2,7 +2,7 @@ using InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes;
|
using InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes;
|
||||||
using InnovEnergy.Lib.Victron.VeDBus;
|
using InnovEnergy.Lib.Victron.VeDBus;
|
||||||
|
|
||||||
namespace InnovEnergy.EmuMeter;
|
namespace InnovEnergy.App.EmuMeterDriver;
|
||||||
|
|
||||||
public record Signal(Func<EmuMeterStatus, Object> Source, ObjectPath Path, String Format = "")
|
public record Signal(Func<EmuMeterStatus, Object> Source, ObjectPath Path, String Format = "")
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.EmuMeter;
|
namespace InnovEnergy.App.EmuMeterDriver;
|
||||||
|
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
|
@ -11,7 +11,6 @@
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
<PublishReadyToRun>true</PublishReadyToRun>
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
|
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
@ -23,3 +22,4 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer;
|
namespace InnovEnergy.App.OpenVpnCertificatesServer;
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
public static class Files
|
public static class Files
|
||||||
{
|
{
|
|
@ -1,9 +1,8 @@
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Flurl;
|
using Flurl;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.OpenVpnCertificatesServer;
|
||||||
|
|
||||||
public static class Http
|
public static class Http
|
||||||
{
|
{
|
||||||
// TODO: use worker thread
|
// TODO: use worker thread
|
|
@ -1,10 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="../InnovEnergy.app.props" />
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RootNamespace>InnovEnergy.OpenVpnCertificatesServer</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BouncyCastle" Version="1.8.9" />
|
<PackageReference Include="BouncyCastle" Version="1.8.9" />
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.2" />
|
<PackageReference Include="Flurl.Http" Version="3.2.2" />
|
||||||
|
@ -12,8 +8,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../lib/Protocols/DBus/DBus.csproj" />
|
<ProjectReference Include="../../Lib/Protocols/DBus/DBus.csproj" />
|
||||||
<ProjectReference Include="../../lib/Victron/VictronVRM/VictronVRM.csproj" />
|
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -7,7 +7,7 @@ using Org.BouncyCastle.Math;
|
||||||
using Org.BouncyCastle.Security;
|
using Org.BouncyCastle.Security;
|
||||||
using Org.BouncyCastle.X509;
|
using Org.BouncyCastle.X509;
|
||||||
|
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer.PKI;
|
namespace InnovEnergy.App.OpenVpnCertificatesServer.PKI;
|
||||||
|
|
||||||
public static class CertificateAuthority
|
public static class CertificateAuthority
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using Org.BouncyCastle.OpenSsl;
|
using Org.BouncyCastle.OpenSsl;
|
||||||
|
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer.PKI;
|
namespace InnovEnergy.App.OpenVpnCertificatesServer.PKI;
|
||||||
|
|
||||||
public static class Pem
|
public static class Pem
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using Org.BouncyCastle.OpenSsl;
|
using Org.BouncyCastle.OpenSsl;
|
||||||
|
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer.PKI;
|
namespace InnovEnergy.App.OpenVpnCertificatesServer.PKI;
|
||||||
|
|
||||||
public class PwdFinder : IPasswordFinder
|
public class PwdFinder : IPasswordFinder
|
||||||
{
|
{
|
|
@ -1,15 +1,14 @@
|
||||||
using InnovEnergy.Lib.Utils;
|
using System.Diagnostics;
|
||||||
using InnovEnergy.Lib.Victron.VictronVRM;
|
|
||||||
using InnovEnergy.OpenVpnCertificatesServer.PKI;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Flurl;
|
using Flurl;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
|
using InnovEnergy.App.OpenVpnCertificatesServer.PKI;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using InnovEnergy.Lib.Victron.VictronVRM;
|
||||||
using Org.BouncyCastle.Crypto;
|
using Org.BouncyCastle.Crypto;
|
||||||
|
using static InnovEnergy.App.OpenVpnCertificatesServer.PKI.CertificateAuthority;
|
||||||
|
|
||||||
using static InnovEnergy.OpenVpnCertificatesServer.PKI.CertificateAuthority;
|
namespace InnovEnergy.App.OpenVpnCertificatesServer;
|
||||||
|
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer;
|
|
||||||
|
|
||||||
// dotnet publish OpenVpnCertificatesServer.csproj -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained true && scp bin/Release/net6.0/linux-x64/publish/OpenVpnCertificatesServer ig@salidomo.innovenergy.ch:~/get_cert/get_cert
|
// dotnet publish OpenVpnCertificatesServer.csproj -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained true && scp bin/Release/net6.0/linux-x64/publish/OpenVpnCertificatesServer ig@salidomo.innovenergy.ch:~/get_cert/get_cert
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
namespace InnovEnergy.OpenVpnCertificatesServer;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.OpenVpnCertificatesServer;
|
||||||
|
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
||||||
public static void WriteFile(this TarOutputStream tar, String fileName, Byte[] contents, Boolean executable = false)
|
public static void WriteFile(this TarOutputStream tar, String fileName, Byte[] contents, Boolean executable = false)
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class Login
|
public static class Login
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using System.Reactive.Linq;
|
||||||
using System.Reactive.Threading.Tasks;
|
using System.Reactive.Threading.Tasks;
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class ObservablePipeSource
|
public static class ObservablePipeSource
|
||||||
{
|
{
|
|
@ -4,7 +4,7 @@ using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Victron.VictronVRM;
|
using InnovEnergy.Lib.Victron.VictronVRM;
|
||||||
using static System.Globalization.CompareOptions;
|
using static System.Globalization.CompareOptions;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
// dotnet publish -c release -r ubuntu-x64
|
// dotnet publish -c release -r ubuntu-x64
|
||||||
// dotnet publish RemoteSupportConsole.csproj -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained true
|
// dotnet publish RemoteSupportConsole.csproj -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained true
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
|
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CliWrap" Version="3.6.0" />
|
||||||
|
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,7 +1,7 @@
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class Ssh
|
public static class Ssh
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Victron.VictronVRM;
|
using InnovEnergy.Lib.Victron.VictronVRM;
|
||||||
using static System.ConsoleColor;
|
using static System.ConsoleColor;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class VpnConnection
|
public static class VpnConnection
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using Flurl;
|
||||||
using Flurl.Http;
|
using Flurl.Http;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class VpnInfo
|
public static class VpnInfo
|
||||||
{
|
{
|
|
@ -7,7 +7,7 @@ using InnovEnergy.Lib.Victron.VictronVRM;
|
||||||
using static System.ConsoleColor;
|
using static System.ConsoleColor;
|
||||||
using static System.StringSplitOptions;
|
using static System.StringSplitOptions;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public static class VrmConnection
|
public static class VrmConnection
|
||||||
{
|
{
|
|
@ -3,7 +3,7 @@ using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.Victron.VictronVRM;
|
using InnovEnergy.Lib.Victron.VictronVRM;
|
||||||
using static System.ConsoleColor;
|
using static System.ConsoleColor;
|
||||||
|
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
|
|
||||||
public static class VrmInfo
|
public static class VrmInfo
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.RemoteSupportConsole;
|
namespace InnovEnergy.App.RemoteSupportConsole;
|
||||||
|
|
||||||
public readonly record struct VrmProxy(IDisposable Connection, String User, String Host, String Port) : IDisposable
|
public readonly record struct VrmProxy(IDisposable Connection, String User, String Host, String Port) : IDisposable
|
||||||
{
|
{
|
|
@ -0,0 +1,25 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/Adam6060/Adam6060.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/AMPT/Ampt.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/Battery48TL/Battery48TL.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/EmuMeter/EmuMeter.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvert/TruConvert.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/StatusApi/StatusApi.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj"/>
|
||||||
|
<ProjectReference Include="../../Lib/Time/Time.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CliWrap" Version="3.6.0"/>
|
||||||
|
<PackageReference Include="Flurl.Http" Version="3.2.4"/>
|
||||||
|
<PackageReference Include="System.IO.Ports" Version="7.0.0"/>
|
||||||
|
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,6 +1,6 @@
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.SaliMax;
|
namespace InnovEnergy.App.SaliMax;
|
||||||
|
|
||||||
public static class AsciiArt
|
public static class AsciiArt
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.SaliMax;
|
namespace InnovEnergy.App.SaliMax;
|
||||||
|
|
||||||
public enum BusPort
|
public enum BusPort
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using InnovEnergy.Lib.Devices.Battery48TL;
|
using InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
namespace InnovEnergy.SaliMax.Controller;
|
namespace InnovEnergy.App.SaliMax.Controller;
|
||||||
|
|
||||||
public class AvgBatteriesStatus
|
public class AvgBatteriesStatus
|
||||||
{
|
{
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue