This commit is contained in:
Sina Blattmann 2023-04-14 07:53:18 +02:00
commit dcab3ab4d1
15 changed files with 80 additions and 39 deletions

View File

@ -3,6 +3,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.5.1" />
<PackageReference Include="MailKit" Version="3.6.0" /> <PackageReference Include="MailKit" Version="3.6.0" />
<PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" /> <PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" />
<PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.3" /> <PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.3" />

View File

@ -19,12 +19,17 @@ public class Controller : ControllerBase
var user = Db.GetUserByName(username); var user = Db.GetUserByName(username);
if (user is null) if (user is null)
return Unauthorized(); {
throw new Exceptions(400,"Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);
}
if (!(user.Password is null && user.MustResetPassword)) if (!(user.Password is null && user.MustResetPassword))
{ {
if (!user.VerifyPassword(password)) if (!user.VerifyPassword(password))
return Unauthorized(); {
//return Unauthorized("No Password set");
throw new Exceptions(401,"Wrong Password Exception", "Please try again.", Request.Path.Value!);
}
} }
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user)); var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
@ -33,7 +38,7 @@ public class Controller : ControllerBase
return Db.Create(session) return Db.Create(session)
? session ? session
: Unauthorized(); : throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!);
} }
@ -427,8 +432,6 @@ public class Controller : ControllerBase
: Unauthorized(); : Unauthorized();
} }
} }

View File

@ -187,6 +187,7 @@ public static class SessionMethods
return sessionUser is not null return sessionUser is not null
&& sessionUser && sessionUser
.Do(() => sessionUser.Password = sessionUser.SaltAndHashPassword(newPassword)) .Do(() => sessionUser.Password = sessionUser.SaltAndHashPassword(newPassword))
.Do(() => sessionUser.MustResetPassword = false)
.Apply(Db.Update); .Apply(Db.Update);
} }

View File

@ -0,0 +1,16 @@
namespace InnovEnergy.App.Backend;
public class Exceptions : Exception
{
public String Type { get; set; }
public String Detail { get; set; }
public String Instance { get; set; }
public Int32? Status { get; set; }
public Exceptions(Int32? status, String type, String detail, String instance)
{
Type = type;
Detail = detail;
Instance = instance;
Status = status;
}
}

View File

@ -1,4 +1,6 @@
using Hellang.Middleware.ProblemDetails;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace InnovEnergy.App.Backend; namespace InnovEnergy.App.Backend;
@ -16,6 +18,21 @@ public static class Program
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddProblemDetails(setup =>
{
//This includes the stacktrace in Development Env
setup.IncludeExceptionDetails = (ctx, env) => builder.Environment.IsDevelopment() || builder.Environment.IsStaging();
//This handles our Exceptions
setup.Map<Exceptions>(exception => new ProblemDetails()
{
Detail = exception.Detail,
Status = exception.Status,
Type = exception.Type,
Instance = exception.Instance
});
});
builder.Services.AddSwaggerGen(c => builder.Services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", OpenApiInfo); c.SwaggerDoc("v1", OpenApiInfo);
@ -34,6 +51,7 @@ public static class Program
app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ; app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ;
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.MapControllers(); app.MapControllers();
app.UseProblemDetails();
app.Run(); app.Run();
} }

Binary file not shown.

View File

@ -38,7 +38,7 @@ public readonly struct BatteryConnection
var ttys = await fs.GetFiles("/dev", FileType.CharacterDevice); var ttys = await fs.GetFiles("/dev", FileType.CharacterDevice);
var candidateTtys = ttys var candidateTtys = ttys
.Where(f => f.StartsWith("ttyUSB")) .Where(f => f.Contains("ttyUSB"))
.Select(t => t.Split("/").LastOrDefault()) .Select(t => t.Split("/").LastOrDefault())
.NotNull() .NotNull()
.ToList(); .ToList();

View File

@ -23,15 +23,16 @@ public class BmsTunnel : IDisposable
private const Byte TunnelCode = 0x41; private const Byte TunnelCode = 0x41;
private const String CrcError = "?? CRC FAILED"; private const String CrcError = "?? CRC FAILED";
public BmsTunnel(String tty, Byte node) public BmsTunnel(String tty, Byte node, SshHost? host)
{ {
Tty = tty; Tty = tty;
Node = node; Node = node;
StopSerialStarter(); StopSerialStarter(host);
SerialPort = new SerialPort(Tty, BaudRate, Parity, DataBits, StopBits); SerialPort = new SerialPort(Tty, BaudRate, Parity, DataBits, StopBits);
SerialPort.ReadTimeout = 100; SerialPort.ReadTimeout = 100;
SerialPort.Open(); SerialPort.Open();
} }
@ -164,10 +165,11 @@ public class BmsTunnel : IDisposable
.Concat(NewLine); .Concat(NewLine);
} }
private void StopSerialStarter() private void StopSerialStarter(SshHost? host)
{ {
CliPrograms.StopTty CliPrograms.StopTty
.WithArguments(Tty) .WithArguments(Tty)
.OnHost(host)
.ExecuteBufferedAsync() .ExecuteBufferedAsync()
.Task .Task
.Wait(3000); .Wait(3000);

View File

@ -13,16 +13,19 @@ public static class Program
public static async Task<Int32> Main(String[] args) public static async Task<Int32> Main(String[] args)
{ {
var connection = await ConnectToBms(args.FirstOrDefault()); var hostName = args.FirstOrDefault();
BatteryConnection? connection = hostName is not null ? await ConnectToBms(hostName, new SshHost(hostName)): new BatteryConnection();
if (connection is null) if (connection is null)
return 2; return 2;
// connection.Tty;
Console.WriteLine("\nstarting BMS tunnel\n"); Console.WriteLine("\nstarting BMS tunnel\n");
// var path = $"/dev/{tty}"; var path = $"/dev/{connection.Value.Tty}";
// if (host != null) // if (hostName != null)
// path = $"root@{host}" + path; // path = $"root@{hostName}:" + path;
// //
// var node = connection.Nodes.Any() // var node = connection.Nodes.Any()
// ? connection.Nodes.First() // ? connection.Nodes.First()
@ -30,13 +33,13 @@ public static class Program
// //
// TODO: Fixme // TODO: Fixme
var path = ""; // var path = "";
Byte node = 2; Byte node = 2;
var nodes = new Byte[] { 1, 2, 3 }; var nodes = new Byte[] { 1, 2, 3 };
// TODO: Fixme // TODO: Fixme
using var tunnel = new BmsTunnel(path, node); using var tunnel = new BmsTunnel(path, node, hostName is not null ? new SshHost(hostName): null);
ExplainNode(); ExplainNode();
ExplainNodes(); ExplainNodes();
@ -117,12 +120,8 @@ public static class Program
} }
} }
private static async Task<BatteryConnection?> ConnectToBms(String? hostName) private static async Task<BatteryConnection?> ConnectToBms(String? hostName, SshHost host)
{ {
if (hostName is null)
return new BatteryConnection();
var host = new SshHost(hostName);
if (await host.Ping()) if (await host.Ping())
return await BatteryConnection.Connect(host); return await BatteryConnection.Connect(host);

View File

@ -41,7 +41,7 @@ public class FileSystem
public async Task<IReadOnlyList<String>> GetFiles(String path, FileType fileType, Int32 maxDepth = 1) public async Task<IReadOnlyList<String>> GetFiles(String path, FileType fileType, Int32 maxDepth = 1)
{ {
var result = await Find var result = await Find
.WithArguments($"find {path} -maxdepth {maxDepth} -type {(Char)fileType}") .WithArguments($"{path} -maxdepth {maxDepth} -type {(Char)fileType}")
.OnHost(Host) .OnHost(Host)
.ExecuteBufferedAsync(); .ExecuteBufferedAsync();

View File

@ -8,6 +8,7 @@ const resolution = TimeSpan.fromSeconds(2);
const cache = new DataCache(x => (Promise.resolve({foo: 0})), resolution) const cache = new DataCache(x => (Promise.resolve({foo: 0})), resolution)
// @ts-ignore
const sampleTimes = [1, 2, 3, 4, 5, 6].select(e => UnixTime.fromTicks(e)).toArray(); const sampleTimes = [1, 2, 3, 4, 5, 6].select(e => UnixTime.fromTicks(e)).toArray();
const series = cache.getSeries(sampleTimes) const series = cache.getSeries(sampleTimes)

View File

@ -115,6 +115,7 @@ export default class DataCache<T extends Record<string, number>>
let interpolated: Partial<Record<string, number>> = {} let interpolated: Partial<Record<string, number>> = {}
//What about string nodes? like Alarms
for (const k of Object.keys(dataBefore)) for (const k of Object.keys(dataBefore))
{ {
interpolated[k] = (dataBefore[k] * n + dataAfter[k] * p) / pn interpolated[k] = (dataBefore[k] * n + dataAfter[k] * p) / pn

View File

@ -59,7 +59,7 @@ const AvailableUserDialog = () => {
scroll="paper" scroll="paper"
> >
<DialogTitle id="available-user-dialog-title"> <DialogTitle id="available-user-dialog-title">
Create new folder Grant access
</DialogTitle> </DialogTitle>
<DialogContent id="available-user-dialog-content"> <DialogContent id="available-user-dialog-content">
<Autocomplete <Autocomplete

View File

@ -27,7 +27,7 @@ const AddUser = () => {
sx={{ my: 1 }} sx={{ my: 1 }}
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
> >
<FormattedMessage id="addUser" defaultMessage="Add user" /> <FormattedMessage id="addUser" defaultMessage="Create user" />
</InnovenergyButton> </InnovenergyButton>
<Dialog <Dialog
onClose={() => setOpen(false)} onClose={() => setOpen(false)}

View File

@ -1,4 +1,5 @@
import { Selector } from 'testcafe'; import { Selector } from 'testcafe';
import { login, logout } from "./helper.js";
fixture('Login testing') fixture('Login testing')
.page('http://localhost:3000/'); //Todo write me in a config file .page('http://localhost:3000/'); //Todo write me in a config file
@ -8,33 +9,31 @@ fixture('Login testing')
test('Lulu login', async t => { test('Lulu login', async t => {
// await t // await t
// .debug(); // .debug();
await t login('lulu', '1233'); //Todo write me in a config file
.login('lulu', '1233') //Todo write me in a config file await t.expect(Selector('#demo-simple-select').visible).ok();
.expect(Selector('#demo-simple-select').visible).ok();
}); });
test('Wrong PW', async t => { test('Wrong PW', async t => {
await t login('lulu', '12334'); //Todo write me in a config file
.login('lulu', '12334') //Todo write me in a config file await t.expect(Selector('#demo-simple-select').visible).notOk(); //Todo check for feedback
.expect(Selector('#demo-simple-select').visible).notOk(); //Todo check for feedback
}); });
test('Invalid Username', async t => { test('Invalid Username', async t => {
login('Not Agent Smith', '12334')
await t await t
.login('Not Agent Smith', '12334') //Todo write me in a config file
.expect(Selector('#demo-simple-select').visible).notOk(); //Todo check for feedback .expect(Selector('#demo-simple-select').visible).notOk(); //Todo check for feedback
}); });
test('Logout', async t => { test('Logout', async t => {
login('lulu', '1233');
logout();
await t await t
.login('lulu', '1233') //Todo write me in a config file
.logout()
.expect(Selector('#username-textfield').visible).Ok(); .expect(Selector('#username-textfield').visible).Ok();
}); });
test('Installation List Loads', async t => { test('Installation List Loads', async t => {
login('lulu', '1233');
await t await t
.login('lulu', '1233') //Todo write me in a config file
.expect(Selector('').visible).Ok(); .expect(Selector('').visible).Ok();
}); });