mirror of
https://github.com/Stefan-5422/-T5-Elektrifikatsiya.git
synced 2026-09-04 08:55:59 +02:00
Merge branch 'feature-Dashboard' of https://github.com/Stefan-5422/-T5-Elektrifikatsiya into feature-Dashboard
This commit is contained in:
@@ -1,64 +1,63 @@
|
||||
<Blazorise.ThemeProvider Theme="@theme">
|
||||
<Router AppAssembly="typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Elektrifikatsiya.Layouts.MainLayout)" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<MessageProvider />
|
||||
<NotificationProvider />
|
||||
<PageProgressProvider />
|
||||
@using Blazorise.Components
|
||||
<Blazorise.ThemeProvider Theme="@theme">
|
||||
<Router AppAssembly="typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Elektrifikatsiya.Layouts.MainLayout)" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<p>Sorry, there's nothing at this address.</p>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<MessageProvider />
|
||||
<NotificationProvider />
|
||||
<PageProgressProvider />
|
||||
</Blazorise.ThemeProvider>
|
||||
@code {
|
||||
private Theme theme = new()
|
||||
{
|
||||
BarOptions = new()
|
||||
{
|
||||
HorizontalHeight = "72px",
|
||||
DarkColors = new()
|
||||
{
|
||||
ItemColorOptions = new()
|
||||
{
|
||||
ActiveBackgroundColor = "#1a237e",
|
||||
ActiveColor = "#1a237e",
|
||||
HoverColor = "#1a237e",
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
ColorOptions = new()
|
||||
{
|
||||
Primary = "#1a237e",
|
||||
Secondary = "#A65529",
|
||||
Success = "#23C02E",
|
||||
Info = "#9BD8FE",
|
||||
Warning = "#F8B86C",
|
||||
Danger = "#F95741",
|
||||
Light = "#F0F0F0",
|
||||
Dark = "#535353",
|
||||
},
|
||||
BackgroundOptions = new()
|
||||
{
|
||||
Primary = "#1a237e",
|
||||
Secondary = "#A65529",
|
||||
Success = "#23C02E",
|
||||
Info = "#9BD8FE",
|
||||
Warning = "#F8B86C",
|
||||
Danger = "#F95741",
|
||||
Light = "#F0F0F0",
|
||||
Dark = "#535353",
|
||||
},
|
||||
InputOptions = new()
|
||||
{
|
||||
CheckColor = "#1a237e",
|
||||
},
|
||||
TextColorOptions = new()
|
||||
{
|
||||
Dark = "#000000",
|
||||
Primary = "#1a237e"
|
||||
}
|
||||
};
|
||||
private Theme theme = new()
|
||||
{
|
||||
BarOptions = new()
|
||||
{
|
||||
HorizontalHeight = "72px",
|
||||
DarkColors = new()
|
||||
{
|
||||
ItemColorOptions = new()
|
||||
{
|
||||
ActiveBackgroundColor = "#1a237e",
|
||||
ActiveColor = "#1a237e",
|
||||
HoverColor = "#1a237e",
|
||||
}
|
||||
}
|
||||
},
|
||||
ColorOptions = new()
|
||||
{
|
||||
Primary = "#1a237e",
|
||||
Secondary = "#A65529",
|
||||
Success = "#23C02E",
|
||||
Info = "#9BD8FE",
|
||||
Warning = "#F8B86C",
|
||||
Danger = "#F95741",
|
||||
Light = "#F0F0F0",
|
||||
Dark = "#535353",
|
||||
},
|
||||
BackgroundOptions = new()
|
||||
{
|
||||
Primary = "#1a237e",
|
||||
Secondary = "#A65529",
|
||||
Success = "#23C02E",
|
||||
Info = "#9BD8FE",
|
||||
Warning = "#F8B86C",
|
||||
Danger = "#F95741",
|
||||
Light = "#F0F0F0",
|
||||
Dark = "#535353",
|
||||
},
|
||||
InputOptions = new()
|
||||
{
|
||||
CheckColor = "#1a237e",
|
||||
},
|
||||
TextColorOptions = new()
|
||||
{
|
||||
Dark = "#000000",
|
||||
Primary = "#1a237e"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,5 +6,9 @@ namespace Elektrifikatsiya.Database
|
||||
public class DeviceManagmentDatabaseContext : DbContext
|
||||
{
|
||||
public DbSet<Device> Devices { get; set; }
|
||||
|
||||
public DeviceManagmentDatabaseContext(DbContextOptions<DeviceManagmentDatabaseContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,22 @@
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Middleware\**" />
|
||||
<Compile Remove="Model\**" />
|
||||
<Content Remove="Middleware\**" />
|
||||
<Content Remove="Model\**" />
|
||||
<EmbeddedResource Remove="Middleware\**" />
|
||||
<EmbeddedResource Remove="Model\**" />
|
||||
<None Remove="Middleware\**" />
|
||||
<None Remove="Model\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Models\NewFile.txt" />
|
||||
<None Remove="Models\ShellyResponse" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blazorise" Version="1.2.0" />
|
||||
<PackageReference Include="Blazorise.Charts" Version="1.2.0" />
|
||||
@@ -28,11 +44,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Middleware\" />
|
||||
<Folder Include="Model\" />
|
||||
<PackageReference Include="Tmds.MDns" Version="0.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
@using System.Diagnostics;
|
||||
@inherits LayoutComponentBase
|
||||
<Image Source="logo.png" Width="Width.Is50" Height="Height.Is50"> </Image>
|
||||
<Card Width="Width.Is50" Position="Position.Absolute.Top.Is50.Start.Is50.Translate.Middle" Padding="Padding.Is5">
|
||||
<Field Width="Width.Is25" Position="Position.Relative.Top.Is50.Start.Is50.Translate.Middle">
|
||||
<TextEdit Placeholder="Username" />
|
||||
</Field>
|
||||
<Field Width="Width.Is25" Position="Position.Relative.Top.Is50.Start.Is50.Translate.Middle">
|
||||
<TextEdit Role="TextRole.Password" Placeholder="Password" />
|
||||
</Field>
|
||||
<Button Width="Width.Is25" Color="Color.Primary" Clicked="Clicked" Position="Position.Relative.Bottom.Is50.Start.Is50.Translate.MiddleX" >Login</Button>
|
||||
</Card>
|
||||
@code {
|
||||
private void Clicked()
|
||||
{
|
||||
Debug.WriteLine("Button was clicked!");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
@using Elektrifikatsiya.Components.Layout
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
|
||||
|
||||
<Layout Sider>
|
||||
<LayoutSider>
|
||||
<LayoutSiderContent>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Net;
|
||||
|
||||
namespace Elektrifikatsiya.Models;
|
||||
@@ -6,21 +7,30 @@ namespace Elektrifikatsiya.Models;
|
||||
public class Device
|
||||
{
|
||||
[Key]
|
||||
public string Key { get; private set; }
|
||||
public string MacAddress { get; private set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
public IPAddress Address { get; private set; }
|
||||
public IPAddress IpAddress { get; set; }
|
||||
|
||||
[Required]
|
||||
public User User { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public int PowerUsage { get; set; }
|
||||
public string User { get; set; }
|
||||
|
||||
public string Room { get; set; }
|
||||
|
||||
public Device(string key, string name, IPAddress address, int powerUsage, string user, string room)
|
||||
[NotMapped]
|
||||
public bool Available { get; set; }
|
||||
|
||||
public Device(string macAddress, string name, IPAddress address, User user, string room)
|
||||
{
|
||||
Key = key;
|
||||
MacAddress = macAddress;
|
||||
Name = name;
|
||||
Address = address;
|
||||
PowerUsage = powerUsage;
|
||||
IpAddress = address;
|
||||
User = user;
|
||||
Room = room;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Elektrifikatsiya.Models;
|
||||
|
||||
public class DeviceStatusChagedEventArgs : EventArgs
|
||||
{
|
||||
public string MacAddress { get; set; }
|
||||
|
||||
public DeviceStatusChagedEventArgs(string macAddress)
|
||||
{
|
||||
MacAddress = macAddress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Elektrifikatsiya.Models;
|
||||
|
||||
public enum Status
|
||||
{
|
||||
Success,
|
||||
Error
|
||||
}
|
||||
|
||||
public enum ResultType
|
||||
{
|
||||
Matrix,
|
||||
Vector,
|
||||
Scalar,
|
||||
String
|
||||
}
|
||||
|
||||
public class PrometheusQueryResult
|
||||
{
|
||||
public Status Status { get; set; }
|
||||
|
||||
public string? ErrorType { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<string>? Warnings { get; set; }
|
||||
|
||||
public PrometheusQueryResult(Status status, string? errorType, string? error, List<string>? warnings)
|
||||
{
|
||||
Status = status;
|
||||
ErrorType = errorType;
|
||||
Error = error;
|
||||
Warnings = warnings;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrometheusDataWrapper
|
||||
{
|
||||
public ResultType ResultType { get; set; }
|
||||
public List<PrometheusData> Result { get; set; }
|
||||
|
||||
public PrometheusDataWrapper(ResultType resultType, List<PrometheusData> result)
|
||||
{
|
||||
ResultType = resultType;
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrometheusDataMetric
|
||||
{
|
||||
[JsonPropertyName("__name__")] public string Name { get; set; }
|
||||
|
||||
public string Job { get; set; }
|
||||
public string Instance { get; set; }
|
||||
|
||||
public PrometheusDataMetric(string name, string job, string instance)
|
||||
{
|
||||
Name = name;
|
||||
Job = job;
|
||||
Instance = instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrometheusData
|
||||
{
|
||||
public PrometheusDataMetric Metric { get; set; }
|
||||
public object Value { get; set; }
|
||||
|
||||
public PrometheusData(PrometheusDataMetric metric, object value)
|
||||
{
|
||||
Metric = metric;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Elektrifikatsiya.Models;
|
||||
|
||||
public class ShellyResponse
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Mac { get; set; }
|
||||
|
||||
public ShellyResponse(string type, string mac)
|
||||
{
|
||||
Type = type;
|
||||
Mac = mac;
|
||||
}
|
||||
}
|
||||
@@ -3,88 +3,90 @@
|
||||
@using Elektrifikatsiya.Models;
|
||||
@inject IVersionProvider VersionProvider
|
||||
|
||||
<Row Padding="Padding.Is3">
|
||||
<Div Display="Display.Flex.Row.OnDesktop" Margin="Margin.Is3.FromBottom" Width="Width.Is100">
|
||||
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
|
||||
<Bar Breakpoint="Breakpoint.Desktop"
|
||||
Background="Background.White"
|
||||
ThemeContrast="ThemeContrast.None"
|
||||
Shadow="Shadow.Remove"
|
||||
Border="Border.Is1.RoundedTop">
|
||||
<BarBrand>
|
||||
Plugs
|
||||
</BarBrand>
|
||||
</Bar>
|
||||
<ListView TItem="Device"
|
||||
Data="@plugs"
|
||||
TextField="@((_)=>(""))"
|
||||
ValueField="@((_)=>(""))"
|
||||
Mode="ListGroupMode.Static"
|
||||
Style="border-radius: 0px 0px">
|
||||
<ItemTemplate>
|
||||
<ListGroupItem Border="Border.OnBottom">
|
||||
<Row>
|
||||
<Column ColumnSize="ColumnSize.Is8">
|
||||
<Row>
|
||||
<Column ColumnSize="ColumnSize.Is7">
|
||||
<Heading Size="HeadingSize.Is6" Margin="Margin.Is1.FromBottom">@context.Item.Name</Heading>
|
||||
</Column>
|
||||
</Row>
|
||||
<Row>
|
||||
<Column>
|
||||
<Small>User: @context.Item.User</Small>
|
||||
<br />
|
||||
<Small>Room: @context.Item.Room</Small>
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is5">
|
||||
<Paragraph Margin="Margin.Is1.OnY">@context.Item.PowerUsage W</Paragraph>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is4">
|
||||
<Button Position="Position.Absolute.Top.Is50.Start.Is50.Translate.Middle" Color="Color.Dark" Outline>
|
||||
<BarIcon IconName="IconName.Bolt" />
|
||||
</Button>
|
||||
</Column>
|
||||
</Row>
|
||||
</ListGroupItem>
|
||||
</ItemTemplate>
|
||||
</ListView>
|
||||
<Div>
|
||||
<Row Padding="Padding.Is3">
|
||||
<Div Display="Display.Flex.Row.OnDesktop" Margin=" Margin.Is3.FromBottom" Width="Width.Is100">
|
||||
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
|
||||
<Bar Breakpoint="Breakpoint.Desktop"
|
||||
Background="Background.White"
|
||||
ThemeContrast="ThemeContrast.None"
|
||||
Shadow="Shadow.Remove"
|
||||
Border="Border.Is1.RoundedTop">
|
||||
<BarBrand>
|
||||
Plugs
|
||||
</BarBrand>
|
||||
</Bar>
|
||||
<ListView TItem="Device"
|
||||
Data="@plugs"
|
||||
TextField="@((_)=>(""))"
|
||||
ValueField="@((_)=>(""))"
|
||||
Mode="ListGroupMode.Static"
|
||||
Style="border-radius: 0px 0px">
|
||||
<ItemTemplate >
|
||||
<ListGroupItem>
|
||||
<Row>
|
||||
<Column ColumnSize="ColumnSize.Is8">
|
||||
<Row>
|
||||
<Column ColumnSize="ColumnSize.Is7">
|
||||
<Heading Size="HeadingSize.Is6" Margin="Margin.Is1.FromBottom">@context.Item.Name</Heading>
|
||||
</Column>
|
||||
</Row>
|
||||
<Row>
|
||||
<Column>
|
||||
<Small>User: @context.Item.User</Small>
|
||||
<br />
|
||||
<Small>Room: @context.Item.Room</Small>
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is5">
|
||||
<Paragraph Margin="Margin.Is1.OnY">@context.Item.PowerUsage W</Paragraph>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is4">
|
||||
<Button Position="Position.Absolute.Top.Is50.Start.Is50.Translate.Middle" Color="Color.Dark" Outline>
|
||||
<BarIcon IconName="IconName.Bolt" />
|
||||
</Button>
|
||||
</Column>
|
||||
</Row>
|
||||
</ListGroupItem>
|
||||
</ItemTemplate>
|
||||
</ListView>
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
|
||||
<Bar Breakpoint="Breakpoint.Desktop"
|
||||
Background="Background.White"
|
||||
ThemeContrast="ThemeContrast.None"
|
||||
Shadow="Shadow.Remove"
|
||||
Border="Border.Is1.Rounded.RoundedTop">
|
||||
<BarBrand>
|
||||
Logs
|
||||
</BarBrand>
|
||||
</Bar>
|
||||
<ListView TItem="Event"
|
||||
Data="@events"
|
||||
TextField="@((_)=>(""))"
|
||||
ValueField="@((_)=>(""))"
|
||||
Mode="ListGroupMode.Static"
|
||||
Style="border-radius: 0px 0px">
|
||||
<ItemTemplate>
|
||||
<Div Flex="Flex.InlineFlex.JustifyContent.Between" Width="Width.Is100">
|
||||
<Heading Size="HeadingSize.Is6" Margin="Margin.Is2.FromBottom">@context.Item.EventName</Heading>
|
||||
<Small>@context.Item.Date</Small>
|
||||
</Div>
|
||||
<Paragraph Margin="Margin.Is2.FromBottom">@context.Item.Description</Paragraph>
|
||||
</ItemTemplate>
|
||||
</ListView>
|
||||
</Column>
|
||||
</Div>
|
||||
</Row>
|
||||
<Row Padding="Padding.Is3">
|
||||
<Column>
|
||||
<Button Color="Color.Primary" Style="border-radius: 0px" Clicked="@(async () => await HandleRedraw())">Aktualisieren</Button>
|
||||
<LineChart @ref="lineChart" TItem="double" />
|
||||
</Column>
|
||||
<Column ColumnSize="ColumnSize.Is6">
|
||||
<Bar Breakpoint="Breakpoint.Desktop"
|
||||
Background="Background.White"
|
||||
ThemeContrast="ThemeContrast.None"
|
||||
Shadow="Shadow.Remove"
|
||||
Border="Border.Is1.Rounded.RoundedTop">
|
||||
<BarBrand>
|
||||
Plugs
|
||||
</BarBrand>
|
||||
</Bar>
|
||||
<ListView TItem="Event"
|
||||
Data="@events"
|
||||
TextField="@((_)=>(""))"
|
||||
ValueField="@((_)=>(""))"
|
||||
Mode="ListGroupMode.Static"
|
||||
Style="border-radius: 0px 0px">
|
||||
</Row>
|
||||
</Div>
|
||||
|
||||
<ItemTemplate>
|
||||
<Div Flex="Flex.InlineFlex.JustifyContent.Between" Width="Width.Is100">
|
||||
<Heading Size="HeadingSize.Is6" Margin="Margin.Is2.FromBottom">@context.Item.EventName</Heading>
|
||||
<Small>@context.Item.Date</Small>
|
||||
</Div>
|
||||
<Paragraph Border="Border.OnBottom" Margin="Margin.Is2.FromBottom">@context.Item.Description</Paragraph>
|
||||
</ItemTemplate>
|
||||
</ListView>
|
||||
</Column>
|
||||
</Div>
|
||||
</Row>
|
||||
<Row Padding="Padding.Is3">
|
||||
<Column>
|
||||
<Button Color="Color.Primary" Style="border-radius: 0px" Clicked="@(async () => await HandleRedraw())">Aktualisieren</Button>
|
||||
<LineChart @ref="lineChart" TItem="double" />
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
@code {
|
||||
//TODO: insert new event here if plug produces one
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@using System.Diagnostics;
|
||||
@using Elektrifikatsiya.Layouts;
|
||||
@page "/login"
|
||||
@layout LoginLayout;
|
||||
@@ -4,6 +4,8 @@ using Blazorise.Icons.Material;
|
||||
using Blazorise.Material;
|
||||
|
||||
using Elektrifikatsiya.Database;
|
||||
using Elektrifikatsiya.Services;
|
||||
using Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -12,7 +14,16 @@ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddHostedService<UpdateService>();
|
||||
builder.Services.AddSingleton<IDeviceStatusService, DeviceStatusService>();
|
||||
builder.Services.AddTransient<IDeviceManagmentService, DeviceManagmentService>();
|
||||
builder.Services.AddDbContext<UserDatabaseContext>(options => options.UseSqlite("Data Source=./UserDatabase.sqlite"));
|
||||
builder.Services.AddDbContext<DeviceManagmentDatabaseContext>((options) => options.UseSqlite("Data Source=./DeviceManagement.sqlite"));
|
||||
builder.Services.AddBootstrapProviders();
|
||||
builder.Services.AddBlazorise(options =>
|
||||
{
|
||||
options.Immediate = true;
|
||||
});
|
||||
|
||||
AddBlazorise(builder.Services);
|
||||
|
||||
@@ -37,19 +48,16 @@ app.MapFallbackToPage("/_Host");
|
||||
IServiceScope serviceScope = app.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||
serviceScope.ServiceProvider.GetRequiredService<UserDatabaseContext>().Database.EnsureCreated();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
AsyncServiceScope scope = app.Services.CreateAsyncScope();
|
||||
scope.ServiceProvider.GetRequiredService<DeviceManagmentDatabaseContext>().Database.EnsureCreated();
|
||||
|
||||
app.Run();
|
||||
|
||||
void AddBlazorise(IServiceCollection services)
|
||||
{
|
||||
_ = services
|
||||
.AddBlazorise();
|
||||
_ = services
|
||||
.AddMaterialProviders()
|
||||
.AddMaterialIcons();
|
||||
_ = services.AddBlazorise();
|
||||
_ = services.AddMaterialProviders();
|
||||
_ = services.AddMaterialIcons();
|
||||
}
|
||||
builder.Services
|
||||
.AddBlazorise(options =>
|
||||
{
|
||||
options.Immediate = true;
|
||||
})
|
||||
.AddBootstrapProviders();
|
||||
@@ -15,7 +15,9 @@ public interface IAuthenticationService
|
||||
/// <summary>
|
||||
/// Registers a new user.
|
||||
/// </summary>
|
||||
/// <param name="email">The E-mail address of the user.</param>
|
||||
/// <param name="name">The name address of the user.</param>
|
||||
/// <param name="password">The password of the user.</param>
|
||||
/// <param name="role">The default role of the user.</param>
|
||||
/// <returns>A <see cref="Task"/> to <see langword="await"/>.</returns>
|
||||
|
||||
Task<Result> RegisterUserAsync(string name, string password, Role role);
|
||||
@@ -23,7 +25,8 @@ public interface IAuthenticationService
|
||||
/// <summary>
|
||||
/// Logs a user in.
|
||||
/// </summary>
|
||||
/// <param name="name">The E-mail address of the user.</param>
|
||||
/// <param name="name">The name of the user.</param>
|
||||
/// <param name="password">The password of the user.</param>
|
||||
/// <returns>A <see cref="Task"/> to <see langword="await"/>.</returns>
|
||||
Task<Result> LoginUserAsync(string name, string password);
|
||||
|
||||
@@ -36,14 +39,13 @@ public interface IAuthenticationService
|
||||
/// <summary>
|
||||
/// Deletes the current user.
|
||||
/// </summary>
|
||||
/// <param name="deletionToken">The deletion token.</param>
|
||||
/// <returns>A <see cref="Task"/> to <see langword="await"/> and a <see cref="bool"/> that indicates if the operation was successful.</returns>
|
||||
/// <returns>A <see cref="Task"/> to <see langword="await"/>.</returns>
|
||||
Task<Result> DeleteUserAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user exists.
|
||||
/// </summary>
|
||||
/// <param name="name">The E-mail address of the user.</param>
|
||||
/// <param name="name">The name address of the user.</param>
|
||||
/// <returns>A <see cref="Task"/> to <see langword="await"/> and a <see cref="bool"/> that indicates if the user exists.</returns>
|
||||
Task<Result<bool>> UserExistsAsync(string name);
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Elektrifikatsiya.Models;
|
||||
|
||||
using FluentResults;
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace Elektrifikatsiya.Services;
|
||||
|
||||
public interface IDeviceManagmentService
|
||||
{
|
||||
public Task<Result<Device>> Register(IPAddress ip, User user, string? name = null, string room = "default");
|
||||
|
||||
public Task<Result> Unregister(string macAdress);
|
||||
|
||||
public Result<Device> GetDevice(string macAdress);
|
||||
|
||||
public Result<List<Device>> GetDevices();
|
||||
|
||||
public Result<List<Device>> GetDevicesInRoom(string room);
|
||||
|
||||
public Result<List<Device>> GetDevicesOfUser(int userId);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Elektrifikatsiya.Models;
|
||||
|
||||
using FluentResults;
|
||||
|
||||
namespace Elektrifikatsiya.Services;
|
||||
|
||||
public interface IDeviceStatusService
|
||||
{
|
||||
public event EventHandler<DeviceStatusChagedEventArgs> OnDeviceStatusChanged;
|
||||
|
||||
public Result<List<Device>> GetDevices();
|
||||
|
||||
public Result UpdateDeviceStatus(Device device);
|
||||
|
||||
public Result TrackDevice(Device device);
|
||||
|
||||
public Result UntrackDevice(string macAddress);
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
using Elektrifikatsiya.Database;
|
||||
using Elektrifikatsiya.Models;
|
||||
|
||||
using FluentResults;
|
||||
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
public class DeviceManagmentService : IDeviceManagmentService
|
||||
{
|
||||
private readonly DeviceManagmentDatabaseContext deviceManagmentDatabaseContext;
|
||||
private readonly IDeviceStatusService deviceStatusService;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public DeviceManagmentService(DeviceManagmentDatabaseContext deviceManagmentDatabaseContext, IDeviceStatusService deviceStatusService, HttpClient httpClient)
|
||||
{
|
||||
this.deviceManagmentDatabaseContext = deviceManagmentDatabaseContext;
|
||||
this.deviceStatusService = deviceStatusService;
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Result<Device> GetDevice(string macAdress)
|
||||
{
|
||||
Result<List<Device>> getDevicesResult = deviceStatusService.GetDevices();
|
||||
|
||||
if (getDevicesResult.IsFailed)
|
||||
{
|
||||
return getDevicesResult.ToResult();
|
||||
}
|
||||
|
||||
Device? device = getDevicesResult.Value.FirstOrDefault(d => d.MacAddress == macAdress);
|
||||
|
||||
if (device is null)
|
||||
{
|
||||
return Result.Fail("Device does not exist!");
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public Result<List<Device>> GetDevices()
|
||||
{
|
||||
Result<List<Device>> getDevicesResult = deviceStatusService.GetDevices();
|
||||
|
||||
if (getDevicesResult.IsFailed)
|
||||
{
|
||||
return getDevicesResult.ToResult();
|
||||
}
|
||||
|
||||
return getDevicesResult.Value.ToList();
|
||||
}
|
||||
|
||||
public Result<List<Device>> GetDevicesInRoom(string room)
|
||||
{
|
||||
Result<List<Device>> getDevicesResult = deviceStatusService.GetDevices();
|
||||
|
||||
if (getDevicesResult.IsFailed)
|
||||
{
|
||||
return getDevicesResult.ToResult();
|
||||
}
|
||||
|
||||
return getDevicesResult.Value.Where(d => d.Room == room).ToList();
|
||||
}
|
||||
|
||||
public Result<List<Device>> GetDevicesOfUser(int userId)
|
||||
{
|
||||
Result<List<Device>> getDevicesResult = deviceStatusService.GetDevices();
|
||||
|
||||
if (getDevicesResult.IsFailed)
|
||||
{
|
||||
return getDevicesResult.ToResult();
|
||||
}
|
||||
|
||||
return getDevicesResult.Value.Where(d => d.User.Id == userId).ToList();
|
||||
}
|
||||
|
||||
public async Task<Result<Device>> Register(IPAddress ip, User user, string? name = null, string room = "default")
|
||||
{
|
||||
ShellyResponse? shellyResponse = await httpClient.GetFromJsonAsync<ShellyResponse>($"{ip}/shelly");
|
||||
|
||||
if(shellyResponse is null || shellyResponse.Type != "SHPLG-S")
|
||||
{
|
||||
return Result.Fail("Device is not a \"SHPLG-S\" or not reachable!");
|
||||
}
|
||||
|
||||
string mac = shellyResponse.Mac;
|
||||
|
||||
if(PhysicalAddress.TryParse(mac, out _))
|
||||
{
|
||||
return Result.Fail("Invalid mac address!");
|
||||
}
|
||||
|
||||
Device device = new Device(mac, name ?? mac, ip, user, room);
|
||||
|
||||
deviceManagmentDatabaseContext.Add(device);
|
||||
Result saveDatabaseChangesResult = await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync());
|
||||
|
||||
Result trackDeviceResult = deviceStatusService.TrackDevice(device);
|
||||
|
||||
return Result.Merge(saveDatabaseChangesResult, trackDeviceResult).ToResult(device);
|
||||
}
|
||||
|
||||
public async Task<Result> Unregister(string macAdress)
|
||||
{
|
||||
Result<Device> getDeviceResult = GetDevice(macAdress);
|
||||
|
||||
if (getDeviceResult.IsFailed)
|
||||
{
|
||||
return getDeviceResult.ToResult();
|
||||
}
|
||||
|
||||
Result untrackDeviceResult = deviceStatusService.UntrackDevice(macAdress);
|
||||
|
||||
if (untrackDeviceResult.IsFailed)
|
||||
{
|
||||
return untrackDeviceResult;
|
||||
}
|
||||
|
||||
deviceManagmentDatabaseContext.Remove(getDeviceResult.Value);
|
||||
|
||||
return await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Elektrifikatsiya.Models;
|
||||
|
||||
using FluentResults;
|
||||
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
public class DeviceStatusService : IDeviceStatusService
|
||||
{
|
||||
public event EventHandler<DeviceStatusChagedEventArgs>? OnDeviceStatusChanged;
|
||||
|
||||
private readonly Dictionary<string, Device> devices = new();
|
||||
|
||||
public Result<List<Device>> GetDevices()
|
||||
{
|
||||
return devices.Values.ToList();
|
||||
}
|
||||
|
||||
public Result UpdateDeviceStatus(Device device)
|
||||
{
|
||||
Device? modDevice = devices.GetValueOrDefault(device.MacAddress);
|
||||
|
||||
if (modDevice is null)
|
||||
{
|
||||
return Result.Fail("Device not tracked!");
|
||||
}
|
||||
|
||||
modDevice.PowerUsage = device.PowerUsage;
|
||||
modDevice.Available = device.Available;
|
||||
modDevice.IpAddress = device.IpAddress;
|
||||
modDevice.Name = device.Name;
|
||||
|
||||
OnDeviceStatusChanged?.Invoke(this, new DeviceStatusChagedEventArgs(device.MacAddress));
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
public Result TrackDevice(Device device)
|
||||
{
|
||||
if (devices.ContainsKey(device.MacAddress))
|
||||
{
|
||||
return Result.Fail("Device already tracked!");
|
||||
}
|
||||
|
||||
devices[device.MacAddress] = device;
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
public Result UntrackDevice(string macAddress)
|
||||
{
|
||||
return devices.Remove(macAddress) ? Result.Ok() : Result.Fail("Device is not tracked!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Elektrifikatsiya.Database;
|
||||
using Elektrifikatsiya.Models;
|
||||
using FluentResults;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
public class UpdateService : IHostedService, IDisposable
|
||||
{
|
||||
private readonly ILogger<UpdateService> logger;
|
||||
private readonly IDeviceStatusService deviceStatusService;
|
||||
private readonly DeviceManagmentDatabaseContext deviceManagmentDatabaseContext;
|
||||
private Timer? timer = null;
|
||||
|
||||
public UpdateService(ILogger<UpdateService> logger, IDeviceStatusService deviceStatusService, DeviceManagmentDatabaseContext deviceManagmentDatabaseContext)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.deviceStatusService = deviceStatusService;
|
||||
this.deviceManagmentDatabaseContext = deviceManagmentDatabaseContext;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogInformation("Starting update service...");
|
||||
|
||||
foreach (Device device in await deviceManagmentDatabaseContext.Devices.AsNoTracking().ToListAsync(cancellationToken))
|
||||
{
|
||||
_ = deviceStatusService.TrackDevice(device);
|
||||
}
|
||||
|
||||
timer = new Timer(async (_) => await Update(), null, TimeSpan.Zero, TimeSpan.FromSeconds(15));
|
||||
|
||||
logger.LogInformation("Update service started.");
|
||||
}
|
||||
|
||||
private async Task Update()
|
||||
{
|
||||
Result<List<Device>> getDeviceStatusResult = deviceStatusService.GetDevices();
|
||||
|
||||
if (getDeviceStatusResult.IsFailed)
|
||||
{
|
||||
logger.LogError("Updating devices failed!");
|
||||
}
|
||||
|
||||
foreach(Device device in getDeviceStatusResult.Value)
|
||||
{
|
||||
//TODO: Some update magic
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogInformation("Stopping update service.");
|
||||
|
||||
_ = timer?.Change(Timeout.Infinite, 0);
|
||||
|
||||
logger.LogInformation("Stopped update service.");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
timer?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using Elektrifikatsiya.Models;
|
||||
using FluentResults;
|
||||
|
||||
namespace Elektrifikatsiya.Utilities;
|
||||
|
||||
public class PrometheusQuery
|
||||
{
|
||||
private string connectionString;
|
||||
private readonly HttpClient client = new();
|
||||
|
||||
public PrometheusQuery(string connectionString)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
client.BaseAddress = new Uri(connectionString);
|
||||
}
|
||||
|
||||
public Task<PrometheusQueryResult?> Query(string query)
|
||||
{
|
||||
return client.GetFromJsonAsync<PrometheusQueryResult>($"/v1/query?{UrlEncoder.Create().Encode(query)}");
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,24 @@
|
||||
|
||||
public static class TokenGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates secure token and uses an UID to make it unique.
|
||||
/// </summary>
|
||||
/// <param name="tokenType">The type of the token.</param>
|
||||
/// <param name="uid">The UID of a user.</param>
|
||||
/// <param name="length">The length of the token.</param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateToken(string tokenType, int uid = 0, int length = 128)
|
||||
{
|
||||
return tokenType + "-" + SecureStringGenerator.CreateCryptographicRandomString(length, uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a token is valid.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to validate.</param>
|
||||
/// <param name="tokenType">The type the token should have.</param>
|
||||
/// <returns></returns>
|
||||
public static bool ValidateToken(string? token, string tokenType)
|
||||
{
|
||||
if (token is null)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 383 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 82 KiB |
Reference in New Issue
Block a user